Linux-2.6.12-rc2

Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.

Let it rip!
This commit is contained in:
Linus Torvalds
2005-04-16 15:20:36 -07:00
commit 1da177e4c3
17291 changed files with 6718755 additions and 0 deletions

110
net/ax25/Kconfig Normal file
View File

@@ -0,0 +1,110 @@
#
# Amateur Radio protocols and AX.25 device configuration
#
# 19971130 Now in an own category to make correct compilation of the
# AX.25 stuff easier...
# Joerg Reuter DL1BKE <jreuter@yaina.de>
# 19980129 Moved to net/ax25/Config.in, sourcing device drivers.
menuconfig HAMRADIO
depends on NET
bool "Amateur Radio support"
help
If you want to connect your Linux box to an amateur radio, answer Y
here. You want to read <http://www.tapr.org/tapr/html/pkthome.html> and
the AX25-HOWTO, available from <http://www.tldp.org/docs.html#howto>.
Note that the answer to this question won't directly affect the
kernel: saying N will just cause the configurator to skip all
the questions about amateur radio.
comment "Packet Radio protocols"
depends on HAMRADIO && NET
config AX25
tristate "Amateur Radio AX.25 Level 2 protocol"
depends on HAMRADIO && NET
---help---
This is the protocol used for computer communication over amateur
radio. It is either used by itself for point-to-point links, or to
carry other protocols such as tcp/ip. To use it, you need a device
that connects your Linux box to your amateur radio. You can either
use a low speed TNC (a Terminal Node Controller acts as a kind of
modem connecting your computer's serial port to your radio's
microphone input and speaker output) supporting the KISS protocol or
one of the various SCC cards that are supported by the generic Z8530
or the DMA SCC driver. Another option are the Baycom modem serial
and parallel port hacks or the sound card modem (supported by their
own drivers). If you say Y here, you also have to say Y to one of
those drivers.
Information about where to get supporting software for Linux amateur
radio as well as information about how to configure an AX.25 port is
contained in the AX25-HOWTO, available from
<http://www.tldp.org/docs.html#howto>. You might also want to
check out the file <file:Documentation/networking/ax25.txt> in the
kernel source. More information about digital amateur radio in
general is on the WWW at
<http://www.tapr.org/tapr/html/pkthome.html>.
To compile this driver as a module, choose M here: the
module will be called ax25.
config AX25_DAMA_SLAVE
bool "AX.25 DAMA Slave support"
depends on AX25
help
DAMA is a mechanism to prevent collisions when doing AX.25
networking. A DAMA server (called "master") accepts incoming traffic
from clients (called "slaves") and redistributes it to other slaves.
If you say Y here, your Linux box will act as a DAMA slave; this is
transparent in that you don't have to do any special DAMA
configuration. (Linux cannot yet act as a DAMA server.) If unsure,
say N.
# bool ' AX.25 DAMA Master support' CONFIG_AX25_DAMA_MASTER
config NETROM
tristate "Amateur Radio NET/ROM protocol"
depends on AX25
---help---
NET/ROM is a network layer protocol on top of AX.25 useful for
routing.
A comprehensive listing of all the software for Linux amateur radio
users as well as information about how to configure an AX.25 port is
contained in the AX25-HOWTO, available from
<http://www.tldp.org/docs.html#howto>. You also might want to
check out the file <file:Documentation/networking/ax25.txt>. More
information about digital amateur radio in general is on the WWW at
<http://www.tapr.org/tapr/html/pkthome.html>.
To compile this driver as a module, choose M here: the
module will be called netrom.
config ROSE
tristate "Amateur Radio X.25 PLP (Rose)"
depends on AX25
---help---
The Packet Layer Protocol (PLP) is a way to route packets over X.25
connections in general and amateur radio AX.25 connections in
particular, essentially an alternative to NET/ROM.
A comprehensive listing of all the software for Linux amateur radio
users as well as information about how to configure an AX.25 port is
contained in the AX25-HOWTO, available from
<http://www.tldp.org/docs.html#howto>. You also might want to
check out the file <file:Documentation/networking/ax25.txt>. More
information about digital amateur radio in general is on the WWW at
<http://www.tapr.org/tapr/html/pkthome.html>.
To compile this driver as a module, choose M here: the
module will be called rose.
menu "AX.25 network device drivers"
depends on HAMRADIO && NET && AX25!=n
source "drivers/net/hamradio/Kconfig"
endmenu

11
net/ax25/Makefile Normal file
View File

@@ -0,0 +1,11 @@
#
# Makefile for the Linux AX.25 layer.
#
obj-$(CONFIG_AX25) += ax25.o
ax25-y := ax25_addr.o ax25_dev.o ax25_iface.o ax25_in.o ax25_ip.o ax25_out.o \
ax25_route.o ax25_std_in.o ax25_std_subr.o ax25_std_timer.o \
ax25_subr.o ax25_timer.o ax25_uid.o af_ax25.o
ax25-$(CONFIG_AX25_DAMA_SLAVE) += ax25_ds_in.o ax25_ds_subr.o ax25_ds_timer.o
ax25-$(CONFIG_SYSCTL) += sysctl_net_ax25.o

24
net/ax25/TODO Normal file
View File

@@ -0,0 +1,24 @@
Do the ax25_list_lock, ax25_dev_lock, linkfail_lockreally, ax25_frag_lock and
listen_lock have to be bh-safe?
Do the netrom and rose locks have to be bh-safe?
A device might be deleted after lookup in the SIOCADDRT ioctl but before it's
being used.
Routes to a device being taken down might be deleted by ax25_rt_device_down
but added by somebody else before the device has been deleted fully.
Massive amounts of lock_kernel / unlock_kernel are just a temporary solution to
get around the removal of SOCKOPS_WRAP. A serious locking strategy has to be
implemented.
The ax25_rt_find_route synopsys is pervert but I somehow had to deal with
the race caused by the static variable in it's previous implementation.
Implement proper socket locking in netrom and rose.
Check socket locking when ax25_rcv is sending to raw sockets. In particular
ax25_send_to_raw() seems fishy. Heck - ax25_rcv is fishy.
Handle XID and TEST frames properly.

2050
net/ax25/af_ax25.c Normal file

File diff suppressed because it is too large Load Diff

290
net/ax25/ax25_addr.c Normal file
View File

@@ -0,0 +1,290 @@
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk)
*/
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/socket.h>
#include <linux/in.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/timer.h>
#include <linux/string.h>
#include <linux/sockios.h>
#include <linux/net.h>
#include <net/ax25.h>
#include <linux/inet.h>
#include <linux/netdevice.h>
#include <linux/skbuff.h>
#include <net/sock.h>
#include <asm/uaccess.h>
#include <asm/system.h>
#include <linux/fcntl.h>
#include <linux/mm.h>
#include <linux/interrupt.h>
/*
* The null address is defined as a callsign of all spaces with an
* SSID of zero.
*/
ax25_address null_ax25_address = {{0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00}};
/*
* ax25 -> ascii conversion
*/
char *ax2asc(ax25_address *a)
{
static char buf[11];
char c, *s;
int n;
for (n = 0, s = buf; n < 6; n++) {
c = (a->ax25_call[n] >> 1) & 0x7F;
if (c != ' ') *s++ = c;
}
*s++ = '-';
if ((n = ((a->ax25_call[6] >> 1) & 0x0F)) > 9) {
*s++ = '1';
n -= 10;
}
*s++ = n + '0';
*s++ = '\0';
if (*buf == '\0' || *buf == '-')
return "*";
return buf;
}
/*
* ascii -> ax25 conversion
*/
ax25_address *asc2ax(char *callsign)
{
static ax25_address addr;
char *s;
int n;
for (s = callsign, n = 0; n < 6; n++) {
if (*s != '\0' && *s != '-')
addr.ax25_call[n] = *s++;
else
addr.ax25_call[n] = ' ';
addr.ax25_call[n] <<= 1;
addr.ax25_call[n] &= 0xFE;
}
if (*s++ == '\0') {
addr.ax25_call[6] = 0x00;
return &addr;
}
addr.ax25_call[6] = *s++ - '0';
if (*s != '\0') {
addr.ax25_call[6] *= 10;
addr.ax25_call[6] += *s++ - '0';
}
addr.ax25_call[6] <<= 1;
addr.ax25_call[6] &= 0x1E;
return &addr;
}
/*
* Compare two ax.25 addresses
*/
int ax25cmp(ax25_address *a, ax25_address *b)
{
int ct = 0;
while (ct < 6) {
if ((a->ax25_call[ct] & 0xFE) != (b->ax25_call[ct] & 0xFE)) /* Clean off repeater bits */
return 1;
ct++;
}
if ((a->ax25_call[ct] & 0x1E) == (b->ax25_call[ct] & 0x1E)) /* SSID without control bit */
return 0;
return 2; /* Partial match */
}
/*
* Compare two AX.25 digipeater paths.
*/
int ax25digicmp(ax25_digi *digi1, ax25_digi *digi2)
{
int i;
if (digi1->ndigi != digi2->ndigi)
return 1;
if (digi1->lastrepeat != digi2->lastrepeat)
return 1;
for (i = 0; i < digi1->ndigi; i++)
if (ax25cmp(&digi1->calls[i], &digi2->calls[i]) != 0)
return 1;
return 0;
}
/*
* Given an AX.25 address pull of to, from, digi list, command/response and the start of data
*
*/
unsigned char *ax25_addr_parse(unsigned char *buf, int len, ax25_address *src, ax25_address *dest, ax25_digi *digi, int *flags, int *dama)
{
int d = 0;
if (len < 14) return NULL;
if (flags != NULL) {
*flags = 0;
if (buf[6] & AX25_CBIT)
*flags = AX25_COMMAND;
if (buf[13] & AX25_CBIT)
*flags = AX25_RESPONSE;
}
if (dama != NULL)
*dama = ~buf[13] & AX25_DAMA_FLAG;
/* Copy to, from */
if (dest != NULL)
memcpy(dest, buf + 0, AX25_ADDR_LEN);
if (src != NULL)
memcpy(src, buf + 7, AX25_ADDR_LEN);
buf += 2 * AX25_ADDR_LEN;
len -= 2 * AX25_ADDR_LEN;
digi->lastrepeat = -1;
digi->ndigi = 0;
while (!(buf[-1] & AX25_EBIT)) {
if (d >= AX25_MAX_DIGIS) return NULL; /* Max of 6 digis */
if (len < 7) return NULL; /* Short packet */
memcpy(&digi->calls[d], buf, AX25_ADDR_LEN);
digi->ndigi = d + 1;
if (buf[6] & AX25_HBIT) {
digi->repeated[d] = 1;
digi->lastrepeat = d;
} else {
digi->repeated[d] = 0;
}
buf += AX25_ADDR_LEN;
len -= AX25_ADDR_LEN;
d++;
}
return buf;
}
/*
* Assemble an AX.25 header from the bits
*/
int ax25_addr_build(unsigned char *buf, ax25_address *src, ax25_address *dest, ax25_digi *d, int flag, int modulus)
{
int len = 0;
int ct = 0;
memcpy(buf, dest, AX25_ADDR_LEN);
buf[6] &= ~(AX25_EBIT | AX25_CBIT);
buf[6] |= AX25_SSSID_SPARE;
if (flag == AX25_COMMAND) buf[6] |= AX25_CBIT;
buf += AX25_ADDR_LEN;
len += AX25_ADDR_LEN;
memcpy(buf, src, AX25_ADDR_LEN);
buf[6] &= ~(AX25_EBIT | AX25_CBIT);
buf[6] &= ~AX25_SSSID_SPARE;
if (modulus == AX25_MODULUS)
buf[6] |= AX25_SSSID_SPARE;
else
buf[6] |= AX25_ESSID_SPARE;
if (flag == AX25_RESPONSE) buf[6] |= AX25_CBIT;
/*
* Fast path the normal digiless path
*/
if (d == NULL || d->ndigi == 0) {
buf[6] |= AX25_EBIT;
return 2 * AX25_ADDR_LEN;
}
buf += AX25_ADDR_LEN;
len += AX25_ADDR_LEN;
while (ct < d->ndigi) {
memcpy(buf, &d->calls[ct], AX25_ADDR_LEN);
if (d->repeated[ct])
buf[6] |= AX25_HBIT;
else
buf[6] &= ~AX25_HBIT;
buf[6] &= ~AX25_EBIT;
buf[6] |= AX25_SSSID_SPARE;
buf += AX25_ADDR_LEN;
len += AX25_ADDR_LEN;
ct++;
}
buf[-1] |= AX25_EBIT;
return len;
}
int ax25_addr_size(ax25_digi *dp)
{
if (dp == NULL)
return 2 * AX25_ADDR_LEN;
return AX25_ADDR_LEN * (2 + dp->ndigi);
}
/*
* Reverse Digipeat List. May not pass both parameters as same struct
*/
void ax25_digi_invert(ax25_digi *in, ax25_digi *out)
{
int ct;
out->ndigi = in->ndigi;
out->lastrepeat = in->ndigi - in->lastrepeat - 2;
/* Invert the digipeaters */
for (ct = 0; ct < in->ndigi; ct++) {
out->calls[ct] = in->calls[in->ndigi - ct - 1];
if (ct <= out->lastrepeat) {
out->calls[ct].ax25_call[6] |= AX25_HBIT;
out->repeated[ct] = 1;
} else {
out->calls[ct].ax25_call[6] &= ~AX25_HBIT;
out->repeated[ct] = 0;
}
}
}

208
net/ax25/ax25_dev.c Normal file
View File

@@ -0,0 +1,208 @@
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk)
*/
#include <linux/config.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/socket.h>
#include <linux/in.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/timer.h>
#include <linux/string.h>
#include <linux/sockios.h>
#include <linux/net.h>
#include <linux/spinlock.h>
#include <net/ax25.h>
#include <linux/inet.h>
#include <linux/netdevice.h>
#include <linux/if_arp.h>
#include <linux/skbuff.h>
#include <net/sock.h>
#include <asm/uaccess.h>
#include <asm/system.h>
#include <linux/fcntl.h>
#include <linux/mm.h>
#include <linux/interrupt.h>
#include <linux/init.h>
ax25_dev *ax25_dev_list;
DEFINE_SPINLOCK(ax25_dev_lock);
ax25_dev *ax25_addr_ax25dev(ax25_address *addr)
{
ax25_dev *ax25_dev, *res = NULL;
spin_lock_bh(&ax25_dev_lock);
for (ax25_dev = ax25_dev_list; ax25_dev != NULL; ax25_dev = ax25_dev->next)
if (ax25cmp(addr, (ax25_address *)ax25_dev->dev->dev_addr) == 0) {
res = ax25_dev;
}
spin_unlock_bh(&ax25_dev_lock);
return res;
}
/*
* This is called when an interface is brought up. These are
* reasonable defaults.
*/
void ax25_dev_device_up(struct net_device *dev)
{
ax25_dev *ax25_dev;
if ((ax25_dev = kmalloc(sizeof(*ax25_dev), GFP_ATOMIC)) == NULL) {
printk(KERN_ERR "AX.25: ax25_dev_device_up - out of memory\n");
return;
}
ax25_unregister_sysctl();
memset(ax25_dev, 0x00, sizeof(*ax25_dev));
dev->ax25_ptr = ax25_dev;
ax25_dev->dev = dev;
dev_hold(dev);
ax25_dev->forward = NULL;
ax25_dev->values[AX25_VALUES_IPDEFMODE] = AX25_DEF_IPDEFMODE;
ax25_dev->values[AX25_VALUES_AXDEFMODE] = AX25_DEF_AXDEFMODE;
ax25_dev->values[AX25_VALUES_BACKOFF] = AX25_DEF_BACKOFF;
ax25_dev->values[AX25_VALUES_CONMODE] = AX25_DEF_CONMODE;
ax25_dev->values[AX25_VALUES_WINDOW] = AX25_DEF_WINDOW;
ax25_dev->values[AX25_VALUES_EWINDOW] = AX25_DEF_EWINDOW;
ax25_dev->values[AX25_VALUES_T1] = AX25_DEF_T1;
ax25_dev->values[AX25_VALUES_T2] = AX25_DEF_T2;
ax25_dev->values[AX25_VALUES_T3] = AX25_DEF_T3;
ax25_dev->values[AX25_VALUES_IDLE] = AX25_DEF_IDLE;
ax25_dev->values[AX25_VALUES_N2] = AX25_DEF_N2;
ax25_dev->values[AX25_VALUES_PACLEN] = AX25_DEF_PACLEN;
ax25_dev->values[AX25_VALUES_PROTOCOL] = AX25_DEF_PROTOCOL;
ax25_dev->values[AX25_VALUES_DS_TIMEOUT]= AX25_DEF_DS_TIMEOUT;
#if defined(CONFIG_AX25_DAMA_SLAVE) || defined(CONFIG_AX25_DAMA_MASTER)
init_timer(&ax25_dev->dama.slave_timer);
#endif
spin_lock_bh(&ax25_dev_lock);
ax25_dev->next = ax25_dev_list;
ax25_dev_list = ax25_dev;
spin_unlock_bh(&ax25_dev_lock);
ax25_register_sysctl();
}
void ax25_dev_device_down(struct net_device *dev)
{
ax25_dev *s, *ax25_dev;
if ((ax25_dev = ax25_dev_ax25dev(dev)) == NULL)
return;
ax25_unregister_sysctl();
spin_lock_bh(&ax25_dev_lock);
#ifdef CONFIG_AX25_DAMA_SLAVE
ax25_ds_del_timer(ax25_dev);
#endif
/*
* Remove any packet forwarding that points to this device.
*/
for (s = ax25_dev_list; s != NULL; s = s->next)
if (s->forward == dev)
s->forward = NULL;
if ((s = ax25_dev_list) == ax25_dev) {
ax25_dev_list = s->next;
spin_unlock_bh(&ax25_dev_lock);
dev_put(dev);
kfree(ax25_dev);
ax25_register_sysctl();
return;
}
while (s != NULL && s->next != NULL) {
if (s->next == ax25_dev) {
s->next = ax25_dev->next;
spin_unlock_bh(&ax25_dev_lock);
dev_put(dev);
kfree(ax25_dev);
ax25_register_sysctl();
return;
}
s = s->next;
}
spin_unlock_bh(&ax25_dev_lock);
dev->ax25_ptr = NULL;
ax25_register_sysctl();
}
int ax25_fwd_ioctl(unsigned int cmd, struct ax25_fwd_struct *fwd)
{
ax25_dev *ax25_dev, *fwd_dev;
if ((ax25_dev = ax25_addr_ax25dev(&fwd->port_from)) == NULL)
return -EINVAL;
switch (cmd) {
case SIOCAX25ADDFWD:
if ((fwd_dev = ax25_addr_ax25dev(&fwd->port_to)) == NULL)
return -EINVAL;
if (ax25_dev->forward != NULL)
return -EINVAL;
ax25_dev->forward = fwd_dev->dev;
break;
case SIOCAX25DELFWD:
if (ax25_dev->forward == NULL)
return -EINVAL;
ax25_dev->forward = NULL;
break;
default:
return -EINVAL;
}
return 0;
}
struct net_device *ax25_fwd_dev(struct net_device *dev)
{
ax25_dev *ax25_dev;
if ((ax25_dev = ax25_dev_ax25dev(dev)) == NULL)
return dev;
if (ax25_dev->forward == NULL)
return dev;
return ax25_dev->forward;
}
/*
* Free all memory associated with device structures.
*/
void __exit ax25_dev_free(void)
{
ax25_dev *s, *ax25_dev;
spin_lock_bh(&ax25_dev_lock);
ax25_dev = ax25_dev_list;
while (ax25_dev != NULL) {
s = ax25_dev;
dev_put(ax25_dev->dev);
ax25_dev = ax25_dev->next;
kfree(s);
}
ax25_dev_list = NULL;
spin_unlock_bh(&ax25_dev_lock);
}

