mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-29 19:21:31 -04:00
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:
commit
1da177e4c3
17291 changed files with 6718755 additions and 0 deletions
10
net/x25/Makefile
Normal file
10
net/x25/Makefile
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
#
|
||||
# Makefile for the Linux X.25 Packet layer.
|
||||
#
|
||||
|
||||
obj-$(CONFIG_X25) += x25.o
|
||||
|
||||
x25-y := af_x25.o x25_dev.o x25_facilities.o x25_in.o \
|
||||
x25_link.o x25_out.o x25_route.o x25_subr.o \
|
||||
x25_timer.o x25_proc.o
|
||||
x25-$(CONFIG_SYSCTL) += sysctl_net_x25.o
|
||||
1435
net/x25/af_x25.c
Normal file
1435
net/x25/af_x25.c
Normal file
File diff suppressed because it is too large
Load diff
107
net/x25/sysctl_net_x25.c
Normal file
107
net/x25/sysctl_net_x25.c
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
/* -*- linux-c -*-
|
||||
* sysctl_net_x25.c: sysctl interface to net X.25 subsystem.
|
||||
*
|
||||
* Begun April 1, 1996, Mike Shaver.
|
||||
* Added /proc/sys/net/x25 directory entry (empty =) ). [MS]
|
||||
*/
|
||||
|
||||
#include <linux/sysctl.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/socket.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/init.h>
|
||||
#include <net/x25.h>
|
||||
|
||||
static int min_timer[] = { 1 * HZ };
|
||||
static int max_timer[] = { 300 * HZ };
|
||||
|
||||
static struct ctl_table_header *x25_table_header;
|
||||
|
||||
static struct ctl_table x25_table[] = {
|
||||
{
|
||||
.ctl_name = NET_X25_RESTART_REQUEST_TIMEOUT,
|
||||
.procname = "restart_request_timeout",
|
||||
.data = &sysctl_x25_restart_request_timeout,
|
||||
.maxlen = sizeof(int),
|
||||
.mode = 0644,
|
||||
.proc_handler = &proc_dointvec_minmax,
|
||||
.strategy = &sysctl_intvec,
|
||||
.extra1 = &min_timer,
|
||||
.extra2 = &max_timer,
|
||||
},
|
||||
{
|
||||
.ctl_name = NET_X25_CALL_REQUEST_TIMEOUT,
|
||||
.procname = "call_request_timeout",
|
||||
.data = &sysctl_x25_call_request_timeout,
|
||||
.maxlen = sizeof(int),
|
||||
.mode = 0644,
|
||||
.proc_handler = &proc_dointvec_minmax,
|
||||
.strategy = &sysctl_intvec,
|
||||
.extra1 = &min_timer,
|
||||
.extra2 = &max_timer,
|
||||
},
|
||||
{
|
||||
.ctl_name = NET_X25_RESET_REQUEST_TIMEOUT,
|
||||
.procname = "reset_request_timeout",
|
||||
.data = &sysctl_x25_reset_request_timeout,
|
||||
.maxlen = sizeof(int),
|
||||
.mode = 0644,
|
||||
.proc_handler = &proc_dointvec_minmax,
|
||||
.strategy = &sysctl_intvec,
|
||||
.extra1 = &min_timer,
|
||||
.extra2 = &max_timer,
|
||||
},
|
||||
{
|
||||
.ctl_name = NET_X25_CLEAR_REQUEST_TIMEOUT,
|
||||
.procname = "clear_request_timeout",
|
||||
.data = &sysctl_x25_clear_request_timeout,
|
||||
.maxlen = sizeof(int),
|
||||
.mode = 0644,
|
||||
.proc_handler = &proc_dointvec_minmax,
|
||||
.strategy = &sysctl_intvec,
|
||||
.extra1 = &min_timer,
|
||||
.extra2 = &max_timer,
|
||||
},
|
||||
{
|
||||
.ctl_name = NET_X25_ACK_HOLD_BACK_TIMEOUT,
|
||||
.procname = "acknowledgement_hold_back_timeout",
|
||||
.data = &sysctl_x25_ack_holdback_timeout,
|
||||
.maxlen = sizeof(int),
|
||||
.mode = 0644,
|
||||
.proc_handler = &proc_dointvec_minmax,
|
||||
.strategy = &sysctl_intvec,
|
||||
.extra1 = &min_timer,
|
||||
.extra2 = &max_timer,
|
||||
},
|
||||
{ 0, },
|
||||
};
|
||||
|
||||
static struct ctl_table x25_dir_table[] = {
|
||||
{
|
||||
.ctl_name = NET_X25,
|
||||
.procname = "x25",
|
||||
.mode = 0555,
|
||||
.child = x25_table,
|
||||
},
|
||||
{ 0, },
|
||||
};
|
||||
|
||||
static struct ctl_table x25_root_table[] = {
|
||||
{
|
||||
.ctl_name = CTL_NET,
|
||||
.procname = "net",
|
||||
.mode = 0555,
|
||||
.child = x25_dir_table,
|
||||
},
|
||||
{ 0, },
|
||||
};
|
||||
|
||||
void __init x25_register_sysctl(void)
|
||||
{
|
||||
x25_table_header = register_sysctl_table(x25_root_table, 1);
|
||||
}
|
||||
|
||||
void x25_unregister_sysctl(void)
|
||||
{
|
||||
unregister_sysctl_table(x25_table_header);
|
||||
}
|
||||
207
net/x25/x25_dev.c
Normal file
207
net/x25/x25_dev.c
Normal file
|
|
@ -0,0 +1,207 @@
|
|||
/*
|
||||
* X.25 Packet Layer release 002
|
||||
*
|
||||
* This is ALPHA test software. This code may break your machine, randomly fail to work with new
|
||||
* releases, misbehave and/or generally screw up. It might even work.
|
||||
*
|
||||
* This code REQUIRES 2.1.15 or higher
|
||||
*
|
||||
* This module:
|
||||
* This module is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version
|
||||
* 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* History
|
||||
* X.25 001 Jonathan Naylor Started coding.
|
||||
* 2000-09-04 Henner Eisen Prevent freeing a dangling skb.
|
||||
*/
|
||||
|
||||
#include <linux/config.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <net/sock.h>
|
||||
#include <linux/if_arp.h>
|
||||
#include <net/x25.h>
|
||||
|
||||
static int x25_receive_data(struct sk_buff *skb, struct x25_neigh *nb)
|
||||
{
|
||||
struct sock *sk;
|
||||
unsigned short frametype;
|
||||
unsigned int lci;
|
||||
|
||||
frametype = skb->data[2];
|
||||
lci = ((skb->data[0] << 8) & 0xF00) + ((skb->data[1] << 0) & 0x0FF);
|
||||
|
||||
/*
|
||||
* LCI of zero is always for us, and its always a link control
|
||||
* frame.
|
||||
*/
|
||||
if (lci == 0) {
|
||||
x25_link_control(skb, nb, frametype);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Find an existing socket.
|
||||
*/
|
||||
if ((sk = x25_find_socket(lci, nb)) != NULL) {
|
||||
int queued = 1;
|
||||
|
||||
skb->h.raw = skb->data;
|
||||
bh_lock_sock(sk);
|
||||
if (!sock_owned_by_user(sk)) {
|
||||
queued = x25_process_rx_frame(sk, skb);
|
||||
} else {
|
||||
sk_add_backlog(sk, skb);
|
||||
}
|
||||
bh_unlock_sock(sk);
|
||||
return queued;
|
||||
}
|
||||
|
||||
/*
|
||||
* Is is a Call Request ? if so process it.
|
||||
*/
|
||||
if (frametype == X25_CALL_REQUEST)
|
||||
return x25_rx_call_request(skb, nb, lci);
|
||||
|
||||
/*
|
||||
* Its not a Call Request, nor is it a control frame.
|
||||
* Let caller throw it away.
|
||||
*/
|
||||
/*
|
||||
x25_transmit_clear_request(nb, lci, 0x0D);
|
||||
*/
|
||||
|
||||
if (frametype != X25_CLEAR_CONFIRMATION)
|
||||
printk(KERN_DEBUG "x25_receive_data(): unknown frame type %2x\n",frametype);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int x25_lapb_receive_frame(struct sk_buff *skb, struct net_device *dev,
|
||||
struct packet_type *ptype)
|
||||
{
|
||||
struct sk_buff *nskb;
|
||||
struct x25_neigh *nb;
|
||||
|
||||
nskb = skb_copy(skb, GFP_ATOMIC);
|
||||
if (!nskb)
|
||||
goto drop;
|
||||
kfree_skb(skb);
|
||||
skb = nskb;
|
||||
|
||||
/*
|
||||
* Packet received from unrecognised device, throw it away.
|
||||
*/
|
||||
nb = x25_get_neigh(dev);
|
||||
if (!nb) {
|
||||
printk(KERN_DEBUG "X.25: unknown neighbour - %s\n", dev->name);
|
||||
goto drop;
|
||||
}
|
||||
|
||||
switch (skb->data[0]) {
|
||||
case 0x00:
|
||||
skb_pull(skb, 1);
|
||||
if (x25_receive_data(skb, nb)) {
|
||||
x25_neigh_put(nb);
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
case 0x01:
|
||||
x25_link_established(nb);
|
||||
break;
|
||||
case 0x02:
|
||||
x25_link_terminated(nb);
|
||||
break;
|
||||
}
|
||||
x25_neigh_put(nb);
|
||||
drop:
|
||||
kfree_skb(skb);
|
||||
out:
|
||||
return 0;
|
||||
}
|
||||
|
||||
void x25_establish_link(struct x25_neigh *nb)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
unsigned char *ptr;
|
||||
|
||||
switch (nb->dev->type) {
|
||||
case ARPHRD_X25:
|
||||
if ((skb = alloc_skb(1, GFP_ATOMIC)) == NULL) {
|
||||
printk(KERN_ERR "x25_dev: out of memory\n");
|
||||
return;
|
||||
}
|
||||
ptr = skb_put(skb, 1);
|
||||
*ptr = 0x01;
|
||||
break;
|
||||
|
||||
#if defined(CONFIG_LLC) || defined(CONFIG_LLC_MODULE)
|
||||
case ARPHRD_ETHER:
|
||||
return;
|
||||
#endif
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
skb->protocol = htons(ETH_P_X25);
|
||||
skb->dev = nb->dev;
|
||||
|
||||
dev_queue_xmit(skb);
|
||||
}
|
||||
|
||||
void x25_terminate_link(struct x25_neigh *nb)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
unsigned char *ptr;
|
||||
|
||||
#if defined(CONFIG_LLC) || defined(CONFIG_LLC_MODULE)
|
||||
if (nb->dev->type == ARPHRD_ETHER)
|
||||
return;
|
||||
#endif
|
||||
if (nb->dev->type != ARPHRD_X25)
|
||||
return;
|
||||
|
||||
skb = alloc_skb(1, GFP_ATOMIC);
|
||||
if (!skb) {
|
||||
printk(KERN_ERR "x25_dev: out of memory\n");
|
||||
return;
|
||||
}
|
||||
|
||||
ptr = skb_put(skb, 1);
|
||||
*ptr = 0x02;
|
||||
|
||||
skb->protocol = htons(ETH_P_X25);
|
||||
skb->dev = nb->dev;
|
||||
dev_queue_xmit(skb);
|
||||
}
|
||||
|
||||
void x25_send_frame(struct sk_buff *skb, struct x25_neigh *nb)
|
||||
{
|
||||
unsigned char *dptr;
|
||||
|
||||
skb->nh.raw = skb->data;
|
||||
|
||||
switch (nb->dev->type) {
|
||||
case ARPHRD_X25:
|
||||
dptr = skb_push(skb, 1);
|
||||
*dptr = 0x00;
|
||||
break;
|
||||
|
||||
#if defined(CONFIG_LLC) || defined(CONFIG_LLC_MODULE)
|
||||
case ARPHRD_ETHER:
|
||||
kfree_skb(skb);
|
||||
return;
|
||||
#endif
|
||||
default:
|
||||
kfree_skb(skb);
|
||||
return;
|
||||
}
|
||||
|
||||
skb->protocol = htons(ETH_P_X25);
|
||||
skb->dev = nb->dev;
|
||||
|
||||
dev_queue_xmit(skb);
|
||||
}
|
||||
231
net/x25/x25_facilities.c
Normal file
231
net/x25/x25_facilities.c
Normal file
|
|
@ -0,0 +1,231 @@
|
|||
/*
|
||||
* X.25 Packet Layer release 002
|
||||
*
|
||||
* This is ALPHA test software. This code may break your machine,
|
||||
* randomly fail to work with new releases, misbehave and/or generally
|
||||
* screw up. It might even work.
|
||||
*
|
||||
* This code REQUIRES 2.1.15 or higher
|
||||
*
|
||||
* This module:
|
||||
* This module is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version
|
||||
* 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* History
|
||||
* X.25 001 Split from x25_subr.c
|
||||
* mar/20/00 Daniela Squassoni Disabling/enabling of facilities
|
||||
* negotiation.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <net/sock.h>
|
||||
#include <net/x25.h>
|
||||
|
||||
/*
|
||||
* Parse a set of facilities into the facilities structure. Unrecognised
|
||||
* facilities are written to the debug log file.
|
||||
*/
|
||||
int x25_parse_facilities(struct sk_buff *skb,
|
||||
struct x25_facilities *facilities,
|
||||
unsigned long *vc_fac_mask)
|
||||
{
|
||||
unsigned char *p = skb->data;
|
||||
unsigned int len = *p++;
|
||||
|
||||
*vc_fac_mask = 0;
|
||||
|
||||
while (len > 0) {
|
||||
switch (*p & X25_FAC_CLASS_MASK) {
|
||||
case X25_FAC_CLASS_A:
|
||||
switch (*p) {
|
||||
case X25_FAC_REVERSE:
|
||||
facilities->reverse = p[1] & 0x01;
|
||||
*vc_fac_mask |= X25_MASK_REVERSE;
|
||||
break;
|
||||
case X25_FAC_THROUGHPUT:
|
||||
facilities->throughput = p[1];
|
||||
*vc_fac_mask |= X25_MASK_THROUGHPUT;
|
||||
break;
|
||||
default:
|
||||
printk(KERN_DEBUG "X.25: unknown facility "
|
||||
"%02X, value %02X\n",
|
||||
p[0], p[1]);
|
||||
break;
|
||||
}
|
||||
p += 2;
|
||||
len -= 2;
|
||||
break;
|
||||
case X25_FAC_CLASS_B:
|
||||
switch (*p) {
|
||||
case X25_FAC_PACKET_SIZE:
|
||||
facilities->pacsize_in = p[1];
|
||||
facilities->pacsize_out = p[2];
|
||||
*vc_fac_mask |= X25_MASK_PACKET_SIZE;
|
||||
break;
|
||||
case X25_FAC_WINDOW_SIZE:
|
||||
facilities->winsize_in = p[1];
|
||||
facilities->winsize_out = p[2];
|
||||
*vc_fac_mask |= X25_MASK_WINDOW_SIZE;
|
||||
break;
|
||||
default:
|
||||
printk(KERN_DEBUG "X.25: unknown facility "
|
||||
"%02X, values %02X, %02X\n",
|
||||
p[0], p[1], p[2]);
|
||||
break;
|
||||
}
|
||||
p += 3;
|
||||
len -= 3;
|
||||
break;
|
||||
case X25_FAC_CLASS_C:
|
||||
printk(KERN_DEBUG "X.25: unknown facility %02X, "
|
||||
"values %02X, %02X, %02X\n",
|
||||
p[0], p[1], p[2], p[3]);
|
||||
p += 4;
|
||||
len -= 4;
|
||||
break;
|
||||
case X25_FAC_CLASS_D:
|
||||
printk(KERN_DEBUG "X.25: unknown facility %02X, "
|
||||
"length %d, values %02X, %02X, %02X, %02X\n",
|
||||
p[0], p[1], p[2], p[3], p[4], p[5]);
|
||||
len -= p[1] + 2;
|
||||
p += p[1] + 2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return p - skb->data;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a set of facilities.
|
||||
*/
|
||||
int x25_create_facilities(unsigned char *buffer,
|
||||
struct x25_facilities *facilities,
|
||||
unsigned long facil_mask)
|
||||
{
|
||||
unsigned char *p = buffer + 1;
|
||||
int len;
|
||||
|
||||
if (!facil_mask) {
|
||||
/*
|
||||
* Length of the facilities field in call_req or
|
||||
* call_accept packets
|
||||
*/
|
||||
buffer[0] = 0;
|
||||
len = 1; /* 1 byte for the length field */
|
||||
return len;
|
||||
}
|
||||
|
||||
if (facilities->reverse && (facil_mask & X25_MASK_REVERSE)) {
|
||||
*p++ = X25_FAC_REVERSE;
|
||||
*p++ = !!facilities->reverse;
|
||||
}
|
||||
|
||||
if (facilities->throughput && (facil_mask & X25_MASK_THROUGHPUT)) {
|
||||
*p++ = X25_FAC_THROUGHPUT;
|
||||
*p++ = facilities->throughput;
|
||||
}
|
||||
|
||||
if ((facilities->pacsize_in || facilities->pacsize_out) &&
|
||||
(facil_mask & X25_MASK_PACKET_SIZE)) {
|
||||
*p++ = X25_FAC_PACKET_SIZE;
|
||||
*p++ = facilities->pacsize_in ? : facilities->pacsize_out;
|
||||
*p++ = facilities->pacsize_out ? : facilities->pacsize_in;
|
||||
}
|
||||
|
||||
if ((facilities->winsize_in || facilities->winsize_out) &&
|
||||
(facil_mask & X25_MASK_WINDOW_SIZE)) {
|
||||
*p++ = X25_FAC_WINDOW_SIZE;
|
||||
*p++ = facilities->winsize_in ? : facilities->winsize_out;
|
||||
*p++ = facilities->winsize_out ? : facilities->winsize_in;
|
||||
}
|
||||
|
||||
len = p - buffer;
|
||||
buffer[0] = len - 1;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
/*
|
||||
* Try to reach a compromise on a set of facilities.
|
||||
*
|
||||
* The only real problem is with reverse charging.
|
||||
*/
|
||||
int x25_negotiate_facilities(struct sk_buff *skb, struct sock *sk,
|
||||
struct x25_facilities *new)
|
||||
{
|
||||
struct x25_sock *x25 = x25_sk(sk);
|
||||
struct x25_facilities *ours = &x25->facilities;
|
||||
struct x25_facilities theirs;
|
||||
int len;
|
||||
|
||||
memset(&theirs, 0, sizeof(theirs));
|
||||
memcpy(new, ours, sizeof(*new));
|
||||
|
||||
len = x25_parse_facilities(skb, &theirs, &x25->vc_facil_mask);
|
||||
|
||||
/*
|
||||
* They want reverse charging, we won't accept it.
|
||||
*/
|
||||
if (theirs.reverse && ours->reverse) {
|
||||
SOCK_DEBUG(sk, "X.25: rejecting reverse charging request");
|
||||
return -1;
|
||||
}
|
||||
|
||||
new->reverse = theirs.reverse;
|
||||
|
||||
if (theirs.throughput) {
|
||||
if (theirs.throughput < ours->throughput) {
|
||||
SOCK_DEBUG(sk, "X.25: throughput negotiated down");
|
||||
new->throughput = theirs.throughput;
|
||||
}
|
||||
}
|
||||
|
||||
if (theirs.pacsize_in && theirs.pacsize_out) {
|
||||
if (theirs.pacsize_in < ours->pacsize_in) {
|
||||
SOCK_DEBUG(sk, "X.25: packet size inwards negotiated down");
|
||||
new->pacsize_in = theirs.pacsize_in;
|
||||
}
|
||||
if (theirs.pacsize_out < ours->pacsize_out) {
|
||||
SOCK_DEBUG(sk, "X.25: packet size outwards negotiated down");
|
||||
new->pacsize_out = theirs.pacsize_out;
|
||||
}
|
||||
}
|
||||
|
||||
if (theirs.winsize_in && theirs.winsize_out) {
|
||||
if (theirs.winsize_in < ours->winsize_in) {
|
||||
SOCK_DEBUG(sk, "X.25: window size inwards negotiated down");
|
||||
new->winsize_in = theirs.winsize_in;
|
||||
}
|
||||
if (theirs.winsize_out < ours->winsize_out) {
|
||||
SOCK_DEBUG(sk, "X.25: window size outwards negotiated down");
|
||||
new->winsize_out = theirs.winsize_out;
|
||||
}
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
/*
|
||||
* Limit values of certain facilities according to the capability of the
|
||||
* currently attached x25 link.
|
||||
*/
|
||||
void x25_limit_facilities(struct x25_facilities *facilities,
|
||||
struct x25_neigh *nb)
|
||||
{
|
||||
|
||||
if (!nb->extended) {
|
||||
if (facilities->winsize_in > 7) {
|
||||
printk(KERN_DEBUG "X.25: incoming winsize limited to 7\n");
|
||||
facilities->winsize_in = 7;
|
||||
}
|
||||
if (facilities->winsize_out > 7) {
|
||||
facilities->winsize_out = 7;
|
||||
printk( KERN_DEBUG "X.25: outgoing winsize limited to 7\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
361
net/x25/x25_in.c
Normal file
361
net/x25/x25_in.c
Normal file
|
|
@ -0,0 +1,361 @@
|
|||
/*
|
||||
* X.25 Packet Layer release 002
|
||||
*
|
||||
* This is ALPHA test software. This code may break your machine,
|
||||
* randomly fail to work with new releases, misbehave and/or generally
|
||||
* screw up. It might even work.
|
||||
*
|
||||
* This code REQUIRES 2.1.15 or higher
|
||||
*
|
||||
* This module:
|
||||
* This module is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version
|
||||
* 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* History
|
||||
* X.25 001 Jonathan Naylor Started coding.
|
||||
* X.25 002 Jonathan Naylor Centralised disconnection code.
|
||||
* New timer architecture.
|
||||
* 2000-03-20 Daniela Squassoni Disabling/enabling of facilities
|
||||
* negotiation.
|
||||
* 2000-11-10 Henner Eisen Check and reset for out-of-sequence
|
||||
* i-frames.
|
||||
*/
|
||||
|
||||
#include <linux/errno.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <net/sock.h>
|
||||
#include <net/tcp.h>
|
||||
#include <net/x25.h>
|
||||
|
||||
static int x25_queue_rx_frame(struct sock *sk, struct sk_buff *skb, int more)
|
||||
{
|
||||
struct sk_buff *skbo, *skbn = skb;
|
||||
struct x25_sock *x25 = x25_sk(sk);
|
||||
|
||||
if (more) {
|
||||
x25->fraglen += skb->len;
|
||||
skb_queue_tail(&x25->fragment_queue, skb);
|
||||
skb_set_owner_r(skb, sk);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!more && x25->fraglen > 0) { /* End of fragment */
|
||||
int len = x25->fraglen + skb->len;
|
||||
|
||||
if ((skbn = alloc_skb(len, GFP_ATOMIC)) == NULL){
|
||||
kfree_skb(skb);
|
||||
return 1;
|
||||
}
|
||||
|
||||
skb_queue_tail(&x25->fragment_queue, skb);
|
||||
|
||||
skbn->h.raw = skbn->data;
|
||||
|
||||
skbo = skb_dequeue(&x25->fragment_queue);
|
||||
memcpy(skb_put(skbn, skbo->len), skbo->data, skbo->len);
|
||||
kfree_skb(skbo);
|
||||
|
||||
while ((skbo =
|
||||
skb_dequeue(&x25->fragment_queue)) != NULL) {
|
||||
skb_pull(skbo, (x25->neighbour->extended) ?
|
||||
X25_EXT_MIN_LEN : X25_STD_MIN_LEN);
|
||||
memcpy(skb_put(skbn, skbo->len), skbo->data, skbo->len);
|
||||
kfree_skb(skbo);
|
||||
}
|
||||
|
||||
x25->fraglen = 0;
|
||||
}
|
||||
|
||||
skb_set_owner_r(skbn, sk);
|
||||
skb_queue_tail(&sk->sk_receive_queue, skbn);
|
||||
if (!sock_flag(sk, SOCK_DEAD))
|
||||
sk->sk_data_ready(sk, skbn->len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* State machine for state 1, Awaiting Call Accepted State.
|
||||
* The handling of the timer(s) is in file x25_timer.c.
|
||||
* Handling of state 0 and connection release is in af_x25.c.
|
||||
*/
|
||||
static int x25_state1_machine(struct sock *sk, struct sk_buff *skb, int frametype)
|
||||
{
|
||||
struct x25_address source_addr, dest_addr;
|
||||
|
||||
switch (frametype) {
|
||||
case X25_CALL_ACCEPTED: {
|
||||
struct x25_sock *x25 = x25_sk(sk);
|
||||
|
||||
x25_stop_timer(sk);
|
||||
x25->condition = 0x00;
|
||||
x25->vs = 0;
|
||||
x25->va = 0;
|
||||
x25->vr = 0;
|
||||
x25->vl = 0;
|
||||
x25->state = X25_STATE_3;
|
||||
sk->sk_state = TCP_ESTABLISHED;
|
||||
/*
|
||||
* Parse the data in the frame.
|
||||
*/
|
||||
skb_pull(skb, X25_STD_MIN_LEN);
|
||||
skb_pull(skb, x25_addr_ntoa(skb->data, &source_addr, &dest_addr));
|
||||
skb_pull(skb,
|
||||
x25_parse_facilities(skb, &x25->facilities,
|
||||
&x25->vc_facil_mask));
|
||||
/*
|
||||
* Copy any Call User Data.
|
||||
*/
|
||||
if (skb->len >= 0) {
|
||||
memcpy(x25->calluserdata.cuddata, skb->data,
|
||||
skb->len);
|
||||
x25->calluserdata.cudlength = skb->len;
|
||||
}
|
||||
if (!sock_flag(sk, SOCK_DEAD))
|
||||
sk->sk_state_change(sk);
|
||||
break;
|
||||
}
|
||||
case X25_CLEAR_REQUEST:
|
||||
x25_write_internal(sk, X25_CLEAR_CONFIRMATION);
|
||||
x25_disconnect(sk, ECONNREFUSED, skb->data[3], skb->data[4]);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* State machine for state 2, Awaiting Clear Confirmation State.
|
||||
* The handling of the timer(s) is in file x25_timer.c
|
||||
* Handling of state 0 and connection release is in af_x25.c.
|
||||
*/
|
||||
static int x25_state2_machine(struct sock *sk, struct sk_buff *skb, int frametype)
|
||||
{
|
||||
switch (frametype) {
|
||||
|
||||
case X25_CLEAR_REQUEST:
|
||||
x25_write_internal(sk, X25_CLEAR_CONFIRMATION);
|
||||
x25_disconnect(sk, 0, skb->data[3], skb->data[4]);
|
||||
break;
|
||||
|
||||
case X25_CLEAR_CONFIRMATION:
|
||||
x25_disconnect(sk, 0, 0, 0);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* State machine for state 3, Connected State.
|
||||
* The handling of the timer(s) is in file x25_timer.c
|
||||
* Handling of state 0 and connection release is in af_x25.c.
|
||||
*/
|
||||
static int x25_state3_machine(struct sock *sk, struct sk_buff *skb, int frametype, int ns, int nr, int q, int d, int m)
|
||||
{
|
||||
int queued = 0;
|
||||
int modulus;
|
||||
struct x25_sock *x25 = x25_sk(sk);
|
||||
|
||||
modulus = (x25->neighbour->extended) ? X25_EMODULUS : X25_SMODULUS;
|
||||
|
||||
switch (frametype) {
|
||||
|
||||
case X25_RESET_REQUEST:
|
||||
x25_write_internal(sk, X25_RESET_CONFIRMATION);
|
||||
x25_stop_timer(sk);
|
||||
x25->condition = 0x00;
|
||||
x25->vs = 0;
|
||||
x25->vr = 0;
|
||||
x25->va = 0;
|
||||
x25->vl = 0;
|
||||
x25_requeue_frames(sk);
|
||||
break;
|
||||
|
||||
case X25_CLEAR_REQUEST:
|
||||
x25_write_internal(sk, X25_CLEAR_CONFIRMATION);
|
||||
x25_disconnect(sk, 0, skb->data[3], skb->data[4]);
|
||||
break;
|
||||
|
||||
case X25_RR:
|
||||
case X25_RNR:
|
||||
if (!x25_validate_nr(sk, nr)) {
|
||||
x25_clear_queues(sk);
|
||||
x25_write_internal(sk, X25_RESET_REQUEST);
|
||||
x25_start_t22timer(sk);
|
||||
x25->condition = 0x00;
|
||||
x25->vs = 0;
|
||||
x25->vr = 0;
|
||||
x25->va = 0;
|
||||
x25->vl = 0;
|
||||
x25->state = X25_STATE_4;
|
||||
} else {
|
||||
x25_frames_acked(sk, nr);
|
||||
if (frametype == X25_RNR) {
|
||||
x25->condition |= X25_COND_PEER_RX_BUSY;
|
||||
} else {
|
||||
x25->condition &= ~X25_COND_PEER_RX_BUSY;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case X25_DATA: /* XXX */
|
||||
x25->condition &= ~X25_COND_PEER_RX_BUSY;
|
||||
if ((ns != x25->vr) || !x25_validate_nr(sk, nr)) {
|
||||
x25_clear_queues(sk);
|
||||
x25_write_internal(sk, X25_RESET_REQUEST);
|
||||
x25_start_t22timer(sk);
|
||||
x25->condition = 0x00;
|
||||
x25->vs = 0;
|
||||
x25->vr = 0;
|
||||
x25->va = 0;
|
||||
x25->vl = 0;
|
||||
x25->state = X25_STATE_4;
|
||||
break;
|
||||
}
|
||||
x25_frames_acked(sk, nr);
|
||||
if (ns == x25->vr) {
|
||||
if (x25_queue_rx_frame(sk, skb, m) == 0) {
|
||||
x25->vr = (x25->vr + 1) % modulus;
|
||||
queued = 1;
|
||||
} else {
|
||||
/* Should never happen */
|
||||
x25_clear_queues(sk);
|
||||
x25_write_internal(sk, X25_RESET_REQUEST);
|
||||
x25_start_t22timer(sk);
|
||||
x25->condition = 0x00;
|
||||
x25->vs = 0;
|
||||
x25->vr = 0;
|
||||
x25->va = 0;
|
||||
x25->vl = 0;
|
||||
x25->state = X25_STATE_4;
|
||||
break;
|
||||
}
|
||||
if (atomic_read(&sk->sk_rmem_alloc) >
|
||||
(sk->sk_rcvbuf / 2))
|
||||
x25->condition |= X25_COND_OWN_RX_BUSY;
|
||||
}
|
||||
/*
|
||||
* If the window is full Ack it immediately, else
|
||||
* start the holdback timer.
|
||||
*/
|
||||
if (((x25->vl + x25->facilities.winsize_in) % modulus) == x25->vr) {
|
||||
x25->condition &= ~X25_COND_ACK_PENDING;
|
||||
x25_stop_timer(sk);
|
||||
x25_enquiry_response(sk);
|
||||
} else {
|
||||
x25->condition |= X25_COND_ACK_PENDING;
|
||||
x25_start_t2timer(sk);
|
||||
}
|
||||
break;
|
||||
|
||||
case X25_INTERRUPT_CONFIRMATION:
|
||||
x25->intflag = 0;
|
||||
break;
|
||||
|
||||
case X25_INTERRUPT:
|
||||
if (sock_flag(sk, SOCK_URGINLINE))
|
||||
queued = !sock_queue_rcv_skb(sk, skb);
|
||||
else {
|
||||
skb_set_owner_r(skb, sk);
|
||||
skb_queue_tail(&x25->interrupt_in_queue, skb);
|
||||
queued = 1;
|
||||
}
|
||||
sk_send_sigurg(sk);
|
||||
x25_write_internal(sk, X25_INTERRUPT_CONFIRMATION);
|
||||
break;
|
||||
|
||||
default:
|
||||
printk(KERN_WARNING "x25: unknown %02X in state 3\n", frametype);
|
||||
break;
|
||||
}
|
||||
|
||||
return queued;
|
||||
}
|
||||
|
||||
/*
|
||||
* State machine for state 4, Awaiting Reset Confirmation State.
|
||||
* The handling of the timer(s) is in file x25_timer.c
|
||||
* Handling of state 0 and connection release is in af_x25.c.
|
||||
*/
|
||||
static int x25_state4_machine(struct sock *sk, struct sk_buff *skb, int frametype)
|
||||
{
|
||||
switch (frametype) {
|
||||
|
||||
case X25_RESET_REQUEST:
|
||||
x25_write_internal(sk, X25_RESET_CONFIRMATION);
|
||||
case X25_RESET_CONFIRMATION: {
|
||||
struct x25_sock *x25 = x25_sk(sk);
|
||||
|
||||
x25_stop_timer(sk);
|
||||
x25->condition = 0x00;
|
||||
x25->va = 0;
|
||||
x25->vr = 0;
|
||||
x25->vs = 0;
|
||||
x25->vl = 0;
|
||||
x25->state = X25_STATE_3;
|
||||
x25_requeue_frames(sk);
|
||||
break;
|
||||
}
|
||||
case X25_CLEAR_REQUEST:
|
||||
x25_write_internal(sk, X25_CLEAR_CONFIRMATION);
|
||||
x25_disconnect(sk, 0, skb->data[3], skb->data[4]);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Higher level upcall for a LAPB frame */
|
||||
int x25_process_rx_frame(struct sock *sk, struct sk_buff *skb)
|
||||
{
|
||||
struct x25_sock *x25 = x25_sk(sk);
|
||||
int queued = 0, frametype, ns, nr, q, d, m;
|
||||
|
||||
if (x25->state == X25_STATE_0)
|
||||
return 0;
|
||||
|
||||
frametype = x25_decode(sk, skb, &ns, &nr, &q, &d, &m);
|
||||
|
||||
switch (x25->state) {
|
||||
case X25_STATE_1:
|
||||
queued = x25_state1_machine(sk, skb, frametype);
|
||||
break;
|
||||
case X25_STATE_2:
|
||||
queued = x25_state2_machine(sk, skb, frametype);
|
||||
break;
|
||||
case X25_STATE_3:
|
||||
queued = x25_state3_machine(sk, skb, frametype, ns, nr, q, d, m);
|
||||
break;
|
||||
case X25_STATE_4:
|
||||
queued = x25_state4_machine(sk, skb, frametype);
|
||||
break;
|
||||
}
|
||||
|
||||
x25_kick(sk);
|
||||
|
||||
return queued;
|
||||
}
|
||||
|
||||
int x25_backlog_rcv(struct sock *sk, struct sk_buff *skb)
|
||||
{
|
||||
int queued = x25_process_rx_frame(sk, skb);
|
||||
|
||||
if (!queued)
|
||||
kfree_skb(skb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
401
net/x25/x25_link.c
Normal file
401
net/x25/x25_link.c
Normal file
|
|
@ -0,0 +1,401 @@
|
|||
/*
|
||||
* X.25 Packet Layer release 002
|
||||
*
|
||||
* This is ALPHA test software. This code may break your machine,
|
||||
* randomly fail to work with new releases, misbehave and/or generally
|
||||
* screw up. It might even work.
|
||||
*
|
||||
* This code REQUIRES 2.1.15 or higher
|
||||
*
|
||||
* This module:
|
||||
* This module is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version
|
||||
* 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* History
|
||||
* X.25 001 Jonathan Naylor Started coding.
|
||||
* X.25 002 Jonathan Naylor New timer architecture.
|
||||
* mar/20/00 Daniela Squassoni Disabling/enabling of facilities
|
||||
* negotiation.
|
||||
* 2000-09-04 Henner Eisen dev_hold() / dev_put() for x25_neigh.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include <linux/init.h>
|
||||
#include <net/x25.h>
|
||||
|
||||
static struct list_head x25_neigh_list = LIST_HEAD_INIT(x25_neigh_list);
|
||||
static DEFINE_RWLOCK(x25_neigh_list_lock);
|
||||
|
||||
static void x25_t20timer_expiry(unsigned long);
|
||||
|
||||
static void x25_transmit_restart_confirmation(struct x25_neigh *nb);
|
||||
static void x25_transmit_restart_request(struct x25_neigh *nb);
|
||||
|
||||
/*
|
||||
* Linux set/reset timer routines
|
||||
*/
|
||||
static inline void x25_start_t20timer(struct x25_neigh *nb)
|
||||
{
|
||||
mod_timer(&nb->t20timer, jiffies + nb->t20);
|
||||
}
|
||||
|
||||
static void x25_t20timer_expiry(unsigned long param)
|
||||
{
|
||||
struct x25_neigh *nb = (struct x25_neigh *)param;
|
||||
|
||||
x25_transmit_restart_request(nb);
|
||||
|
||||
x25_start_t20timer(nb);
|
||||
}
|
||||
|
||||
static inline void x25_stop_t20timer(struct x25_neigh *nb)
|
||||
{
|
||||
del_timer(&nb->t20timer);
|
||||
}
|
||||
|
||||
static inline int x25_t20timer_pending(struct x25_neigh *nb)
|
||||
{
|
||||
return timer_pending(&nb->t20timer);
|
||||
}
|
||||
|
||||
/*
|
||||
* This handles all restart and diagnostic frames.
|
||||
*/
|
||||
void x25_link_control(struct sk_buff *skb, struct x25_neigh *nb,
|
||||
unsigned short frametype)
|
||||
{
|
||||
struct sk_buff *skbn;
|
||||
int confirm;
|
||||
|
||||
switch (frametype) {
|
||||
case X25_RESTART_REQUEST:
|
||||
confirm = !x25_t20timer_pending(nb);
|
||||
x25_stop_t20timer(nb);
|
||||
nb->state = X25_LINK_STATE_3;
|
||||
if (confirm)
|
||||
x25_transmit_restart_confirmation(nb);
|
||||
break;
|
||||
|
||||
case X25_RESTART_CONFIRMATION:
|
||||
x25_stop_t20timer(nb);
|
||||
nb->state = X25_LINK_STATE_3;
|
||||
break;
|
||||
|
||||
case X25_DIAGNOSTIC:
|
||||
printk(KERN_WARNING "x25: diagnostic #%d - "
|
||||
"%02X %02X %02X\n",
|
||||
skb->data[3], skb->data[4],
|
||||
skb->data[5], skb->data[6]);
|
||||
break;
|
||||
|
||||
default:
|
||||
printk(KERN_WARNING "x25: received unknown %02X "
|
||||
"with LCI 000\n", frametype);
|
||||
break;
|
||||
}
|
||||
|
||||
if (nb->state == X25_LINK_STATE_3)
|
||||
while ((skbn = skb_dequeue(&nb->queue)) != NULL)
|
||||
x25_send_frame(skbn, nb);
|
||||
}
|
||||
|
||||
/*
|
||||
* This routine is called when a Restart Request is needed
|
||||
*/
|
||||
static void x25_transmit_restart_request(struct x25_neigh *nb)
|
||||
{
|
||||
unsigned char *dptr;
|
||||
int len = X25_MAX_L2_LEN + X25_STD_MIN_LEN + 2;
|
||||
struct sk_buff *skb = alloc_skb(len, GFP_ATOMIC);
|
||||
|
||||
if (!skb)
|
||||
return;
|
||||
|
||||
skb_reserve(skb, X25_MAX_L2_LEN);
|
||||
|
||||
dptr = skb_put(skb, X25_STD_MIN_LEN + 2);
|
||||
|
||||
*dptr++ = nb->extended ? X25_GFI_EXTSEQ : X25_GFI_STDSEQ;
|
||||
*dptr++ = 0x00;
|
||||
*dptr++ = X25_RESTART_REQUEST;
|
||||
*dptr++ = 0x00;
|
||||
*dptr++ = 0;
|
||||
|
||||
skb->sk = NULL;
|
||||
|
||||
x25_send_frame(skb, nb);
|
||||
}
|
||||
|
||||
/*
|
||||
* This routine is called when a Restart Confirmation is needed
|
||||
*/
|
||||
static void x25_transmit_restart_confirmation(struct x25_neigh *nb)
|
||||
{
|
||||
unsigned char *dptr;
|
||||
int len = X25_MAX_L2_LEN + X25_STD_MIN_LEN;
|
||||
struct sk_buff *skb = alloc_skb(len, GFP_ATOMIC);
|
||||
|
||||
if (!skb)
|
||||
return;
|
||||
|
||||
skb_reserve(skb, X25_MAX_L2_LEN);
|
||||
|
||||
dptr = skb_put(skb, X25_STD_MIN_LEN);
|
||||
|
||||
*dptr++ = nb->extended ? X25_GFI_EXTSEQ : X25_GFI_STDSEQ;
|
||||
*dptr++ = 0x00;
|
||||
*dptr++ = X25_RESTART_CONFIRMATION;
|
||||
|
||||
skb->sk = NULL;
|
||||
|
||||
x25_send_frame(skb, nb);
|
||||
}
|
||||
|
||||
/*
|
||||
* This routine is called when a Clear Request is needed outside of the context
|
||||
* of a connected socket.
|
||||
*/
|
||||
void x25_transmit_clear_request(struct x25_neigh *nb, unsigned int lci,
|
||||
unsigned char cause)
|
||||
{
|
||||
unsigned char *dptr;
|
||||
int len = X25_MAX_L2_LEN + X25_STD_MIN_LEN + 2;
|
||||
struct sk_buff *skb = alloc_skb(len, GFP_ATOMIC);
|
||||
|
||||
if (!skb)
|
||||
return;
|
||||
|
||||
skb_reserve(skb, X25_MAX_L2_LEN);
|
||||
|
||||
dptr = skb_put(skb, X25_STD_MIN_LEN + 2);
|
||||
|
||||
*dptr++ = ((lci >> 8) & 0x0F) | (nb->extended ?
|
||||
X25_GFI_EXTSEQ :
|
||||
X25_GFI_STDSEQ);
|
||||
*dptr++ = (lci >> 0) & 0xFF;
|
||||
*dptr++ = X25_CLEAR_REQUEST;
|
||||
*dptr++ = cause;
|
||||
*dptr++ = 0x00;
|
||||
|
||||
skb->sk = NULL;
|
||||
|
||||
x25_send_frame(skb, nb);
|
||||
}
|
||||
|
||||
void x25_transmit_link(struct sk_buff *skb, struct x25_neigh *nb)
|
||||
{
|
||||
switch (nb->state) {
|
||||
case X25_LINK_STATE_0:
|
||||
skb_queue_tail(&nb->queue, skb);
|
||||
nb->state = X25_LINK_STATE_1;
|
||||
x25_establish_link(nb);
|
||||
break;
|
||||
case X25_LINK_STATE_1:
|
||||
case X25_LINK_STATE_2:
|
||||
skb_queue_tail(&nb->queue, skb);
|
||||
break;
|
||||
case X25_LINK_STATE_3:
|
||||
x25_send_frame(skb, nb);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Called when the link layer has become established.
|
||||
*/
|
||||
void x25_link_established(struct x25_neigh *nb)
|
||||
{
|
||||
switch (nb->state) {
|
||||
case X25_LINK_STATE_0:
|
||||
nb->state = X25_LINK_STATE_2;
|
||||
break;
|
||||
case X25_LINK_STATE_1:
|
||||
x25_transmit_restart_request(nb);
|
||||
nb->state = X25_LINK_STATE_2;
|
||||
x25_start_t20timer(nb);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Called when the link layer has terminated, or an establishment
|
||||
* request has failed.
|
||||
*/
|
||||
|
||||
void x25_link_terminated(struct x25_neigh *nb)
|
||||
{
|
||||
nb->state = X25_LINK_STATE_0;
|
||||
/* Out of order: clear existing virtual calls (X.25 03/93 4.6.3) */
|
||||
x25_kill_by_neigh(nb);
|
||||
}
|
||||
|
||||
/*
|
||||
* Add a new device.
|
||||
*/
|
||||
void x25_link_device_up(struct net_device *dev)
|
||||
{
|
||||
struct x25_neigh *nb = kmalloc(sizeof(*nb), GFP_ATOMIC);
|
||||
|
||||
if (!nb)
|
||||
return;
|
||||
|
||||
skb_queue_head_init(&nb->queue);
|
||||
|
||||
init_timer(&nb->t20timer);
|
||||
nb->t20timer.data = (unsigned long)nb;
|
||||
nb->t20timer.function = &x25_t20timer_expiry;
|
||||
|
||||
dev_hold(dev);
|
||||
nb->dev = dev;
|
||||
nb->state = X25_LINK_STATE_0;
|
||||
nb->extended = 0;
|
||||
/*
|
||||
* Enables negotiation
|
||||
*/
|
||||
nb->global_facil_mask = X25_MASK_REVERSE |
|
||||
X25_MASK_THROUGHPUT |
|
||||
X25_MASK_PACKET_SIZE |
|
||||
X25_MASK_WINDOW_SIZE;
|
||||
nb->t20 = sysctl_x25_restart_request_timeout;
|
||||
atomic_set(&nb->refcnt, 1);
|
||||
|
||||
write_lock_bh(&x25_neigh_list_lock);
|
||||
list_add(&nb->node, &x25_neigh_list);
|
||||
write_unlock_bh(&x25_neigh_list_lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* __x25_remove_neigh - remove neighbour from x25_neigh_list
|
||||
* @nb - neigh to remove
|
||||
*
|
||||
* Remove neighbour from x25_neigh_list. If it was there.
|
||||
* Caller must hold x25_neigh_list_lock.
|
||||
*/
|
||||
static void __x25_remove_neigh(struct x25_neigh *nb)
|
||||
{
|
||||
skb_queue_purge(&nb->queue);
|
||||
x25_stop_t20timer(nb);
|
||||
|
||||
if (nb->node.next) {
|
||||
list_del(&nb->node);
|
||||
x25_neigh_put(nb);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* A device has been removed, remove its links.
|
||||
*/
|
||||
void x25_link_device_down(struct net_device *dev)
|
||||
{
|
||||
struct x25_neigh *nb;
|
||||
struct list_head *entry, *tmp;
|
||||
|
||||
write_lock_bh(&x25_neigh_list_lock);
|
||||
|
||||
list_for_each_safe(entry, tmp, &x25_neigh_list) {
|
||||
nb = list_entry(entry, struct x25_neigh, node);
|
||||
|
||||
if (nb->dev == dev) {
|
||||
__x25_remove_neigh(nb);
|
||||
dev_put(dev);
|
||||
}
|
||||
}
|
||||
|
||||
write_unlock_bh(&x25_neigh_list_lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* Given a device, return the neighbour address.
|
||||
*/
|
||||
struct x25_neigh *x25_get_neigh(struct net_device *dev)
|
||||
{
|
||||
struct x25_neigh *nb, *use = NULL;
|
||||
struct list_head *entry;
|
||||
|
||||
read_lock_bh(&x25_neigh_list_lock);
|
||||
list_for_each(entry, &x25_neigh_list) {
|
||||
nb = list_entry(entry, struct x25_neigh, node);
|
||||
|
||||
if (nb->dev == dev) {
|
||||
use = nb;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (use)
|
||||
x25_neigh_hold(use);
|
||||
read_unlock_bh(&x25_neigh_list_lock);
|
||||
return use;
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle the ioctls that control the subscription functions.
|
||||
*/
|
||||
int x25_subscr_ioctl(unsigned int cmd, void __user *arg)
|
||||
{
|
||||
struct x25_subscrip_struct x25_subscr;
|
||||
struct x25_neigh *nb;
|
||||
struct net_device *dev;
|
||||
int rc = -EINVAL;
|
||||
|
||||
if (cmd != SIOCX25GSUBSCRIP && cmd != SIOCX25SSUBSCRIP)
|
||||
goto out;
|
||||
|
||||
rc = -EFAULT;
|
||||
if (copy_from_user(&x25_subscr, arg, sizeof(x25_subscr)))
|
||||
goto out;
|
||||
|
||||
rc = -EINVAL;
|
||||
if ((dev = x25_dev_get(x25_subscr.device)) == NULL)
|
||||
goto out;
|
||||
|
||||
if ((nb = x25_get_neigh(dev)) == NULL)
|
||||
goto out_dev_put;
|
||||
|
||||
dev_put(dev);
|
||||
|
||||
if (cmd == SIOCX25GSUBSCRIP) {
|
||||
x25_subscr.extended = nb->extended;
|
||||
x25_subscr.global_facil_mask = nb->global_facil_mask;
|
||||
rc = copy_to_user(arg, &x25_subscr,
|
||||
sizeof(x25_subscr)) ? -EFAULT : 0;
|
||||
} else {
|
||||
rc = -EINVAL;
|
||||
if (!(x25_subscr.extended && x25_subscr.extended != 1)) {
|
||||
rc = 0;
|
||||
nb->extended = x25_subscr.extended;
|
||||
nb->global_facil_mask = x25_subscr.global_facil_mask;
|
||||
}
|
||||
}
|
||||
x25_neigh_put(nb);
|
||||
out:
|
||||
return rc;
|
||||
out_dev_put:
|
||||
dev_put(dev);
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Release all memory associated with X.25 neighbour structures.
|
||||
*/
|
||||
void __exit x25_link_free(void)
|
||||
{
|
||||
struct x25_neigh *nb;
|
||||
struct list_head *entry, *tmp;
|
||||
|
||||
write_lock_bh(&x25_neigh_list_lock);
|
||||
|
||||
list_for_each_safe(entry, tmp, &x25_neigh_list) {
|
||||
nb = list_entry(entry, struct x25_neigh, node);
|
||||
__x25_remove_neigh(nb);
|
||||
}
|
||||
write_unlock_bh(&x25_neigh_list_lock);
|
||||
}
|
||||
226
net/x25/x25_out.c
Normal file
226
net/x25/x25_out.c
Normal file
|
|
@ -0,0 +1,226 @@
|
|||
/*
|
||||
* X.25 Packet Layer release 002
|
||||
*
|
||||
* This is ALPHA test software. This code may break your machine,
|
||||
* randomly fail to work with new releases, misbehave and/or generally
|
||||
* screw up. It might even work.
|
||||
*
|
||||
* This code REQUIRES 2.1.15 or higher
|
||||
*
|
||||
* This module:
|
||||
* This module is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version
|
||||
* 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* History
|
||||
* X.25 001 Jonathan Naylor Started coding.
|
||||
* X.25 002 Jonathan Naylor New timer architecture.
|
||||
* 2000-09-04 Henner Eisen Prevented x25_output() skb leakage.
|
||||
* 2000-10-27 Henner Eisen MSG_DONTWAIT for fragment allocation.
|
||||
* 2000-11-10 Henner Eisen x25_send_iframe(): re-queued frames
|
||||
* needed cleaned seq-number fields.
|
||||
*/
|
||||
|
||||
#include <linux/socket.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <net/sock.h>
|
||||
#include <net/x25.h>
|
||||
|
||||
static int x25_pacsize_to_bytes(unsigned int pacsize)
|
||||
{
|
||||
int bytes = 1;
|
||||
|
||||
if (!pacsize)
|
||||
return 128;
|
||||
|
||||
while (pacsize-- > 0)
|
||||
bytes *= 2;
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
/*
|
||||
* This is where all X.25 information frames pass.
|
||||
*
|
||||
* Returns the amount of user data bytes sent on success
|
||||
* or a negative error code on failure.
|
||||
*/
|
||||
int x25_output(struct sock *sk, struct sk_buff *skb)
|
||||
{
|
||||
struct sk_buff *skbn;
|
||||
unsigned char header[X25_EXT_MIN_LEN];
|
||||
int err, frontlen, len;
|
||||
int sent=0, noblock = X25_SKB_CB(skb)->flags & MSG_DONTWAIT;
|
||||
struct x25_sock *x25 = x25_sk(sk);
|
||||
int header_len = x25->neighbour->extended ? X25_EXT_MIN_LEN :
|
||||
X25_STD_MIN_LEN;
|
||||
int max_len = x25_pacsize_to_bytes(x25->facilities.pacsize_out);
|
||||
|
||||
if (skb->len - header_len > max_len) {
|
||||
/* Save a copy of the Header */
|
||||
memcpy(header, skb->data, header_len);
|
||||
skb_pull(skb, header_len);
|
||||
|
||||
frontlen = skb_headroom(skb);
|
||||
|
||||
while (skb->len > 0) {
|
||||
if ((skbn = sock_alloc_send_skb(sk, frontlen + max_len,
|
||||
noblock, &err)) == NULL){
|
||||
if (err == -EWOULDBLOCK && noblock){
|
||||
kfree_skb(skb);
|
||||
return sent;
|
||||
}
|
||||
SOCK_DEBUG(sk, "x25_output: fragment alloc"
|
||||
" failed, err=%d, %d bytes "
|
||||
"sent\n", err, sent);
|
||||
return err;
|
||||
}
|
||||
|
||||
skb_reserve(skbn, frontlen);
|
||||
|
||||
len = max_len > skb->len ? skb->len : max_len;
|
||||
|
||||
/* Copy the user data */
|
||||
memcpy(skb_put(skbn, len), skb->data, len);
|
||||
skb_pull(skb, len);
|
||||
|
||||
/* Duplicate the Header */
|
||||
skb_push(skbn, header_len);
|
||||
memcpy(skbn->data, header, header_len);
|
||||
|
||||
if (skb->len > 0) {
|
||||
if (x25->neighbour->extended)
|
||||
skbn->data[3] |= X25_EXT_M_BIT;
|
||||
else
|
||||
skbn->data[2] |= X25_STD_M_BIT;
|
||||
}
|
||||
|
||||
skb_queue_tail(&sk->sk_write_queue, skbn);
|
||||
sent += len;
|
||||
}
|
||||
|
||||
kfree_skb(skb);
|
||||
} else {
|
||||
skb_queue_tail(&sk->sk_write_queue, skb);
|
||||
sent = skb->len - header_len;
|
||||
}
|
||||
return sent;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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 x25_send_iframe(struct sock *sk, struct sk_buff *skb)
|
||||
{
|
||||
struct x25_sock *x25 = x25_sk(sk);
|
||||
|
||||
if (!skb)
|
||||
return;
|
||||
|
||||
if (x25->neighbour->extended) {
|
||||
skb->data[2] = (x25->vs << 1) & 0xFE;
|
||||
skb->data[3] &= X25_EXT_M_BIT;
|
||||
skb->data[3] |= (x25->vr << 1) & 0xFE;
|
||||
} else {
|
||||
skb->data[2] &= X25_STD_M_BIT;
|
||||
skb->data[2] |= (x25->vs << 1) & 0x0E;
|
||||
skb->data[2] |= (x25->vr << 5) & 0xE0;
|
||||
}
|
||||
|
||||
x25_transmit_link(skb, x25->neighbour);
|
||||
}
|
||||
|
||||
void x25_kick(struct sock *sk)
|
||||
{
|
||||
struct sk_buff *skb, *skbn;
|
||||
unsigned short start, end;
|
||||
int modulus;
|
||||
struct x25_sock *x25 = x25_sk(sk);
|
||||
|
||||
if (x25->state != X25_STATE_3)
|
||||
return;
|
||||
|
||||
/*
|
||||
* Transmit interrupt data.
|
||||
*/
|
||||
if (!x25->intflag && skb_peek(&x25->interrupt_out_queue) != NULL) {
|
||||
x25->intflag = 1;
|
||||
skb = skb_dequeue(&x25->interrupt_out_queue);
|
||||
x25_transmit_link(skb, x25->neighbour);
|
||||
}
|
||||
|
||||
if (x25->condition & X25_COND_PEER_RX_BUSY)
|
||||
return;
|
||||
|
||||
if (!skb_peek(&sk->sk_write_queue))
|
||||
return;
|
||||
|
||||
modulus = x25->neighbour->extended ? X25_EMODULUS : X25_SMODULUS;
|
||||
|
||||
start = skb_peek(&x25->ack_queue) ? x25->vs : x25->va;
|
||||
end = (x25->va + x25->facilities.winsize_out) % modulus;
|
||||
|
||||
if (start == end)
|
||||
return;
|
||||
|
||||
x25->vs = start;
|
||||
|
||||
/*
|
||||
* Transmit data until either we're out of data to send or
|
||||
* the window is full.
|
||||
*/
|
||||
|
||||
skb = skb_dequeue(&sk->sk_write_queue);
|
||||
|
||||
do {
|
||||
if ((skbn = skb_clone(skb, GFP_ATOMIC)) == NULL) {
|
||||
skb_queue_head(&sk->sk_write_queue, skb);
|
||||
break;
|
||||
}
|
||||
|
||||
skb_set_owner_w(skbn, sk);
|
||||
|
||||
/*
|
||||
* Transmit the frame copy.
|
||||
*/
|
||||
x25_send_iframe(sk, skbn);
|
||||
|
||||
x25->vs = (x25->vs + 1) % modulus;
|
||||
|
||||
/*
|
||||
* Requeue the original data frame.
|
||||
*/
|
||||
skb_queue_tail(&x25->ack_queue, skb);
|
||||
|
||||
} while (x25->vs != end &&
|
||||
(skb = skb_dequeue(&sk->sk_write_queue)) != NULL);
|
||||
|
||||
x25->vl = x25->vr;
|
||||
x25->condition &= ~X25_COND_ACK_PENDING;
|
||||
|
||||
x25_stop_timer(sk);
|
||||
}
|
||||
|
||||
/*
|
||||
* The following routines are taken from page 170 of the 7th ARRL Computer
|
||||
* Networking Conference paper, as is the whole state machine.
|
||||
*/
|
||||
|
||||
void x25_enquiry_response(struct sock *sk)
|
||||
{
|
||||
struct x25_sock *x25 = x25_sk(sk);
|
||||
|
||||
if (x25->condition & X25_COND_OWN_RX_BUSY)
|
||||
x25_write_internal(sk, X25_RNR);
|
||||
else
|
||||
x25_write_internal(sk, X25_RR);
|
||||
|
||||
x25->vl = x25->vr;
|
||||
x25->condition &= ~X25_COND_ACK_PENDING;
|
||||
|
||||
x25_stop_timer(sk);
|
||||
}
|
||||
256
net/x25/x25_proc.c
Normal file
256
net/x25/x25_proc.c
Normal file
|
|
@ -0,0 +1,256 @@
|
|||
/*
|
||||
* X.25 Packet Layer release 002
|
||||
*
|
||||
* This is ALPHA test software. This code may break your machine,
|
||||
* randomly fail to work with new releases, misbehave and/or generally
|
||||
* screw up. It might even work.
|
||||
*
|
||||
* This code REQUIRES 2.4 with seq_file support
|
||||
*
|
||||
* This module:
|
||||
* This module is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version
|
||||
* 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* History
|
||||
* 2002/10/06 Arnaldo Carvalho de Melo seq_file support
|
||||
*/
|
||||
|
||||
#include <linux/config.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <net/sock.h>
|
||||
#include <net/x25.h>
|
||||
|
||||
#ifdef CONFIG_PROC_FS
|
||||
static __inline__ struct x25_route *x25_get_route_idx(loff_t pos)
|
||||
{
|
||||
struct list_head *route_entry;
|
||||
struct x25_route *rt = NULL;
|
||||
|
||||
list_for_each(route_entry, &x25_route_list) {
|
||||
rt = list_entry(route_entry, struct x25_route, node);
|
||||
if (!pos--)
|
||||
goto found;
|
||||
}
|
||||
rt = NULL;
|
||||
found:
|
||||
return rt;
|
||||
}
|
||||
|
||||
static void *x25_seq_route_start(struct seq_file *seq, loff_t *pos)
|
||||
{
|
||||
loff_t l = *pos;
|
||||
|
||||
read_lock_bh(&x25_route_list_lock);
|
||||
return l ? x25_get_route_idx(--l) : SEQ_START_TOKEN;
|
||||
}
|
||||
|
||||
static void *x25_seq_route_next(struct seq_file *seq, void *v, loff_t *pos)
|
||||
{
|
||||
struct x25_route *rt;
|
||||
|
||||
++*pos;
|
||||
if (v == SEQ_START_TOKEN) {
|
||||
rt = NULL;
|
||||
if (!list_empty(&x25_route_list))
|
||||
rt = list_entry(x25_route_list.next,
|
||||
struct x25_route, node);
|
||||
goto out;
|
||||
}
|
||||
rt = v;
|
||||
if (rt->node.next != &x25_route_list)
|
||||
rt = list_entry(rt->node.next, struct x25_route, node);
|
||||
else
|
||||
rt = NULL;
|
||||
out:
|
||||
return rt;
|
||||
}
|
||||
|
||||
static void x25_seq_route_stop(struct seq_file *seq, void *v)
|
||||
{
|
||||
read_unlock_bh(&x25_route_list_lock);
|
||||
}
|
||||
|
||||
static int x25_seq_route_show(struct seq_file *seq, void *v)
|
||||
{
|
||||
struct x25_route *rt;
|
||||
|
||||
if (v == SEQ_START_TOKEN) {
|
||||
seq_puts(seq, "Address Digits Device\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
rt = v;
|
||||
seq_printf(seq, "%-15s %-6d %-5s\n",
|
||||
rt->address.x25_addr, rt->sigdigits,
|
||||
rt->dev ? rt->dev->name : "???");
|
||||
out:
|
||||
return 0;
|
||||
}
|
||||
|
||||
static __inline__ struct sock *x25_get_socket_idx(loff_t pos)
|
||||
{
|
||||
struct sock *s;
|
||||
struct hlist_node *node;
|
||||
|
||||
sk_for_each(s, node, &x25_list)
|
||||
if (!pos--)
|
||||
goto found;
|
||||
s = NULL;
|
||||
found:
|
||||
return s;
|
||||
}
|
||||
|
||||
static void *x25_seq_socket_start(struct seq_file *seq, loff_t *pos)
|
||||
{
|
||||
loff_t l = *pos;
|
||||
|
||||
read_lock_bh(&x25_list_lock);
|
||||
return l ? x25_get_socket_idx(--l) : SEQ_START_TOKEN;
|
||||
}
|
||||
|
||||
static void *x25_seq_socket_next(struct seq_file *seq, void *v, loff_t *pos)
|
||||
{
|
||||
struct sock *s;
|
||||
|
||||
++*pos;
|
||||
if (v == SEQ_START_TOKEN) {
|
||||
s = sk_head(&x25_list);
|
||||
goto out;
|
||||
}
|
||||
s = sk_next(v);
|
||||
out:
|
||||
return s;
|
||||
}
|
||||
|
||||
static void x25_seq_socket_stop(struct seq_file *seq, void *v)
|
||||
{
|
||||
read_unlock_bh(&x25_list_lock);
|
||||
}
|
||||
|
||||
static int x25_seq_socket_show(struct seq_file *seq, void *v)
|
||||
{
|
||||
struct sock *s;
|
||||
struct x25_sock *x25;
|
||||
struct net_device *dev;
|
||||
const char *devname;
|
||||
|
||||
if (v == SEQ_START_TOKEN) {
|
||||
seq_printf(seq, "dest_addr src_addr dev lci st vs vr "
|
||||
"va t t2 t21 t22 t23 Snd-Q Rcv-Q inode\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
s = v;
|
||||
x25 = x25_sk(s);
|
||||
|
||||
if (!x25->neighbour || (dev = x25->neighbour->dev) == NULL)
|
||||
devname = "???";
|
||||
else
|
||||
devname = x25->neighbour->dev->name;
|
||||
|
||||
seq_printf(seq, "%-10s %-10s %-5s %3.3X %d %d %d %d %3lu %3lu "
|
||||
"%3lu %3lu %3lu %5d %5d %ld\n",
|
||||
!x25->dest_addr.x25_addr[0] ? "*" : x25->dest_addr.x25_addr,
|
||||
!x25->source_addr.x25_addr[0] ? "*" : x25->source_addr.x25_addr,
|
||||
devname, x25->lci & 0x0FFF, x25->state, x25->vs, x25->vr,
|
||||
x25->va, x25_display_timer(s) / HZ, x25->t2 / HZ,
|
||||
x25->t21 / HZ, x25->t22 / HZ, x25->t23 / HZ,
|
||||
atomic_read(&s->sk_wmem_alloc),
|
||||
atomic_read(&s->sk_rmem_alloc),
|
||||
s->sk_socket ? SOCK_INODE(s->sk_socket)->i_ino : 0L);
|
||||
out:
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct seq_operations x25_seq_route_ops = {
|
||||
.start = x25_seq_route_start,
|
||||
.next = x25_seq_route_next,
|
||||
.stop = x25_seq_route_stop,
|
||||
.show = x25_seq_route_show,
|
||||
};
|
||||
|
||||
static struct seq_operations x25_seq_socket_ops = {
|
||||
.start = x25_seq_socket_start,
|
||||
.next = x25_seq_socket_next,
|
||||
.stop = x25_seq_socket_stop,
|
||||
.show = x25_seq_socket_show,
|
||||
};
|
||||
|
||||
static int x25_seq_socket_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return seq_open(file, &x25_seq_socket_ops);
|
||||
}
|
||||
|
||||
static int x25_seq_route_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return seq_open(file, &x25_seq_route_ops);
|
||||
}
|
||||
|
||||
static struct file_operations x25_seq_socket_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = x25_seq_socket_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = seq_release,
|
||||
};
|
||||
|
||||
static struct file_operations x25_seq_route_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = x25_seq_route_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = seq_release,
|
||||
};
|
||||
|
||||
static struct proc_dir_entry *x25_proc_dir;
|
||||
|
||||
int __init x25_proc_init(void)
|
||||
{
|
||||
struct proc_dir_entry *p;
|
||||
int rc = -ENOMEM;
|
||||
|
||||
x25_proc_dir = proc_mkdir("x25", proc_net);
|
||||
if (!x25_proc_dir)
|
||||
goto out;
|
||||
|
||||
p = create_proc_entry("route", S_IRUGO, x25_proc_dir);
|
||||
if (!p)
|
||||
goto out_route;
|
||||
p->proc_fops = &x25_seq_route_fops;
|
||||
|
||||
p = create_proc_entry("socket", S_IRUGO, x25_proc_dir);
|
||||
if (!p)
|
||||
goto out_socket;
|
||||
p->proc_fops = &x25_seq_socket_fops;
|
||||
rc = 0;
|
||||
out:
|
||||
return rc;
|
||||
out_socket:
|
||||
remove_proc_entry("route", x25_proc_dir);
|
||||
out_route:
|
||||
remove_proc_entry("x25", proc_net);
|
||||
goto out;
|
||||
}
|
||||
|
||||
void __exit x25_proc_exit(void)
|
||||
{
|
||||
remove_proc_entry("route", x25_proc_dir);
|
||||
remove_proc_entry("socket", x25_proc_dir);
|
||||
remove_proc_entry("x25", proc_net);
|
||||
}
|
||||
|
||||
#else /* CONFIG_PROC_FS */
|
||||
|
||||
int __init x25_proc_init(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void __exit x25_proc_exit(void)
|
||||
{
|
||||
}
|
||||
#endif /* CONFIG_PROC_FS */
|
||||
221
net/x25/x25_route.c
Normal file
221
net/x25/x25_route.c
Normal file
|
|
@ -0,0 +1,221 @@
|
|||
/*
|
||||
* X.25 Packet Layer release 002
|
||||
*
|
||||
* This is ALPHA test software. This code may break your machine,
|
||||
* randomly fail to work with new releases, misbehave and/or generally
|
||||
* screw up. It might even work.
|
||||
*
|
||||
* This code REQUIRES 2.1.15 or higher
|
||||
*
|
||||
* This module:
|
||||
* This module is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version
|
||||
* 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* History
|
||||
* X.25 001 Jonathan Naylor Started coding.
|
||||
*/
|
||||
|
||||
#include <linux/config.h>
|
||||
#include <linux/if_arp.h>
|
||||
#include <linux/init.h>
|
||||
#include <net/x25.h>
|
||||
|
||||
struct list_head x25_route_list = LIST_HEAD_INIT(x25_route_list);
|
||||
DEFINE_RWLOCK(x25_route_list_lock);
|
||||
|
||||
/*
|
||||
* Add a new route.
|
||||
*/
|
||||
static int x25_add_route(struct x25_address *address, unsigned int sigdigits,
|
||||
struct net_device *dev)
|
||||
{
|
||||
struct x25_route *rt;
|
||||
struct list_head *entry;
|
||||
int rc = -EINVAL;
|
||||
|
||||
write_lock_bh(&x25_route_list_lock);
|
||||
|
||||
list_for_each(entry, &x25_route_list) {
|
||||
rt = list_entry(entry, struct x25_route, node);
|
||||
|
||||
if (!memcmp(&rt->address, address, sigdigits) &&
|
||||
rt->sigdigits == sigdigits)
|
||||
goto out;
|
||||
}
|
||||
|
||||
rt = kmalloc(sizeof(*rt), GFP_ATOMIC);
|
||||
rc = -ENOMEM;
|
||||
if (!rt)
|
||||
goto out;
|
||||
|
||||
strcpy(rt->address.x25_addr, "000000000000000");
|
||||
memcpy(rt->address.x25_addr, address->x25_addr, sigdigits);
|
||||
|
||||
rt->sigdigits = sigdigits;
|
||||
rt->dev = dev;
|
||||
atomic_set(&rt->refcnt, 1);
|
||||
|
||||
list_add(&rt->node, &x25_route_list);
|
||||
rc = 0;
|
||||
out:
|
||||
write_unlock_bh(&x25_route_list_lock);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* __x25_remove_route - remove route from x25_route_list
|
||||
* @rt - route to remove
|
||||
*
|
||||
* Remove route from x25_route_list. If it was there.
|
||||
* Caller must hold x25_route_list_lock.
|
||||
*/
|
||||
static void __x25_remove_route(struct x25_route *rt)
|
||||
{
|
||||
if (rt->node.next) {
|
||||
list_del(&rt->node);
|
||||
x25_route_put(rt);
|
||||
}
|
||||
}
|
||||
|
||||
static int x25_del_route(struct x25_address *address, unsigned int sigdigits,
|
||||
struct net_device *dev)
|
||||
{
|
||||
struct x25_route *rt;
|
||||
struct list_head *entry;
|
||||
int rc = -EINVAL;
|
||||
|
||||
write_lock_bh(&x25_route_list_lock);
|
||||
|
||||
list_for_each(entry, &x25_route_list) {
|
||||
rt = list_entry(entry, struct x25_route, node);
|
||||
|
||||
if (!memcmp(&rt->address, address, sigdigits) &&
|
||||
rt->sigdigits == sigdigits && rt->dev == dev) {
|
||||
__x25_remove_route(rt);
|
||||
rc = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
write_unlock_bh(&x25_route_list_lock);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* A device has been removed, remove its routes.
|
||||
*/
|
||||
void x25_route_device_down(struct net_device *dev)
|
||||
{
|
||||
struct x25_route *rt;
|
||||
struct list_head *entry, *tmp;
|
||||
|
||||
write_lock_bh(&x25_route_list_lock);
|
||||
|
||||
list_for_each_safe(entry, tmp, &x25_route_list) {
|
||||
rt = list_entry(entry, struct x25_route, node);
|
||||
|
||||
if (rt->dev == dev)
|
||||
__x25_remove_route(rt);
|
||||
}
|
||||
write_unlock_bh(&x25_route_list_lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check that the device given is a valid X.25 interface that is "up".
|
||||
*/
|
||||
struct net_device *x25_dev_get(char *devname)
|
||||
{
|
||||
struct net_device *dev = dev_get_by_name(devname);
|
||||
|
||||
if (dev &&
|
||||
(!(dev->flags & IFF_UP) || (dev->type != ARPHRD_X25
|
||||
#if defined(CONFIG_LLC) || defined(CONFIG_LLC_MODULE)
|
||||
&& dev->type != ARPHRD_ETHER
|
||||
#endif
|
||||
)))
|
||||
dev_put(dev);
|
||||
|
||||
return dev;
|
||||
}
|
||||
|
||||
/**
|
||||
* x25_get_route - Find a route given an X.25 address.
|
||||
* @addr - address to find a route for
|
||||
*
|
||||
* Find a route given an X.25 address.
|
||||
*/
|
||||
struct x25_route *x25_get_route(struct x25_address *addr)
|
||||
{
|
||||
struct x25_route *rt, *use = NULL;
|
||||
struct list_head *entry;
|
||||
|
||||
read_lock_bh(&x25_route_list_lock);
|
||||
|
||||
list_for_each(entry, &x25_route_list) {
|
||||
rt = list_entry(entry, struct x25_route, node);
|
||||
|
||||
if (!memcmp(&rt->address, addr, rt->sigdigits)) {
|
||||
if (!use)
|
||||
use = rt;
|
||||
else if (rt->sigdigits > use->sigdigits)
|
||||
use = rt;
|
||||
}
|
||||
}
|
||||
|
||||
if (use)
|
||||
x25_route_hold(use);
|
||||
|
||||
read_unlock_bh(&x25_route_list_lock);
|
||||
return use;
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle the ioctls that control the routing functions.
|
||||
*/
|
||||
int x25_route_ioctl(unsigned int cmd, void __user *arg)
|
||||
{
|
||||
struct x25_route_struct rt;
|
||||
struct net_device *dev;
|
||||
int rc = -EINVAL;
|
||||
|
||||
if (cmd != SIOCADDRT && cmd != SIOCDELRT)
|
||||
goto out;
|
||||
|
||||
rc = -EFAULT;
|
||||
if (copy_from_user(&rt, arg, sizeof(rt)))
|
||||
goto out;
|
||||
|
||||
rc = -EINVAL;
|
||||
if (rt.sigdigits < 0 || rt.sigdigits > 15)
|
||||
goto out;
|
||||
|
||||
dev = x25_dev_get(rt.device);
|
||||
if (!dev)
|
||||
goto out;
|
||||
|
||||
if (cmd == SIOCADDRT)
|
||||
rc = x25_add_route(&rt.address, rt.sigdigits, dev);
|
||||
else
|
||||
rc = x25_del_route(&rt.address, rt.sigdigits, dev);
|
||||
dev_put(dev);
|
||||
out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* Release all memory associated with X.25 routing structures.
|
||||
*/
|
||||
void __exit x25_route_free(void)
|
||||
{
|
||||
struct x25_route *rt;
|
||||
struct list_head *entry, *tmp;
|
||||
|
||||
write_lock_bh(&x25_route_list_lock);
|
||||
list_for_each_safe(entry, tmp, &x25_route_list) {
|
||||
rt = list_entry(entry, struct x25_route, node);
|
||||
__x25_remove_route(rt);
|
||||
}
|
||||
write_unlock_bh(&x25_route_list_lock);
|
||||
}
|
||||
374
net/x25/x25_subr.c
Normal file
374
net/x25/x25_subr.c
Normal file
|
|
@ -0,0 +1,374 @@
|
|||
/*
|
||||
* X.25 Packet Layer release 002
|
||||
*
|
||||
* This is ALPHA test software. This code may break your machine,
|
||||
* randomly fail to work with new releases, misbehave and/or generally
|
||||
* screw up. It might even work.
|
||||
*
|
||||
* This code REQUIRES 2.1.15 or higher
|
||||
*
|
||||
* This module:
|
||||
* This module is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version
|
||||
* 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* History
|
||||
* X.25 001 Jonathan Naylor Started coding.
|
||||
* X.25 002 Jonathan Naylor Centralised disconnection processing.
|
||||
* mar/20/00 Daniela Squassoni Disabling/enabling of facilities
|
||||
* negotiation.
|
||||
* jun/24/01 Arnaldo C. Melo use skb_queue_purge, cleanups
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <net/sock.h>
|
||||
#include <net/tcp.h>
|
||||
#include <net/x25.h>
|
||||
|
||||
/*
|
||||
* This routine purges all of the queues of frames.
|
||||
*/
|
||||
void x25_clear_queues(struct sock *sk)
|
||||
{
|
||||
struct x25_sock *x25 = x25_sk(sk);
|
||||
|
||||
skb_queue_purge(&sk->sk_write_queue);
|
||||
skb_queue_purge(&x25->ack_queue);
|
||||
skb_queue_purge(&x25->interrupt_in_queue);
|
||||
skb_queue_purge(&x25->interrupt_out_queue);
|
||||
skb_queue_purge(&x25->fragment_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 x25_frames_acked(struct sock *sk, unsigned short nr)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
struct x25_sock *x25 = x25_sk(sk);
|
||||
int modulus = x25->neighbour->extended ? X25_EMODULUS : X25_SMODULUS;
|
||||
|
||||
/*
|
||||
* Remove all the ack-ed frames from the ack queue.
|
||||
*/
|
||||
if (x25->va != nr)
|
||||
while (skb_peek(&x25->ack_queue) && x25->va != nr) {
|
||||
skb = skb_dequeue(&x25->ack_queue);
|
||||
kfree_skb(skb);
|
||||
x25->va = (x25->va + 1) % modulus;
|
||||
}
|
||||
}
|
||||
|
||||
void x25_requeue_frames(struct sock *sk)
|
||||
{
|
||||
struct sk_buff *skb, *skb_prev = NULL;
|
||||
|
||||
/*
|
||||
* Requeue all the un-ack-ed frames on the output queue to be picked
|
||||
* up by x25_kick. This arrangement handles the possibility of an empty
|
||||
* output queue.
|
||||
*/
|
||||
while ((skb = skb_dequeue(&x25_sk(sk)->ack_queue)) != NULL) {
|
||||
if (!skb_prev)
|
||||
skb_queue_head(&sk->sk_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 x25_validate_nr(struct sock *sk, unsigned short nr)
|
||||
{
|
||||
struct x25_sock *x25 = x25_sk(sk);
|
||||
unsigned short vc = x25->va;
|
||||
int modulus = x25->neighbour->extended ? X25_EMODULUS : X25_SMODULUS;
|
||||
|
||||
while (vc != x25->vs) {
|
||||
if (nr == vc)
|
||||
return 1;
|
||||
vc = (vc + 1) % modulus;
|
||||
}
|
||||
|
||||
return nr == x25->vs ? 1 : 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* This routine is called when the packet layer internally generates a
|
||||
* control frame.
|
||||
*/
|
||||
void x25_write_internal(struct sock *sk, int frametype)
|
||||
{
|
||||
struct x25_sock *x25 = x25_sk(sk);
|
||||
struct sk_buff *skb;
|
||||
unsigned char *dptr;
|
||||
unsigned char facilities[X25_MAX_FAC_LEN];
|
||||
unsigned char addresses[1 + X25_ADDR_LEN];
|
||||
unsigned char lci1, lci2;
|
||||
/*
|
||||
* Default safe frame size.
|
||||
*/
|
||||
int len = X25_MAX_L2_LEN + X25_EXT_MIN_LEN;
|
||||
|
||||
/*
|
||||
* Adjust frame size.
|
||||
*/
|
||||
switch (frametype) {
|
||||
case X25_CALL_REQUEST:
|
||||
len += 1 + X25_ADDR_LEN + X25_MAX_FAC_LEN +
|
||||
X25_MAX_CUD_LEN;
|
||||
break;
|
||||
case X25_CALL_ACCEPTED:
|
||||
len += 1 + X25_MAX_FAC_LEN + X25_MAX_CUD_LEN;
|
||||
break;
|
||||
case X25_CLEAR_REQUEST:
|
||||
case X25_RESET_REQUEST:
|
||||
len += 2;
|
||||
break;
|
||||
case X25_RR:
|
||||
case X25_RNR:
|
||||
case X25_REJ:
|
||||
case X25_CLEAR_CONFIRMATION:
|
||||
case X25_INTERRUPT_CONFIRMATION:
|
||||
case X25_RESET_CONFIRMATION:
|
||||
break;
|
||||
default:
|
||||
printk(KERN_ERR "X.25: invalid frame type %02X\n",
|
||||
frametype);
|
||||
return;
|
||||
}
|
||||
|
||||
if ((skb = alloc_skb(len, GFP_ATOMIC)) == NULL)
|
||||
return;
|
||||
|
||||
/*
|
||||
* Space for Ethernet and 802.2 LLC headers.
|
||||
*/
|
||||
skb_reserve(skb, X25_MAX_L2_LEN);
|
||||
|
||||
/*
|
||||
* Make space for the GFI and LCI, and fill them in.
|
||||
*/
|
||||
dptr = skb_put(skb, 2);
|
||||
|
||||
lci1 = (x25->lci >> 8) & 0x0F;
|
||||
lci2 = (x25->lci >> 0) & 0xFF;
|
||||
|
||||
if (x25->neighbour->extended) {
|
||||
*dptr++ = lci1 | X25_GFI_EXTSEQ;
|
||||
*dptr++ = lci2;
|
||||
} else {
|
||||
*dptr++ = lci1 | X25_GFI_STDSEQ;
|
||||
*dptr++ = lci2;
|
||||
}
|
||||
|
||||
/*
|
||||
* Now fill in the frame type specific information.
|
||||
*/
|
||||
switch (frametype) {
|
||||
|
||||
case X25_CALL_REQUEST:
|
||||
dptr = skb_put(skb, 1);
|
||||
*dptr++ = X25_CALL_REQUEST;
|
||||
len = x25_addr_aton(addresses, &x25->dest_addr,
|
||||
&x25->source_addr);
|
||||
dptr = skb_put(skb, len);
|
||||
memcpy(dptr, addresses, len);
|
||||
len = x25_create_facilities(facilities,
|
||||
&x25->facilities,
|
||||
x25->neighbour->global_facil_mask);
|
||||
dptr = skb_put(skb, len);
|
||||
memcpy(dptr, facilities, len);
|
||||
dptr = skb_put(skb, x25->calluserdata.cudlength);
|
||||
memcpy(dptr, x25->calluserdata.cuddata,
|
||||
x25->calluserdata.cudlength);
|
||||
x25->calluserdata.cudlength = 0;
|
||||
break;
|
||||
|
||||
case X25_CALL_ACCEPTED:
|
||||
dptr = skb_put(skb, 2);
|
||||
*dptr++ = X25_CALL_ACCEPTED;
|
||||
*dptr++ = 0x00; /* Address lengths */
|
||||
len = x25_create_facilities(facilities,
|
||||
&x25->facilities,
|
||||
x25->vc_facil_mask);
|
||||
dptr = skb_put(skb, len);
|
||||
memcpy(dptr, facilities, len);
|
||||
dptr = skb_put(skb, x25->calluserdata.cudlength);
|
||||
memcpy(dptr, x25->calluserdata.cuddata,
|
||||
x25->calluserdata.cudlength);
|
||||
x25->calluserdata.cudlength = 0;
|
||||
break;
|
||||
|
||||
case X25_CLEAR_REQUEST:
|
||||
case X25_RESET_REQUEST:
|
||||
dptr = skb_put(skb, 3);
|
||||
*dptr++ = frametype;
|
||||
*dptr++ = 0x00; /* XXX */
|
||||
*dptr++ = 0x00; /* XXX */
|
||||
break;
|
||||
|
||||
case X25_RR:
|
||||
case X25_RNR:
|
||||
case X25_REJ:
|
||||
if (x25->neighbour->extended) {
|
||||
dptr = skb_put(skb, 2);
|
||||
*dptr++ = frametype;
|
||||
*dptr++ = (x25->vr << 1) & 0xFE;
|
||||
} else {
|
||||
dptr = skb_put(skb, 1);
|
||||
*dptr = frametype;
|
||||
*dptr++ |= (x25->vr << 5) & 0xE0;
|
||||
}
|
||||
break;
|
||||
|
||||
case X25_CLEAR_CONFIRMATION:
|
||||
case X25_INTERRUPT_CONFIRMATION:
|
||||
case X25_RESET_CONFIRMATION:
|
||||
dptr = skb_put(skb, 1);
|
||||
*dptr = frametype;
|
||||
break;
|
||||
}
|
||||
|
||||
x25_transmit_link(skb, x25->neighbour);
|
||||
}
|
||||
|
||||
/*
|
||||
* Unpick the contents of the passed X.25 Packet Layer frame.
|
||||
*/
|
||||
int x25_decode(struct sock *sk, struct sk_buff *skb, int *ns, int *nr, int *q,
|
||||
int *d, int *m)
|
||||
{
|
||||
struct x25_sock *x25 = x25_sk(sk);
|
||||
unsigned char *frame = skb->data;
|
||||
|
||||
*ns = *nr = *q = *d = *m = 0;
|
||||
|
||||
switch (frame[2]) {
|
||||
case X25_CALL_REQUEST:
|
||||
case X25_CALL_ACCEPTED:
|
||||
case X25_CLEAR_REQUEST:
|
||||
case X25_CLEAR_CONFIRMATION:
|
||||
case X25_INTERRUPT:
|
||||
case X25_INTERRUPT_CONFIRMATION:
|
||||
case X25_RESET_REQUEST:
|
||||
case X25_RESET_CONFIRMATION:
|
||||
case X25_RESTART_REQUEST:
|
||||
case X25_RESTART_CONFIRMATION:
|
||||
case X25_REGISTRATION_REQUEST:
|
||||
case X25_REGISTRATION_CONFIRMATION:
|
||||
case X25_DIAGNOSTIC:
|
||||
return frame[2];
|
||||
}
|
||||
|
||||
if (x25->neighbour->extended) {
|
||||
if (frame[2] == X25_RR ||
|
||||
frame[2] == X25_RNR ||
|
||||
frame[2] == X25_REJ) {
|
||||
*nr = (frame[3] >> 1) & 0x7F;
|
||||
return frame[2];
|
||||
}
|
||||
} else {
|
||||
if ((frame[2] & 0x1F) == X25_RR ||
|
||||
(frame[2] & 0x1F) == X25_RNR ||
|
||||
(frame[2] & 0x1F) == X25_REJ) {
|
||||
*nr = (frame[2] >> 5) & 0x07;
|
||||
return frame[2] & 0x1F;
|
||||
}
|
||||
}
|
||||
|
||||
if (x25->neighbour->extended) {
|
||||
if ((frame[2] & 0x01) == X25_DATA) {
|
||||
*q = (frame[0] & X25_Q_BIT) == X25_Q_BIT;
|
||||
*d = (frame[0] & X25_D_BIT) == X25_D_BIT;
|
||||
*m = (frame[3] & X25_EXT_M_BIT) == X25_EXT_M_BIT;
|
||||
*nr = (frame[3] >> 1) & 0x7F;
|
||||
*ns = (frame[2] >> 1) & 0x7F;
|
||||
return X25_DATA;
|
||||
}
|
||||
} else {
|
||||
if ((frame[2] & 0x01) == X25_DATA) {
|
||||
*q = (frame[0] & X25_Q_BIT) == X25_Q_BIT;
|
||||
*d = (frame[0] & X25_D_BIT) == X25_D_BIT;
|
||||
*m = (frame[2] & X25_STD_M_BIT) == X25_STD_M_BIT;
|
||||
*nr = (frame[2] >> 5) & 0x07;
|
||||
*ns = (frame[2] >> 1) & 0x07;
|
||||
return X25_DATA;
|
||||
}
|
||||
}
|
||||
|
||||
printk(KERN_DEBUG "X.25: invalid PLP frame %02X %02X %02X\n",
|
||||
frame[0], frame[1], frame[2]);
|
||||
|
||||
return X25_ILLEGAL;
|
||||
}
|
||||
|
||||
void x25_disconnect(struct sock *sk, int reason, unsigned char cause,
|
||||
unsigned char diagnostic)
|
||||
{
|
||||
struct x25_sock *x25 = x25_sk(sk);
|
||||
|
||||
x25_clear_queues(sk);
|
||||
x25_stop_timer(sk);
|
||||
|
||||
x25->lci = 0;
|
||||
x25->state = X25_STATE_0;
|
||||
|
||||
x25->causediag.cause = cause;
|
||||
x25->causediag.diagnostic = diagnostic;
|
||||
|
||||
sk->sk_state = TCP_CLOSE;
|
||||
sk->sk_err = reason;
|
||||
sk->sk_shutdown |= SEND_SHUTDOWN;
|
||||
|
||||
if (!sock_flag(sk, SOCK_DEAD)) {
|
||||
sk->sk_state_change(sk);
|
||||
sock_set_flag(sk, SOCK_DEAD);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Clear an own-rx-busy condition and tell the peer about this, provided
|
||||
* that there is a significant amount of free receive buffer space available.
|
||||
*/
|
||||
void x25_check_rbuf(struct sock *sk)
|
||||
{
|
||||
struct x25_sock *x25 = x25_sk(sk);
|
||||
|
||||
if (atomic_read(&sk->sk_rmem_alloc) < (sk->sk_rcvbuf / 2) &&
|
||||
(x25->condition & X25_COND_OWN_RX_BUSY)) {
|
||||
x25->condition &= ~X25_COND_OWN_RX_BUSY;
|
||||
x25->condition &= ~X25_COND_ACK_PENDING;
|
||||
x25->vl = x25->vr;
|
||||
x25_write_internal(sk, X25_RR);
|
||||
x25_stop_timer(sk);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Compare 2 calluserdata structures, used to find correct listening sockets
|
||||
* when call user data is used.
|
||||
*/
|
||||
int x25_check_calluserdata(struct x25_calluserdata *ours, struct x25_calluserdata *theirs)
|
||||
{
|
||||
int i;
|
||||
if (ours->cudlength != theirs->cudlength)
|
||||
return 0;
|
||||
|
||||
for (i=0;i<ours->cudlength;i++) {
|
||||
if (ours->cuddata[i] != theirs->cuddata[i]) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
176
net/x25/x25_timer.c
Normal file
176
net/x25/x25_timer.c
Normal file
|
|
@ -0,0 +1,176 @@
|
|||
/*
|
||||
* X.25 Packet Layer release 002
|
||||
*
|
||||
* This is ALPHA test software. This code may break your machine,
|
||||
* randomly fail to work with new releases, misbehave and/or generally
|
||||
* screw up. It might even work.
|
||||
*
|
||||
* This code REQUIRES 2.1.15 or higher
|
||||
*
|
||||
* This module:
|
||||
* This module is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version
|
||||
* 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* History
|
||||
* X.25 001 Jonathan Naylor Started coding.
|
||||
* X.25 002 Jonathan Naylor New timer architecture.
|
||||
* Centralised disconnection processing.
|
||||
*/
|
||||
|
||||
#include <linux/errno.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/timer.h>
|
||||
#include <net/sock.h>
|
||||
#include <net/tcp.h>
|
||||
#include <net/x25.h>
|
||||
|
||||
static void x25_heartbeat_expiry(unsigned long);
|
||||
static void x25_timer_expiry(unsigned long);
|
||||
|
||||
void x25_init_timers(struct sock *sk)
|
||||
{
|
||||
struct x25_sock *x25 = x25_sk(sk);
|
||||
|
||||
init_timer(&x25->timer);
|
||||
x25->timer.data = (unsigned long)sk;
|
||||
x25->timer.function = &x25_timer_expiry;
|
||||
|
||||
/* initialized by sock_init_data */
|
||||
sk->sk_timer.data = (unsigned long)sk;
|
||||
sk->sk_timer.function = &x25_heartbeat_expiry;
|
||||
}
|
||||
|
||||
void x25_start_heartbeat(struct sock *sk)
|
||||
{
|
||||
mod_timer(&sk->sk_timer, jiffies + 5 * HZ);
|
||||
}
|
||||
|
||||
void x25_stop_heartbeat(struct sock *sk)
|
||||
{
|
||||
del_timer(&sk->sk_timer);
|
||||
}
|
||||
|
||||
void x25_start_t2timer(struct sock *sk)
|
||||
{
|
||||
struct x25_sock *x25 = x25_sk(sk);
|
||||
|
||||
mod_timer(&x25->timer, jiffies + x25->t2);
|
||||
}
|
||||
|
||||
void x25_start_t21timer(struct sock *sk)
|
||||
{
|
||||
struct x25_sock *x25 = x25_sk(sk);
|
||||
|
||||
mod_timer(&x25->timer, jiffies + x25->t21);
|
||||
}
|
||||
|
||||
void x25_start_t22timer(struct sock *sk)
|
||||
{
|
||||
struct x25_sock *x25 = x25_sk(sk);
|
||||
|
||||
mod_timer(&x25->timer, jiffies + x25->t22);
|
||||
}
|
||||
|
||||
void x25_start_t23timer(struct sock *sk)
|
||||
{
|
||||
struct x25_sock *x25 = x25_sk(sk);
|
||||
|
||||
mod_timer(&x25->timer, jiffies + x25->t23);
|
||||
}
|
||||
|
||||
void x25_stop_timer(struct sock *sk)
|
||||
{
|
||||
del_timer(&x25_sk(sk)->timer);
|
||||
}
|
||||
|
||||
unsigned long x25_display_timer(struct sock *sk)
|
||||
{
|
||||
struct x25_sock *x25 = x25_sk(sk);
|
||||
|
||||
if (!timer_pending(&x25->timer))
|
||||
return 0;
|
||||
|
||||
return x25->timer.expires - jiffies;
|
||||
}
|
||||
|
||||
static void x25_heartbeat_expiry(unsigned long param)
|
||||
{
|
||||
struct sock *sk = (struct sock *)param;
|
||||
|
||||
bh_lock_sock(sk);
|
||||
if (sock_owned_by_user(sk)) /* can currently only occur in state 3 */
|
||||
goto restart_heartbeat;
|
||||
|
||||
switch (x25_sk(sk)->state) {
|
||||
|
||||
case X25_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 (sock_flag(sk, SOCK_DESTROY) ||
|
||||
(sk->sk_state == TCP_LISTEN &&
|
||||
sock_flag(sk, SOCK_DEAD))) {
|
||||
x25_destroy_socket(sk);
|
||||
goto unlock;
|
||||
}
|
||||
break;
|
||||
|
||||
case X25_STATE_3:
|
||||
/*
|
||||
* Check for the state of the receive buffer.
|
||||
*/
|
||||
x25_check_rbuf(sk);
|
||||
break;
|
||||
}
|
||||
restart_heartbeat:
|
||||
x25_start_heartbeat(sk);
|
||||
unlock:
|
||||
bh_unlock_sock(sk);
|
||||
}
|
||||
|
||||
/*
|
||||
* Timer has expired, it may have been T2, T21, T22, or T23. We can tell
|
||||
* by the state machine state.
|
||||
*/
|
||||
static inline void x25_do_timer_expiry(struct sock * sk)
|
||||
{
|
||||
struct x25_sock *x25 = x25_sk(sk);
|
||||
|
||||
switch (x25->state) {
|
||||
|
||||
case X25_STATE_3: /* T2 */
|
||||
if (x25->condition & X25_COND_ACK_PENDING) {
|
||||
x25->condition &= ~X25_COND_ACK_PENDING;
|
||||
x25_enquiry_response(sk);
|
||||
}
|
||||
break;
|
||||
|
||||
case X25_STATE_1: /* T21 */
|
||||
case X25_STATE_4: /* T22 */
|
||||
x25_write_internal(sk, X25_CLEAR_REQUEST);
|
||||
x25->state = X25_STATE_2;
|
||||
x25_start_t23timer(sk);
|
||||
break;
|
||||
|
||||
case X25_STATE_2: /* T23 */
|
||||
x25_disconnect(sk, ETIMEDOUT, 0, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void x25_timer_expiry(unsigned long param)
|
||||
{
|
||||
struct sock *sk = (struct sock *)param;
|
||||
|
||||
bh_lock_sock(sk);
|
||||
if (sock_owned_by_user(sk)) { /* can currently only occur in state 3 */
|
||||
if (x25_sk(sk)->state == X25_STATE_3)
|
||||
x25_start_t2timer(sk);
|
||||
} else
|
||||
x25_do_timer_expiry(sk);
|
||||
bh_unlock_sock(sk);
|
||||
}
|
||||
Loading…
Reference in a new issue