mirror of
https://github.com/AetherDroid/android_kernel_samsung_on5xelte.git
synced 2025-09-08 09:08:05 -04:00
Fixed MTP to work with TWRP
This commit is contained in:
commit
f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions
35
net/x25/Kconfig
Normal file
35
net/x25/Kconfig
Normal file
|
@ -0,0 +1,35 @@
|
|||
#
|
||||
# CCITT X.25 Packet Layer
|
||||
#
|
||||
|
||||
config X25
|
||||
tristate "CCITT X.25 Packet Layer"
|
||||
---help---
|
||||
X.25 is a set of standardized network protocols, similar in scope to
|
||||
frame relay; the one physical line from your box to the X.25 network
|
||||
entry point can carry several logical point-to-point connections
|
||||
(called "virtual circuits") to other computers connected to the X.25
|
||||
network. Governments, banks, and other organizations tend to use it
|
||||
to connect to each other or to form Wide Area Networks (WANs). Many
|
||||
countries have public X.25 networks. X.25 consists of two
|
||||
protocols: the higher level Packet Layer Protocol (PLP) (say Y here
|
||||
if you want that) and the lower level data link layer protocol LAPB
|
||||
(say Y to "LAPB Data Link Driver" below if you want that).
|
||||
|
||||
You can read more about X.25 at <http://www.sangoma.com/tutorials/x25/> and
|
||||
<http://docwiki.cisco.com/wiki/X.25>.
|
||||
Information about X.25 for Linux is contained in the files
|
||||
<file:Documentation/networking/x25.txt> and
|
||||
<file:Documentation/networking/x25-iface.txt>.
|
||||
|
||||
One connects to an X.25 network either with a dedicated network card
|
||||
using the X.21 protocol (not yet supported by Linux) or one can do
|
||||
X.25 over a standard telephone line using an ordinary modem (say Y
|
||||
to "X.25 async driver" below) or over Ethernet using an ordinary
|
||||
Ethernet card and the LAPB over Ethernet (say Y to "LAPB Data Link
|
||||
Driver" and "LAPB over Ethernet driver" below).
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called x25. If unsure, say N.
|
||||
|
||||
|
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_forward.o
|
||||
x25-$(CONFIG_SYSCTL) += sysctl_net_x25.o
|
1852
net/x25/af_x25.c
Normal file
1852
net/x25/af_x25.c
Normal file
File diff suppressed because it is too large
Load diff
84
net/x25/sysctl_net_x25.c
Normal file
84
net/x25/sysctl_net_x25.c
Normal file
|
@ -0,0 +1,84 @@
|
|||
/* -*- 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[] = {
|
||||
{
|
||||
.procname = "restart_request_timeout",
|
||||
.data = &sysctl_x25_restart_request_timeout,
|
||||
.maxlen = sizeof(int),
|
||||
.mode = 0644,
|
||||
.proc_handler = proc_dointvec_minmax,
|
||||
.extra1 = &min_timer,
|
||||
.extra2 = &max_timer,
|
||||
},
|
||||
{
|
||||
.procname = "call_request_timeout",
|
||||
.data = &sysctl_x25_call_request_timeout,
|
||||
.maxlen = sizeof(int),
|
||||
.mode = 0644,
|
||||
.proc_handler = proc_dointvec_minmax,
|
||||
.extra1 = &min_timer,
|
||||
.extra2 = &max_timer,
|
||||
},
|
||||
{
|
||||
.procname = "reset_request_timeout",
|
||||
.data = &sysctl_x25_reset_request_timeout,
|
||||
.maxlen = sizeof(int),
|
||||
.mode = 0644,
|
||||
.proc_handler = proc_dointvec_minmax,
|
||||
.extra1 = &min_timer,
|
||||
.extra2 = &max_timer,
|
||||
},
|
||||
{
|
||||
.procname = "clear_request_timeout",
|
||||
.data = &sysctl_x25_clear_request_timeout,
|
||||
.maxlen = sizeof(int),
|
||||
.mode = 0644,
|
||||
.proc_handler = proc_dointvec_minmax,
|
||||
.extra1 = &min_timer,
|
||||
.extra2 = &max_timer,
|
||||
},
|
||||
{
|
||||
.procname = "acknowledgement_hold_back_timeout",
|
||||
.data = &sysctl_x25_ack_holdback_timeout,
|
||||
.maxlen = sizeof(int),
|
||||
.mode = 0644,
|
||||
.proc_handler = proc_dointvec_minmax,
|
||||
.extra1 = &min_timer,
|
||||
.extra2 = &max_timer,
|
||||
},
|
||||
{
|
||||
.procname = "x25_forward",
|
||||
.data = &sysctl_x25_forward,
|
||||
.maxlen = sizeof(int),
|
||||
.mode = 0644,
|
||||
.proc_handler = proc_dointvec,
|
||||
},
|
||||
{ 0, },
|
||||
};
|
||||
|
||||
void __init x25_register_sysctl(void)
|
||||
{
|
||||
x25_table_header = register_net_sysctl(&init_net, "net/x25", x25_table);
|
||||
}
|
||||
|
||||
void x25_unregister_sysctl(void)
|
||||
{
|
||||
unregister_net_sysctl_table(x25_table_header);
|
||||
}
|
232
net/x25/x25_dev.c
Normal file
232
net/x25/x25_dev.c
Normal file
|
@ -0,0 +1,232 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "X25: " fmt
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/slab.h>
|
||||
#include <net/sock.h>
|
||||
#include <linux/if_arp.h>
|
||||
#include <net/x25.h>
|
||||
#include <net/x25device.h>
|
||||
|
||||
static int x25_receive_data(struct sk_buff *skb, struct x25_neigh *nb)
|
||||
{
|
||||
struct sock *sk;
|
||||
unsigned short frametype;
|
||||
unsigned int lci;
|
||||
|
||||
if (!pskb_may_pull(skb, X25_STD_MIN_LEN))
|
||||
return 0;
|
||||
|
||||
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_reset_transport_header(skb);
|
||||
bh_lock_sock(sk);
|
||||
if (!sock_owned_by_user(sk)) {
|
||||
queued = x25_process_rx_frame(sk, skb);
|
||||
} else {
|
||||
queued = !sk_add_backlog(sk, skb, sk->sk_rcvbuf);
|
||||
}
|
||||
bh_unlock_sock(sk);
|
||||
sock_put(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.
|
||||
* Can we forward it?
|
||||
*/
|
||||
|
||||
if (x25_forward_data(lci, nb, skb)) {
|
||||
if (frametype == X25_CLEAR_CONFIRMATION) {
|
||||
x25_clear_forward_by_lci(lci);
|
||||
}
|
||||
kfree_skb(skb);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
x25_transmit_clear_request(nb, lci, 0x0D);
|
||||
*/
|
||||
|
||||
if (frametype != X25_CLEAR_CONFIRMATION)
|
||||
pr_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 net_device *orig_dev)
|
||||
{
|
||||
struct sk_buff *nskb;
|
||||
struct x25_neigh *nb;
|
||||
|
||||
if (!net_eq(dev_net(dev), &init_net))
|
||||
goto drop;
|
||||
|
||||
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) {
|
||||
pr_debug("unknown neighbour - %s\n", dev->name);
|
||||
goto drop;
|
||||
}
|
||||
|
||||
if (!pskb_may_pull(skb, 1))
|
||||
return 0;
|
||||
|
||||
switch (skb->data[0]) {
|
||||
|
||||
case X25_IFACE_DATA:
|
||||
skb_pull(skb, 1);
|
||||
if (x25_receive_data(skb, nb)) {
|
||||
x25_neigh_put(nb);
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
|
||||
case X25_IFACE_CONNECT:
|
||||
x25_link_established(nb);
|
||||
break;
|
||||
|
||||
case X25_IFACE_DISCONNECT:
|
||||
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) {
|
||||
pr_err("x25_dev: out of memory\n");
|
||||
return;
|
||||
}
|
||||
ptr = skb_put(skb, 1);
|
||||
*ptr = X25_IFACE_CONNECT;
|
||||
break;
|
||||
|
||||
#if IS_ENABLED(CONFIG_LLC)
|
||||
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 IS_ENABLED(CONFIG_LLC)
|
||||
if (nb->dev->type == ARPHRD_ETHER)
|
||||
return;
|
||||
#endif
|
||||
if (nb->dev->type != ARPHRD_X25)
|
||||
return;
|
||||
|
||||
skb = alloc_skb(1, GFP_ATOMIC);
|
||||
if (!skb) {
|
||||
pr_err("x25_dev: out of memory\n");
|
||||
return;
|
||||
}
|
||||
|
||||
ptr = skb_put(skb, 1);
|
||||
*ptr = X25_IFACE_DISCONNECT;
|
||||
|
||||
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_reset_network_header(skb);
|
||||
|
||||
switch (nb->dev->type) {
|
||||
case ARPHRD_X25:
|
||||
dptr = skb_push(skb, 1);
|
||||
*dptr = X25_IFACE_DATA;
|
||||
break;
|
||||
|
||||
#if IS_ENABLED(CONFIG_LLC)
|
||||
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);
|
||||
}
|
354
net/x25/x25_facilities.c
Normal file
354
net/x25/x25_facilities.c
Normal file
|
@ -0,0 +1,354 @@
|
|||
/*
|
||||
* 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.
|
||||
* apr/14/05 Shaun Pereira - Allow fast select with no restriction
|
||||
* on response.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "X25: " fmt
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <net/sock.h>
|
||||
#include <net/x25.h>
|
||||
|
||||
/**
|
||||
* x25_parse_facilities - Parse facilities from skb into the facilities structs
|
||||
*
|
||||
* @skb: sk_buff to parse
|
||||
* @facilities: Regular facilities, updated as facilities are found
|
||||
* @dte_facs: ITU DTE facilities, updated as DTE facilities are found
|
||||
* @vc_fac_mask: mask is updated with all facilities found
|
||||
*
|
||||
* Return codes:
|
||||
* -1 - Parsing error, caller should drop call and clean up
|
||||
* 0 - Parse OK, this skb has no facilities
|
||||
* >0 - Parse OK, returns the length of the facilities header
|
||||
*
|
||||
*/
|
||||
int x25_parse_facilities(struct sk_buff *skb, struct x25_facilities *facilities,
|
||||
struct x25_dte_facilities *dte_facs, unsigned long *vc_fac_mask)
|
||||
{
|
||||
unsigned char *p;
|
||||
unsigned int len;
|
||||
|
||||
*vc_fac_mask = 0;
|
||||
|
||||
/*
|
||||
* The kernel knows which facilities were set on an incoming call but
|
||||
* currently this information is not available to userspace. Here we
|
||||
* give userspace who read incoming call facilities 0 length to indicate
|
||||
* it wasn't set.
|
||||
*/
|
||||
dte_facs->calling_len = 0;
|
||||
dte_facs->called_len = 0;
|
||||
memset(dte_facs->called_ae, '\0', sizeof(dte_facs->called_ae));
|
||||
memset(dte_facs->calling_ae, '\0', sizeof(dte_facs->calling_ae));
|
||||
|
||||
if (!pskb_may_pull(skb, 1))
|
||||
return 0;
|
||||
|
||||
len = skb->data[0];
|
||||
|
||||
if (!pskb_may_pull(skb, 1 + len))
|
||||
return -1;
|
||||
|
||||
p = skb->data + 1;
|
||||
|
||||
while (len > 0) {
|
||||
switch (*p & X25_FAC_CLASS_MASK) {
|
||||
case X25_FAC_CLASS_A:
|
||||
if (len < 2)
|
||||
return -1;
|
||||
switch (*p) {
|
||||
case X25_FAC_REVERSE:
|
||||
if((p[1] & 0x81) == 0x81) {
|
||||
facilities->reverse = p[1] & 0x81;
|
||||
*vc_fac_mask |= X25_MASK_REVERSE;
|
||||
break;
|
||||
}
|
||||
|
||||
if((p[1] & 0x01) == 0x01) {
|
||||
facilities->reverse = p[1] & 0x01;
|
||||
*vc_fac_mask |= X25_MASK_REVERSE;
|
||||
break;
|
||||
}
|
||||
|
||||
if((p[1] & 0x80) == 0x80) {
|
||||
facilities->reverse = p[1] & 0x80;
|
||||
*vc_fac_mask |= X25_MASK_REVERSE;
|
||||
break;
|
||||
}
|
||||
|
||||
if(p[1] == 0x00) {
|
||||
facilities->reverse
|
||||
= X25_DEFAULT_REVERSE;
|
||||
*vc_fac_mask |= X25_MASK_REVERSE;
|
||||
break;
|
||||
}
|
||||
|
||||
case X25_FAC_THROUGHPUT:
|
||||
facilities->throughput = p[1];
|
||||
*vc_fac_mask |= X25_MASK_THROUGHPUT;
|
||||
break;
|
||||
case X25_MARKER:
|
||||
break;
|
||||
default:
|
||||
pr_debug("unknown facility "
|
||||
"%02X, value %02X\n",
|
||||
p[0], p[1]);
|
||||
break;
|
||||
}
|
||||
p += 2;
|
||||
len -= 2;
|
||||
break;
|
||||
case X25_FAC_CLASS_B:
|
||||
if (len < 3)
|
||||
return -1;
|
||||
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:
|
||||
pr_debug("unknown facility "
|
||||
"%02X, values %02X, %02X\n",
|
||||
p[0], p[1], p[2]);
|
||||
break;
|
||||
}
|
||||
p += 3;
|
||||
len -= 3;
|
||||
break;
|
||||
case X25_FAC_CLASS_C:
|
||||
if (len < 4)
|
||||
return -1;
|
||||
pr_debug("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:
|
||||
if (len < p[1] + 2)
|
||||
return -1;
|
||||
switch (*p) {
|
||||
case X25_FAC_CALLING_AE:
|
||||
if (p[1] > X25_MAX_DTE_FACIL_LEN || p[1] <= 1)
|
||||
return -1;
|
||||
if (p[2] > X25_MAX_AE_LEN)
|
||||
return -1;
|
||||
dte_facs->calling_len = p[2];
|
||||
memcpy(dte_facs->calling_ae, &p[3], p[1] - 1);
|
||||
*vc_fac_mask |= X25_MASK_CALLING_AE;
|
||||
break;
|
||||
case X25_FAC_CALLED_AE:
|
||||
if (p[1] > X25_MAX_DTE_FACIL_LEN || p[1] <= 1)
|
||||
return -1;
|
||||
if (p[2] > X25_MAX_AE_LEN)
|
||||
return -1;
|
||||
dte_facs->called_len = p[2];
|
||||
memcpy(dte_facs->called_ae, &p[3], p[1] - 1);
|
||||
*vc_fac_mask |= X25_MASK_CALLED_AE;
|
||||
break;
|
||||
default:
|
||||
pr_debug("unknown facility %02X,"
|
||||
"length %d\n", p[0], p[1]);
|
||||
break;
|
||||
}
|
||||
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,
|
||||
struct x25_dte_facilities *dte_facs, 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;
|
||||
}
|
||||
|
||||
if (facil_mask & (X25_MASK_CALLING_AE|X25_MASK_CALLED_AE)) {
|
||||
*p++ = X25_MARKER;
|
||||
*p++ = X25_DTE_SERVICES;
|
||||
}
|
||||
|
||||
if (dte_facs->calling_len && (facil_mask & X25_MASK_CALLING_AE)) {
|
||||
unsigned int bytecount = (dte_facs->calling_len + 1) >> 1;
|
||||
*p++ = X25_FAC_CALLING_AE;
|
||||
*p++ = 1 + bytecount;
|
||||
*p++ = dte_facs->calling_len;
|
||||
memcpy(p, dte_facs->calling_ae, bytecount);
|
||||
p += bytecount;
|
||||
}
|
||||
|
||||
if (dte_facs->called_len && (facil_mask & X25_MASK_CALLED_AE)) {
|
||||
unsigned int bytecount = (dte_facs->called_len % 2) ?
|
||||
dte_facs->called_len / 2 + 1 :
|
||||
dte_facs->called_len / 2;
|
||||
*p++ = X25_FAC_CALLED_AE;
|
||||
*p++ = 1 + bytecount;
|
||||
*p++ = dte_facs->called_len;
|
||||
memcpy(p, dte_facs->called_ae, bytecount);
|
||||
p+=bytecount;
|
||||
}
|
||||
|
||||
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_dte_facilities *dte)
|
||||
{
|
||||
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, dte, &x25->vc_facil_mask);
|
||||
if (len < 0)
|
||||
return len;
|
||||
|
||||
/*
|
||||
* They want reverse charging, we won't accept it.
|
||||
*/
|
||||
if ((theirs.reverse & 0x01 ) && (ours->reverse & 0x01)) {
|
||||
SOCK_DEBUG(sk, "X.25: rejecting reverse charging request\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
new->reverse = theirs.reverse;
|
||||
|
||||
if (theirs.throughput) {
|
||||
int theirs_in = theirs.throughput & 0x0f;
|
||||
int theirs_out = theirs.throughput & 0xf0;
|
||||
int ours_in = ours->throughput & 0x0f;
|
||||
int ours_out = ours->throughput & 0xf0;
|
||||
if (!ours_in || theirs_in < ours_in) {
|
||||
SOCK_DEBUG(sk, "X.25: inbound throughput negotiated\n");
|
||||
new->throughput = (new->throughput & 0xf0) | theirs_in;
|
||||
}
|
||||
if (!ours_out || theirs_out < ours_out) {
|
||||
SOCK_DEBUG(sk,
|
||||
"X.25: outbound throughput negotiated\n");
|
||||
new->throughput = (new->throughput & 0x0f) | theirs_out;
|
||||
}
|
||||
}
|
||||
|
||||
if (theirs.pacsize_in && theirs.pacsize_out) {
|
||||
if (theirs.pacsize_in < ours->pacsize_in) {
|
||||
SOCK_DEBUG(sk, "X.25: packet size inwards negotiated down\n");
|
||||
new->pacsize_in = theirs.pacsize_in;
|
||||
}
|
||||
if (theirs.pacsize_out < ours->pacsize_out) {
|
||||
SOCK_DEBUG(sk, "X.25: packet size outwards negotiated down\n");
|
||||
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\n");
|
||||
new->winsize_in = theirs.winsize_in;
|
||||
}
|
||||
if (theirs.winsize_out < ours->winsize_out) {
|
||||
SOCK_DEBUG(sk, "X.25: window size outwards negotiated down\n");
|
||||
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) {
|
||||
pr_debug("incoming winsize limited to 7\n");
|
||||
facilities->winsize_in = 7;
|
||||
}
|
||||
if (facilities->winsize_out > 7) {
|
||||
facilities->winsize_out = 7;
|
||||
pr_debug("outgoing winsize limited to 7\n");
|
||||
}
|
||||
}
|
||||
}
|
170
net/x25/x25_forward.c
Normal file
170
net/x25/x25_forward.c
Normal file
|
@ -0,0 +1,170 @@
|
|||
/*
|
||||
* 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
|
||||
* 03-01-2007 Added forwarding for x.25 Andrew Hendry
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "X25: " fmt
|
||||
|
||||
#include <linux/if_arp.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <net/x25.h>
|
||||
|
||||
LIST_HEAD(x25_forward_list);
|
||||
DEFINE_RWLOCK(x25_forward_list_lock);
|
||||
|
||||
int x25_forward_call(struct x25_address *dest_addr, struct x25_neigh *from,
|
||||
struct sk_buff *skb, int lci)
|
||||
{
|
||||
struct x25_route *rt;
|
||||
struct x25_neigh *neigh_new = NULL;
|
||||
struct list_head *entry;
|
||||
struct x25_forward *x25_frwd, *new_frwd;
|
||||
struct sk_buff *skbn;
|
||||
short same_lci = 0;
|
||||
int rc = 0;
|
||||
|
||||
if ((rt = x25_get_route(dest_addr)) == NULL)
|
||||
goto out_no_route;
|
||||
|
||||
if ((neigh_new = x25_get_neigh(rt->dev)) == NULL) {
|
||||
/* This shouldn't happen, if it occurs somehow
|
||||
* do something sensible
|
||||
*/
|
||||
goto out_put_route;
|
||||
}
|
||||
|
||||
/* Avoid a loop. This is the normal exit path for a
|
||||
* system with only one x.25 iface and default route
|
||||
*/
|
||||
if (rt->dev == from->dev) {
|
||||
goto out_put_nb;
|
||||
}
|
||||
|
||||
/* Remote end sending a call request on an already
|
||||
* established LCI? It shouldn't happen, just in case..
|
||||
*/
|
||||
read_lock_bh(&x25_forward_list_lock);
|
||||
list_for_each(entry, &x25_forward_list) {
|
||||
x25_frwd = list_entry(entry, struct x25_forward, node);
|
||||
if (x25_frwd->lci == lci) {
|
||||
pr_warn("call request for lci which is already registered!, transmitting but not registering new pair\n");
|
||||
same_lci = 1;
|
||||
}
|
||||
}
|
||||
read_unlock_bh(&x25_forward_list_lock);
|
||||
|
||||
/* Save the forwarding details for future traffic */
|
||||
if (!same_lci){
|
||||
if ((new_frwd = kmalloc(sizeof(struct x25_forward),
|
||||
GFP_ATOMIC)) == NULL){
|
||||
rc = -ENOMEM;
|
||||
goto out_put_nb;
|
||||
}
|
||||
new_frwd->lci = lci;
|
||||
new_frwd->dev1 = rt->dev;
|
||||
new_frwd->dev2 = from->dev;
|
||||
write_lock_bh(&x25_forward_list_lock);
|
||||
list_add(&new_frwd->node, &x25_forward_list);
|
||||
write_unlock_bh(&x25_forward_list_lock);
|
||||
}
|
||||
|
||||
/* Forward the call request */
|
||||
if ( (skbn = skb_clone(skb, GFP_ATOMIC)) == NULL){
|
||||
goto out_put_nb;
|
||||
}
|
||||
x25_transmit_link(skbn, neigh_new);
|
||||
rc = 1;
|
||||
|
||||
|
||||
out_put_nb:
|
||||
x25_neigh_put(neigh_new);
|
||||
|
||||
out_put_route:
|
||||
x25_route_put(rt);
|
||||
|
||||
out_no_route:
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
int x25_forward_data(int lci, struct x25_neigh *from, struct sk_buff *skb) {
|
||||
|
||||
struct x25_forward *frwd;
|
||||
struct list_head *entry;
|
||||
struct net_device *peer = NULL;
|
||||
struct x25_neigh *nb;
|
||||
struct sk_buff *skbn;
|
||||
int rc = 0;
|
||||
|
||||
read_lock_bh(&x25_forward_list_lock);
|
||||
list_for_each(entry, &x25_forward_list) {
|
||||
frwd = list_entry(entry, struct x25_forward, node);
|
||||
if (frwd->lci == lci) {
|
||||
/* The call is established, either side can send */
|
||||
if (from->dev == frwd->dev1) {
|
||||
peer = frwd->dev2;
|
||||
} else {
|
||||
peer = frwd->dev1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
read_unlock_bh(&x25_forward_list_lock);
|
||||
|
||||
if ( (nb = x25_get_neigh(peer)) == NULL)
|
||||
goto out;
|
||||
|
||||
if ( (skbn = pskb_copy(skb, GFP_ATOMIC)) == NULL){
|
||||
goto output;
|
||||
|
||||
}
|
||||
x25_transmit_link(skbn, nb);
|
||||
|
||||
rc = 1;
|
||||
output:
|
||||
x25_neigh_put(nb);
|
||||
out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
void x25_clear_forward_by_lci(unsigned int lci)
|
||||
{
|
||||
struct x25_forward *fwd;
|
||||
struct list_head *entry, *tmp;
|
||||
|
||||
write_lock_bh(&x25_forward_list_lock);
|
||||
|
||||
list_for_each_safe(entry, tmp, &x25_forward_list) {
|
||||
fwd = list_entry(entry, struct x25_forward, node);
|
||||
if (fwd->lci == lci) {
|
||||
list_del(&fwd->node);
|
||||
kfree(fwd);
|
||||
}
|
||||
}
|
||||
write_unlock_bh(&x25_forward_list_lock);
|
||||
}
|
||||
|
||||
|
||||
void x25_clear_forward_by_dev(struct net_device *dev)
|
||||
{
|
||||
struct x25_forward *fwd;
|
||||
struct list_head *entry, *tmp;
|
||||
|
||||
write_lock_bh(&x25_forward_list_lock);
|
||||
|
||||
list_for_each_safe(entry, tmp, &x25_forward_list) {
|
||||
fwd = list_entry(entry, struct x25_forward, node);
|
||||
if ((fwd->dev1 == dev) || (fwd->dev2 == dev)){
|
||||
list_del(&fwd->node);
|
||||
kfree(fwd);
|
||||
}
|
||||
}
|
||||
write_unlock_bh(&x25_forward_list_lock);
|
||||
}
|
419
net/x25/x25_in.c
Normal file
419
net/x25/x25_in.c
Normal file
|
@ -0,0 +1,419 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "X25: " fmt
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <net/sock.h>
|
||||
#include <net/tcp_states.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);
|
||||
|
||||
skb_reset_transport_header(skbn);
|
||||
|
||||
skbo = skb_dequeue(&x25->fragment_queue);
|
||||
skb_copy_from_linear_data(skbo, skb_put(skbn, skbo->len),
|
||||
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);
|
||||
skb_copy_from_linear_data(skbo,
|
||||
skb_put(skbn, skbo->len),
|
||||
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);
|
||||
|
||||
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;
|
||||
int len;
|
||||
struct x25_sock *x25 = x25_sk(sk);
|
||||
|
||||
switch (frametype) {
|
||||
case X25_CALL_ACCEPTED: {
|
||||
|
||||
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.
|
||||
*/
|
||||
if (!pskb_may_pull(skb, X25_STD_MIN_LEN))
|
||||
goto out_clear;
|
||||
skb_pull(skb, X25_STD_MIN_LEN);
|
||||
|
||||
len = x25_parse_address_block(skb, &source_addr,
|
||||
&dest_addr);
|
||||
if (len > 0)
|
||||
skb_pull(skb, len);
|
||||
else if (len < 0)
|
||||
goto out_clear;
|
||||
|
||||
len = x25_parse_facilities(skb, &x25->facilities,
|
||||
&x25->dte_facilities,
|
||||
&x25->vc_facil_mask);
|
||||
if (len > 0)
|
||||
skb_pull(skb, len);
|
||||
else if (len < 0)
|
||||
goto out_clear;
|
||||
/*
|
||||
* Copy any Call User Data.
|
||||
*/
|
||||
if (skb->len > 0) {
|
||||
if (skb->len > X25_MAX_CUD_LEN)
|
||||
goto out_clear;
|
||||
|
||||
skb_copy_bits(skb, 0, x25->calluserdata.cuddata,
|
||||
skb->len);
|
||||
x25->calluserdata.cudlength = skb->len;
|
||||
}
|
||||
if (!sock_flag(sk, SOCK_DEAD))
|
||||
sk->sk_state_change(sk);
|
||||
break;
|
||||
}
|
||||
case X25_CLEAR_REQUEST:
|
||||
if (!pskb_may_pull(skb, X25_STD_MIN_LEN + 2))
|
||||
goto out_clear;
|
||||
|
||||
x25_write_internal(sk, X25_CLEAR_CONFIRMATION);
|
||||
x25_disconnect(sk, ECONNREFUSED, skb->data[3], skb->data[4]);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
out_clear:
|
||||
x25_write_internal(sk, X25_CLEAR_REQUEST);
|
||||
x25->state = X25_STATE_2;
|
||||
x25_start_t23timer(sk);
|
||||
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:
|
||||
if (!pskb_may_pull(skb, X25_STD_MIN_LEN + 2))
|
||||
goto out_clear;
|
||||
|
||||
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;
|
||||
|
||||
out_clear:
|
||||
x25_write_internal(sk, X25_CLEAR_REQUEST);
|
||||
x25_start_t23timer(sk);
|
||||
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:
|
||||
if (!pskb_may_pull(skb, X25_STD_MIN_LEN + 2))
|
||||
goto out_clear;
|
||||
|
||||
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 >> 1))
|
||||
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:
|
||||
clear_bit(X25_INTERRUPT_FLAG, &x25->flags);
|
||||
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:
|
||||
pr_warn("unknown %02X in state 3\n", frametype);
|
||||
break;
|
||||
}
|
||||
|
||||
return queued;
|
||||
|
||||
out_clear:
|
||||
x25_write_internal(sk, X25_CLEAR_REQUEST);
|
||||
x25->state = X25_STATE_2;
|
||||
x25_start_t23timer(sk);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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)
|
||||
{
|
||||
struct x25_sock *x25 = x25_sk(sk);
|
||||
|
||||
switch (frametype) {
|
||||
|
||||
case X25_RESET_REQUEST:
|
||||
x25_write_internal(sk, X25_RESET_CONFIRMATION);
|
||||
case X25_RESET_CONFIRMATION: {
|
||||
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:
|
||||
if (!pskb_may_pull(skb, X25_STD_MIN_LEN + 2))
|
||||
goto out_clear;
|
||||
|
||||
x25_write_internal(sk, X25_CLEAR_CONFIRMATION);
|
||||
x25_disconnect(sk, 0, skb->data[3], skb->data[4]);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
out_clear:
|
||||
x25_write_internal(sk, X25_CLEAR_REQUEST);
|
||||
x25->state = X25_STATE_2;
|
||||
x25_start_t23timer(sk);
|
||||
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;
|
||||
}
|
411
net/x25/x25_link.c
Normal file
411
net/x25/x25_link.c
Normal file
|
@ -0,0 +1,411 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "X25: " fmt
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include <linux/init.h>
|
||||
#include <net/x25.h>
|
||||
|
||||
LIST_HEAD(x25_neigh_list);
|
||||
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:
|
||||
if (!pskb_may_pull(skb, X25_STD_MIN_LEN + 4))
|
||||
break;
|
||||
|
||||
pr_warn("diagnostic #%d - %02X %02X %02X\n",
|
||||
skb->data[3], skb->data[4],
|
||||
skb->data[5], skb->data[6]);
|
||||
break;
|
||||
|
||||
default:
|
||||
pr_warn("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);
|
||||
setup_timer(&nb->t20timer, x25_t20timer_expiry, (unsigned long)nb);
|
||||
|
||||
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) {
|
||||
read_lock_bh(&x25_neigh_list_lock);
|
||||
x25_subscr.extended = nb->extended;
|
||||
x25_subscr.global_facil_mask = nb->global_facil_mask;
|
||||
read_unlock_bh(&x25_neigh_list_lock);
|
||||
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;
|
||||
write_lock_bh(&x25_neigh_list_lock);
|
||||
nb->extended = x25_subscr.extended;
|
||||
nb->global_facil_mask = x25_subscr.global_facil_mask;
|
||||
write_unlock_bh(&x25_neigh_list_lock);
|
||||
}
|
||||
}
|
||||
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) {
|
||||
struct net_device *dev;
|
||||
|
||||
nb = list_entry(entry, struct x25_neigh, node);
|
||||
dev = nb->dev;
|
||||
__x25_remove_neigh(nb);
|
||||
dev_put(dev);
|
||||
}
|
||||
write_unlock_bh(&x25_neigh_list_lock);
|
||||
}
|
231
net/x25/x25_out.c
Normal file
231
net/x25/x25_out.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 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/slab.h>
|
||||
#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 */
|
||||
skb_copy_from_linear_data(skb, header, header_len);
|
||||
skb_pull(skb, header_len);
|
||||
|
||||
frontlen = skb_headroom(skb);
|
||||
|
||||
while (skb->len > 0) {
|
||||
release_sock(sk);
|
||||
skbn = sock_alloc_send_skb(sk, frontlen + max_len,
|
||||
noblock, &err);
|
||||
lock_sock(sk);
|
||||
if (!skbn) {
|
||||
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 */
|
||||
skb_copy_from_linear_data(skb, skb_put(skbn, len), len);
|
||||
skb_pull(skb, len);
|
||||
|
||||
/* Duplicate the Header */
|
||||
skb_push(skbn, header_len);
|
||||
skb_copy_to_linear_data(skbn, 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 (skb_peek(&x25->interrupt_out_queue) != NULL &&
|
||||
!test_and_set_bit(X25_INTERRUPT_FLAG, &x25->flags)) {
|
||||
|
||||
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);
|
||||
}
|
248
net/x25/x25_proc.c
Normal file
248
net/x25/x25_proc.c
Normal file
|
@ -0,0 +1,248 @@
|
|||
/*
|
||||
* 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/init.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/export.h>
|
||||
#include <net/net_namespace.h>
|
||||
#include <net/sock.h>
|
||||
#include <net/x25.h>
|
||||
|
||||
#ifdef CONFIG_PROC_FS
|
||||
|
||||
static void *x25_seq_route_start(struct seq_file *seq, loff_t *pos)
|
||||
__acquires(x25_route_list_lock)
|
||||
{
|
||||
read_lock_bh(&x25_route_list_lock);
|
||||
return seq_list_start_head(&x25_route_list, *pos);
|
||||
}
|
||||
|
||||
static void *x25_seq_route_next(struct seq_file *seq, void *v, loff_t *pos)
|
||||
{
|
||||
return seq_list_next(v, &x25_route_list, pos);
|
||||
}
|
||||
|
||||
static void x25_seq_route_stop(struct seq_file *seq, void *v)
|
||||
__releases(x25_route_list_lock)
|
||||
{
|
||||
read_unlock_bh(&x25_route_list_lock);
|
||||
}
|
||||
|
||||
static int x25_seq_route_show(struct seq_file *seq, void *v)
|
||||
{
|
||||
struct x25_route *rt = list_entry(v, struct x25_route, node);
|
||||
|
||||
if (v == &x25_route_list) {
|
||||
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 void *x25_seq_socket_start(struct seq_file *seq, loff_t *pos)
|
||||
__acquires(x25_list_lock)
|
||||
{
|
||||
read_lock_bh(&x25_list_lock);
|
||||
return seq_hlist_start_head(&x25_list, *pos);
|
||||
}
|
||||
|
||||
static void *x25_seq_socket_next(struct seq_file *seq, void *v, loff_t *pos)
|
||||
{
|
||||
return seq_hlist_next(v, &x25_list, pos);
|
||||
}
|
||||
|
||||
static void x25_seq_socket_stop(struct seq_file *seq, void *v)
|
||||
__releases(x25_list_lock)
|
||||
{
|
||||
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 = sk_entry(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,
|
||||
sk_wmem_alloc_get(s),
|
||||
sk_rmem_alloc_get(s),
|
||||
s->sk_socket ? SOCK_INODE(s->sk_socket)->i_ino : 0L);
|
||||
out:
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void *x25_seq_forward_start(struct seq_file *seq, loff_t *pos)
|
||||
__acquires(x25_forward_list_lock)
|
||||
{
|
||||
read_lock_bh(&x25_forward_list_lock);
|
||||
return seq_list_start_head(&x25_forward_list, *pos);
|
||||
}
|
||||
|
||||
static void *x25_seq_forward_next(struct seq_file *seq, void *v, loff_t *pos)
|
||||
{
|
||||
return seq_list_next(v, &x25_forward_list, pos);
|
||||
}
|
||||
|
||||
static void x25_seq_forward_stop(struct seq_file *seq, void *v)
|
||||
__releases(x25_forward_list_lock)
|
||||
{
|
||||
read_unlock_bh(&x25_forward_list_lock);
|
||||
}
|
||||
|
||||
static int x25_seq_forward_show(struct seq_file *seq, void *v)
|
||||
{
|
||||
struct x25_forward *f = list_entry(v, struct x25_forward, node);
|
||||
|
||||
if (v == &x25_forward_list) {
|
||||
seq_printf(seq, "lci dev1 dev2\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
f = v;
|
||||
|
||||
seq_printf(seq, "%d %-10s %-10s\n",
|
||||
f->lci, f->dev1->name, f->dev2->name);
|
||||
out:
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const 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 const 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 const struct seq_operations x25_seq_forward_ops = {
|
||||
.start = x25_seq_forward_start,
|
||||
.next = x25_seq_forward_next,
|
||||
.stop = x25_seq_forward_stop,
|
||||
.show = x25_seq_forward_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 int x25_seq_forward_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return seq_open(file, &x25_seq_forward_ops);
|
||||
}
|
||||
|
||||
static const struct file_operations x25_seq_socket_fops = {
|
||||
.open = x25_seq_socket_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = seq_release,
|
||||
};
|
||||
|
||||
static const struct file_operations x25_seq_route_fops = {
|
||||
.open = x25_seq_route_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = seq_release,
|
||||
};
|
||||
|
||||
static const struct file_operations x25_seq_forward_fops = {
|
||||
.open = x25_seq_forward_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = seq_release,
|
||||
};
|
||||
|
||||
int __init x25_proc_init(void)
|
||||
{
|
||||
if (!proc_mkdir("x25", init_net.proc_net))
|
||||
return -ENOMEM;
|
||||
|
||||
if (!proc_create("x25/route", S_IRUGO, init_net.proc_net,
|
||||
&x25_seq_route_fops))
|
||||
goto out;
|
||||
|
||||
if (!proc_create("x25/socket", S_IRUGO, init_net.proc_net,
|
||||
&x25_seq_socket_fops))
|
||||
goto out;
|
||||
|
||||
if (!proc_create("x25/forward", S_IRUGO, init_net.proc_net,
|
||||
&x25_seq_forward_fops))
|
||||
goto out;
|
||||
return 0;
|
||||
|
||||
out:
|
||||
remove_proc_subtree("x25", init_net.proc_net);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
void __exit x25_proc_exit(void)
|
||||
{
|
||||
remove_proc_subtree("x25", init_net.proc_net);
|
||||
}
|
||||
|
||||
#else /* CONFIG_PROC_FS */
|
||||
|
||||
int __init x25_proc_init(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void __exit x25_proc_exit(void)
|
||||
{
|
||||
}
|
||||
#endif /* CONFIG_PROC_FS */
|
226
net/x25/x25_route.c
Normal file
226
net/x25/x25_route.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.
|
||||
*/
|
||||
|
||||
#include <linux/if_arp.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <net/x25.h>
|
||||
|
||||
LIST_HEAD(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);
|
||||
|
||||
/* Remove any related forwarding */
|
||||
x25_clear_forward_by_dev(dev);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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(&init_net, devname);
|
||||
|
||||
if (dev &&
|
||||
(!(dev->flags & IFF_UP) || (dev->type != ARPHRD_X25
|
||||
#if IS_ENABLED(CONFIG_LLC)
|
||||
&& dev->type != ARPHRD_ETHER
|
||||
#endif
|
||||
))){
|
||||
dev_put(dev);
|
||||
dev = NULL;
|
||||
}
|
||||
|
||||
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 > 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);
|
||||
}
|
390
net/x25/x25_subr.c
Normal file
390
net/x25/x25_subr.c
Normal file
|
@ -0,0 +1,390 @@
|
|||
/*
|
||||
* 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
|
||||
* apr/04/15 Shaun Pereira Fast select with no
|
||||
* restriction on response.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "X25: " fmt
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <net/sock.h>
|
||||
#include <net/tcp_states.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, &sk->sk_write_queue);
|
||||
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: /* fast sel with no restr on resp */
|
||||
if (x25->facilities.reverse & 0x80) {
|
||||
len += 1 + X25_MAX_FAC_LEN + X25_MAX_CUD_LEN;
|
||||
} else {
|
||||
len += 1 + X25_MAX_FAC_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:
|
||||
pr_err("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->dte_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->dte_facilities,
|
||||
x25->vc_facil_mask);
|
||||
dptr = skb_put(skb, len);
|
||||
memcpy(dptr, facilities, len);
|
||||
|
||||
/* fast select with no restriction on response
|
||||
allows call user data. Userland must
|
||||
ensure it is ours and not theirs */
|
||||
if(x25->facilities.reverse & 0x80) {
|
||||
dptr = skb_put(skb,
|
||||
x25->calluserdata.cudlength);
|
||||
memcpy(dptr, x25->calluserdata.cuddata,
|
||||
x25->calluserdata.cudlength);
|
||||
}
|
||||
x25->calluserdata.cudlength = 0;
|
||||
break;
|
||||
|
||||
case X25_CLEAR_REQUEST:
|
||||
dptr = skb_put(skb, 3);
|
||||
*dptr++ = frametype;
|
||||
*dptr++ = x25->causediag.cause;
|
||||
*dptr++ = x25->causediag.diagnostic;
|
||||
break;
|
||||
|
||||
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;
|
||||
|
||||
if (!pskb_may_pull(skb, X25_STD_MIN_LEN))
|
||||
return X25_ILLEGAL;
|
||||
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) {
|
||||
if (!pskb_may_pull(skb, X25_EXT_MIN_LEN))
|
||||
return X25_ILLEGAL;
|
||||
frame = skb->data;
|
||||
|
||||
*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) {
|
||||
if (!pskb_may_pull(skb, X25_EXT_MIN_LEN))
|
||||
return X25_ILLEGAL;
|
||||
frame = skb->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;
|
||||
}
|
||||
}
|
||||
|
||||
pr_debug("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 >> 1) &&
|
||||
(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);
|
||||
}
|
||||
}
|
||||
|
174
net/x25/x25_timer.c
Normal file
174
net/x25/x25_timer.c
Normal file
|
@ -0,0 +1,174 @@
|
|||
/*
|
||||
* 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_states.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);
|
||||
|
||||
setup_timer(&x25->timer, x25_timer_expiry, (unsigned long)sk);
|
||||
|
||||
/* 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))) {
|
||||
bh_unlock_sock(sk);
|
||||
x25_destroy_socket_from_timer(sk);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
case X25_STATE_3:
|
||||
/*
|
||||
* Check for the state of the receive buffer.
|
||||
*/
|
||||
x25_check_rbuf(sk);
|
||||
break;
|
||||
}
|
||||
restart_heartbeat:
|
||||
x25_start_heartbeat(sk);
|
||||
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…
Add table
Add a link
Reference in a new issue