305
net/ax25/ax25_ds_in.c Normal file
View File

@@ -0,0 +1,305 @@
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk)
* Copyright (C) Joerg Reuter DL1BKE (jreuter@yaina.de)
*/
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/socket.h>
#include <linux/in.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/timer.h>
#include <linux/string.h>
#include <linux/sockios.h>
#include <linux/net.h>
#include <net/ax25.h>
#include <linux/inet.h>
#include <linux/netdevice.h>
#include <linux/skbuff.h>
#include <net/sock.h>
#include <net/ip.h> /* For ip_rcv */
#include <net/tcp.h>
#include <asm/uaccess.h>
#include <asm/system.h>
#include <linux/fcntl.h>
#include <linux/mm.h>
#include <linux/interrupt.h>
/*
* State machine for state 1, Awaiting Connection State.
* The handling of the timer(s) is in file ax25_ds_timer.c.
* Handling of state 0 and connection release is in ax25.c.
*/
static int ax25_ds_state1_machine(ax25_cb *ax25, struct sk_buff *skb, int frametype, int pf, int type)
{
switch (frametype) {
case AX25_SABM:
ax25->modulus = AX25_MODULUS;
ax25->window = ax25->ax25_dev->values[AX25_VALUES_WINDOW];
ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE);
break;
case AX25_SABME:
ax25->modulus = AX25_EMODULUS;
ax25->window = ax25->ax25_dev->values[AX25_VALUES_EWINDOW];
ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE);
break;
case AX25_DISC:
ax25_send_control(ax25, AX25_DM, pf, AX25_RESPONSE);
break;
case AX25_UA:
ax25_calculate_rtt(ax25);
ax25_stop_t1timer(ax25);
ax25_start_t3timer(ax25);
ax25_start_idletimer(ax25);
ax25->vs = 0;
ax25->va = 0;
ax25->vr = 0;
ax25->state = AX25_STATE_3;
ax25->n2count = 0;
if (ax25->sk != NULL) {
bh_lock_sock(ax25->sk);
ax25->sk->sk_state = TCP_ESTABLISHED;
/*
* For WAIT_SABM connections we will produce an accept
* ready socket here
*/
if (!sock_flag(ax25->sk, SOCK_DEAD))
ax25->sk->sk_state_change(ax25->sk);
bh_unlock_sock(ax25->sk);
}
ax25_dama_on(ax25);
/* according to DK4EG<45>s spec we are required to
* send a RR RESPONSE FINAL NR=0.
*/
ax25_std_enquiry_response(ax25);
break;
case AX25_DM:
if (pf)
ax25_disconnect(ax25, ECONNREFUSED);
break;
default:
if (pf)
ax25_send_control(ax25, AX25_SABM, AX25_POLLON, AX25_COMMAND);
break;
}
return 0;
}
/*
* State machine for state 2, Awaiting Release State.
* The handling of the timer(s) is in file ax25_ds_timer.c
* Handling of state 0 and connection release is in ax25.c.
*/
static int ax25_ds_state2_machine(ax25_cb *ax25, struct sk_buff *skb, int frametype, int pf, int type)
{
switch (frametype) {
case AX25_SABM:
case AX25_SABME:
ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND);
ax25_dama_off(ax25);
break;
case AX25_DISC:
ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE);
ax25_dama_off(ax25);
ax25_disconnect(ax25, 0);
break;
case AX25_DM:
case AX25_UA:
if (pf) {
ax25_dama_off(ax25);
ax25_disconnect(ax25, 0);
}
break;
case AX25_I:
case AX25_REJ:
case AX25_RNR:
case AX25_RR:
if (pf) {
ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND);
ax25_dama_off(ax25);
}
break;
default:
break;
}
return 0;
}
/*
* State machine for state 3, Connected State.
* The handling of the timer(s) is in file ax25_timer.c
* Handling of state 0 and connection release is in ax25.c.
*/
static int ax25_ds_state3_machine(ax25_cb *ax25, struct sk_buff *skb, int frametype, int ns, int nr, int pf, int type)
{
int queued = 0;
switch (frametype) {
case AX25_SABM:
case AX25_SABME:
if (frametype == AX25_SABM) {
ax25->modulus = AX25_MODULUS;
ax25->window = ax25->ax25_dev->values[AX25_VALUES_WINDOW];
} else {
ax25->modulus = AX25_EMODULUS;
ax25->window = ax25->ax25_dev->values[AX25_VALUES_EWINDOW];
}
ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE);
ax25_stop_t1timer(ax25);
ax25_start_t3timer(ax25);
ax25_start_idletimer(ax25);
ax25->condition = 0x00;
ax25->vs = 0;
ax25->va = 0;
ax25->vr = 0;
ax25_requeue_frames(ax25);
ax25_dama_on(ax25);
break;
case AX25_DISC:
ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE);
ax25_dama_off(ax25);
ax25_disconnect(ax25, 0);
break;
case AX25_DM:
ax25_dama_off(ax25);
ax25_disconnect(ax25, ECONNRESET);
break;
case AX25_RR:
case AX25_RNR:
if (frametype == AX25_RR)
ax25->condition &= ~AX25_COND_PEER_RX_BUSY;
else
ax25->condition |= AX25_COND_PEER_RX_BUSY;
if (ax25_validate_nr(ax25, nr)) {
if (ax25_check_iframes_acked(ax25, nr))
ax25->n2count=0;
if (type == AX25_COMMAND && pf)
ax25_ds_enquiry_response(ax25);
} else {
ax25_ds_nr_error_recovery(ax25);
ax25->state = AX25_STATE_1;
}
break;
case AX25_REJ:
ax25->condition &= ~AX25_COND_PEER_RX_BUSY;
if (ax25_validate_nr(ax25, nr)) {
if (ax25->va != nr)
ax25->n2count=0;
ax25_frames_acked(ax25, nr);
ax25_calculate_rtt(ax25);
ax25_stop_t1timer(ax25);
ax25_start_t3timer(ax25);
ax25_requeue_frames(ax25);
if (type == AX25_COMMAND && pf)
ax25_ds_enquiry_response(ax25);
} else {
ax25_ds_nr_error_recovery(ax25);
ax25->state = AX25_STATE_1;
}
break;
case AX25_I:
if (!ax25_validate_nr(ax25, nr)) {
ax25_ds_nr_error_recovery(ax25);
ax25->state = AX25_STATE_1;
break;
}
if (ax25->condition & AX25_COND_PEER_RX_BUSY) {
ax25_frames_acked(ax25, nr);
ax25->n2count = 0;
} else {
if (ax25_check_iframes_acked(ax25, nr))
ax25->n2count = 0;
}
if (ax25->condition & AX25_COND_OWN_RX_BUSY) {
if (pf) ax25_ds_enquiry_response(ax25);
break;
}
if (ns == ax25->vr) {
ax25->vr = (ax25->vr + 1) % ax25->modulus;
queued = ax25_rx_iframe(ax25, skb);
if (ax25->condition & AX25_COND_OWN_RX_BUSY)
ax25->vr = ns; /* ax25->vr - 1 */
ax25->condition &= ~AX25_COND_REJECT;
if (pf) {
ax25_ds_enquiry_response(ax25);
} else {
if (!(ax25->condition & AX25_COND_ACK_PENDING)) {
ax25->condition |= AX25_COND_ACK_PENDING;
ax25_start_t2timer(ax25);
}
}
} else {
if (ax25->condition & AX25_COND_REJECT) {
if (pf) ax25_ds_enquiry_response(ax25);
} else {
ax25->condition |= AX25_COND_REJECT;
ax25_ds_enquiry_response(ax25);
ax25->condition &= ~AX25_COND_ACK_PENDING;
}
}
break;
case AX25_FRMR:
case AX25_ILLEGAL:
ax25_ds_establish_data_link(ax25);
ax25->state = AX25_STATE_1;
break;
default:
break;
}
return queued;
}
/*
* Higher level upcall for a LAPB frame
*/
int ax25_ds_frame_in(ax25_cb *ax25, struct sk_buff *skb, int type)
{
int queued = 0, frametype, ns, nr, pf;
frametype = ax25_decode(ax25, skb, &ns, &nr, &pf);
switch (ax25->state) {
case AX25_STATE_1:
queued = ax25_ds_state1_machine(ax25, skb, frametype, pf, type);
break;
case AX25_STATE_2:
queued = ax25_ds_state2_machine(ax25, skb, frametype, pf, type);
break;
case AX25_STATE_3:
queued = ax25_ds_state3_machine(ax25, skb, frametype, ns, nr, pf, type);
break;
}
return queued;
}

212
net/ax25/ax25_ds_subr.c Normal file
View File

@@ -0,0 +1,212 @@
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk)
* Copyright (C) Joerg Reuter DL1BKE (jreuter@yaina.de)
*/
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/socket.h>
#include <linux/in.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/timer.h>
#include <linux/string.h>
#include <linux/sockios.h>
#include <linux/spinlock.h>
#include <linux/net.h>
#include <net/ax25.h>
#include <linux/inet.h>
#include <linux/netdevice.h>
#include <linux/skbuff.h>
#include <net/sock.h>
#include <asm/uaccess.h>
#include <asm/system.h>
#include <linux/fcntl.h>
#include <linux/mm.h>
#include <linux/interrupt.h>
void ax25_ds_nr_error_recovery(ax25_cb *ax25)
{
ax25_ds_establish_data_link(ax25);
}
/*
* dl1bke 960114: transmit I frames on DAMA poll
*/
void ax25_ds_enquiry_response(ax25_cb *ax25)
{
ax25_cb *ax25o;
struct hlist_node *node;
/* Please note that neither DK4EG<45>s nor DG2FEF<45>s
* DAMA spec mention the following behaviour as seen
* with TheFirmware:
*
* DB0ACH->DL1BKE <RR C P R0> [DAMA]
* DL1BKE->DB0ACH <I NR=0 NS=0>
* DL1BKE-7->DB0PRA-6 DB0ACH <I C S3 R5>
* DL1BKE->DB0ACH <RR R F R0>
*
* The Flexnet DAMA Master implementation apparently
* insists on the "proper" AX.25 behaviour:
*
* DB0ACH->DL1BKE <RR C P R0> [DAMA]
* DL1BKE->DB0ACH <RR R F R0>
* DL1BKE->DB0ACH <I NR=0 NS=0>
* DL1BKE-7->DB0PRA-6 DB0ACH <I C S3 R5>
*
* Flexnet refuses to send us *any* I frame if we send
* a REJ in case AX25_COND_REJECT is set. It is superfluous in
* this mode anyway (a RR or RNR invokes the retransmission).
* Is this a Flexnet bug?
*/
ax25_std_enquiry_response(ax25);
if (!(ax25->condition & AX25_COND_PEER_RX_BUSY)) {
ax25_requeue_frames(ax25);
ax25_kick(ax25);
}
if (ax25->state == AX25_STATE_1 || ax25->state == AX25_STATE_2 || skb_peek(&ax25->ack_queue) != NULL)
ax25_ds_t1_timeout(ax25);
else
ax25->n2count = 0;
ax25_start_t3timer(ax25);
ax25_ds_set_timer(ax25->ax25_dev);
spin_lock_bh(&ax25_list_lock);
ax25_for_each(ax25o, node, &ax25_list) {
if (ax25o == ax25)
continue;
if (ax25o->ax25_dev != ax25->ax25_dev)
continue;
if (ax25o->state == AX25_STATE_1 || ax25o->state == AX25_STATE_2) {
ax25_ds_t1_timeout(ax25o);
continue;
}
if (!(ax25o->condition & AX25_COND_PEER_RX_BUSY) && ax25o->state == AX25_STATE_3) {
ax25_requeue_frames(ax25o);
ax25_kick(ax25o);
}
if (ax25o->state == AX25_STATE_1 || ax25o->state == AX25_STATE_2 || skb_peek(&ax25o->ack_queue) != NULL)
ax25_ds_t1_timeout(ax25o);
/* do not start T3 for listening sockets (tnx DD8NE) */
if (ax25o->state != AX25_STATE_0)
ax25_start_t3timer(ax25o);
}
spin_unlock_bh(&ax25_list_lock);
}
void ax25_ds_establish_data_link(ax25_cb *ax25)
{
ax25->condition &= AX25_COND_DAMA_MODE;
ax25->n2count = 0;
ax25_calculate_t1(ax25);
ax25_start_t1timer(ax25);
ax25_stop_t2timer(ax25);
ax25_start_t3timer(ax25);
}
/*
* :::FIXME:::
* This is a kludge. Not all drivers recognize kiss commands.
* We need a driver level request to switch duplex mode, that does
* either SCC changing, PI config or KISS as required. Currently
* this request isn't reliable.
*/
static void ax25_kiss_cmd(ax25_dev *ax25_dev, unsigned char cmd, unsigned char param)
{
struct sk_buff *skb;
unsigned char *p;
if (ax25_dev->dev == NULL)
return;
if ((skb = alloc_skb(2, GFP_ATOMIC)) == NULL)
return;
skb->nh.raw = skb->data;
p = skb_put(skb, 2);
*p++ = cmd;
*p++ = param;
skb->dev = ax25_dev->dev;
skb->protocol = htons(ETH_P_AX25);
dev_queue_xmit(skb);
}
/*
* A nasty problem arises if we count the number of DAMA connections
* wrong, especially when connections on the device already existed
* and our network node (or the sysop) decides to turn on DAMA Master
* mode. We thus flag the 'real' slave connections with
* ax25->dama_slave=1 and look on every disconnect if still slave
* connections exist.
*/
static int ax25_check_dama_slave(ax25_dev *ax25_dev)
{
ax25_cb *ax25;
int res = 0;
struct hlist_node *node;
spin_lock_bh(&ax25_list_lock);
ax25_for_each(ax25, node, &ax25_list)
if (ax25->ax25_dev == ax25_dev && (ax25->condition & AX25_COND_DAMA_MODE) && ax25->state > AX25_STATE_1) {
res = 1;
break;
}
spin_unlock_bh(&ax25_list_lock);
return res;
}
static void ax25_dev_dama_on(ax25_dev *ax25_dev)
{
if (ax25_dev == NULL)
return;
if (ax25_dev->dama.slave == 0)
ax25_kiss_cmd(ax25_dev, 5, 1);
ax25_dev->dama.slave = 1;
ax25_ds_set_timer(ax25_dev);
}
void ax25_dev_dama_off(ax25_dev *ax25_dev)
{
if (ax25_dev == NULL)
return;
if (ax25_dev->dama.slave && !ax25_check_dama_slave(ax25_dev)) {
ax25_kiss_cmd(ax25_dev, 5, 0);
ax25_dev->dama.slave = 0;
ax25_ds_del_timer(ax25_dev);
}
}
void ax25_dama_on(ax25_cb *ax25)
{
ax25_dev_dama_on(ax25->ax25_dev);
ax25->condition |= AX25_COND_DAMA_MODE;
}
void ax25_dama_off(ax25_cb *ax25)
{
ax25->condition &= ~AX25_COND_DAMA_MODE;
ax25_dev_dama_off(ax25->ax25_dev);
}

241
net/ax25/ax25_ds_timer.c Normal file
View File

@@ -0,0 +1,241 @@
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk)
* Copyright (C) Joerg Reuter DL1BKE (jreuter@yaina.de)
*/
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/socket.h>
#include <linux/spinlock.h>
#include <linux/in.h>
#include <linux/kernel.h>
#include <linux/jiffies.h>
#include <linux/timer.h>
#include <linux/string.h>
#include <linux/sockios.h>
#include <linux/net.h>
#include <net/tcp.h>
#include <net/ax25.h>
#include <linux/inet.h>
#include <linux/netdevice.h>
#include <linux/skbuff.h>
#include <net/sock.h>
#include <asm/uaccess.h>
#include <asm/system.h>
#include <linux/fcntl.h>
#include <linux/mm.h>
#include <linux/interrupt.h>
static void ax25_ds_timeout(unsigned long);
/*
* Add DAMA slave timeout timer to timer list.
* Unlike the connection based timers the timeout function gets
* triggered every second. Please note that NET_AX25_DAMA_SLAVE_TIMEOUT
* (aka /proc/sys/net/ax25/{dev}/dama_slave_timeout) is still in
* 1/10th of a second.
*/
static void ax25_ds_add_timer(ax25_dev *ax25_dev)
{
struct timer_list *t = &ax25_dev->dama.slave_timer;
t->data = (unsigned long) ax25_dev;
t->function = &ax25_ds_timeout;
t->expires = jiffies + HZ;
add_timer(t);
}
void ax25_ds_del_timer(ax25_dev *ax25_dev)
{
if (ax25_dev)
del_timer(&ax25_dev->dama.slave_timer);
}
void ax25_ds_set_timer(ax25_dev *ax25_dev)
{
if (ax25_dev == NULL) /* paranoia */
return;
del_timer(&ax25_dev->dama.slave_timer);
ax25_dev->dama.slave_timeout = ax25_dev->values[AX25_VALUES_DS_TIMEOUT] / 10;
ax25_ds_add_timer(ax25_dev);
}
/*
* DAMA Slave Timeout
* Silently discard all (slave) connections in case our master forgot us...
*/
static void ax25_ds_timeout(unsigned long arg)
{
ax25_dev *ax25_dev = (struct ax25_dev *) arg;
ax25_cb *ax25;
struct hlist_node *node;
if (ax25_dev == NULL || !ax25_dev->dama.slave)
return; /* Yikes! */
if (!ax25_dev->dama.slave_timeout || --ax25_dev->dama.slave_timeout) {
ax25_ds_set_timer(ax25_dev);
return;
}
spin_lock_bh(&ax25_list_lock);
ax25_for_each(ax25, node, &ax25_list) {
if (ax25->ax25_dev != ax25_dev || !(ax25->condition & AX25_COND_DAMA_MODE))
continue;
ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND);
ax25_disconnect(ax25, ETIMEDOUT);
}
spin_unlock_bh(&ax25_list_lock);
ax25_dev_dama_off(ax25_dev);
}
void ax25_ds_heartbeat_expiry(ax25_cb *ax25)
{
struct sock *sk=ax25->sk;
if (sk)
bh_lock_sock(sk);
switch (ax25->state) {
case AX25_STATE_0:
/* Magic here: If we listen() and a new link dies before it
is accepted() it isn't 'dead' so doesn't get removed. */
if (!sk || sock_flag(sk, SOCK_DESTROY) ||
(sk->sk_state == TCP_LISTEN &&
sock_flag(sk, SOCK_DEAD))) {
if (sk) {
sock_hold(sk);
ax25_destroy_socket(ax25);
sock_put(sk);
bh_unlock_sock(sk);
} else
ax25_destroy_socket(ax25);
return;
}
break;
case AX25_STATE_3:
/*
* Check the state of the receive buffer.
*/
if (sk != NULL) {
if (atomic_read(&sk->sk_rmem_alloc) <
(sk->sk_rcvbuf / 2) &&
(ax25->condition & AX25_COND_OWN_RX_BUSY)) {
ax25->condition &= ~AX25_COND_OWN_RX_BUSY;
ax25->condition &= ~AX25_COND_ACK_PENDING;
break;
}
}
break;
}
if (sk)
bh_unlock_sock(sk);
ax25_start_heartbeat(ax25);
}
/* dl1bke 960114: T3 works much like the IDLE timeout, but
* gets reloaded with every frame for this
* connection.
*/
void ax25_ds_t3timer_expiry(ax25_cb *ax25)
{
ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND);
ax25_dama_off(ax25);
ax25_disconnect(ax25, ETIMEDOUT);
}
/* dl1bke 960228: close the connection when IDLE expires.
* unlike T3 this timer gets reloaded only on
* I frames.
*/
void ax25_ds_idletimer_expiry(ax25_cb *ax25)
{
ax25_clear_queues(ax25);
ax25->n2count = 0;
ax25->state = AX25_STATE_2;
ax25_calculate_t1(ax25);
ax25_start_t1timer(ax25);
ax25_stop_t3timer(ax25);
if (ax25->sk != NULL) {
bh_lock_sock(ax25->sk);
ax25->sk->sk_state = TCP_CLOSE;
ax25->sk->sk_err = 0;
ax25->sk->sk_shutdown |= SEND_SHUTDOWN;
if (!sock_flag(ax25->sk, SOCK_DEAD)) {
ax25->sk->sk_state_change(ax25->sk);
sock_set_flag(ax25->sk, SOCK_DEAD);
}
bh_unlock_sock(ax25->sk);
}
}
/* dl1bke 960114: The DAMA protocol requires to send data and SABM/DISC
* within the poll of any connected channel. Remember
* that we are not allowed to send anything unless we
* get polled by the Master.
*
* Thus we'll have to do parts of our T1 handling in
* ax25_enquiry_response().
*/
void ax25_ds_t1_timeout(ax25_cb *ax25)
{
switch (ax25->state) {
case AX25_STATE_1:
if (ax25->n2count == ax25->n2) {
if (ax25->modulus == AX25_MODULUS) {
ax25_disconnect(ax25, ETIMEDOUT);
return;
} else {
ax25->modulus = AX25_MODULUS;
ax25->window = ax25->ax25_dev->values[AX25_VALUES_WINDOW];
ax25->n2count = 0;
ax25_send_control(ax25, AX25_SABM, AX25_POLLOFF, AX25_COMMAND);
}
} else {
ax25->n2count++;
if (ax25->modulus == AX25_MODULUS)
ax25_send_control(ax25, AX25_SABM, AX25_POLLOFF, AX25_COMMAND);
else
ax25_send_control(ax25, AX25_SABME, AX25_POLLOFF, AX25_COMMAND);
}
break;
case AX25_STATE_2:
if (ax25->n2count == ax25->n2) {
ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND);
ax25_disconnect(ax25, ETIMEDOUT);
return;
} else {
ax25->n2count++;
}
break;
case AX25_STATE_3:
if (ax25->n2count == ax25->n2) {
ax25_send_control(ax25, AX25_DM, AX25_POLLON, AX25_RESPONSE);
ax25_disconnect(ax25, ETIMEDOUT);
return;
} else {
ax25->n2count++;
}
break;
}
ax25_calculate_t1(ax25);
ax25_start_t1timer(ax25);
}

266
net/ax25/ax25_iface.c Normal file
View File

@@ -0,0 +1,266 @@
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk)
*/
#include <linux/config.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/socket.h>
#include <linux/in.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/spinlock.h>
#include <linux/timer.h>
#include <linux/string.h>
#include <linux/sockios.h>
#include <linux/net.h>
#include <net/ax25.h>
#include <linux/inet.h>
#include <linux/netdevice.h>
#include <linux/skbuff.h>
#include <net/sock.h>
#include <asm/uaccess.h>
#include <asm/system.h>
#include <linux/fcntl.h>
#include <linux/mm.h>
#include <linux/interrupt.h>
static struct protocol_struct {
struct protocol_struct *next;
unsigned int pid;
int (*func)(struct sk_buff *, ax25_cb *);
} *protocol_list = NULL;
static DEFINE_RWLOCK(protocol_list_lock);
static struct linkfail_struct {
struct linkfail_struct *next;
void (*func)(ax25_cb *, int);
} *linkfail_list = NULL;
static DEFINE_SPINLOCK(linkfail_lock);
static struct listen_struct {
struct listen_struct *next;
ax25_address callsign;
struct net_device *dev;
} *listen_list = NULL;
static DEFINE_SPINLOCK(listen_lock);
int ax25_protocol_register(unsigned int pid,
int (*func)(struct sk_buff *, ax25_cb *))
{
struct protocol_struct *protocol;
if (pid == AX25_P_TEXT || pid == AX25_P_SEGMENT)
return 0;
#ifdef CONFIG_INET
if (pid == AX25_P_IP || pid == AX25_P_ARP)
return 0;
#endif
if ((protocol = kmalloc(sizeof(*protocol), GFP_ATOMIC)) == NULL)
return 0;
protocol->pid = pid;
protocol->func = func;
write_lock(&protocol_list_lock);
protocol->next = protocol_list;
protocol_list = protocol;
write_unlock(&protocol_list_lock);
return 1;
}
void ax25_protocol_release(unsigned int pid)
{
struct protocol_struct *s, *protocol;
write_lock(&protocol_list_lock);
protocol = protocol_list;
if (protocol == NULL) {
write_unlock(&protocol_list_lock);
return;
}
if (protocol->pid == pid) {
protocol_list = protocol->next;
write_unlock(&protocol_list_lock);
kfree(protocol);
return;
}
while (protocol != NULL && protocol->next != NULL) {
if (protocol->next->pid == pid) {
s = protocol->next;
protocol->next = protocol->next->next;
write_unlock(&protocol_list_lock);
kfree(s);
return;
}
protocol = protocol->next;
}
write_unlock(&protocol_list_lock);
}
int ax25_linkfail_register(void (*func)(ax25_cb *, int))
{
struct linkfail_struct *linkfail;
if ((linkfail = kmalloc(sizeof(*linkfail), GFP_ATOMIC)) == NULL)
return 0;
linkfail->func = func;
spin_lock_bh(&linkfail_lock);
linkfail->next = linkfail_list;
linkfail_list = linkfail;
spin_unlock_bh(&linkfail_lock);
return 1;
}
void ax25_linkfail_release(void (*func)(ax25_cb *, int))
{
struct linkfail_struct *s, *linkfail;
spin_lock_bh(&linkfail_lock);
linkfail = linkfail_list;
if (linkfail == NULL) {
spin_unlock_bh(&linkfail_lock);
return;
}
if (linkfail->func == func) {
linkfail_list = linkfail->next;
spin_unlock_bh(&linkfail_lock);
kfree(linkfail);
return;
}
while (linkfail != NULL && linkfail->next != NULL) {
if (linkfail->next->func == func) {
s = linkfail->next;
linkfail->next = linkfail->next->next;
spin_unlock_bh(&linkfail_lock);
kfree(s);
return;
}
linkfail = linkfail->next;
}
spin_unlock_bh(&linkfail_lock);
}
int ax25_listen_register(ax25_address *callsign, struct net_device *dev)
{
struct listen_struct *listen;
if (ax25_listen_mine(callsign, dev))
return 0;
if ((listen = kmalloc(sizeof(*listen), GFP_ATOMIC)) == NULL)
return 0;
listen->callsign = *callsign;
listen->dev = dev;
spin_lock_bh(&listen_lock);
listen->next = listen_list;
listen_list = listen;
spin_unlock_bh(&listen_lock);
return 1;
}
void ax25_listen_release(ax25_address *callsign, struct net_device *dev)
{
struct listen_struct *s, *listen;
spin_lock_bh(&listen_lock);
listen = listen_list;
if (listen == NULL) {
spin_unlock_bh(&listen_lock);
return;
}
if (ax25cmp(&listen->callsign, callsign) == 0 && listen->dev == dev) {
listen_list = listen->next;
spin_unlock_bh(&listen_lock);
kfree(listen);
return;
}
while (listen != NULL && listen->next != NULL) {
if (ax25cmp(&listen->next->callsign, callsign) == 0 && listen->next->dev == dev) {
s = listen->next;
listen->next = listen->next->next;
spin_unlock_bh(&listen_lock);
kfree(s);
return;
}
listen = listen->next;
}
spin_unlock_bh(&listen_lock);
}
int (*ax25_protocol_function(unsigned int pid))(struct sk_buff *, ax25_cb *)
{
int (*res)(struct sk_buff *, ax25_cb *) = NULL;
struct protocol_struct *protocol;
read_lock(&protocol_list_lock);
for (protocol = protocol_list; protocol != NULL; protocol = protocol->next)
if (protocol->pid == pid) {
res = protocol->func;
break;
}
read_unlock(&protocol_list_lock);
return res;
}
int ax25_listen_mine(ax25_address *callsign, struct net_device *dev)
{
struct listen_struct *listen;
spin_lock_bh(&listen_lock);
for (listen = listen_list; listen != NULL; listen = listen->next)
if (ax25cmp(&listen->callsign, callsign) == 0 && (listen->dev == dev || listen->dev == NULL)) {
spin_unlock_bh(&listen_lock);
return 1;
}
spin_unlock_bh(&listen_lock);
return 0;
}
void ax25_link_failed(ax25_cb *ax25, int reason)
{
struct linkfail_struct *linkfail;
spin_lock_bh(&linkfail_lock);
for (linkfail = linkfail_list; linkfail != NULL; linkfail = linkfail->next)
(linkfail->func)(ax25, reason);
spin_unlock_bh(&linkfail_lock);
}
int ax25_protocol_is_registered(unsigned int pid)
{
struct protocol_struct *protocol;
int res = 0;
read_lock(&protocol_list_lock);
for (protocol = protocol_list; protocol != NULL; protocol = protocol->next)
if (protocol->pid == pid) {
res = 1;
break;
}
read_unlock(&protocol_list_lock);
return res;
}

470
net/ax25/ax25_in.c Normal file
View File

@@ -0,0 +1,470 @@
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Copyright (C) Alan Cox GW4PTS (alan@lxorguk.ukuu.org.uk)
* Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk)
* Copyright (C) Joerg Reuter DL1BKE (jreuter@yaina.de)
* Copyright (C) Hans-Joachim Hetscher DD8NE (dd8ne@bnv-bamberg.de)
*/
#include <linux/config.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/socket.h>
#include <linux/in.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/timer.h>
#include <linux/string.h>
#include <linux/sockios.h>
#include <linux/net.h>
#include <net/ax25.h>
#include <linux/inet.h>
#include <linux/netdevice.h>
#include <linux/skbuff.h>
#include <linux/netfilter.h>
#include <net/sock.h>
#include <net/ip.h> /* For ip_rcv */
#include <net/tcp.h>
#include <net/arp.h> /* For arp_rcv */
#include <asm/uaccess.h>
#include <asm/system.h>
#include <linux/fcntl.h>
#include <linux/mm.h>
#include <linux/interrupt.h>
/*
* Given a fragment, queue it on the fragment queue and if the fragment
* is complete, send it back to ax25_rx_iframe.
*/
static int ax25_rx_fragment(ax25_cb *ax25, struct sk_buff *skb)
{
struct sk_buff *skbn, *skbo;
if (ax25->fragno != 0) {
if (!(*skb->data & AX25_SEG_FIRST)) {
if ((ax25->fragno - 1) == (*skb->data & AX25_SEG_REM)) {
/* Enqueue fragment */
ax25->fragno = *skb->data & AX25_SEG_REM;
skb_pull(skb, 1); /* skip fragno */
ax25->fraglen += skb->len;
skb_queue_tail(&ax25->frag_queue, skb);
/* Last fragment received ? */
if (ax25->fragno == 0) {
skbn = alloc_skb(AX25_MAX_HEADER_LEN +
ax25->fraglen,
GFP_ATOMIC);
if (!skbn) {
skb_queue_purge(&ax25->frag_queue);
return 1;
}
skb_reserve(skbn, AX25_MAX_HEADER_LEN);
skbn->dev = ax25->ax25_dev->dev;
skbn->h.raw = skbn->data;
skbn->nh.raw = skbn->data;
/* Copy data from the fragments */
while ((skbo = skb_dequeue(&ax25->frag_queue)) != NULL) {
memcpy(skb_put(skbn, skbo->len), skbo->data, skbo->len);
kfree_skb(skbo);
}
ax25->fraglen = 0;
if (ax25_rx_iframe(ax25, skbn) == 0)
kfree_skb(skbn);
}
return 1;
}
}
} else {
/* First fragment received */
if (*skb->data & AX25_SEG_FIRST) {
skb_queue_purge(&ax25->frag_queue);
ax25->fragno = *skb->data & AX25_SEG_REM;
skb_pull(skb, 1); /* skip fragno */
ax25->fraglen = skb->len;
skb_queue_tail(&ax25->frag_queue, skb);
return 1;
}
}
return 0;
}
/*
* This is where all valid I frames are sent to, to be dispatched to
* whichever protocol requires them.
*/
int ax25_rx_iframe(ax25_cb *ax25, struct sk_buff *skb)
{
int (*func)(struct sk_buff *, ax25_cb *);
volatile int queued = 0;
unsigned char pid;
if (skb == NULL) return 0;
ax25_start_idletimer(ax25);
pid = *skb->data;
#ifdef CONFIG_INET
if (pid == AX25_P_IP) {
/* working around a TCP bug to keep additional listeners
* happy. TCP re-uses the buffer and destroys the original
* content.
*/
struct sk_buff *skbn = skb_copy(skb, GFP_ATOMIC);
if (skbn != NULL) {
kfree_skb(skb);
skb = skbn;
}
skb_pull(skb, 1); /* Remove PID */
skb->h.raw = skb->data;
skb->nh.raw = skb->data;
skb->dev = ax25->ax25_dev->dev;
skb->pkt_type = PACKET_HOST;
skb->protocol = htons(ETH_P_IP);
ip_rcv(skb, skb->dev, NULL); /* Wrong ptype */
return 1;
}
#endif
if (pid == AX25_P_SEGMENT) {
skb_pull(skb, 1); /* Remove PID */
return ax25_rx_fragment(ax25, skb);
}
if ((func = ax25_protocol_function(pid)) != NULL) {
skb_pull(skb, 1); /* Remove PID */
return (*func)(skb, ax25);
}
if (ax25->sk != NULL && ax25->ax25_dev->values[AX25_VALUES_CONMODE] == 2) {
if ((!ax25->pidincl && ax25->sk->sk_protocol == pid) ||
ax25->pidincl) {
if (sock_queue_rcv_skb(ax25->sk, skb) == 0)
queued = 1;
else
ax25->condition |= AX25_COND_OWN_RX_BUSY;
}
}
return queued;
}
/*
* Higher level upcall for a LAPB frame
*/
static int ax25_process_rx_frame(ax25_cb *ax25, struct sk_buff *skb, int type, int dama)
{
int queued = 0;
if (ax25->state == AX25_STATE_0)
return 0;
switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) {
case AX25_PROTO_STD_SIMPLEX:
case AX25_PROTO_STD_DUPLEX:
queued = ax25_std_frame_in(ax25, skb, type);
break;
#ifdef CONFIG_AX25_DAMA_SLAVE
case AX25_PROTO_DAMA_SLAVE:
if (dama || ax25->ax25_dev->dama.slave)
queued = ax25_ds_frame_in(ax25, skb, type);
else
queued = ax25_std_frame_in(ax25, skb, type);
break;
#endif
}
return queued;
}
static int ax25_rcv(struct sk_buff *skb, struct net_device *dev,
ax25_address *dev_addr, struct packet_type *ptype)
{
ax25_address src, dest, *next_digi = NULL;
int type = 0, mine = 0, dama;
struct sock *make, *sk;
ax25_digi dp, reverse_dp;
ax25_cb *ax25;
ax25_dev *ax25_dev;
/*
* Process the AX.25/LAPB frame.
*/
skb->h.raw = skb->data;
if ((ax25_dev = ax25_dev_ax25dev(dev)) == NULL) {
kfree_skb(skb);
return 0;
}
/*
* Parse the address header.
*/
if (ax25_addr_parse(skb->data, skb->len, &src, &dest, &dp, &type, &dama) == NULL) {
kfree_skb(skb);
return 0;
}
/*
* Ours perhaps ?
*/
if (dp.lastrepeat + 1 < dp.ndigi) /* Not yet digipeated completely */
next_digi = &dp.calls[dp.lastrepeat + 1];
/*
* Pull of the AX.25 headers leaving the CTRL/PID bytes
*/
skb_pull(skb, ax25_addr_size(&dp));
/* For our port addresses ? */
if (ax25cmp(&dest, dev_addr) == 0 && dp.lastrepeat + 1 == dp.ndigi)
mine = 1;
/* Also match on any registered callsign from L3/4 */
if (!mine && ax25_listen_mine(&dest, dev) && dp.lastrepeat + 1 == dp.ndigi)
mine = 1;
/* UI frame - bypass LAPB processing */
if ((*skb->data & ~0x10) == AX25_UI && dp.lastrepeat + 1 == dp.ndigi) {
skb->h.raw = skb->data + 2; /* skip control and pid */
ax25_send_to_raw(&dest, skb, skb->data[1]);
if (!mine && ax25cmp(&dest, (ax25_address *)dev->broadcast) != 0) {
kfree_skb(skb);
return 0;
}
/* Now we are pointing at the pid byte */
switch (skb->data[1]) {
#ifdef CONFIG_INET
case AX25_P_IP:
skb_pull(skb,2); /* drop PID/CTRL */
skb->h.raw = skb->data;
skb->nh.raw = skb->data;
skb->dev = dev;
skb->pkt_type = PACKET_HOST;
skb->protocol = htons(ETH_P_IP);
ip_rcv(skb, dev, ptype); /* Note ptype here is the wrong one, fix me later */
break;
case AX25_P_ARP:
skb_pull(skb,2);
skb->h.raw = skb->data;
skb->nh.raw = skb->data;
skb->dev = dev;
skb->pkt_type = PACKET_HOST;
skb->protocol = htons(ETH_P_ARP);
arp_rcv(skb, dev, ptype); /* Note ptype here is wrong... */
break;
#endif
case AX25_P_TEXT:
/* Now find a suitable dgram socket */
sk = ax25_get_socket(&dest, &src, SOCK_DGRAM);
if (sk != NULL) {
bh_lock_sock(sk);
if (atomic_read(&sk->sk_rmem_alloc) >=
sk->sk_rcvbuf) {
kfree_skb(skb);
} else {
/*
* Remove the control and PID.
*/
skb_pull(skb, 2);
if (sock_queue_rcv_skb(sk, skb) != 0)
kfree_skb(skb);
}
bh_unlock_sock(sk);
sock_put(sk);
} else {
kfree_skb(skb);
}
break;
default:
kfree_skb(skb); /* Will scan SOCK_AX25 RAW sockets */
break;
}
return 0;
}
/*
* Is connected mode supported on this device ?
* If not, should we DM the incoming frame (except DMs) or
* silently ignore them. For now we stay quiet.
*/
if (ax25_dev->values[AX25_VALUES_CONMODE] == 0) {
kfree_skb(skb);
return 0;
}
/* LAPB */
/* AX.25 state 1-4 */
ax25_digi_invert(&dp, &reverse_dp);
if ((ax25 = ax25_find_cb(&dest, &src, &reverse_dp, dev)) != NULL) {
/*
* Process the frame. If it is queued up internally it
* returns one otherwise we free it immediately. This
* routine itself wakes the user context layers so we do
* no further work
*/
if (ax25_process_rx_frame(ax25, skb, type, dama) == 0)
kfree_skb(skb);
ax25_cb_put(ax25);
return 0;
}
/* AX.25 state 0 (disconnected) */
/* a) received not a SABM(E) */
if ((*skb->data & ~AX25_PF) != AX25_SABM &&
(*skb->data & ~AX25_PF) != AX25_SABME) {
/*
* Never reply to a DM. Also ignore any connects for
* addresses that are not our interfaces and not a socket.
*/
if ((*skb->data & ~AX25_PF) != AX25_DM && mine)
ax25_return_dm(dev, &src, &dest, &dp);
kfree_skb(skb);
return 0;
}
/* b) received SABM(E) */
if (dp.lastrepeat + 1 == dp.ndigi)
sk = ax25_find_listener(&dest, 0, dev, SOCK_SEQPACKET);
else
sk = ax25_find_listener(next_digi, 1, dev, SOCK_SEQPACKET);
if (sk != NULL) {
bh_lock_sock(sk);
if (sk_acceptq_is_full(sk) ||
(make = ax25_make_new(sk, ax25_dev)) == NULL) {
if (mine)
ax25_return_dm(dev, &src, &dest, &dp);
kfree_skb(skb);
bh_unlock_sock(sk);
sock_put(sk);
return 0;
}
ax25 = ax25_sk(make);
skb_set_owner_r(skb, make);
skb_queue_head(&sk->sk_receive_queue, skb);
make->sk_state = TCP_ESTABLISHED;
sk->sk_ack_backlog++;
bh_unlock_sock(sk);
} else {
if (!mine) {
kfree_skb(skb);
return 0;
}
if ((ax25 = ax25_create_cb()) == NULL) {
ax25_return_dm(dev, &src, &dest, &dp);
kfree_skb(skb);
return 0;
}
ax25_fillin_cb(ax25, ax25_dev);
}
ax25->source_addr = dest;
ax25->dest_addr = src;
/*
* Sort out any digipeated paths.
*/
if (dp.ndigi && !ax25->digipeat &&
(ax25->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL) {
kfree_skb(skb);
ax25_destroy_socket(ax25);
if (sk)
sock_put(sk);
return 0;
}
if (dp.ndigi == 0) {
if (ax25->digipeat != NULL) {
kfree(ax25->digipeat);
ax25->digipeat = NULL;
}
} else {
/* Reverse the source SABM's path */
memcpy(ax25->digipeat, &reverse_dp, sizeof(ax25_digi));
}
if ((*skb->data & ~AX25_PF) == AX25_SABME) {
ax25->modulus = AX25_EMODULUS;
ax25->window = ax25_dev->values[AX25_VALUES_EWINDOW];
} else {
ax25->modulus = AX25_MODULUS;
ax25->window = ax25_dev->values[AX25_VALUES_WINDOW];
}
ax25_send_control(ax25, AX25_UA, AX25_POLLON, AX25_RESPONSE);
#ifdef CONFIG_AX25_DAMA_SLAVE
if (dama && ax25->ax25_dev->values[AX25_VALUES_PROTOCOL] == AX25_PROTO_DAMA_SLAVE)
ax25_dama_on(ax25);
#endif
ax25->state = AX25_STATE_3;
ax25_cb_add(ax25);
ax25_start_heartbeat(ax25);
ax25_start_t3timer(ax25);
ax25_start_idletimer(ax25);
if (sk) {
if (!sock_flag(sk, SOCK_DEAD))
sk->sk_data_ready(sk, skb->len);
sock_put(sk);
} else
kfree_skb(skb);
return 0;
}
/*
* Receive an AX.25 frame via a SLIP interface.
*/
int ax25_kiss_rcv(struct sk_buff *skb, struct net_device *dev,
struct packet_type *ptype)
{
skb->sk = NULL; /* Initially we don't know who it's for */
skb->destructor = NULL; /* Who initializes this, dammit?! */
if ((*skb->data & 0x0F) != 0) {
kfree_skb(skb); /* Not a KISS data frame */
return 0;
}
skb_pull(skb, AX25_KISS_HEADER_LEN); /* Remove the KISS byte */
return ax25_rcv(skb, dev, (ax25_address *)dev->dev_addr, ptype);
}

225
net/ax25/ax25_ip.c Normal file
View File

@@ -0,0 +1,225 @@
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk)
*/
#include <linux/config.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/socket.h>
#include <linux/in.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/timer.h>
#include <linux/string.h>
#include <linux/sockios.h>
#include <linux/net.h>
#include <net/ax25.h>
#include <linux/inet.h>
#include <linux/netdevice.h>
#include <linux/if_arp.h>
#include <linux/skbuff.h>
#include <net/sock.h>
#include <asm/uaccess.h>
#include <asm/system.h>
#include <linux/fcntl.h>
#include <linux/termios.h> /* For TIOCINQ/OUTQ */
#include <linux/mm.h>
#include <linux/interrupt.h>
#include <linux/notifier.h>
#include <linux/proc_fs.h>
#include <linux/stat.h>
#include <linux/netfilter.h>
#include <linux/sysctl.h>
#include <net/ip.h>
#include <net/arp.h>
/*
* IP over AX.25 encapsulation.
*/
/*
* Shove an AX.25 UI header on an IP packet and handle ARP
*/
#ifdef CONFIG_INET
int ax25_encapsulate(struct sk_buff *skb, struct net_device *dev, unsigned short type, void *daddr, void *saddr, unsigned len)
{
unsigned char *buff;
/* they sometimes come back to us... */
if (type == ETH_P_AX25)
return 0;
/* header is an AX.25 UI frame from us to them */
buff = skb_push(skb, AX25_HEADER_LEN);
*buff++ = 0x00; /* KISS DATA */
if (daddr != NULL)
memcpy(buff, daddr, dev->addr_len); /* Address specified */
buff[6] &= ~AX25_CBIT;
buff[6] &= ~AX25_EBIT;
buff[6] |= AX25_SSSID_SPARE;
buff += AX25_ADDR_LEN;
if (saddr != NULL)
memcpy(buff, saddr, dev->addr_len);
else
memcpy(buff, dev->dev_addr, dev->addr_len);
buff[6] &= ~AX25_CBIT;
buff[6] |= AX25_EBIT;
buff[6] |= AX25_SSSID_SPARE;
buff += AX25_ADDR_LEN;
*buff++ = AX25_UI; /* UI */
/* Append a suitable AX.25 PID */
switch (type) {
case ETH_P_IP:
*buff++ = AX25_P_IP;
break;
case ETH_P_ARP:
*buff++ = AX25_P_ARP;
break;
default:
printk(KERN_ERR "AX.25: ax25_encapsulate - wrong protocol type 0x%2.2x\n", type);
*buff++ = 0;
break;
}
if (daddr != NULL)
return AX25_HEADER_LEN;
return -AX25_HEADER_LEN; /* Unfinished header */
}
int ax25_rebuild_header(struct sk_buff *skb)
{
struct sk_buff *ourskb;
unsigned char *bp = skb->data;
struct net_device *dev;
ax25_address *src, *dst;
ax25_dev *ax25_dev;
ax25_route _route, *route = &_route;
ax25_cb *ax25;
dst = (ax25_address *)(bp + 1);
src = (ax25_address *)(bp + 8);
if (arp_find(bp + 1, skb))
return 1;
route = ax25_rt_find_route(route, dst, NULL);
dev = route->dev;
if (dev == NULL)
dev = skb->dev;
if ((ax25_dev = ax25_dev_ax25dev(dev)) == NULL) {
goto put;
}
if (bp[16] == AX25_P_IP) {
if (route->ip_mode == 'V' || (route->ip_mode == ' ' && ax25_dev->values[AX25_VALUES_IPDEFMODE])) {
/*
* We copy the buffer and release the original thereby
* keeping it straight
*
* Note: we report 1 back so the caller will
* not feed the frame direct to the physical device
* We don't want that to happen. (It won't be upset
* as we have pulled the frame from the queue by
* freeing it).
*
* NB: TCP modifies buffers that are still
* on a device queue, thus we use skb_copy()
* instead of using skb_clone() unless this
* gets fixed.
*/
ax25_address src_c;
ax25_address dst_c;
if ((ourskb = skb_copy(skb, GFP_ATOMIC)) == NULL) {
kfree_skb(skb);
goto put;
}
if (skb->sk != NULL)
skb_set_owner_w(ourskb, skb->sk);
kfree_skb(skb);
/* dl9sau: bugfix
* after kfree_skb(), dst and src which were pointer
* to bp which is part of skb->data would not be valid
* anymore hope that after skb_pull(ourskb, ..) our
* dsc_c and src_c will not become invalid
*/
bp = ourskb->data;
dst_c = *(ax25_address *)(bp + 1);
src_c = *(ax25_address *)(bp + 8);
skb_pull(ourskb, AX25_HEADER_LEN - 1); /* Keep PID */
ourskb->nh.raw = ourskb->data;
ax25=ax25_send_frame(
ourskb,
ax25_dev->values[AX25_VALUES_PACLEN],
&src_c,
&dst_c, route->digipeat, dev);
if (ax25) {
ax25_cb_put(ax25);
}
goto put;
}
}
bp[7] &= ~AX25_CBIT;
bp[7] &= ~AX25_EBIT;
bp[7] |= AX25_SSSID_SPARE;
bp[14] &= ~AX25_CBIT;
bp[14] |= AX25_EBIT;
bp[14] |= AX25_SSSID_SPARE;
skb_pull(skb, AX25_KISS_HEADER_LEN);
if (route->digipeat != NULL) {
if ((ourskb = ax25_rt_build_path(skb, src, dst, route->digipeat)) == NULL) {
kfree_skb(skb);
goto put;
}
skb = ourskb;
}
skb->dev = dev;
ax25_queue_xmit(skb);
put:
ax25_put_route(route);
return 1;
}
#else /* INET */
int ax25_encapsulate(struct sk_buff *skb, struct net_device *dev, unsigned short type, void *daddr, void *saddr, unsigned len)
{
return -AX25_HEADER_LEN;
}
int ax25_rebuild_header(struct sk_buff *skb)
{
return 1;
}
#endif

383
net/ax25/ax25_out.c Normal file
View File

@@ -0,0 +1,383 @@
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Copyright (C) Alan Cox GW4PTS (alan@lxorguk.ukuu.org.uk)
* Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk)
* Copyright (C) Joerg Reuter DL1BKE (jreuter@yaina.de)
*/
#include <linux/config.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/socket.h>
#include <linux/in.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/timer.h>
#include <linux/string.h>
#include <linux/sockios.h>
#include <linux/spinlock.h>
#include <linux/net.h>
#include <net/ax25.h>
#include <linux/inet.h>
#include <linux/netdevice.h>
#include <linux/skbuff.h>
#include <linux/netfilter.h>
#include <net/sock.h>
#include <asm/uaccess.h>
#include <asm/system.h>
#include <linux/fcntl.h>
#include <linux/mm.h>
#include <linux/interrupt.h>
static DEFINE_SPINLOCK(ax25_frag_lock);
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_cb *ax25;
/*
* Take the default packet length for the device if zero is
* specified.
*/
if (paclen == 0) {
if ((ax25_dev = ax25_dev_ax25dev(dev)) == NULL)
return NULL;
paclen = ax25_dev->values[AX25_VALUES_PACLEN];
}
/*
* Look for an existing connection.
*/
if ((ax25 = ax25_find_cb(src, dest, digi, dev)) != NULL) {
ax25_output(ax25, paclen, skb);
return ax25; /* It already existed */
}
if ((ax25_dev = ax25_dev_ax25dev(dev)) == NULL)
return NULL;
if ((ax25 = ax25_create_cb()) == NULL)
return NULL;
ax25_fillin_cb(ax25, ax25_dev);
ax25->source_addr = *src;
ax25->dest_addr = *dest;
if (digi != NULL) {
if ((ax25->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL) {
ax25_cb_put(ax25);
return NULL;
}
memcpy(ax25->digipeat, digi, sizeof(ax25_digi));
}
switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) {
case AX25_PROTO_STD_SIMPLEX:
case AX25_PROTO_STD_DUPLEX:
ax25_std_establish_data_link(ax25);
break;
#ifdef CONFIG_AX25_DAMA_SLAVE
case AX25_PROTO_DAMA_SLAVE:
if (ax25_dev->dama.slave)
ax25_ds_establish_data_link(ax25);
else
ax25_std_establish_data_link(ax25);
break;
#endif
}
ax25_cb_add(ax25);
ax25->state = AX25_STATE_1;
ax25_start_heartbeat(ax25);
ax25_output(ax25, paclen, skb);
return ax25; /* We had to create it */
}
/*
* All outgoing AX.25 I frames pass via this routine. Therefore this is
* where the fragmentation of frames takes place. If fragment is set to
* zero then we are not allowed to do fragmentation, even if the frame
* is too large.
*/
void ax25_output(ax25_cb *ax25, int paclen, struct sk_buff *skb)
{
struct sk_buff *skbn;
unsigned char *p;
int frontlen, len, fragno, ka9qfrag, first = 1;
if ((skb->len - 1) > paclen) {
if (*skb->data == AX25_P_TEXT) {
skb_pull(skb, 1); /* skip PID */
ka9qfrag = 0;
} else {
paclen -= 2; /* Allow for fragment control info */
ka9qfrag = 1;
}
fragno = skb->len / paclen;
if (skb->len % paclen == 0) fragno--;
frontlen = skb_headroom(skb); /* Address space + CTRL */
while (skb->len > 0) {
spin_lock_bh(&ax25_frag_lock);
if ((skbn = alloc_skb(paclen + 2 + frontlen, GFP_ATOMIC)) == NULL) {
spin_unlock_bh(&ax25_frag_lock);
printk(KERN_CRIT "AX.25: ax25_output - out of memory\n");
return;
}
if (skb->sk != NULL)
skb_set_owner_w(skbn, skb->sk);
spin_unlock_bh(&ax25_frag_lock);
len = (paclen > skb->len) ? skb->len : paclen;
if (ka9qfrag == 1) {
skb_reserve(skbn, frontlen + 2);
skbn->nh.raw = skbn->data + (skb->nh.raw - skb->data);
memcpy(skb_put(skbn, len), skb->data, len);
p = skb_push(skbn, 2);
*p++ = AX25_P_SEGMENT;
*p = fragno--;
if (first) {
*p |= AX25_SEG_FIRST;
first = 0;
}
} else {
skb_reserve(skbn, frontlen + 1);
skbn->nh.raw = skbn->data + (skb->nh.raw - skb->data);
memcpy(skb_put(skbn, len), skb->data, len);
p = skb_push(skbn, 1);
*p = AX25_P_TEXT;
}
skb_pull(skb, len);
skb_queue_tail(&ax25->write_queue, skbn); /* Throw it on the queue */
}
kfree_skb(skb);
} else {
skb_queue_tail(&ax25->write_queue, skb); /* Throw it on the queue */
}
switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) {
case AX25_PROTO_STD_SIMPLEX:
case AX25_PROTO_STD_DUPLEX:
ax25_kick(ax25);
break;
#ifdef CONFIG_AX25_DAMA_SLAVE
/*
* A DAMA slave is _required_ to work as normal AX.25L2V2
* if no DAMA master is available.
*/
case AX25_PROTO_DAMA_SLAVE:
if (!ax25->ax25_dev->dama.slave) ax25_kick(ax25);
break;
#endif
}
}
/*
* This procedure is passed a buffer descriptor for an iframe. It builds
* the rest of the control part of the frame and then writes it out.
*/
static void ax25_send_iframe(ax25_cb *ax25, struct sk_buff *skb, int poll_bit)
{
unsigned char *frame;
if (skb == NULL)
return;
skb->nh.raw = skb->data;
if (ax25->modulus == AX25_MODULUS) {
frame = skb_push(skb, 1);
*frame = AX25_I;
*frame |= (poll_bit) ? AX25_PF : 0;
*frame |= (ax25->vr << 5);
*frame |= (ax25->vs << 1);
} else {
frame = skb_push(skb, 2);
frame[0] = AX25_I;
frame[0] |= (ax25->vs << 1);
frame[1] = (poll_bit) ? AX25_EPF : 0;
frame[1] |= (ax25->vr << 1);
}
ax25_start_idletimer(ax25);
ax25_transmit_buffer(ax25, skb, AX25_COMMAND);
}
void ax25_kick(ax25_cb *ax25)
{
struct sk_buff *skb, *skbn;
int last = 1;
unsigned short start, end, next;
if (ax25->state != AX25_STATE_3 && ax25->state != AX25_STATE_4)
return;
if (ax25->condition & AX25_COND_PEER_RX_BUSY)
return;
if (skb_peek(&ax25->write_queue) == NULL)
return;
start = (skb_peek(&ax25->ack_queue) == NULL) ? ax25->va : ax25->vs;
end = (ax25->va + ax25->window) % ax25->modulus;
if (start == end)
return;
ax25->vs = start;
/*
* Transmit data until either we're out of data to send or
* the window is full. Send a poll on the final I frame if
* the window is filled.
*/
/*
* Dequeue the frame and copy it.
*/
skb = skb_dequeue(&ax25->write_queue);
do {
if ((skbn = skb_clone(skb, GFP_ATOMIC)) == NULL) {
skb_queue_head(&ax25->write_queue, skb);
break;
}
if (skb->sk != NULL)
skb_set_owner_w(skbn, skb->sk);
next = (ax25->vs + 1) % ax25->modulus;
last = (next == end);
/*
* Transmit the frame copy.
* bke 960114: do not set the Poll bit on the last frame
* in DAMA mode.
*/
switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) {
case AX25_PROTO_STD_SIMPLEX:
case AX25_PROTO_STD_DUPLEX:
ax25_send_iframe(ax25, skbn, (last) ? AX25_POLLON : AX25_POLLOFF);
break;
#ifdef CONFIG_AX25_DAMA_SLAVE
case AX25_PROTO_DAMA_SLAVE:
ax25_send_iframe(ax25, skbn, AX25_POLLOFF);
break;
#endif
}
ax25->vs = next;
/*
* Requeue the original data frame.
*/
skb_queue_tail(&ax25->ack_queue, skb);
} while (!last && (skb = skb_dequeue(&ax25->write_queue)) != NULL);
ax25->condition &= ~AX25_COND_ACK_PENDING;
if (!ax25_t1timer_running(ax25)) {
ax25_stop_t3timer(ax25);
ax25_calculate_t1(ax25);
ax25_start_t1timer(ax25);
}
}
void ax25_transmit_buffer(ax25_cb *ax25, struct sk_buff *skb, int type)
{
struct sk_buff *skbn;
unsigned char *ptr;
int headroom;
if (ax25->ax25_dev == NULL) {
ax25_disconnect(ax25, ENETUNREACH);
return;
}
headroom = ax25_addr_size(ax25->digipeat);
if (skb_headroom(skb) < headroom) {
if ((skbn = skb_realloc_headroom(skb, headroom)) == NULL) {
printk(KERN_CRIT "AX.25: ax25_transmit_buffer - out of memory\n");
kfree_skb(skb);
return;
}
if (skb->sk != NULL)
skb_set_owner_w(skbn, skb->sk);
kfree_skb(skb);
skb = skbn;
}
ptr = skb_push(skb, headroom);
ax25_addr_build(ptr, &ax25->source_addr, &ax25->dest_addr, ax25->digipeat, type, ax25->modulus);
skb->dev = ax25->ax25_dev->dev;
ax25_queue_xmit(skb);
}
/*
* A small shim to dev_queue_xmit to add the KISS control byte, and do
* any packet forwarding in operation.
*/
void ax25_queue_xmit(struct sk_buff *skb)
{
unsigned char *ptr;
skb->protocol = htons(ETH_P_AX25);
skb->dev = ax25_fwd_dev(skb->dev);
ptr = skb_push(skb, 1);
*ptr = 0x00; /* KISS */
dev_queue_xmit(skb);
}
int ax25_check_iframes_acked(ax25_cb *ax25, unsigned short nr)
{
if (ax25->vs == nr) {
ax25_frames_acked(ax25, nr);
ax25_calculate_rtt(ax25);
ax25_stop_t1timer(ax25);
ax25_start_t3timer(ax25);
return 1;
} else {
if (ax25->va != nr) {
ax25_frames_acked(ax25, nr);
ax25_calculate_t1(ax25);
ax25_start_t1timer(ax25);
return 1;
}
}
return 0;
}

534
net/ax25/ax25_route.c Normal file
View File

@@ -0,0 +1,534 @@
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Copyright (C) Alan Cox GW4PTS (alan@lxorguk.ukuu.org.uk)
* Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk)
* Copyright (C) Steven Whitehouse GW7RRM (stevew@acm.org)
* Copyright (C) Joerg Reuter DL1BKE (jreuter@yaina.de)
* Copyright (C) Hans-Joachim Hetscher DD8NE (dd8ne@bnv-bamberg.de)
* Copyright (C) Frederic Rible F1OAT (frible@teaser.fr)
*/
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/socket.h>
#include <linux/timer.h>
#include <linux/in.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/string.h>
#include <linux/sockios.h>
#include <linux/net.h>
#include <net/ax25.h>
#include <linux/inet.h>
#include <linux/netdevice.h>
#include <linux/if_arp.h>
#include <linux/skbuff.h>
#include <linux/spinlock.h>
#include <net/sock.h>
#include <asm/uaccess.h>
#include <asm/system.h>
#include <linux/fcntl.h>
#include <linux/mm.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/seq_file.h>
static ax25_route *ax25_route_list;
static DEFINE_RWLOCK(ax25_route_lock);
static ax25_route *ax25_get_route(ax25_address *, struct net_device *);
void ax25_rt_device_down(struct net_device *dev)
{
ax25_route *s, *t, *ax25_rt;
write_lock(&ax25_route_lock);
ax25_rt = ax25_route_list;
while (ax25_rt != NULL) {
s = ax25_rt;
ax25_rt = ax25_rt->next;
if (s->dev == dev) {
if (ax25_route_list == s) {
ax25_route_list = s->next;
if (s->digipeat != NULL)
kfree(s->digipeat);
kfree(s);
} else {
for (t = ax25_route_list; t != NULL; t = t->next) {
if (t->next == s) {
t->next = s->next;
if (s->digipeat != NULL)
kfree(s->digipeat);
kfree(s);
break;
}
}
}
}
}
write_unlock(&ax25_route_lock);
}
static int ax25_rt_add(struct ax25_routes_struct *route)
{
ax25_route *ax25_rt;
ax25_dev *ax25_dev;
int i;
if ((ax25_dev = ax25_addr_ax25dev(&route->port_addr)) == NULL)
return -EINVAL;
if (route->digi_count > AX25_MAX_DIGIS)
return -EINVAL;
write_lock(&ax25_route_lock);
ax25_rt = ax25_route_list;
while (ax25_rt != NULL) {
if (ax25cmp(&ax25_rt->callsign, &route->dest_addr) == 0 &&
ax25_rt->dev == ax25_dev->dev) {
if (ax25_rt->digipeat != NULL) {
kfree(ax25_rt->digipeat);
ax25_rt->digipeat = NULL;
}
if (route->digi_count != 0) {
if ((ax25_rt->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL) {
write_unlock(&ax25_route_lock);
return -ENOMEM;
}
ax25_rt->digipeat->lastrepeat = -1;
ax25_rt->digipeat->ndigi = route->digi_count;
for (i = 0; i < route->digi_count; i++) {
ax25_rt->digipeat->repeated[i] = 0;
ax25_rt->digipeat->calls[i] = route->digi_addr[i];
}
}
write_unlock(&ax25_route_lock);
return 0;
}
ax25_rt = ax25_rt->next;
}
if ((ax25_rt = kmalloc(sizeof(ax25_route), GFP_ATOMIC)) == NULL) {
write_unlock(&ax25_route_lock);
return -ENOMEM;
}
atomic_set(&ax25_rt->ref, 0);
ax25_rt->callsign = route->dest_addr;
ax25_rt->dev = ax25_dev->dev;
ax25_rt->digipeat = NULL;
ax25_rt->ip_mode = ' ';
if (route->digi_count != 0) {
if ((ax25_rt->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL) {
write_unlock(&ax25_route_lock);
kfree(ax25_rt);
return -ENOMEM;
}
ax25_rt->digipeat->lastrepeat = -1;
ax25_rt->digipeat->ndigi = route->digi_count;
for (i = 0; i < route->digi_count; i++) {
ax25_rt->digipeat->repeated[i] = 0;
ax25_rt->digipeat->calls[i] = route->digi_addr[i];
}
}
ax25_rt->next = ax25_route_list;
ax25_route_list = ax25_rt;
write_unlock(&ax25_route_lock);
return 0;
}
static void 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);
return;
}
/*
* Uh... Route is still in use; we can't yet destroy it. Retry later.
*/
init_timer(&ax25_rt->timer);
ax25_rt->timer.data = (unsigned long) ax25_rt;
ax25_rt->timer.function = (void *) ax25_rt_destroy;
ax25_rt->timer.expires = jiffies + 5 * HZ;
add_timer(&ax25_rt->timer);
}
static int ax25_rt_del(struct ax25_routes_struct *route)
{
ax25_route *s, *t, *ax25_rt;
ax25_dev *ax25_dev;
if ((ax25_dev = ax25_addr_ax25dev(&route->port_addr)) == NULL)
return -EINVAL;
write_lock(&ax25_route_lock);
ax25_rt = ax25_route_list;
while (ax25_rt != NULL) {
s = ax25_rt;
ax25_rt = ax25_rt->next;
if (s->dev == ax25_dev->dev &&
ax25cmp(&route->dest_addr, &s->callsign) == 0) {
if (ax25_route_list == s) {
ax25_route_list = s->next;
ax25_rt_destroy(s);
} else {
for (t = ax25_route_list; t != NULL; t = t->next) {
if (t->next == s) {
t->next = s->next;
ax25_rt_destroy(s);
break;
}
}
}
}
}
write_unlock(&ax25_route_lock);
return 0;
}
static int ax25_rt_opt(struct ax25_route_opt_struct *rt_option)
{
ax25_route *ax25_rt;
ax25_dev *ax25_dev;
int err = 0;
if ((ax25_dev = ax25_addr_ax25dev(&rt_option->port_addr)) == NULL)
return -EINVAL;
write_lock(&ax25_route_lock);
ax25_rt = ax25_route_list;
while (ax25_rt != NULL) {
if (ax25_rt->dev == ax25_dev->dev &&
ax25cmp(&rt_option->dest_addr, &ax25_rt->callsign) == 0) {
switch (rt_option->cmd) {
case AX25_SET_RT_IPMODE:
switch (rt_option->arg) {
case ' ':
case 'D':
case 'V':
ax25_rt->ip_mode = rt_option->arg;
break;
default:
err = -EINVAL;
goto out;
}
break;
default:
err = -EINVAL;
goto out;
}
}
ax25_rt = ax25_rt->next;
}
out:
write_unlock(&ax25_route_lock);
return err;
}
int ax25_rt_ioctl(unsigned int cmd, void __user *arg)
{
struct ax25_route_opt_struct rt_option;
struct ax25_routes_struct route;
switch (cmd) {
case SIOCADDRT:
if (copy_from_user(&route, arg, sizeof(route)))
return -EFAULT;
return ax25_rt_add(&route);
case SIOCDELRT:
if (copy_from_user(&route, arg, sizeof(route)))
return -EFAULT;
return ax25_rt_del(&route);
case SIOCAX25OPTRT:
if (copy_from_user(&rt_option, arg, sizeof(rt_option)))
return -EFAULT;
return ax25_rt_opt(&rt_option);
default:
return -EINVAL;
}
}
#ifdef CONFIG_PROC_FS
static void *ax25_rt_seq_start(struct seq_file *seq, loff_t *pos)
{
struct ax25_route *ax25_rt;
int i = 1;
read_lock(&ax25_route_lock);
if (*pos == 0)
return SEQ_START_TOKEN;
for (ax25_rt = ax25_route_list; ax25_rt != NULL; ax25_rt = ax25_rt->next) {
if (i == *pos)
return ax25_rt;
++i;
}
return NULL;
}
static void *ax25_rt_seq_next(struct seq_file *seq, void *v, loff_t *pos)
{
++*pos;
return (v == SEQ_START_TOKEN) ? ax25_route_list :
((struct ax25_route *) v)->next;
}
static void ax25_rt_seq_stop(struct seq_file *seq, void *v)
{
read_unlock(&ax25_route_lock);
}
static int ax25_rt_seq_show(struct seq_file *seq, void *v)
{
if (v == SEQ_START_TOKEN)
seq_puts(seq, "callsign dev mode digipeaters\n");
else {
struct ax25_route *ax25_rt = v;
const char *callsign;
int i;
if (ax25cmp(&ax25_rt->callsign, &null_ax25_address) == 0)
callsign = "default";
else
callsign = ax2asc(&ax25_rt->callsign);
seq_printf(seq, "%-9s %-4s",
callsign,
ax25_rt->dev ? ax25_rt->dev->name : "???");
switch (ax25_rt->ip_mode) {
case 'V':
seq_puts(seq, " vc");
break;
case 'D':
seq_puts(seq, " dg");
break;
default:
seq_puts(seq, " *");
break;
}
if (ax25_rt->digipeat != NULL)
for (i = 0; i < ax25_rt->digipeat->ndigi; i++)
seq_printf(seq, " %s", ax2asc(&ax25_rt->digipeat->calls[i]));
seq_puts(seq, "\n");
}
return 0;
}
static struct seq_operations ax25_rt_seqops = {
.start = ax25_rt_seq_start,
.next = ax25_rt_seq_next,
.stop = ax25_rt_seq_stop,
.show = ax25_rt_seq_show,
};
static int ax25_rt_info_open(struct inode *inode, struct file *file)
{
return seq_open(file, &ax25_rt_seqops);
}
struct file_operations ax25_route_fops = {
.owner = THIS_MODULE,
.open = ax25_rt_info_open,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release,
};
#endif
/*
* Find AX.25 route
*
* Only routes with a refernce rout of zero can be destroyed.
*/
static ax25_route *ax25_get_route(ax25_address *addr, struct net_device *dev)
{
ax25_route *ax25_spe_rt = NULL;
ax25_route *ax25_def_rt = NULL;
ax25_route *ax25_rt;
read_lock(&ax25_route_lock);
/*
* Bind to the physical interface we heard them on, or the default
* route if none is found;
*/
for (ax25_rt = ax25_route_list; ax25_rt != NULL; ax25_rt = ax25_rt->next) {
if (dev == NULL) {
if (ax25cmp(&ax25_rt->callsign, addr) == 0 && ax25_rt->dev != NULL)
ax25_spe_rt = ax25_rt;
if (ax25cmp(&ax25_rt->callsign, &null_ax25_address) == 0 && ax25_rt->dev != NULL)
ax25_def_rt = ax25_rt;
} else {
if (ax25cmp(&ax25_rt->callsign, addr) == 0 && ax25_rt->dev == dev)
ax25_spe_rt = ax25_rt;
if (ax25cmp(&ax25_rt->callsign, &null_ax25_address) == 0 && ax25_rt->dev == dev)
ax25_def_rt = ax25_rt;
}
}
ax25_rt = ax25_def_rt;
if (ax25_spe_rt != NULL)
ax25_rt = ax25_spe_rt;
if (ax25_rt != NULL)
atomic_inc(&ax25_rt->ref);
read_unlock(&ax25_route_lock);
return ax25_rt;
}
/*
* Adjust path: If you specify a default route and want to connect
* a target on the digipeater path but w/o having a special route
* set before, the path has to be truncated from your target on.
*/
static inline void ax25_adjust_path(ax25_address *addr, ax25_digi *digipeat)
{
int k;
for (k = 0; k < digipeat->ndigi; k++) {
if (ax25cmp(addr, &digipeat->calls[k]) == 0)
break;
}
digipeat->ndigi = k;
}
/*
* Find which interface to use.
*/
int ax25_rt_autobind(ax25_cb *ax25, ax25_address *addr)
{
ax25_route *ax25_rt;
ax25_address *call;
int err;
if ((ax25_rt = ax25_get_route(addr, NULL)) == NULL)
return -EHOSTUNREACH;
if ((ax25->ax25_dev = ax25_dev_ax25dev(ax25_rt->dev)) == NULL) {
err = -EHOSTUNREACH;
goto put;
}
if ((call = ax25_findbyuid(current->euid)) == NULL) {
if (ax25_uid_policy && !capable(CAP_NET_BIND_SERVICE)) {
err = -EPERM;
goto put;
}
call = (ax25_address *)ax25->ax25_dev->dev->dev_addr;
}
ax25->source_addr = *call;
if (ax25_rt->digipeat != NULL) {
if ((ax25->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL) {
err = -ENOMEM;
goto put;
}
memcpy(ax25->digipeat, ax25_rt->digipeat, sizeof(ax25_digi));
ax25_adjust_path(addr, ax25->digipeat);
}
if (ax25->sk != NULL) {
bh_lock_sock(ax25->sk);
sock_reset_flag(ax25->sk, SOCK_ZAPPED);
bh_unlock_sock(ax25->sk);
}
put:
ax25_put_route(ax25_rt);
return 0;
}
ax25_route *ax25_rt_find_route(ax25_route * route, ax25_address *addr,
struct net_device *dev)
{
ax25_route *ax25_rt;
if ((ax25_rt = ax25_get_route(addr, dev)))
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 *skbn;
unsigned char *bp;
int len;
len = digi->ndigi * AX25_ADDR_LEN;
if (skb_headroom(skb) < len) {
if ((skbn = skb_realloc_headroom(skb, len)) == NULL) {
printk(KERN_CRIT "AX.25: ax25_dg_build_path - out of memory\n");
return NULL;
}
if (skb->sk != NULL)
skb_set_owner_w(skbn, skb->sk);
kfree_skb(skb);
skb = skbn;
}
bp = skb_push(skb, len);
ax25_addr_build(bp, src, dest, digi, AX25_COMMAND, AX25_MODULUS);
return skb;
}
/*
* Free all memory associated with routing structures.
*/
void __exit ax25_rt_free(void)
{
ax25_route *s, *ax25_rt = ax25_route_list;
write_lock(&ax25_route_lock);
while (ax25_rt != NULL) {
s = ax25_rt;
ax25_rt = ax25_rt->next;
if (s->digipeat != NULL)
kfree(s->digipeat);
kfree(s);
}
write_unlock(&ax25_route_lock);
}

449
net/ax25/ax25_std_in.c Normal file
View File

@@ -0,0 +1,449 @@
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Copyright (C) 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)
*
* Most of this code is based on the SDL diagrams published in the 7th ARRL
* Computer Networking Conference papers. The diagrams have mistakes in them,
* but are mostly correct. Before you modify the code could you read the SDL
* diagrams as the code is not obvious and probably very easy to break.
*/
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/socket.h>
#include <linux/in.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/timer.h>
#include <linux/string.h>
#include <linux/sockios.h>
#include <linux/net.h>
#include <net/ax25.h>
#include <linux/inet.h>
#include <linux/netdevice.h>
#include <linux/skbuff.h>
#include <net/sock.h>
#include <net/ip.h> /* For ip_rcv */
#include <net/tcp.h>
#include <asm/uaccess.h>
#include <asm/system.h>
#include <linux/fcntl.h>
#include <linux/mm.h>
#include <linux/interrupt.h>
/*
* State machine for state 1, Awaiting Connection State.
* The handling of the timer(s) is in file ax25_std_timer.c.
* Handling of state 0 and connection release is in ax25.c.
*/
static int ax25_std_state1_machine(ax25_cb *ax25, struct sk_buff *skb, int frametype, int pf, int type)
{
switch (frametype) {
case AX25_SABM:
ax25->modulus = AX25_MODULUS;
ax25->window = ax25->ax25_dev->values[AX25_VALUES_WINDOW];
ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE);
break;
case AX25_SABME:
ax25->modulus = AX25_EMODULUS;
ax25->window = ax25->ax25_dev->values[AX25_VALUES_EWINDOW];
ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE);
break;
case AX25_DISC:
ax25_send_control(ax25, AX25_DM, pf, AX25_RESPONSE);
break;
case AX25_UA:
if (pf) {
ax25_calculate_rtt(ax25);
ax25_stop_t1timer(ax25);
ax25_start_t3timer(ax25);
ax25_start_idletimer(ax25);
ax25->vs = 0;
ax25->va = 0;
ax25->vr = 0;
ax25->state = AX25_STATE_3;
ax25->n2count = 0;
if (ax25->sk != NULL) {
bh_lock_sock(ax25->sk);
ax25->sk->sk_state = TCP_ESTABLISHED;
/* For WAIT_SABM connections we will produce an accept ready socket here */
if (!sock_flag(ax25->sk, SOCK_DEAD))
ax25->sk->sk_state_change(ax25->sk);
bh_unlock_sock(ax25->sk);
}
}
break;
case AX25_DM:
if (pf) {
if (ax25->modulus == AX25_MODULUS) {
ax25_disconnect(ax25, ECONNREFUSED);
} else {
ax25->modulus = AX25_MODULUS;
ax25->window = ax25->ax25_dev->values[AX25_VALUES_WINDOW];
}
}
break;
default:
break;
}
return 0;
}
/*
* State machine for state 2, Awaiting Release State.
* The handling of the timer(s) is in file ax25_std_timer.c
* Handling of state 0 and connection release is in ax25.c.
*/
static int ax25_std_state2_machine(ax25_cb *ax25, struct sk_buff *skb, int frametype, int pf, int type)
{
switch (frametype) {
case AX25_SABM:
case AX25_SABME:
ax25_send_control(ax25, AX25_DM, pf, AX25_RESPONSE);
break;
case AX25_DISC:
ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE);
ax25_disconnect(ax25, 0);
break;
case AX25_DM:
case AX25_UA:
if (pf)
ax25_disconnect(ax25, 0);
break;
case AX25_I:
case AX25_REJ:
case AX25_RNR:
case AX25_RR:
if (pf) ax25_send_control(ax25, AX25_DM, AX25_POLLON, AX25_RESPONSE);
break;
default:
break;
}
return 0;
}
/*
* State machine for state 3, Connected State.
* The handling of the timer(s) is in file ax25_std_timer.c
* Handling of state 0 and connection release is in ax25.c.
*/
static int ax25_std_state3_machine(ax25_cb *ax25, struct sk_buff *skb, int frametype, int ns, int nr, int pf, int type)
{
int queued = 0;
switch (frametype) {
case AX25_SABM:
case AX25_SABME:
if (frametype == AX25_SABM) {
ax25->modulus = AX25_MODULUS;
ax25->window = ax25->ax25_dev->values[AX25_VALUES_WINDOW];
} else {
ax25->modulus = AX25_EMODULUS;
ax25->window = ax25->ax25_dev->values[AX25_VALUES_EWINDOW];
}
ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE);
ax25_stop_t1timer(ax25);
ax25_stop_t2timer(ax25);
ax25_start_t3timer(ax25);
ax25_start_idletimer(ax25);
ax25->condition = 0x00;
ax25->vs = 0;
ax25->va = 0;
ax25->vr = 0;
ax25_requeue_frames(ax25);
break;
case AX25_DISC:
ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE);
ax25_disconnect(ax25, 0);
break;
case AX25_DM:
ax25_disconnect(ax25, ECONNRESET);
break;
case AX25_RR:
case AX25_RNR:
if (frametype == AX25_RR)
ax25->condition &= ~AX25_COND_PEER_RX_BUSY;
else
ax25->condition |= AX25_COND_PEER_RX_BUSY;
if (type == AX25_COMMAND && pf)
ax25_std_enquiry_response(ax25);
if (ax25_validate_nr(ax25, nr)) {
ax25_check_iframes_acked(ax25, nr);
} else {
ax25_std_nr_error_recovery(ax25);
ax25->state = AX25_STATE_1;
}
break;
case AX25_REJ:
ax25->condition &= ~AX25_COND_PEER_RX_BUSY;
if (type == AX25_COMMAND && pf)
ax25_std_enquiry_response(ax25);
if (ax25_validate_nr(ax25, nr)) {
ax25_frames_acked(ax25, nr);
ax25_calculate_rtt(ax25);
ax25_stop_t1timer(ax25);
ax25_start_t3timer(ax25);
ax25_requeue_frames(ax25);
} else {
ax25_std_nr_error_recovery(ax25);
ax25->state = AX25_STATE_1;
}
break;
case AX25_I:
if (!ax25_validate_nr(ax25, nr)) {
ax25_std_nr_error_recovery(ax25);
ax25->state = AX25_STATE_1;
break;
}
if (ax25->condition & AX25_COND_PEER_RX_BUSY) {
ax25_frames_acked(ax25, nr);
} else {
ax25_check_iframes_acked(ax25, nr);
}
if (ax25->condition & AX25_COND_OWN_RX_BUSY) {
if (pf) ax25_std_enquiry_response(ax25);
break;
}
if (ns == ax25->vr) {
ax25->vr = (ax25->vr + 1) % ax25->modulus;
queued = ax25_rx_iframe(ax25, skb);
if (ax25->condition & AX25_COND_OWN_RX_BUSY)
ax25->vr = ns; /* ax25->vr - 1 */
ax25->condition &= ~AX25_COND_REJECT;
if (pf) {
ax25_std_enquiry_response(ax25);
} else {
if (!(ax25->condition & AX25_COND_ACK_PENDING)) {
ax25->condition |= AX25_COND_ACK_PENDING;
ax25_start_t2timer(ax25);
}
}
} else {
if (ax25->condition & AX25_COND_REJECT) {
if (pf) ax25_std_enquiry_response(ax25);
} else {
ax25->condition |= AX25_COND_REJECT;
ax25_send_control(ax25, AX25_REJ, pf, AX25_RESPONSE);
ax25->condition &= ~AX25_COND_ACK_PENDING;
}
}
break;
case AX25_FRMR:
case AX25_ILLEGAL:
ax25_std_establish_data_link(ax25);
ax25->state = AX25_STATE_1;
break;
default:
break;
}
return queued;
}
/*
* State machine for state 4, Timer Recovery State.
* The handling of the timer(s) is in file ax25_std_timer.c
* Handling of state 0 and connection release is in ax25.c.
*/
static int ax25_std_state4_machine(ax25_cb *ax25, struct sk_buff *skb, int frametype, int ns, int nr, int pf, int type)
{
int queued = 0;
switch (frametype) {
case AX25_SABM:
case AX25_SABME:
if (frametype == AX25_SABM) {
ax25->modulus = AX25_MODULUS;
ax25->window = ax25->ax25_dev->values[AX25_VALUES_WINDOW];
} else {
ax25->modulus = AX25_EMODULUS;
ax25->window = ax25->ax25_dev->values[AX25_VALUES_EWINDOW];
}
ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE);
ax25_stop_t1timer(ax25);
ax25_stop_t2timer(ax25);
ax25_start_t3timer(ax25);
ax25_start_idletimer(ax25);
ax25->condition = 0x00;
ax25->vs = 0;
ax25->va = 0;
ax25->vr = 0;
ax25->state = AX25_STATE_3;
ax25->n2count = 0;
ax25_requeue_frames(ax25);
break;
case AX25_DISC:
ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE);
ax25_disconnect(ax25, 0);
break;
case AX25_DM:
ax25_disconnect(ax25, ECONNRESET);
break;
case AX25_RR:
case AX25_RNR:
if (frametype == AX25_RR)
ax25->condition &= ~AX25_COND_PEER_RX_BUSY;
else
ax25->condition |= AX25_COND_PEER_RX_BUSY;
if (type == AX25_RESPONSE && pf) {
ax25_stop_t1timer(ax25);
ax25->n2count = 0;
if (ax25_validate_nr(ax25, nr)) {
ax25_frames_acked(ax25, nr);
if (ax25->vs == ax25->va) {
ax25_start_t3timer(ax25);
ax25->state = AX25_STATE_3;
} else {
ax25_requeue_frames(ax25);
}
} else {
ax25_std_nr_error_recovery(ax25);
ax25->state = AX25_STATE_1;
}
break;
}
if (type == AX25_COMMAND && pf)
ax25_std_enquiry_response(ax25);
if (ax25_validate_nr(ax25, nr)) {
ax25_frames_acked(ax25, nr);
} else {
ax25_std_nr_error_recovery(ax25);
ax25->state = AX25_STATE_1;
}
break;
case AX25_REJ:
ax25->condition &= ~AX25_COND_PEER_RX_BUSY;
if (pf && type == AX25_RESPONSE) {
ax25_stop_t1timer(ax25);
ax25->n2count = 0;
if (ax25_validate_nr(ax25, nr)) {
ax25_frames_acked(ax25, nr);
if (ax25->vs == ax25->va) {
ax25_start_t3timer(ax25);
ax25->state = AX25_STATE_3;
} else {
ax25_requeue_frames(ax25);
}
} else {
ax25_std_nr_error_recovery(ax25);
ax25->state = AX25_STATE_1;
}
break;
}
if (type == AX25_COMMAND && pf)
ax25_std_enquiry_response(ax25);
if (ax25_validate_nr(ax25, nr)) {
ax25_frames_acked(ax25, nr);
ax25_requeue_frames(ax25);
} else {
ax25_std_nr_error_recovery(ax25);
ax25->state = AX25_STATE_1;
}
break;
case AX25_I:
if (!ax25_validate_nr(ax25, nr)) {
ax25_std_nr_error_recovery(ax25);
ax25->state = AX25_STATE_1;
break;
}
ax25_frames_acked(ax25, nr);
if (ax25->condition & AX25_COND_OWN_RX_BUSY) {
if (pf)
ax25_std_enquiry_response(ax25);
break;
}
if (ns == ax25->vr) {
ax25->vr = (ax25->vr + 1) % ax25->modulus;
queued = ax25_rx_iframe(ax25, skb);
if (ax25->condition & AX25_COND_OWN_RX_BUSY)
ax25->vr = ns; /* ax25->vr - 1 */
ax25->condition &= ~AX25_COND_REJECT;
if (pf) {
ax25_std_enquiry_response(ax25);
} else {
if (!(ax25->condition & AX25_COND_ACK_PENDING)) {
ax25->condition |= AX25_COND_ACK_PENDING;
ax25_start_t2timer(ax25);
}
}
} else {
if (ax25->condition & AX25_COND_REJECT) {
if (pf) ax25_std_enquiry_response(ax25);
} else {
ax25->condition |= AX25_COND_REJECT;
ax25_send_control(ax25, AX25_REJ, pf, AX25_RESPONSE);
ax25->condition &= ~AX25_COND_ACK_PENDING;
}
}
break;
case AX25_FRMR:
case AX25_ILLEGAL:
ax25_std_establish_data_link(ax25);
ax25->state = AX25_STATE_1;
break;
default:
break;
}
return queued;
}
/*
* Higher level upcall for a LAPB frame
*/
int ax25_std_frame_in(ax25_cb *ax25, struct sk_buff *skb, int type)
{
int queued = 0, frametype, ns, nr, pf;
frametype = ax25_decode(ax25, skb, &ns, &nr, &pf);
switch (ax25->state) {
case AX25_STATE_1:
queued = ax25_std_state1_machine(ax25, skb, frametype, pf, type);
break;
case AX25_STATE_2:
queued = ax25_std_state2_machine(ax25, skb, frametype, pf, type);
break;
case AX25_STATE_3:
queued = ax25_std_state3_machine(ax25, skb, frametype, ns, nr, pf, type);
break;
case AX25_STATE_4:
queued = ax25_std_state4_machine(ax25, skb, frametype, ns, nr, pf, type);
break;
}
ax25_kick(ax25);
return queued;
}

88
net/ax25/ax25_std_subr.c Normal file
View File

@@ -0,0 +1,88 @@
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk)
*/
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/socket.h>
#include <linux/in.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/timer.h>
#include <linux/string.h>
#include <linux/sockios.h>
#include <linux/net.h>
#include <net/ax25.h>
#include <linux/inet.h>
#include <linux/netdevice.h>
#include <linux/skbuff.h>
#include <net/sock.h>
#include <asm/uaccess.h>
#include <asm/system.h>
#include <linux/fcntl.h>
#include <linux/mm.h>
#include <linux/interrupt.h>
/*
* The following routines are taken from page 170 of the 7th ARRL Computer
* Networking Conference paper, as is the whole state machine.
*/
void ax25_std_nr_error_recovery(ax25_cb *ax25)
{
ax25_std_establish_data_link(ax25);
}
void ax25_std_establish_data_link(ax25_cb *ax25)
{
ax25->condition = 0x00;
ax25->n2count = 0;
if (ax25->modulus == AX25_MODULUS)
ax25_send_control(ax25, AX25_SABM, AX25_POLLON, AX25_COMMAND);
else
ax25_send_control(ax25, AX25_SABME, AX25_POLLON, AX25_COMMAND);
ax25_calculate_t1(ax25);
ax25_stop_idletimer(ax25);
ax25_stop_t3timer(ax25);
ax25_stop_t2timer(ax25);
ax25_start_t1timer(ax25);
}
void ax25_std_transmit_enquiry(ax25_cb *ax25)
{
if (ax25->condition & AX25_COND_OWN_RX_BUSY)
ax25_send_control(ax25, AX25_RNR, AX25_POLLON, AX25_COMMAND);
else
ax25_send_control(ax25, AX25_RR, AX25_POLLON, AX25_COMMAND);
ax25->condition &= ~AX25_COND_ACK_PENDING;
ax25_calculate_t1(ax25);
ax25_start_t1timer(ax25);
}
void ax25_std_enquiry_response(ax25_cb *ax25)
{
if (ax25->condition & AX25_COND_OWN_RX_BUSY)
ax25_send_control(ax25, AX25_RNR, AX25_POLLON, AX25_RESPONSE);
else
ax25_send_control(ax25, AX25_RR, AX25_POLLON, AX25_RESPONSE);
ax25->condition &= ~AX25_COND_ACK_PENDING;
}
void ax25_std_timeout_response(ax25_cb *ax25)
{
if (ax25->condition & AX25_COND_OWN_RX_BUSY)
ax25_send_control(ax25, AX25_RNR, AX25_POLLOFF, AX25_RESPONSE);
else
ax25_send_control(ax25, AX25_RR, AX25_POLLOFF, AX25_RESPONSE);
ax25->condition &= ~AX25_COND_ACK_PENDING;
}

177
net/ax25/ax25_std_timer.c Normal file
View File

@@ -0,0 +1,177 @@
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Copyright (C) Alan Cox GW4PTS (alan@lxorguk.ukuu.org.uk)
* Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk)
* Copyright (C) Joerg Reuter DL1BKE (jreuter@yaina.de)
* Copyright (C) Frederic Rible F1OAT (frible@teaser.fr)
*/
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/socket.h>
#include <linux/in.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/timer.h>
#include <linux/string.h>
#include <linux/sockios.h>
#include <linux/net.h>
#include <net/ax25.h>
#include <linux/inet.h>
#include <linux/netdevice.h>
#include <linux/skbuff.h>
#include <net/sock.h>
#include <net/tcp.h>
#include <asm/uaccess.h>
#include <asm/system.h>
#include <linux/fcntl.h>
#include <linux/mm.h>
#include <linux/interrupt.h>
void ax25_std_heartbeat_expiry(ax25_cb *ax25)
{
struct sock *sk=ax25->sk;
if (sk)
bh_lock_sock(sk);
switch (ax25->state) {
case AX25_STATE_0:
/* Magic here: If we listen() and a new link dies before it
is accepted() it isn't 'dead' so doesn't get removed. */
if (!sk || sock_flag(sk, SOCK_DESTROY) ||
(sk->sk_state == TCP_LISTEN &&
sock_flag(sk, SOCK_DEAD))) {
if (sk) {
sock_hold(sk);
ax25_destroy_socket(ax25);
bh_unlock_sock(sk);
sock_put(sk);
} else
ax25_destroy_socket(ax25);
return;
}
break;
case AX25_STATE_3:
case AX25_STATE_4:
/*
* Check the state of the receive buffer.
*/
if (sk != NULL) {
if (atomic_read(&sk->sk_rmem_alloc) <
(sk->sk_rcvbuf / 2) &&
(ax25->condition & AX25_COND_OWN_RX_BUSY)) {
ax25->condition &= ~AX25_COND_OWN_RX_BUSY;
ax25->condition &= ~AX25_COND_ACK_PENDING;
ax25_send_control(ax25, AX25_RR, AX25_POLLOFF, AX25_RESPONSE);
break;
}
}
}
if (sk)
bh_unlock_sock(sk);
ax25_start_heartbeat(ax25);
}
void ax25_std_t2timer_expiry(ax25_cb *ax25)
{
if (ax25->condition & AX25_COND_ACK_PENDING) {
ax25->condition &= ~AX25_COND_ACK_PENDING;
ax25_std_timeout_response(ax25);
}
}
void ax25_std_t3timer_expiry(ax25_cb *ax25)
{
ax25->n2count = 0;
ax25_std_transmit_enquiry(ax25);
ax25->state = AX25_STATE_4;
}
void ax25_std_idletimer_expiry(ax25_cb *ax25)
{
ax25_clear_queues(ax25);
ax25->n2count = 0;
ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND);
ax25->state = AX25_STATE_2;
ax25_calculate_t1(ax25);
ax25_start_t1timer(ax25);
ax25_stop_t2timer(ax25);
ax25_stop_t3timer(ax25);
if (ax25->sk != NULL) {
bh_lock_sock(ax25->sk);
ax25->sk->sk_state = TCP_CLOSE;
ax25->sk->sk_err = 0;
ax25->sk->sk_shutdown |= SEND_SHUTDOWN;
if (!sock_flag(ax25->sk, SOCK_DEAD)) {
ax25->sk->sk_state_change(ax25->sk);
sock_set_flag(ax25->sk, SOCK_DEAD);
}
bh_unlock_sock(ax25->sk);
}
}
void ax25_std_t1timer_expiry(ax25_cb *ax25)
{
switch (ax25->state) {
case AX25_STATE_1:
if (ax25->n2count == ax25->n2) {
if (ax25->modulus == AX25_MODULUS) {
ax25_disconnect(ax25, ETIMEDOUT);
return;
} else {
ax25->modulus = AX25_MODULUS;
ax25->window = ax25->ax25_dev->values[AX25_VALUES_WINDOW];
ax25->n2count = 0;
ax25_send_control(ax25, AX25_SABM, AX25_POLLON, AX25_COMMAND);
}
} else {
ax25->n2count++;
if (ax25->modulus == AX25_MODULUS)
ax25_send_control(ax25, AX25_SABM, AX25_POLLON, AX25_COMMAND);
else
ax25_send_control(ax25, AX25_SABME, AX25_POLLON, AX25_COMMAND);
}
break;
case AX25_STATE_2:
if (ax25->n2count == ax25->n2) {
ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND);
ax25_disconnect(ax25, ETIMEDOUT);
return;
} else {
ax25->n2count++;
ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND);
}
break;
case AX25_STATE_3:
ax25->n2count = 1;
ax25_std_transmit_enquiry(ax25);
ax25->state = AX25_STATE_4;
break;
case AX25_STATE_4:
if (ax25->n2count == ax25->n2) {
ax25_send_control(ax25, AX25_DM, AX25_POLLON, AX25_RESPONSE);
ax25_disconnect(ax25, ETIMEDOUT);
return;
} else {
ax25->n2count++;
ax25_std_transmit_enquiry(ax25);
}
break;
}
ax25_calculate_t1(ax25);
ax25_start_t1timer(ax25);
}

295
net/ax25/ax25_subr.c Normal file
View File

@@ -0,0 +1,295 @@
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Copyright (C) Alan Cox GW4PTS (alan@lxorguk.ukuu.org.uk)
* Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk)
* Copyright (C) Joerg Reuter DL1BKE (jreuter@yaina.de)
* Copyright (C) Frederic Rible F1OAT (frible@teaser.fr)
*/
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/socket.h>
#include <linux/in.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/timer.h>
#include <linux/string.h>
#include <linux/sockios.h>
#include <linux/net.h>
#include <net/ax25.h>
#include <linux/inet.h>
#include <linux/netdevice.h>
#include <linux/skbuff.h>
#include <net/sock.h>
#include <net/tcp.h>
#include <asm/uaccess.h>
#include <asm/system.h>
#include <linux/fcntl.h>
#include <linux/mm.h>
#include <linux/interrupt.h>
/*
* This routine purges all the queues of frames.
*/
void ax25_clear_queues(ax25_cb *ax25)
{
skb_queue_purge(&ax25->write_queue);
skb_queue_purge(&ax25->ack_queue);
skb_queue_purge(&ax25->reseq_queue);
skb_queue_purge(&ax25->frag_queue);
}
/*
* This routine purges the input queue of those frames that have been
* acknowledged. This replaces the boxes labelled "V(a) <- N(r)" on the
* SDL diagram.
*/
void ax25_frames_acked(ax25_cb *ax25, unsigned short nr)
{
struct sk_buff *skb;
/*
* Remove all the ack-ed frames from the ack queue.
*/
if (ax25->va != nr) {
while (skb_peek(&ax25->ack_queue) != NULL && ax25->va != nr) {
skb = skb_dequeue(&ax25->ack_queue);
kfree_skb(skb);
ax25->va = (ax25->va + 1) % ax25->modulus;
}
}
}
void ax25_requeue_frames(ax25_cb *ax25)
{
struct sk_buff *skb, *skb_prev = NULL;
/*
* Requeue all the un-ack-ed frames on the output queue to be picked
* up by ax25_kick called from the timer. This arrangement handles the
* possibility of an empty output queue.
*/
while ((skb = skb_dequeue(&ax25->ack_queue)) != NULL) {
if (skb_prev == NULL)
skb_queue_head(&ax25->write_queue, skb);
else
skb_append(skb_prev, skb);
skb_prev = skb;
}
}
/*
* Validate that the value of nr is between va and vs. Return true or
* false for testing.
*/
int ax25_validate_nr(ax25_cb *ax25, unsigned short nr)
{
unsigned short vc = ax25->va;
while (vc != ax25->vs) {
if (nr == vc) return 1;
vc = (vc + 1) % ax25->modulus;
}
if (nr == ax25->vs) return 1;
return 0;
}
/*
* This routine is the centralised routine for parsing the control
* information for the different frame formats.
*/
int ax25_decode(ax25_cb *ax25, struct sk_buff *skb, int *ns, int *nr, int *pf)
{
unsigned char *frame;
int frametype = AX25_ILLEGAL;
frame = skb->data;
*ns = *nr = *pf = 0;
if (ax25->modulus == AX25_MODULUS) {
if ((frame[0] & AX25_S) == 0) {
frametype = AX25_I; /* I frame - carries NR/NS/PF */
*ns = (frame[0] >> 1) & 0x07;
*nr = (frame[0] >> 5) & 0x07;
*pf = frame[0] & AX25_PF;
} else if ((frame[0] & AX25_U) == 1) { /* S frame - take out PF/NR */
frametype = frame[0] & 0x0F;
*nr = (frame[0] >> 5) & 0x07;
*pf = frame[0] & AX25_PF;
} else if ((frame[0] & AX25_U) == 3) { /* U frame - take out PF */
frametype = frame[0] & ~AX25_PF;
*pf = frame[0] & AX25_PF;
}
skb_pull(skb, 1);
} else {
if ((frame[0] & AX25_S) == 0) {
frametype = AX25_I; /* I frame - carries NR/NS/PF */
*ns = (frame[0] >> 1) & 0x7F;
*nr = (frame[1] >> 1) & 0x7F;
*pf = frame[1] & AX25_EPF;
skb_pull(skb, 2);
} else if ((frame[0] & AX25_U) == 1) { /* S frame - take out PF/NR */
frametype = frame[0] & 0x0F;
*nr = (frame[1] >> 1) & 0x7F;
*pf = frame[1] & AX25_EPF;
skb_pull(skb, 2);
} else if ((frame[0] & AX25_U) == 3) { /* U frame - take out PF */
frametype = frame[0] & ~AX25_PF;
*pf = frame[0] & AX25_PF;
skb_pull(skb, 1);
}
}
return frametype;
}
/*
* This routine is called when the HDLC layer internally generates a
* command or response for the remote machine ( eg. RR, UA etc. ).
* Only supervisory or unnumbered frames are processed.
*/
void ax25_send_control(ax25_cb *ax25, int frametype, int poll_bit, int type)
{
struct sk_buff *skb;
unsigned char *dptr;
if ((skb = alloc_skb(ax25->ax25_dev->dev->hard_header_len + 2, GFP_ATOMIC)) == NULL)
return;
skb_reserve(skb, ax25->ax25_dev->dev->hard_header_len);
skb->nh.raw = skb->data;
/* Assume a response - address structure for DTE */
if (ax25->modulus == AX25_MODULUS) {
dptr = skb_put(skb, 1);
*dptr = frametype;
*dptr |= (poll_bit) ? AX25_PF : 0;
if ((frametype & AX25_U) == AX25_S) /* S frames carry NR */
*dptr |= (ax25->vr << 5);
} else {
if ((frametype & AX25_U) == AX25_U) {
dptr = skb_put(skb, 1);
*dptr = frametype;
*dptr |= (poll_bit) ? AX25_PF : 0;
} else {
dptr = skb_put(skb, 2);
dptr[0] = frametype;
dptr[1] = (ax25->vr << 1);
dptr[1] |= (poll_bit) ? AX25_EPF : 0;
}
}
ax25_transmit_buffer(ax25, skb, type);
}
/*
* Send a 'DM' to an unknown connection attempt, or an invalid caller.
*
* Note: src here is the sender, thus it's the target of the DM
*/
void ax25_return_dm(struct net_device *dev, ax25_address *src, ax25_address *dest, ax25_digi *digi)
{
struct sk_buff *skb;
char *dptr;
ax25_digi retdigi;
if (dev == NULL)
return;
if ((skb = alloc_skb(dev->hard_header_len + 1, GFP_ATOMIC)) == NULL)
return; /* Next SABM will get DM'd */
skb_reserve(skb, dev->hard_header_len);
skb->nh.raw = skb->data;
ax25_digi_invert(digi, &retdigi);
dptr = skb_put(skb, 1);
*dptr = AX25_DM | AX25_PF;
/*
* Do the address ourselves
*/
dptr = skb_push(skb, ax25_addr_size(digi));
dptr += ax25_addr_build(dptr, dest, src, &retdigi, AX25_RESPONSE, AX25_MODULUS);
skb->dev = dev;
ax25_queue_xmit(skb);
}
/*
* Exponential backoff for AX.25
*/
void ax25_calculate_t1(ax25_cb *ax25)
{
int n, t = 2;
switch (ax25->backoff) {
case 0:
break;
case 1:
t += 2 * ax25->n2count;
break;
case 2:
for (n = 0; n < ax25->n2count; n++)
t *= 2;
if (t > 8) t = 8;
break;
}
ax25->t1 = t * ax25->rtt;
}
/*
* Calculate the Round Trip Time
*/
void ax25_calculate_rtt(ax25_cb *ax25)
{
if (ax25->backoff == 0)
return;
if (ax25_t1timer_running(ax25) && ax25->n2count == 0)
ax25->rtt = (9 * ax25->rtt + ax25->t1 - ax25_display_timer(&ax25->t1timer)) / 10;
if (ax25->rtt < AX25_T1CLAMPLO)
ax25->rtt = AX25_T1CLAMPLO;
if (ax25->rtt > AX25_T1CLAMPHI)
ax25->rtt = AX25_T1CLAMPHI;
}
void ax25_disconnect(ax25_cb *ax25, int reason)
{
ax25_clear_queues(ax25);
ax25_stop_t1timer(ax25);
ax25_stop_t2timer(ax25);
ax25_stop_t3timer(ax25);
ax25_stop_idletimer(ax25);
ax25->state = AX25_STATE_0;
ax25_link_failed(ax25, reason);
if (ax25->sk != NULL) {
bh_lock_sock(ax25->sk);
ax25->sk->sk_state = TCP_CLOSE;
ax25->sk->sk_err = reason;
ax25->sk->sk_shutdown |= SEND_SHUTDOWN;
if (!sock_flag(ax25->sk, SOCK_DEAD)) {
ax25->sk->sk_state_change(ax25->sk);
sock_set_flag(ax25->sk, SOCK_DEAD);
}
bh_unlock_sock(ax25->sk);
}
}

243
net/ax25/ax25_timer.c Normal file
View File

@@ -0,0 +1,243 @@
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Copyright (C) Alan Cox GW4PTS (alan@lxorguk.ukuu.org.uk)
* Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk)
* Copyright (C) Tomi Manninen OH2BNS (oh2bns@sral.fi)
* Copyright (C) Darryl Miles G7LED (dlm@g7led.demon.co.uk)
* Copyright (C) Joerg Reuter DL1BKE (jreuter@yaina.de)
* Copyright (C) Frederic Rible F1OAT (frible@teaser.fr)
* Copyright (C) 2002 Ralf Baechle DO1GRB (ralf@gnu.org)
*/
#include <linux/config.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/socket.h>
#include <linux/in.h>
#include <linux/kernel.h>
#include <linux/jiffies.h>
#include <linux/timer.h>
#include <linux/string.h>
#include <linux/sockios.h>
#include <linux/net.h>
#include <net/ax25.h>
#include <linux/inet.h>
#include <linux/netdevice.h>
#include <linux/skbuff.h>
#include <net/sock.h>
#include <asm/uaccess.h>
#include <asm/system.h>
#include <linux/fcntl.h>
#include <linux/mm.h>
#include <linux/interrupt.h>
static void ax25_heartbeat_expiry(unsigned long);
static void ax25_t1timer_expiry(unsigned long);
static void ax25_t2timer_expiry(unsigned long);
static void ax25_t3timer_expiry(unsigned long);
static void ax25_idletimer_expiry(unsigned long);
void ax25_start_heartbeat(ax25_cb *ax25)
{
del_timer(&ax25->timer);
ax25->timer.data = (unsigned long)ax25;
ax25->timer.function = &ax25_heartbeat_expiry;
ax25->timer.expires = jiffies + 5 * HZ;
add_timer(&ax25->timer);
}
void ax25_start_t1timer(ax25_cb *ax25)
{
del_timer(&ax25->t1timer);
ax25->t1timer.data = (unsigned long)ax25;
ax25->t1timer.function = &ax25_t1timer_expiry;
ax25->t1timer.expires = jiffies + ax25->t1;
add_timer(&ax25->t1timer);
}
void ax25_start_t2timer(ax25_cb *ax25)
{
del_timer(&ax25->t2timer);
ax25->t2timer.data = (unsigned long)ax25;
ax25->t2timer.function = &ax25_t2timer_expiry;
ax25->t2timer.expires = jiffies + ax25->t2;
add_timer(&ax25->t2timer);
}
void ax25_start_t3timer(ax25_cb *ax25)
{
del_timer(&ax25->t3timer);
if (ax25->t3 > 0) {
ax25->t3timer.data = (unsigned long)ax25;
ax25->t3timer.function = &ax25_t3timer_expiry;
ax25->t3timer.expires = jiffies + ax25->t3;
add_timer(&ax25->t3timer);
}
}
void ax25_start_idletimer(ax25_cb *ax25)
{
del_timer(&ax25->idletimer);
if (ax25->idle > 0) {
ax25->idletimer.data = (unsigned long)ax25;
ax25->idletimer.function = &ax25_idletimer_expiry;
ax25->idletimer.expires = jiffies + ax25->idle;
add_timer(&ax25->idletimer);
}
}
void ax25_stop_heartbeat(ax25_cb *ax25)
{
del_timer(&ax25->timer);
}
void ax25_stop_t1timer(ax25_cb *ax25)
{
del_timer(&ax25->t1timer);
}
void ax25_stop_t2timer(ax25_cb *ax25)
{
del_timer(&ax25->t2timer);
}
void ax25_stop_t3timer(ax25_cb *ax25)
{
del_timer(&ax25->t3timer);
}
void ax25_stop_idletimer(ax25_cb *ax25)
{
del_timer(&ax25->idletimer);
}
int ax25_t1timer_running(ax25_cb *ax25)
{
return timer_pending(&ax25->t1timer);
}
unsigned long ax25_display_timer(struct timer_list *timer)
{
if (!timer_pending(timer))
return 0;
return timer->expires - jiffies;
}
static void ax25_heartbeat_expiry(unsigned long param)
{
int proto = AX25_PROTO_STD_SIMPLEX;
ax25_cb *ax25 = (ax25_cb *)param;
if (ax25->ax25_dev)
proto = ax25->ax25_dev->values[AX25_VALUES_PROTOCOL];
switch (proto) {
case AX25_PROTO_STD_SIMPLEX:
case AX25_PROTO_STD_DUPLEX:
ax25_std_heartbeat_expiry(ax25);
break;
#ifdef CONFIG_AX25_DAMA_SLAVE
case AX25_PROTO_DAMA_SLAVE:
if (ax25->ax25_dev->dama.slave)
ax25_ds_heartbeat_expiry(ax25);
else
ax25_std_heartbeat_expiry(ax25);
break;
#endif
}
}
static void ax25_t1timer_expiry(unsigned long param)
{
ax25_cb *ax25 = (ax25_cb *)param;
switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) {
case AX25_PROTO_STD_SIMPLEX:
case AX25_PROTO_STD_DUPLEX:
ax25_std_t1timer_expiry(ax25);
break;
#ifdef CONFIG_AX25_DAMA_SLAVE
case AX25_PROTO_DAMA_SLAVE:
if (!ax25->ax25_dev->dama.slave)
ax25_std_t1timer_expiry(ax25);
break;
#endif
}
}
static void ax25_t2timer_expiry(unsigned long param)
{
ax25_cb *ax25 = (ax25_cb *)param;
switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) {
case AX25_PROTO_STD_SIMPLEX:
case AX25_PROTO_STD_DUPLEX:
ax25_std_t2timer_expiry(ax25);
break;
#ifdef CONFIG_AX25_DAMA_SLAVE
case AX25_PROTO_DAMA_SLAVE:
if (!ax25->ax25_dev->dama.slave)
ax25_std_t2timer_expiry(ax25);
break;
#endif
}
}
static void ax25_t3timer_expiry(unsigned long param)
{
ax25_cb *ax25 = (ax25_cb *)param;
switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) {
case AX25_PROTO_STD_SIMPLEX:
case AX25_PROTO_STD_DUPLEX:
ax25_std_t3timer_expiry(ax25);
break;
#ifdef CONFIG_AX25_DAMA_SLAVE
case AX25_PROTO_DAMA_SLAVE:
if (ax25->ax25_dev->dama.slave)
ax25_ds_t3timer_expiry(ax25);
else
ax25_std_t3timer_expiry(ax25);
break;
#endif
}
}
static void ax25_idletimer_expiry(unsigned long param)
{
ax25_cb *ax25 = (ax25_cb *)param;
switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) {
case AX25_PROTO_STD_SIMPLEX:
case AX25_PROTO_STD_DUPLEX:
ax25_std_idletimer_expiry(ax25);
break;
#ifdef CONFIG_AX25_DAMA_SLAVE
case AX25_PROTO_DAMA_SLAVE:
if (ax25->ax25_dev->dama.slave)
ax25_ds_idletimer_expiry(ax25);
else
ax25_std_idletimer_expiry(ax25);
break;
#endif
}
}

228
net/ax25/ax25_uid.c Normal file
View File

@@ -0,0 +1,228 @@
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk)
*/
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/socket.h>
#include <linux/in.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/timer.h>
#include <linux/string.h>
#include <linux/sockios.h>
#include <linux/net.h>
#include <linux/spinlock.h>
#include <net/ax25.h>
#include <linux/inet.h>
#include <linux/netdevice.h>
#include <linux/if_arp.h>
#include <linux/skbuff.h>
#include <net/sock.h>
#include <asm/uaccess.h>
#include <asm/system.h>
#include <linux/fcntl.h>
#include <linux/mm.h>
#include <linux/interrupt.h>
#include <linux/notifier.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/netfilter.h>
#include <linux/sysctl.h>
#include <net/ip.h>
#include <net/arp.h>
/*
* Callsign/UID mapper. This is in kernel space for security on multi-amateur machines.
*/
static ax25_uid_assoc *ax25_uid_list;
static DEFINE_RWLOCK(ax25_uid_lock);
int ax25_uid_policy = 0;
ax25_address *ax25_findbyuid(uid_t uid)
{
ax25_uid_assoc *ax25_uid;
ax25_address *res = NULL;
read_lock(&ax25_uid_lock);
for (ax25_uid = ax25_uid_list; ax25_uid != NULL; ax25_uid = ax25_uid->next) {
if (ax25_uid->uid == uid) {
res = &ax25_uid->call;
break;
}
}
read_unlock(&ax25_uid_lock);
return NULL;
}
int ax25_uid_ioctl(int cmd, struct sockaddr_ax25 *sax)
{
ax25_uid_assoc *s, *ax25_uid;
unsigned long res;
switch (cmd) {
case SIOCAX25GETUID:
res = -ENOENT;
read_lock(&ax25_uid_lock);
for (ax25_uid = ax25_uid_list; ax25_uid != NULL; ax25_uid = ax25_uid->next) {
if (ax25cmp(&sax->sax25_call, &ax25_uid->call) == 0) {
res = ax25_uid->uid;
break;
}
}
read_unlock(&ax25_uid_lock);
return res;
case SIOCAX25ADDUID:
if (!capable(CAP_NET_ADMIN))
return -EPERM;
if (ax25_findbyuid(sax->sax25_uid))
return -EEXIST;
if (sax->sax25_uid == 0)
return -EINVAL;
if ((ax25_uid = kmalloc(sizeof(*ax25_uid), GFP_KERNEL)) == NULL)
return -ENOMEM;
ax25_uid->uid = sax->sax25_uid;
ax25_uid->call = sax->sax25_call;
write_lock(&ax25_uid_lock);
ax25_uid->next = ax25_uid_list;
ax25_uid_list = ax25_uid;
write_unlock(&ax25_uid_lock);
return 0;
case SIOCAX25DELUID:
if (!capable(CAP_NET_ADMIN))
return -EPERM;
write_lock(&ax25_uid_lock);
for (ax25_uid = ax25_uid_list; ax25_uid != NULL; ax25_uid = ax25_uid->next) {
if (ax25cmp(&sax->sax25_call, &ax25_uid->call) == 0) {
break;
}
}
if (ax25_uid == NULL) {
write_unlock(&ax25_uid_lock);
return -ENOENT;
}
if ((s = ax25_uid_list) == ax25_uid) {
ax25_uid_list = s->next;
write_unlock(&ax25_uid_lock);
kfree(ax25_uid);
return 0;
}
while (s != NULL && s->next != NULL) {
if (s->next == ax25_uid) {
s->next = ax25_uid->next;
write_unlock(&ax25_uid_lock);
kfree(ax25_uid);
return 0;
}
s = s->next;
}
write_unlock(&ax25_uid_lock);
return -ENOENT;
default:
return -EINVAL;
}
return -EINVAL; /*NOTREACHED */
}
#ifdef CONFIG_PROC_FS
static void *ax25_uid_seq_start(struct seq_file *seq, loff_t *pos)
{
struct ax25_uid_assoc *pt;
int i = 1;
read_lock(&ax25_uid_lock);
if (*pos == 0)
return SEQ_START_TOKEN;
for (pt = ax25_uid_list; pt != NULL; pt = pt->next) {
if (i == *pos)
return pt;
++i;
}
return NULL;
}
static void *ax25_uid_seq_next(struct seq_file *seq, void *v, loff_t *pos)
{
++*pos;
return (v == SEQ_START_TOKEN) ? ax25_uid_list :
((struct ax25_uid_assoc *) v)->next;
}
static void ax25_uid_seq_stop(struct seq_file *seq, void *v)
{
read_unlock(&ax25_uid_lock);
}
static int ax25_uid_seq_show(struct seq_file *seq, void *v)
{
if (v == SEQ_START_TOKEN)
seq_printf(seq, "Policy: %d\n", ax25_uid_policy);
else {
struct ax25_uid_assoc *pt = v;
seq_printf(seq, "%6d %s\n", pt->uid, ax2asc(&pt->call));
}
return 0;
}
static struct seq_operations ax25_uid_seqops = {
.start = ax25_uid_seq_start,
.next = ax25_uid_seq_next,
.stop = ax25_uid_seq_stop,
.show = ax25_uid_seq_show,
};
static int ax25_uid_info_open(struct inode *inode, struct file *file)
{
return seq_open(file, &ax25_uid_seqops);
}
struct file_operations ax25_uid_fops = {
.owner = THIS_MODULE,
.open = ax25_uid_info_open,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release,
};
#endif
/*
* Free all memory associated with UID/Callsign structures.
*/
void __exit ax25_uid_free(void)
{
ax25_uid_assoc *s, *ax25_uid;
write_lock(&ax25_uid_lock);
ax25_uid = ax25_uid_list;
while (ax25_uid != NULL) {
s = ax25_uid;
ax25_uid = ax25_uid->next;
kfree(s);
}
ax25_uid_list = NULL;
write_unlock(&ax25_uid_lock);
}

262
net/ax25/sysctl_net_ax25.c Normal file
View File

@@ -0,0 +1,262 @@
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Copyright (C) 1996 Mike Shaver (shaver@zeroknowledge.com)
*/
#include <linux/config.h>
#include <linux/mm.h>
#include <linux/sysctl.h>
#include <linux/spinlock.h>
#include <net/ax25.h>
static int min_ipdefmode[1], max_ipdefmode[] = {1};
static int min_axdefmode[1], max_axdefmode[] = {1};
static int min_backoff[1], max_backoff[] = {2};
static int min_conmode[1], max_conmode[] = {2};
static int min_window[] = {1}, max_window[] = {7};
static int min_ewindow[] = {1}, max_ewindow[] = {63};
static int min_t1[] = {1}, max_t1[] = {30 * HZ};
static int min_t2[] = {1}, max_t2[] = {20 * HZ};
static int min_t3[1], max_t3[] = {3600 * HZ};
static int min_idle[1], max_idle[] = {65535 * HZ};
static int min_n2[] = {1}, max_n2[] = {31};
static int min_paclen[] = {1}, max_paclen[] = {512};
static int min_proto[1], max_proto[] = {3};
static int min_ds_timeout[1], max_ds_timeout[] = {65535 * HZ};
static struct ctl_table_header *ax25_table_header;
static ctl_table *ax25_table;
static int ax25_table_size;
static ctl_table ax25_dir_table[] = {
{
.ctl_name = NET_AX25,
.procname = "ax25",
.mode = 0555,
},
{ .ctl_name = 0 }
};
static ctl_table ax25_root_table[] = {
{
.ctl_name = CTL_NET,
.procname = "net",
.mode = 0555,
.child = ax25_dir_table
},
{ .ctl_name = 0 }
};
static const ctl_table ax25_param_table[] = {
{
.ctl_name = NET_AX25_IP_DEFAULT_MODE,
.procname = "ip_default_mode",
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec_minmax,
.strategy = &sysctl_intvec,
.extra1 = &min_ipdefmode,
.extra2 = &max_ipdefmode
},
{
.ctl_name = NET_AX25_DEFAULT_MODE,
.procname = "ax25_default_mode",
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec_minmax,
.strategy = &sysctl_intvec,
.extra1 = &min_axdefmode,
.extra2 = &max_axdefmode
},
{
.ctl_name = NET_AX25_BACKOFF_TYPE,
.procname = "backoff_type",
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec_minmax,
.strategy = &sysctl_intvec,
.extra1 = &min_backoff,
.extra2 = &max_backoff
},
{
.ctl_name = NET_AX25_CONNECT_MODE,
.procname = "connect_mode",
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec_minmax,
.strategy = &sysctl_intvec,
.extra1 = &min_conmode,
.extra2 = &max_conmode
},
{
.ctl_name = NET_AX25_STANDARD_WINDOW,
.procname = "standard_window_size",
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec_minmax,
.strategy = &sysctl_intvec,
.extra1 = &min_window,
.extra2 = &max_window
},
{
.ctl_name = NET_AX25_EXTENDED_WINDOW,
.procname = "extended_window_size",
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec_minmax,
.strategy = &sysctl_intvec,
.extra1 = &min_ewindow,
.extra2 = &max_ewindow
},
{
.ctl_name = NET_AX25_T1_TIMEOUT,
.procname = "t1_timeout",
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec_minmax,
.strategy = &sysctl_intvec,
.extra1 = &min_t1,
.extra2 = &max_t1
},
{
.ctl_name = NET_AX25_T2_TIMEOUT,
.procname = "t2_timeout",
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec_minmax,
.strategy = &sysctl_intvec,
.extra1 = &min_t2,
.extra2 = &max_t2
},
{
.ctl_name = NET_AX25_T3_TIMEOUT,
.procname = "t3_timeout",
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec_minmax,
.strategy = &sysctl_intvec,
.extra1 = &min_t3,
.extra2 = &max_t3
},
{
.ctl_name = NET_AX25_IDLE_TIMEOUT,
.procname = "idle_timeout",
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec_minmax,
.strategy = &sysctl_intvec,
.extra1 = &min_idle,
.extra2 = &max_idle
},
{
.ctl_name = NET_AX25_N2,
.procname = "maximum_retry_count",
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec_minmax,
.strategy = &sysctl_intvec,
.extra1 = &min_n2,
.extra2 = &max_n2
},
{
.ctl_name = NET_AX25_PACLEN,
.procname = "maximum_packet_length",
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec_minmax,
.strategy = &sysctl_intvec,
.extra1 = &min_paclen,
.extra2 = &max_paclen
},
{
.ctl_name = NET_AX25_PROTOCOL,
.procname = "protocol",
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec_minmax,
.strategy = &sysctl_intvec,
.extra1 = &min_proto,
.extra2 = &max_proto
},
{
.ctl_name = NET_AX25_DAMA_SLAVE_TIMEOUT,
.procname = "dama_slave_timeout",
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec_minmax,
.strategy = &sysctl_intvec,
.extra1 = &min_ds_timeout,
.extra2 = &max_ds_timeout
},
{ .ctl_name = 0 } /* that's all, folks! */
};
void ax25_register_sysctl(void)
{
ax25_dev *ax25_dev;
int n, k;
spin_lock_bh(&ax25_dev_lock);
for (ax25_table_size = sizeof(ctl_table), ax25_dev = ax25_dev_list; ax25_dev != NULL; ax25_dev = ax25_dev->next)
ax25_table_size += sizeof(ctl_table);
if ((ax25_table = kmalloc(ax25_table_size, GFP_ATOMIC)) == NULL) {
spin_unlock_bh(&ax25_dev_lock);
return;
}
memset(ax25_table, 0x00, ax25_table_size);
for (n = 0, ax25_dev = ax25_dev_list; ax25_dev != NULL; ax25_dev = ax25_dev->next) {
ctl_table *child = kmalloc(sizeof(ax25_param_table), GFP_ATOMIC);
if (!child) {
while (n--)
kfree(ax25_table[n].child);
kfree(ax25_table);
spin_unlock_bh(&ax25_dev_lock);
return;
}
memcpy(child, ax25_param_table, sizeof(ax25_param_table));
ax25_table[n].child = ax25_dev->systable = child;
ax25_table[n].ctl_name = n + 1;
ax25_table[n].procname = ax25_dev->dev->name;
ax25_table[n].mode = 0555;
#ifndef CONFIG_AX25_DAMA_SLAVE
/*
* We do not wish to have a representation of this parameter
* in /proc/sys/ when configured *not* to include the
* AX.25 DAMA slave code, do we?
*/
child[AX25_VALUES_DS_TIMEOUT].procname = NULL;
#endif
child[AX25_MAX_VALUES].ctl_name = 0; /* just in case... */
for (k = 0; k < AX25_MAX_VALUES; k++)
child[k].data = &ax25_dev->values[k];
n++;
}
spin_unlock_bh(&ax25_dev_lock);
ax25_dir_table[0].child = ax25_table;
ax25_table_header = register_sysctl_table(ax25_root_table, 1);
}
void ax25_unregister_sysctl(void)
{
ctl_table *p;
unregister_sysctl_table(ax25_table_header);
ax25_dir_table[0].child = NULL;
for (p = ax25_table; p->ctl_name; p++)
kfree(p->child);
kfree(ax25_table);
}