mirror of
https://github.com/AetherDroid/android_kernel_samsung_on5xelte.git
synced 2025-09-07 00:38: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
9
net/rose/Makefile
Normal file
9
net/rose/Makefile
Normal file
|
@ -0,0 +1,9 @@
|
|||
#
|
||||
# Makefile for the Linux Rose (X.25 PLP) layer.
|
||||
#
|
||||
|
||||
obj-$(CONFIG_ROSE) += rose.o
|
||||
|
||||
rose-y := af_rose.o rose_dev.o rose_in.o rose_link.o rose_loopback.o \
|
||||
rose_out.o rose_route.o rose_subr.o rose_timer.o
|
||||
rose-$(CONFIG_SYSCTL) += sysctl_net_rose.o
|
1637
net/rose/af_rose.c
Normal file
1637
net/rose/af_rose.c
Normal file
File diff suppressed because it is too large
Load diff
171
net/rose/rose_dev.c
Normal file
171
net/rose/rose_dev.c
Normal file
|
@ -0,0 +1,171 @@
|
|||
/*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk)
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/sysctl.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/socket.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/fcntl.h>
|
||||
#include <linux/in.h>
|
||||
#include <linux/if_ether.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
|
||||
#include <linux/inet.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/if_arp.h>
|
||||
#include <linux/skbuff.h>
|
||||
|
||||
#include <net/ip.h>
|
||||
#include <net/arp.h>
|
||||
|
||||
#include <net/ax25.h>
|
||||
#include <net/rose.h>
|
||||
|
||||
static int rose_header(struct sk_buff *skb, struct net_device *dev,
|
||||
unsigned short type,
|
||||
const void *daddr, const void *saddr, unsigned int len)
|
||||
{
|
||||
unsigned char *buff = skb_push(skb, ROSE_MIN_LEN + 2);
|
||||
|
||||
*buff++ = ROSE_GFI | ROSE_Q_BIT;
|
||||
*buff++ = 0x00;
|
||||
*buff++ = ROSE_DATA;
|
||||
*buff++ = 0x7F;
|
||||
*buff++ = AX25_P_IP;
|
||||
|
||||
if (daddr != NULL)
|
||||
return 37;
|
||||
|
||||
return -37;
|
||||
}
|
||||
|
||||
static int rose_rebuild_header(struct sk_buff *skb)
|
||||
{
|
||||
#ifdef CONFIG_INET
|
||||
struct net_device *dev = skb->dev;
|
||||
struct net_device_stats *stats = &dev->stats;
|
||||
unsigned char *bp = (unsigned char *)skb->data;
|
||||
struct sk_buff *skbn;
|
||||
unsigned int len;
|
||||
|
||||
if (arp_find(bp + 7, skb)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if ((skbn = skb_clone(skb, GFP_ATOMIC)) == NULL) {
|
||||
kfree_skb(skb);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (skb->sk != NULL)
|
||||
skb_set_owner_w(skbn, skb->sk);
|
||||
|
||||
kfree_skb(skb);
|
||||
|
||||
len = skbn->len;
|
||||
|
||||
if (!rose_route_frame(skbn, NULL)) {
|
||||
kfree_skb(skbn);
|
||||
stats->tx_errors++;
|
||||
return 1;
|
||||
}
|
||||
|
||||
stats->tx_packets++;
|
||||
stats->tx_bytes += len;
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int rose_set_mac_address(struct net_device *dev, void *addr)
|
||||
{
|
||||
struct sockaddr *sa = addr;
|
||||
int err;
|
||||
|
||||
if (!memcmp(dev->dev_addr, sa->sa_data, dev->addr_len))
|
||||
return 0;
|
||||
|
||||
if (dev->flags & IFF_UP) {
|
||||
err = rose_add_loopback_node((rose_address *)sa->sa_data);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
rose_del_loopback_node((rose_address *)dev->dev_addr);
|
||||
}
|
||||
|
||||
memcpy(dev->dev_addr, sa->sa_data, dev->addr_len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rose_open(struct net_device *dev)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = rose_add_loopback_node((rose_address *)dev->dev_addr);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
netif_start_queue(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rose_close(struct net_device *dev)
|
||||
{
|
||||
netif_stop_queue(dev);
|
||||
rose_del_loopback_node((rose_address *)dev->dev_addr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static netdev_tx_t rose_xmit(struct sk_buff *skb, struct net_device *dev)
|
||||
{
|
||||
struct net_device_stats *stats = &dev->stats;
|
||||
|
||||
if (!netif_running(dev)) {
|
||||
printk(KERN_ERR "ROSE: rose_xmit - called when iface is down\n");
|
||||
return NETDEV_TX_BUSY;
|
||||
}
|
||||
dev_kfree_skb(skb);
|
||||
stats->tx_errors++;
|
||||
return NETDEV_TX_OK;
|
||||
}
|
||||
|
||||
static const struct header_ops rose_header_ops = {
|
||||
.create = rose_header,
|
||||
.rebuild = rose_rebuild_header,
|
||||
};
|
||||
|
||||
static const struct net_device_ops rose_netdev_ops = {
|
||||
.ndo_open = rose_open,
|
||||
.ndo_stop = rose_close,
|
||||
.ndo_start_xmit = rose_xmit,
|
||||
.ndo_set_mac_address = rose_set_mac_address,
|
||||
};
|
||||
|
||||
void rose_setup(struct net_device *dev)
|
||||
{
|
||||
dev->mtu = ROSE_MAX_PACKET_SIZE - 2;
|
||||
dev->netdev_ops = &rose_netdev_ops;
|
||||
|
||||
dev->header_ops = &rose_header_ops;
|
||||
dev->hard_header_len = AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN + ROSE_MIN_LEN;
|
||||
dev->addr_len = ROSE_ADDR_LEN;
|
||||
dev->type = ARPHRD_ROSE;
|
||||
|
||||
/* New-style flags. */
|
||||
dev->flags = IFF_NOARP;
|
||||
}
|
294
net/rose/rose_in.c
Normal file
294
net/rose/rose_in.c
Normal file
|
@ -0,0 +1,294 @@
|
|||
/*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk)
|
||||
*
|
||||
* Most of this code is based on the SDL diagrams published in the 7th ARRL
|
||||
* Computer Networking Conference papers. The diagrams have mistakes in them,
|
||||
* but are mostly correct. Before you modify the code could you read the SDL
|
||||
* diagrams as the code is not obvious and probably very easy to break.
|
||||
*/
|
||||
#include <linux/errno.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/socket.h>
|
||||
#include <linux/in.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/sockios.h>
|
||||
#include <linux/net.h>
|
||||
#include <net/ax25.h>
|
||||
#include <linux/inet.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <net/sock.h>
|
||||
#include <net/tcp_states.h>
|
||||
#include <linux/fcntl.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <net/rose.h>
|
||||
|
||||
/*
|
||||
* State machine for state 1, Awaiting Call Accepted State.
|
||||
* The handling of the timer(s) is in file rose_timer.c.
|
||||
* Handling of state 0 and connection release is in af_rose.c.
|
||||
*/
|
||||
static int rose_state1_machine(struct sock *sk, struct sk_buff *skb, int frametype)
|
||||
{
|
||||
struct rose_sock *rose = rose_sk(sk);
|
||||
|
||||
switch (frametype) {
|
||||
case ROSE_CALL_ACCEPTED:
|
||||
rose_stop_timer(sk);
|
||||
rose_start_idletimer(sk);
|
||||
rose->condition = 0x00;
|
||||
rose->vs = 0;
|
||||
rose->va = 0;
|
||||
rose->vr = 0;
|
||||
rose->vl = 0;
|
||||
rose->state = ROSE_STATE_3;
|
||||
sk->sk_state = TCP_ESTABLISHED;
|
||||
if (!sock_flag(sk, SOCK_DEAD))
|
||||
sk->sk_state_change(sk);
|
||||
break;
|
||||
|
||||
case ROSE_CLEAR_REQUEST:
|
||||
rose_write_internal(sk, ROSE_CLEAR_CONFIRMATION);
|
||||
rose_disconnect(sk, ECONNREFUSED, skb->data[3], skb->data[4]);
|
||||
rose->neighbour->use--;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* State machine for state 2, Awaiting Clear Confirmation State.
|
||||
* The handling of the timer(s) is in file rose_timer.c
|
||||
* Handling of state 0 and connection release is in af_rose.c.
|
||||
*/
|
||||
static int rose_state2_machine(struct sock *sk, struct sk_buff *skb, int frametype)
|
||||
{
|
||||
struct rose_sock *rose = rose_sk(sk);
|
||||
|
||||
switch (frametype) {
|
||||
case ROSE_CLEAR_REQUEST:
|
||||
rose_write_internal(sk, ROSE_CLEAR_CONFIRMATION);
|
||||
rose_disconnect(sk, 0, skb->data[3], skb->data[4]);
|
||||
rose->neighbour->use--;
|
||||
break;
|
||||
|
||||
case ROSE_CLEAR_CONFIRMATION:
|
||||
rose_disconnect(sk, 0, -1, -1);
|
||||
rose->neighbour->use--;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* State machine for state 3, Connected State.
|
||||
* The handling of the timer(s) is in file rose_timer.c
|
||||
* Handling of state 0 and connection release is in af_rose.c.
|
||||
*/
|
||||
static int rose_state3_machine(struct sock *sk, struct sk_buff *skb, int frametype, int ns, int nr, int q, int d, int m)
|
||||
{
|
||||
struct rose_sock *rose = rose_sk(sk);
|
||||
int queued = 0;
|
||||
|
||||
switch (frametype) {
|
||||
case ROSE_RESET_REQUEST:
|
||||
rose_stop_timer(sk);
|
||||
rose_start_idletimer(sk);
|
||||
rose_write_internal(sk, ROSE_RESET_CONFIRMATION);
|
||||
rose->condition = 0x00;
|
||||
rose->vs = 0;
|
||||
rose->vr = 0;
|
||||
rose->va = 0;
|
||||
rose->vl = 0;
|
||||
rose_requeue_frames(sk);
|
||||
break;
|
||||
|
||||
case ROSE_CLEAR_REQUEST:
|
||||
rose_write_internal(sk, ROSE_CLEAR_CONFIRMATION);
|
||||
rose_disconnect(sk, 0, skb->data[3], skb->data[4]);
|
||||
rose->neighbour->use--;
|
||||
break;
|
||||
|
||||
case ROSE_RR:
|
||||
case ROSE_RNR:
|
||||
if (!rose_validate_nr(sk, nr)) {
|
||||
rose_write_internal(sk, ROSE_RESET_REQUEST);
|
||||
rose->condition = 0x00;
|
||||
rose->vs = 0;
|
||||
rose->vr = 0;
|
||||
rose->va = 0;
|
||||
rose->vl = 0;
|
||||
rose->state = ROSE_STATE_4;
|
||||
rose_start_t2timer(sk);
|
||||
rose_stop_idletimer(sk);
|
||||
} else {
|
||||
rose_frames_acked(sk, nr);
|
||||
if (frametype == ROSE_RNR) {
|
||||
rose->condition |= ROSE_COND_PEER_RX_BUSY;
|
||||
} else {
|
||||
rose->condition &= ~ROSE_COND_PEER_RX_BUSY;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case ROSE_DATA: /* XXX */
|
||||
rose->condition &= ~ROSE_COND_PEER_RX_BUSY;
|
||||
if (!rose_validate_nr(sk, nr)) {
|
||||
rose_write_internal(sk, ROSE_RESET_REQUEST);
|
||||
rose->condition = 0x00;
|
||||
rose->vs = 0;
|
||||
rose->vr = 0;
|
||||
rose->va = 0;
|
||||
rose->vl = 0;
|
||||
rose->state = ROSE_STATE_4;
|
||||
rose_start_t2timer(sk);
|
||||
rose_stop_idletimer(sk);
|
||||
break;
|
||||
}
|
||||
rose_frames_acked(sk, nr);
|
||||
if (ns == rose->vr) {
|
||||
rose_start_idletimer(sk);
|
||||
if (sock_queue_rcv_skb(sk, skb) == 0) {
|
||||
rose->vr = (rose->vr + 1) % ROSE_MODULUS;
|
||||
queued = 1;
|
||||
} else {
|
||||
/* Should never happen ! */
|
||||
rose_write_internal(sk, ROSE_RESET_REQUEST);
|
||||
rose->condition = 0x00;
|
||||
rose->vs = 0;
|
||||
rose->vr = 0;
|
||||
rose->va = 0;
|
||||
rose->vl = 0;
|
||||
rose->state = ROSE_STATE_4;
|
||||
rose_start_t2timer(sk);
|
||||
rose_stop_idletimer(sk);
|
||||
break;
|
||||
}
|
||||
if (atomic_read(&sk->sk_rmem_alloc) >
|
||||
(sk->sk_rcvbuf >> 1))
|
||||
rose->condition |= ROSE_COND_OWN_RX_BUSY;
|
||||
}
|
||||
/*
|
||||
* If the window is full, ack the frame, else start the
|
||||
* acknowledge hold back timer.
|
||||
*/
|
||||
if (((rose->vl + sysctl_rose_window_size) % ROSE_MODULUS) == rose->vr) {
|
||||
rose->condition &= ~ROSE_COND_ACK_PENDING;
|
||||
rose_stop_timer(sk);
|
||||
rose_enquiry_response(sk);
|
||||
} else {
|
||||
rose->condition |= ROSE_COND_ACK_PENDING;
|
||||
rose_start_hbtimer(sk);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
printk(KERN_WARNING "ROSE: unknown %02X in state 3\n", frametype);
|
||||
break;
|
||||
}
|
||||
|
||||
return queued;
|
||||
}
|
||||
|
||||
/*
|
||||
* State machine for state 4, Awaiting Reset Confirmation State.
|
||||
* The handling of the timer(s) is in file rose_timer.c
|
||||
* Handling of state 0 and connection release is in af_rose.c.
|
||||
*/
|
||||
static int rose_state4_machine(struct sock *sk, struct sk_buff *skb, int frametype)
|
||||
{
|
||||
struct rose_sock *rose = rose_sk(sk);
|
||||
|
||||
switch (frametype) {
|
||||
case ROSE_RESET_REQUEST:
|
||||
rose_write_internal(sk, ROSE_RESET_CONFIRMATION);
|
||||
case ROSE_RESET_CONFIRMATION:
|
||||
rose_stop_timer(sk);
|
||||
rose_start_idletimer(sk);
|
||||
rose->condition = 0x00;
|
||||
rose->va = 0;
|
||||
rose->vr = 0;
|
||||
rose->vs = 0;
|
||||
rose->vl = 0;
|
||||
rose->state = ROSE_STATE_3;
|
||||
rose_requeue_frames(sk);
|
||||
break;
|
||||
|
||||
case ROSE_CLEAR_REQUEST:
|
||||
rose_write_internal(sk, ROSE_CLEAR_CONFIRMATION);
|
||||
rose_disconnect(sk, 0, skb->data[3], skb->data[4]);
|
||||
rose->neighbour->use--;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* State machine for state 5, Awaiting Call Acceptance State.
|
||||
* The handling of the timer(s) is in file rose_timer.c
|
||||
* Handling of state 0 and connection release is in af_rose.c.
|
||||
*/
|
||||
static int rose_state5_machine(struct sock *sk, struct sk_buff *skb, int frametype)
|
||||
{
|
||||
if (frametype == ROSE_CLEAR_REQUEST) {
|
||||
rose_write_internal(sk, ROSE_CLEAR_CONFIRMATION);
|
||||
rose_disconnect(sk, 0, skb->data[3], skb->data[4]);
|
||||
rose_sk(sk)->neighbour->use--;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Higher level upcall for a LAPB frame */
|
||||
int rose_process_rx_frame(struct sock *sk, struct sk_buff *skb)
|
||||
{
|
||||
struct rose_sock *rose = rose_sk(sk);
|
||||
int queued = 0, frametype, ns, nr, q, d, m;
|
||||
|
||||
if (rose->state == ROSE_STATE_0)
|
||||
return 0;
|
||||
|
||||
frametype = rose_decode(skb, &ns, &nr, &q, &d, &m);
|
||||
|
||||
switch (rose->state) {
|
||||
case ROSE_STATE_1:
|
||||
queued = rose_state1_machine(sk, skb, frametype);
|
||||
break;
|
||||
case ROSE_STATE_2:
|
||||
queued = rose_state2_machine(sk, skb, frametype);
|
||||
break;
|
||||
case ROSE_STATE_3:
|
||||
queued = rose_state3_machine(sk, skb, frametype, ns, nr, q, d, m);
|
||||
break;
|
||||
case ROSE_STATE_4:
|
||||
queued = rose_state4_machine(sk, skb, frametype);
|
||||
break;
|
||||
case ROSE_STATE_5:
|
||||
queued = rose_state5_machine(sk, skb, frametype);
|
||||
break;
|
||||
}
|
||||
|
||||
rose_kick(sk);
|
||||
|
||||
return queued;
|
||||
}
|
292
net/rose/rose_link.c
Normal file
292
net/rose/rose_link.c
Normal file
|
@ -0,0 +1,292 @@
|
|||
/*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk)
|
||||
*/
|
||||
#include <linux/errno.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/socket.h>
|
||||
#include <linux/in.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/sockios.h>
|
||||
#include <linux/net.h>
|
||||
#include <linux/slab.h>
|
||||
#include <net/ax25.h>
|
||||
#include <linux/inet.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <net/sock.h>
|
||||
#include <linux/fcntl.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/netfilter.h>
|
||||
#include <net/rose.h>
|
||||
|
||||
static void rose_ftimer_expiry(unsigned long);
|
||||
static void rose_t0timer_expiry(unsigned long);
|
||||
|
||||
static void rose_transmit_restart_confirmation(struct rose_neigh *neigh);
|
||||
static void rose_transmit_restart_request(struct rose_neigh *neigh);
|
||||
|
||||
void rose_start_ftimer(struct rose_neigh *neigh)
|
||||
{
|
||||
del_timer(&neigh->ftimer);
|
||||
|
||||
neigh->ftimer.data = (unsigned long)neigh;
|
||||
neigh->ftimer.function = &rose_ftimer_expiry;
|
||||
neigh->ftimer.expires =
|
||||
jiffies + msecs_to_jiffies(sysctl_rose_link_fail_timeout);
|
||||
|
||||
add_timer(&neigh->ftimer);
|
||||
}
|
||||
|
||||
static void rose_start_t0timer(struct rose_neigh *neigh)
|
||||
{
|
||||
del_timer(&neigh->t0timer);
|
||||
|
||||
neigh->t0timer.data = (unsigned long)neigh;
|
||||
neigh->t0timer.function = &rose_t0timer_expiry;
|
||||
neigh->t0timer.expires =
|
||||
jiffies + msecs_to_jiffies(sysctl_rose_restart_request_timeout);
|
||||
|
||||
add_timer(&neigh->t0timer);
|
||||
}
|
||||
|
||||
void rose_stop_ftimer(struct rose_neigh *neigh)
|
||||
{
|
||||
del_timer(&neigh->ftimer);
|
||||
}
|
||||
|
||||
void rose_stop_t0timer(struct rose_neigh *neigh)
|
||||
{
|
||||
del_timer(&neigh->t0timer);
|
||||
}
|
||||
|
||||
int rose_ftimer_running(struct rose_neigh *neigh)
|
||||
{
|
||||
return timer_pending(&neigh->ftimer);
|
||||
}
|
||||
|
||||
static int rose_t0timer_running(struct rose_neigh *neigh)
|
||||
{
|
||||
return timer_pending(&neigh->t0timer);
|
||||
}
|
||||
|
||||
static void rose_ftimer_expiry(unsigned long param)
|
||||
{
|
||||
}
|
||||
|
||||
static void rose_t0timer_expiry(unsigned long param)
|
||||
{
|
||||
struct rose_neigh *neigh = (struct rose_neigh *)param;
|
||||
|
||||
rose_transmit_restart_request(neigh);
|
||||
|
||||
neigh->dce_mode = 0;
|
||||
|
||||
rose_start_t0timer(neigh);
|
||||
}
|
||||
|
||||
/*
|
||||
* Interface to ax25_send_frame. Changes my level 2 callsign depending
|
||||
* on whether we have a global ROSE callsign or use the default port
|
||||
* callsign.
|
||||
*/
|
||||
static int rose_send_frame(struct sk_buff *skb, struct rose_neigh *neigh)
|
||||
{
|
||||
ax25_address *rose_call;
|
||||
ax25_cb *ax25s;
|
||||
|
||||
if (ax25cmp(&rose_callsign, &null_ax25_address) == 0)
|
||||
rose_call = (ax25_address *)neigh->dev->dev_addr;
|
||||
else
|
||||
rose_call = &rose_callsign;
|
||||
|
||||
ax25s = neigh->ax25;
|
||||
neigh->ax25 = ax25_send_frame(skb, 260, rose_call, &neigh->callsign, neigh->digipeat, neigh->dev);
|
||||
if (ax25s)
|
||||
ax25_cb_put(ax25s);
|
||||
|
||||
return neigh->ax25 != NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Interface to ax25_link_up. Changes my level 2 callsign depending
|
||||
* on whether we have a global ROSE callsign or use the default port
|
||||
* callsign.
|
||||
*/
|
||||
static int rose_link_up(struct rose_neigh *neigh)
|
||||
{
|
||||
ax25_address *rose_call;
|
||||
ax25_cb *ax25s;
|
||||
|
||||
if (ax25cmp(&rose_callsign, &null_ax25_address) == 0)
|
||||
rose_call = (ax25_address *)neigh->dev->dev_addr;
|
||||
else
|
||||
rose_call = &rose_callsign;
|
||||
|
||||
ax25s = neigh->ax25;
|
||||
neigh->ax25 = ax25_find_cb(rose_call, &neigh->callsign, neigh->digipeat, neigh->dev);
|
||||
if (ax25s)
|
||||
ax25_cb_put(ax25s);
|
||||
|
||||
return neigh->ax25 != NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* This handles all restart and diagnostic frames.
|
||||
*/
|
||||
void rose_link_rx_restart(struct sk_buff *skb, struct rose_neigh *neigh, unsigned short frametype)
|
||||
{
|
||||
struct sk_buff *skbn;
|
||||
|
||||
switch (frametype) {
|
||||
case ROSE_RESTART_REQUEST:
|
||||
rose_stop_t0timer(neigh);
|
||||
neigh->restarted = 1;
|
||||
neigh->dce_mode = (skb->data[3] == ROSE_DTE_ORIGINATED);
|
||||
rose_transmit_restart_confirmation(neigh);
|
||||
break;
|
||||
|
||||
case ROSE_RESTART_CONFIRMATION:
|
||||
rose_stop_t0timer(neigh);
|
||||
neigh->restarted = 1;
|
||||
break;
|
||||
|
||||
case ROSE_DIAGNOSTIC:
|
||||
pr_warn("ROSE: received diagnostic #%d - %3ph\n", skb->data[3],
|
||||
skb->data + 4);
|
||||
break;
|
||||
|
||||
default:
|
||||
printk(KERN_WARNING "ROSE: received unknown %02X with LCI 000\n", frametype);
|
||||
break;
|
||||
}
|
||||
|
||||
if (neigh->restarted) {
|
||||
while ((skbn = skb_dequeue(&neigh->queue)) != NULL)
|
||||
if (!rose_send_frame(skbn, neigh))
|
||||
kfree_skb(skbn);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This routine is called when a Restart Request is needed
|
||||
*/
|
||||
static void rose_transmit_restart_request(struct rose_neigh *neigh)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
unsigned char *dptr;
|
||||
int len;
|
||||
|
||||
len = AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN + ROSE_MIN_LEN + 3;
|
||||
|
||||
if ((skb = alloc_skb(len, GFP_ATOMIC)) == NULL)
|
||||
return;
|
||||
|
||||
skb_reserve(skb, AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN);
|
||||
|
||||
dptr = skb_put(skb, ROSE_MIN_LEN + 3);
|
||||
|
||||
*dptr++ = AX25_P_ROSE;
|
||||
*dptr++ = ROSE_GFI;
|
||||
*dptr++ = 0x00;
|
||||
*dptr++ = ROSE_RESTART_REQUEST;
|
||||
*dptr++ = ROSE_DTE_ORIGINATED;
|
||||
*dptr++ = 0;
|
||||
|
||||
if (!rose_send_frame(skb, neigh))
|
||||
kfree_skb(skb);
|
||||
}
|
||||
|
||||
/*
|
||||
* This routine is called when a Restart Confirmation is needed
|
||||
*/
|
||||
static void rose_transmit_restart_confirmation(struct rose_neigh *neigh)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
unsigned char *dptr;
|
||||
int len;
|
||||
|
||||
len = AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN + ROSE_MIN_LEN + 1;
|
||||
|
||||
if ((skb = alloc_skb(len, GFP_ATOMIC)) == NULL)
|
||||
return;
|
||||
|
||||
skb_reserve(skb, AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN);
|
||||
|
||||
dptr = skb_put(skb, ROSE_MIN_LEN + 1);
|
||||
|
||||
*dptr++ = AX25_P_ROSE;
|
||||
*dptr++ = ROSE_GFI;
|
||||
*dptr++ = 0x00;
|
||||
*dptr++ = ROSE_RESTART_CONFIRMATION;
|
||||
|
||||
if (!rose_send_frame(skb, neigh))
|
||||
kfree_skb(skb);
|
||||
}
|
||||
|
||||
/*
|
||||
* This routine is called when a Clear Request is needed outside of the context
|
||||
* of a connected socket.
|
||||
*/
|
||||
void rose_transmit_clear_request(struct rose_neigh *neigh, unsigned int lci, unsigned char cause, unsigned char diagnostic)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
unsigned char *dptr;
|
||||
int len;
|
||||
|
||||
len = AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN + ROSE_MIN_LEN + 3;
|
||||
|
||||
if ((skb = alloc_skb(len, GFP_ATOMIC)) == NULL)
|
||||
return;
|
||||
|
||||
skb_reserve(skb, AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN);
|
||||
|
||||
dptr = skb_put(skb, ROSE_MIN_LEN + 3);
|
||||
|
||||
*dptr++ = AX25_P_ROSE;
|
||||
*dptr++ = ((lci >> 8) & 0x0F) | ROSE_GFI;
|
||||
*dptr++ = ((lci >> 0) & 0xFF);
|
||||
*dptr++ = ROSE_CLEAR_REQUEST;
|
||||
*dptr++ = cause;
|
||||
*dptr++ = diagnostic;
|
||||
|
||||
if (!rose_send_frame(skb, neigh))
|
||||
kfree_skb(skb);
|
||||
}
|
||||
|
||||
void rose_transmit_link(struct sk_buff *skb, struct rose_neigh *neigh)
|
||||
{
|
||||
unsigned char *dptr;
|
||||
|
||||
if (neigh->loopback) {
|
||||
rose_loopback_queue(skb, neigh);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!rose_link_up(neigh))
|
||||
neigh->restarted = 0;
|
||||
|
||||
dptr = skb_push(skb, 1);
|
||||
*dptr++ = AX25_P_ROSE;
|
||||
|
||||
if (neigh->restarted) {
|
||||
if (!rose_send_frame(skb, neigh))
|
||||
kfree_skb(skb);
|
||||
} else {
|
||||
skb_queue_tail(&neigh->queue, skb);
|
||||
|
||||
if (!rose_t0timer_running(neigh)) {
|
||||
rose_transmit_restart_request(neigh);
|
||||
neigh->dce_mode = 0;
|
||||
rose_start_t0timer(neigh);
|
||||
}
|
||||
}
|
||||
}
|
124
net/rose/rose_loopback.c
Normal file
124
net/rose/rose_loopback.c
Normal file
|
@ -0,0 +1,124 @@
|
|||
/*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk)
|
||||
*/
|
||||
#include <linux/types.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/socket.h>
|
||||
#include <linux/timer.h>
|
||||
#include <net/ax25.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <net/rose.h>
|
||||
#include <linux/init.h>
|
||||
|
||||
static struct sk_buff_head loopback_queue;
|
||||
static struct timer_list loopback_timer;
|
||||
|
||||
static void rose_set_loopback_timer(void);
|
||||
|
||||
void rose_loopback_init(void)
|
||||
{
|
||||
skb_queue_head_init(&loopback_queue);
|
||||
|
||||
init_timer(&loopback_timer);
|
||||
}
|
||||
|
||||
static int rose_loopback_running(void)
|
||||
{
|
||||
return timer_pending(&loopback_timer);
|
||||
}
|
||||
|
||||
int rose_loopback_queue(struct sk_buff *skb, struct rose_neigh *neigh)
|
||||
{
|
||||
struct sk_buff *skbn;
|
||||
|
||||
skbn = skb_clone(skb, GFP_ATOMIC);
|
||||
|
||||
kfree_skb(skb);
|
||||
|
||||
if (skbn != NULL) {
|
||||
skb_queue_tail(&loopback_queue, skbn);
|
||||
|
||||
if (!rose_loopback_running())
|
||||
rose_set_loopback_timer();
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void rose_loopback_timer(unsigned long);
|
||||
|
||||
static void rose_set_loopback_timer(void)
|
||||
{
|
||||
del_timer(&loopback_timer);
|
||||
|
||||
loopback_timer.data = 0;
|
||||
loopback_timer.function = &rose_loopback_timer;
|
||||
loopback_timer.expires = jiffies + 10;
|
||||
|
||||
add_timer(&loopback_timer);
|
||||
}
|
||||
|
||||
static void rose_loopback_timer(unsigned long param)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
struct net_device *dev;
|
||||
rose_address *dest;
|
||||
struct sock *sk;
|
||||
unsigned short frametype;
|
||||
unsigned int lci_i, lci_o;
|
||||
|
||||
while ((skb = skb_dequeue(&loopback_queue)) != NULL) {
|
||||
if (skb->len < ROSE_MIN_LEN) {
|
||||
kfree_skb(skb);
|
||||
continue;
|
||||
}
|
||||
lci_i = ((skb->data[0] << 8) & 0xF00) + ((skb->data[1] << 0) & 0x0FF);
|
||||
frametype = skb->data[2];
|
||||
if (frametype == ROSE_CALL_REQUEST &&
|
||||
(skb->len <= ROSE_CALL_REQ_FACILITIES_OFF ||
|
||||
skb->data[ROSE_CALL_REQ_ADDR_LEN_OFF] !=
|
||||
ROSE_CALL_REQ_ADDR_LEN_VAL)) {
|
||||
kfree_skb(skb);
|
||||
continue;
|
||||
}
|
||||
dest = (rose_address *)(skb->data + ROSE_CALL_REQ_DEST_ADDR_OFF);
|
||||
lci_o = ROSE_DEFAULT_MAXVC + 1 - lci_i;
|
||||
|
||||
skb_reset_transport_header(skb);
|
||||
|
||||
sk = rose_find_socket(lci_o, rose_loopback_neigh);
|
||||
if (sk) {
|
||||
if (rose_process_rx_frame(sk, skb) == 0)
|
||||
kfree_skb(skb);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (frametype == ROSE_CALL_REQUEST) {
|
||||
if ((dev = rose_dev_get(dest)) != NULL) {
|
||||
if (rose_rx_call_request(skb, dev, rose_loopback_neigh, lci_o) == 0)
|
||||
kfree_skb(skb);
|
||||
} else {
|
||||
kfree_skb(skb);
|
||||
}
|
||||
} else {
|
||||
kfree_skb(skb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void __exit rose_loopback_clear(void)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
|
||||
del_timer(&loopback_timer);
|
||||
|
||||
while ((skb = skb_dequeue(&loopback_queue)) != NULL) {
|
||||
skb->sk = NULL;
|
||||
kfree_skb(skb);
|
||||
}
|
||||
}
|
125
net/rose/rose_out.c
Normal file
125
net/rose/rose_out.c
Normal file
|
@ -0,0 +1,125 @@
|
|||
/*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk)
|
||||
*/
|
||||
#include <linux/errno.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/socket.h>
|
||||
#include <linux/in.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/sockios.h>
|
||||
#include <linux/net.h>
|
||||
#include <linux/gfp.h>
|
||||
#include <net/ax25.h>
|
||||
#include <linux/inet.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <net/sock.h>
|
||||
#include <linux/fcntl.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <net/rose.h>
|
||||
|
||||
/*
|
||||
* This procedure is passed a buffer descriptor for an iframe. It builds
|
||||
* the rest of the control part of the frame and then writes it out.
|
||||
*/
|
||||
static void rose_send_iframe(struct sock *sk, struct sk_buff *skb)
|
||||
{
|
||||
struct rose_sock *rose = rose_sk(sk);
|
||||
|
||||
if (skb == NULL)
|
||||
return;
|
||||
|
||||
skb->data[2] |= (rose->vr << 5) & 0xE0;
|
||||
skb->data[2] |= (rose->vs << 1) & 0x0E;
|
||||
|
||||
rose_start_idletimer(sk);
|
||||
|
||||
rose_transmit_link(skb, rose->neighbour);
|
||||
}
|
||||
|
||||
void rose_kick(struct sock *sk)
|
||||
{
|
||||
struct rose_sock *rose = rose_sk(sk);
|
||||
struct sk_buff *skb, *skbn;
|
||||
unsigned short start, end;
|
||||
|
||||
if (rose->state != ROSE_STATE_3)
|
||||
return;
|
||||
|
||||
if (rose->condition & ROSE_COND_PEER_RX_BUSY)
|
||||
return;
|
||||
|
||||
if (!skb_peek(&sk->sk_write_queue))
|
||||
return;
|
||||
|
||||
start = (skb_peek(&rose->ack_queue) == NULL) ? rose->va : rose->vs;
|
||||
end = (rose->va + sysctl_rose_window_size) % ROSE_MODULUS;
|
||||
|
||||
if (start == end)
|
||||
return;
|
||||
|
||||
rose->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.
|
||||
*/
|
||||
rose_send_iframe(sk, skbn);
|
||||
|
||||
rose->vs = (rose->vs + 1) % ROSE_MODULUS;
|
||||
|
||||
/*
|
||||
* Requeue the original data frame.
|
||||
*/
|
||||
skb_queue_tail(&rose->ack_queue, skb);
|
||||
|
||||
} while (rose->vs != end &&
|
||||
(skb = skb_dequeue(&sk->sk_write_queue)) != NULL);
|
||||
|
||||
rose->vl = rose->vr;
|
||||
rose->condition &= ~ROSE_COND_ACK_PENDING;
|
||||
|
||||
rose_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 rose_enquiry_response(struct sock *sk)
|
||||
{
|
||||
struct rose_sock *rose = rose_sk(sk);
|
||||
|
||||
if (rose->condition & ROSE_COND_OWN_RX_BUSY)
|
||||
rose_write_internal(sk, ROSE_RNR);
|
||||
else
|
||||
rose_write_internal(sk, ROSE_RR);
|
||||
|
||||
rose->vl = rose->vr;
|
||||
rose->condition &= ~ROSE_COND_ACK_PENDING;
|
||||
|
||||
rose_stop_timer(sk);
|
||||
}
|
1366
net/rose/rose_route.c
Normal file
1366
net/rose/rose_route.c
Normal file
File diff suppressed because it is too large
Load diff
556
net/rose/rose_subr.c
Normal file
556
net/rose/rose_subr.c
Normal file
|
@ -0,0 +1,556 @@
|
|||
/*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk)
|
||||
*/
|
||||
#include <linux/errno.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/socket.h>
|
||||
#include <linux/in.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/sockios.h>
|
||||
#include <linux/net.h>
|
||||
#include <linux/slab.h>
|
||||
#include <net/ax25.h>
|
||||
#include <linux/inet.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <net/sock.h>
|
||||
#include <net/tcp_states.h>
|
||||
#include <linux/fcntl.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <net/rose.h>
|
||||
|
||||
static int rose_create_facilities(unsigned char *buffer, struct rose_sock *rose);
|
||||
|
||||
/*
|
||||
* This routine purges all of the queues of frames.
|
||||
*/
|
||||
void rose_clear_queues(struct sock *sk)
|
||||
{
|
||||
skb_queue_purge(&sk->sk_write_queue);
|
||||
skb_queue_purge(&rose_sk(sk)->ack_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 rose_frames_acked(struct sock *sk, unsigned short nr)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
struct rose_sock *rose = rose_sk(sk);
|
||||
|
||||
/*
|
||||
* Remove all the ack-ed frames from the ack queue.
|
||||
*/
|
||||
if (rose->va != nr) {
|
||||
while (skb_peek(&rose->ack_queue) != NULL && rose->va != nr) {
|
||||
skb = skb_dequeue(&rose->ack_queue);
|
||||
kfree_skb(skb);
|
||||
rose->va = (rose->va + 1) % ROSE_MODULUS;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void rose_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 rose_kick. This arrangement handles the possibility of an
|
||||
* empty output queue.
|
||||
*/
|
||||
while ((skb = skb_dequeue(&rose_sk(sk)->ack_queue)) != NULL) {
|
||||
if (skb_prev == NULL)
|
||||
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 rose_validate_nr(struct sock *sk, unsigned short nr)
|
||||
{
|
||||
struct rose_sock *rose = rose_sk(sk);
|
||||
unsigned short vc = rose->va;
|
||||
|
||||
while (vc != rose->vs) {
|
||||
if (nr == vc) return 1;
|
||||
vc = (vc + 1) % ROSE_MODULUS;
|
||||
}
|
||||
|
||||
return nr == rose->vs;
|
||||
}
|
||||
|
||||
/*
|
||||
* This routine is called when the packet layer internally generates a
|
||||
* control frame.
|
||||
*/
|
||||
void rose_write_internal(struct sock *sk, int frametype)
|
||||
{
|
||||
struct rose_sock *rose = rose_sk(sk);
|
||||
struct sk_buff *skb;
|
||||
unsigned char *dptr;
|
||||
unsigned char lci1, lci2;
|
||||
char buffer[100];
|
||||
int len, faclen = 0;
|
||||
|
||||
len = AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN + ROSE_MIN_LEN + 1;
|
||||
|
||||
switch (frametype) {
|
||||
case ROSE_CALL_REQUEST:
|
||||
len += 1 + ROSE_ADDR_LEN + ROSE_ADDR_LEN;
|
||||
faclen = rose_create_facilities(buffer, rose);
|
||||
len += faclen;
|
||||
break;
|
||||
case ROSE_CALL_ACCEPTED:
|
||||
case ROSE_CLEAR_REQUEST:
|
||||
case ROSE_RESET_REQUEST:
|
||||
len += 2;
|
||||
break;
|
||||
}
|
||||
|
||||
if ((skb = alloc_skb(len, GFP_ATOMIC)) == NULL)
|
||||
return;
|
||||
|
||||
/*
|
||||
* Space for AX.25 header and PID.
|
||||
*/
|
||||
skb_reserve(skb, AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN + 1);
|
||||
|
||||
dptr = skb_put(skb, skb_tailroom(skb));
|
||||
|
||||
lci1 = (rose->lci >> 8) & 0x0F;
|
||||
lci2 = (rose->lci >> 0) & 0xFF;
|
||||
|
||||
switch (frametype) {
|
||||
case ROSE_CALL_REQUEST:
|
||||
*dptr++ = ROSE_GFI | lci1;
|
||||
*dptr++ = lci2;
|
||||
*dptr++ = frametype;
|
||||
*dptr++ = ROSE_CALL_REQ_ADDR_LEN_VAL;
|
||||
memcpy(dptr, &rose->dest_addr, ROSE_ADDR_LEN);
|
||||
dptr += ROSE_ADDR_LEN;
|
||||
memcpy(dptr, &rose->source_addr, ROSE_ADDR_LEN);
|
||||
dptr += ROSE_ADDR_LEN;
|
||||
memcpy(dptr, buffer, faclen);
|
||||
dptr += faclen;
|
||||
break;
|
||||
|
||||
case ROSE_CALL_ACCEPTED:
|
||||
*dptr++ = ROSE_GFI | lci1;
|
||||
*dptr++ = lci2;
|
||||
*dptr++ = frametype;
|
||||
*dptr++ = 0x00; /* Address length */
|
||||
*dptr++ = 0; /* Facilities length */
|
||||
break;
|
||||
|
||||
case ROSE_CLEAR_REQUEST:
|
||||
*dptr++ = ROSE_GFI | lci1;
|
||||
*dptr++ = lci2;
|
||||
*dptr++ = frametype;
|
||||
*dptr++ = rose->cause;
|
||||
*dptr++ = rose->diagnostic;
|
||||
break;
|
||||
|
||||
case ROSE_RESET_REQUEST:
|
||||
*dptr++ = ROSE_GFI | lci1;
|
||||
*dptr++ = lci2;
|
||||
*dptr++ = frametype;
|
||||
*dptr++ = ROSE_DTE_ORIGINATED;
|
||||
*dptr++ = 0;
|
||||
break;
|
||||
|
||||
case ROSE_RR:
|
||||
case ROSE_RNR:
|
||||
*dptr++ = ROSE_GFI | lci1;
|
||||
*dptr++ = lci2;
|
||||
*dptr = frametype;
|
||||
*dptr++ |= (rose->vr << 5) & 0xE0;
|
||||
break;
|
||||
|
||||
case ROSE_CLEAR_CONFIRMATION:
|
||||
case ROSE_RESET_CONFIRMATION:
|
||||
*dptr++ = ROSE_GFI | lci1;
|
||||
*dptr++ = lci2;
|
||||
*dptr++ = frametype;
|
||||
break;
|
||||
|
||||
default:
|
||||
printk(KERN_ERR "ROSE: rose_write_internal - invalid frametype %02X\n", frametype);
|
||||
kfree_skb(skb);
|
||||
return;
|
||||
}
|
||||
|
||||
rose_transmit_link(skb, rose->neighbour);
|
||||
}
|
||||
|
||||
int rose_decode(struct sk_buff *skb, int *ns, int *nr, int *q, int *d, int *m)
|
||||
{
|
||||
unsigned char *frame;
|
||||
|
||||
frame = skb->data;
|
||||
|
||||
*ns = *nr = *q = *d = *m = 0;
|
||||
|
||||
switch (frame[2]) {
|
||||
case ROSE_CALL_REQUEST:
|
||||
case ROSE_CALL_ACCEPTED:
|
||||
case ROSE_CLEAR_REQUEST:
|
||||
case ROSE_CLEAR_CONFIRMATION:
|
||||
case ROSE_RESET_REQUEST:
|
||||
case ROSE_RESET_CONFIRMATION:
|
||||
return frame[2];
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if ((frame[2] & 0x1F) == ROSE_RR ||
|
||||
(frame[2] & 0x1F) == ROSE_RNR) {
|
||||
*nr = (frame[2] >> 5) & 0x07;
|
||||
return frame[2] & 0x1F;
|
||||
}
|
||||
|
||||
if ((frame[2] & 0x01) == ROSE_DATA) {
|
||||
*q = (frame[0] & ROSE_Q_BIT) == ROSE_Q_BIT;
|
||||
*d = (frame[0] & ROSE_D_BIT) == ROSE_D_BIT;
|
||||
*m = (frame[2] & ROSE_M_BIT) == ROSE_M_BIT;
|
||||
*nr = (frame[2] >> 5) & 0x07;
|
||||
*ns = (frame[2] >> 1) & 0x07;
|
||||
return ROSE_DATA;
|
||||
}
|
||||
|
||||
return ROSE_ILLEGAL;
|
||||
}
|
||||
|
||||
static int rose_parse_national(unsigned char *p, struct rose_facilities_struct *facilities, int len)
|
||||
{
|
||||
unsigned char *pt;
|
||||
unsigned char l, lg, n = 0;
|
||||
int fac_national_digis_received = 0;
|
||||
|
||||
do {
|
||||
switch (*p & 0xC0) {
|
||||
case 0x00:
|
||||
if (len < 2)
|
||||
return -1;
|
||||
p += 2;
|
||||
n += 2;
|
||||
len -= 2;
|
||||
break;
|
||||
|
||||
case 0x40:
|
||||
if (len < 3)
|
||||
return -1;
|
||||
if (*p == FAC_NATIONAL_RAND)
|
||||
facilities->rand = ((p[1] << 8) & 0xFF00) + ((p[2] << 0) & 0x00FF);
|
||||
p += 3;
|
||||
n += 3;
|
||||
len -= 3;
|
||||
break;
|
||||
|
||||
case 0x80:
|
||||
if (len < 4)
|
||||
return -1;
|
||||
p += 4;
|
||||
n += 4;
|
||||
len -= 4;
|
||||
break;
|
||||
|
||||
case 0xC0:
|
||||
if (len < 2)
|
||||
return -1;
|
||||
l = p[1];
|
||||
if (len < 2 + l)
|
||||
return -1;
|
||||
if (*p == FAC_NATIONAL_DEST_DIGI) {
|
||||
if (!fac_national_digis_received) {
|
||||
if (l < AX25_ADDR_LEN)
|
||||
return -1;
|
||||
memcpy(&facilities->source_digis[0], p + 2, AX25_ADDR_LEN);
|
||||
facilities->source_ndigis = 1;
|
||||
}
|
||||
}
|
||||
else if (*p == FAC_NATIONAL_SRC_DIGI) {
|
||||
if (!fac_national_digis_received) {
|
||||
if (l < AX25_ADDR_LEN)
|
||||
return -1;
|
||||
memcpy(&facilities->dest_digis[0], p + 2, AX25_ADDR_LEN);
|
||||
facilities->dest_ndigis = 1;
|
||||
}
|
||||
}
|
||||
else if (*p == FAC_NATIONAL_FAIL_CALL) {
|
||||
if (l < AX25_ADDR_LEN)
|
||||
return -1;
|
||||
memcpy(&facilities->fail_call, p + 2, AX25_ADDR_LEN);
|
||||
}
|
||||
else if (*p == FAC_NATIONAL_FAIL_ADD) {
|
||||
if (l < 1 + ROSE_ADDR_LEN)
|
||||
return -1;
|
||||
memcpy(&facilities->fail_addr, p + 3, ROSE_ADDR_LEN);
|
||||
}
|
||||
else if (*p == FAC_NATIONAL_DIGIS) {
|
||||
if (l % AX25_ADDR_LEN)
|
||||
return -1;
|
||||
fac_national_digis_received = 1;
|
||||
facilities->source_ndigis = 0;
|
||||
facilities->dest_ndigis = 0;
|
||||
for (pt = p + 2, lg = 0 ; lg < l ; pt += AX25_ADDR_LEN, lg += AX25_ADDR_LEN) {
|
||||
if (pt[6] & AX25_HBIT) {
|
||||
if (facilities->dest_ndigis >= ROSE_MAX_DIGIS)
|
||||
return -1;
|
||||
memcpy(&facilities->dest_digis[facilities->dest_ndigis++], pt, AX25_ADDR_LEN);
|
||||
} else {
|
||||
if (facilities->source_ndigis >= ROSE_MAX_DIGIS)
|
||||
return -1;
|
||||
memcpy(&facilities->source_digis[facilities->source_ndigis++], pt, AX25_ADDR_LEN);
|
||||
}
|
||||
}
|
||||
}
|
||||
p += l + 2;
|
||||
n += l + 2;
|
||||
len -= l + 2;
|
||||
break;
|
||||
}
|
||||
} while (*p != 0x00 && len > 0);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
static int rose_parse_ccitt(unsigned char *p, struct rose_facilities_struct *facilities, int len)
|
||||
{
|
||||
unsigned char l, n = 0;
|
||||
char callsign[11];
|
||||
|
||||
do {
|
||||
switch (*p & 0xC0) {
|
||||
case 0x00:
|
||||
if (len < 2)
|
||||
return -1;
|
||||
p += 2;
|
||||
n += 2;
|
||||
len -= 2;
|
||||
break;
|
||||
|
||||
case 0x40:
|
||||
if (len < 3)
|
||||
return -1;
|
||||
p += 3;
|
||||
n += 3;
|
||||
len -= 3;
|
||||
break;
|
||||
|
||||
case 0x80:
|
||||
if (len < 4)
|
||||
return -1;
|
||||
p += 4;
|
||||
n += 4;
|
||||
len -= 4;
|
||||
break;
|
||||
|
||||
case 0xC0:
|
||||
if (len < 2)
|
||||
return -1;
|
||||
l = p[1];
|
||||
|
||||
/* Prevent overflows*/
|
||||
if (l < 10 || l > 20)
|
||||
return -1;
|
||||
|
||||
if (*p == FAC_CCITT_DEST_NSAP) {
|
||||
memcpy(&facilities->source_addr, p + 7, ROSE_ADDR_LEN);
|
||||
memcpy(callsign, p + 12, l - 10);
|
||||
callsign[l - 10] = '\0';
|
||||
asc2ax(&facilities->source_call, callsign);
|
||||
}
|
||||
if (*p == FAC_CCITT_SRC_NSAP) {
|
||||
memcpy(&facilities->dest_addr, p + 7, ROSE_ADDR_LEN);
|
||||
memcpy(callsign, p + 12, l - 10);
|
||||
callsign[l - 10] = '\0';
|
||||
asc2ax(&facilities->dest_call, callsign);
|
||||
}
|
||||
p += l + 2;
|
||||
n += l + 2;
|
||||
len -= l + 2;
|
||||
break;
|
||||
}
|
||||
} while (*p != 0x00 && len > 0);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
int rose_parse_facilities(unsigned char *p, unsigned packet_len,
|
||||
struct rose_facilities_struct *facilities)
|
||||
{
|
||||
int facilities_len, len;
|
||||
|
||||
facilities_len = *p++;
|
||||
|
||||
if (facilities_len == 0 || (unsigned int)facilities_len > packet_len)
|
||||
return 0;
|
||||
|
||||
while (facilities_len >= 3 && *p == 0x00) {
|
||||
facilities_len--;
|
||||
p++;
|
||||
|
||||
switch (*p) {
|
||||
case FAC_NATIONAL: /* National */
|
||||
len = rose_parse_national(p + 1, facilities, facilities_len - 1);
|
||||
break;
|
||||
|
||||
case FAC_CCITT: /* CCITT */
|
||||
len = rose_parse_ccitt(p + 1, facilities, facilities_len - 1);
|
||||
break;
|
||||
|
||||
default:
|
||||
printk(KERN_DEBUG "ROSE: rose_parse_facilities - unknown facilities family %02X\n", *p);
|
||||
len = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
if (len < 0)
|
||||
return 0;
|
||||
if (WARN_ON(len >= facilities_len))
|
||||
return 0;
|
||||
facilities_len -= len + 1;
|
||||
p += len + 1;
|
||||
}
|
||||
|
||||
return facilities_len == 0;
|
||||
}
|
||||
|
||||
static int rose_create_facilities(unsigned char *buffer, struct rose_sock *rose)
|
||||
{
|
||||
unsigned char *p = buffer + 1;
|
||||
char *callsign;
|
||||
char buf[11];
|
||||
int len, nb;
|
||||
|
||||
/* National Facilities */
|
||||
if (rose->rand != 0 || rose->source_ndigis == 1 || rose->dest_ndigis == 1) {
|
||||
*p++ = 0x00;
|
||||
*p++ = FAC_NATIONAL;
|
||||
|
||||
if (rose->rand != 0) {
|
||||
*p++ = FAC_NATIONAL_RAND;
|
||||
*p++ = (rose->rand >> 8) & 0xFF;
|
||||
*p++ = (rose->rand >> 0) & 0xFF;
|
||||
}
|
||||
|
||||
/* Sent before older facilities */
|
||||
if ((rose->source_ndigis > 0) || (rose->dest_ndigis > 0)) {
|
||||
int maxdigi = 0;
|
||||
*p++ = FAC_NATIONAL_DIGIS;
|
||||
*p++ = AX25_ADDR_LEN * (rose->source_ndigis + rose->dest_ndigis);
|
||||
for (nb = 0 ; nb < rose->source_ndigis ; nb++) {
|
||||
if (++maxdigi >= ROSE_MAX_DIGIS)
|
||||
break;
|
||||
memcpy(p, &rose->source_digis[nb], AX25_ADDR_LEN);
|
||||
p[6] |= AX25_HBIT;
|
||||
p += AX25_ADDR_LEN;
|
||||
}
|
||||
for (nb = 0 ; nb < rose->dest_ndigis ; nb++) {
|
||||
if (++maxdigi >= ROSE_MAX_DIGIS)
|
||||
break;
|
||||
memcpy(p, &rose->dest_digis[nb], AX25_ADDR_LEN);
|
||||
p[6] &= ~AX25_HBIT;
|
||||
p += AX25_ADDR_LEN;
|
||||
}
|
||||
}
|
||||
|
||||
/* For compatibility */
|
||||
if (rose->source_ndigis > 0) {
|
||||
*p++ = FAC_NATIONAL_SRC_DIGI;
|
||||
*p++ = AX25_ADDR_LEN;
|
||||
memcpy(p, &rose->source_digis[0], AX25_ADDR_LEN);
|
||||
p += AX25_ADDR_LEN;
|
||||
}
|
||||
|
||||
/* For compatibility */
|
||||
if (rose->dest_ndigis > 0) {
|
||||
*p++ = FAC_NATIONAL_DEST_DIGI;
|
||||
*p++ = AX25_ADDR_LEN;
|
||||
memcpy(p, &rose->dest_digis[0], AX25_ADDR_LEN);
|
||||
p += AX25_ADDR_LEN;
|
||||
}
|
||||
}
|
||||
|
||||
*p++ = 0x00;
|
||||
*p++ = FAC_CCITT;
|
||||
|
||||
*p++ = FAC_CCITT_DEST_NSAP;
|
||||
|
||||
callsign = ax2asc(buf, &rose->dest_call);
|
||||
|
||||
*p++ = strlen(callsign) + 10;
|
||||
*p++ = (strlen(callsign) + 9) * 2; /* ??? */
|
||||
|
||||
*p++ = 0x47; *p++ = 0x00; *p++ = 0x11;
|
||||
*p++ = ROSE_ADDR_LEN * 2;
|
||||
memcpy(p, &rose->dest_addr, ROSE_ADDR_LEN);
|
||||
p += ROSE_ADDR_LEN;
|
||||
|
||||
memcpy(p, callsign, strlen(callsign));
|
||||
p += strlen(callsign);
|
||||
|
||||
*p++ = FAC_CCITT_SRC_NSAP;
|
||||
|
||||
callsign = ax2asc(buf, &rose->source_call);
|
||||
|
||||
*p++ = strlen(callsign) + 10;
|
||||
*p++ = (strlen(callsign) + 9) * 2; /* ??? */
|
||||
|
||||
*p++ = 0x47; *p++ = 0x00; *p++ = 0x11;
|
||||
*p++ = ROSE_ADDR_LEN * 2;
|
||||
memcpy(p, &rose->source_addr, ROSE_ADDR_LEN);
|
||||
p += ROSE_ADDR_LEN;
|
||||
|
||||
memcpy(p, callsign, strlen(callsign));
|
||||
p += strlen(callsign);
|
||||
|
||||
len = p - buffer;
|
||||
buffer[0] = len - 1;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
void rose_disconnect(struct sock *sk, int reason, int cause, int diagnostic)
|
||||
{
|
||||
struct rose_sock *rose = rose_sk(sk);
|
||||
|
||||
rose_stop_timer(sk);
|
||||
rose_stop_idletimer(sk);
|
||||
|
||||
rose_clear_queues(sk);
|
||||
|
||||
rose->lci = 0;
|
||||
rose->state = ROSE_STATE_0;
|
||||
|
||||
if (cause != -1)
|
||||
rose->cause = cause;
|
||||
|
||||
if (diagnostic != -1)
|
||||
rose->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);
|
||||
}
|
||||
}
|
216
net/rose/rose_timer.c
Normal file
216
net/rose/rose_timer.c
Normal file
|
@ -0,0 +1,216 @@
|
|||
/*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk)
|
||||
* Copyright (C) 2002 Ralf Baechle DO1GRB (ralf@gnu.org)
|
||||
*/
|
||||
#include <linux/errno.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/socket.h>
|
||||
#include <linux/in.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/sockios.h>
|
||||
#include <linux/net.h>
|
||||
#include <net/ax25.h>
|
||||
#include <linux/inet.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <net/sock.h>
|
||||
#include <net/tcp_states.h>
|
||||
#include <linux/fcntl.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <net/rose.h>
|
||||
|
||||
static void rose_heartbeat_expiry(unsigned long);
|
||||
static void rose_timer_expiry(unsigned long);
|
||||
static void rose_idletimer_expiry(unsigned long);
|
||||
|
||||
void rose_start_heartbeat(struct sock *sk)
|
||||
{
|
||||
del_timer(&sk->sk_timer);
|
||||
|
||||
sk->sk_timer.data = (unsigned long)sk;
|
||||
sk->sk_timer.function = &rose_heartbeat_expiry;
|
||||
sk->sk_timer.expires = jiffies + 5 * HZ;
|
||||
|
||||
add_timer(&sk->sk_timer);
|
||||
}
|
||||
|
||||
void rose_start_t1timer(struct sock *sk)
|
||||
{
|
||||
struct rose_sock *rose = rose_sk(sk);
|
||||
|
||||
del_timer(&rose->timer);
|
||||
|
||||
rose->timer.data = (unsigned long)sk;
|
||||
rose->timer.function = &rose_timer_expiry;
|
||||
rose->timer.expires = jiffies + rose->t1;
|
||||
|
||||
add_timer(&rose->timer);
|
||||
}
|
||||
|
||||
void rose_start_t2timer(struct sock *sk)
|
||||
{
|
||||
struct rose_sock *rose = rose_sk(sk);
|
||||
|
||||
del_timer(&rose->timer);
|
||||
|
||||
rose->timer.data = (unsigned long)sk;
|
||||
rose->timer.function = &rose_timer_expiry;
|
||||
rose->timer.expires = jiffies + rose->t2;
|
||||
|
||||
add_timer(&rose->timer);
|
||||
}
|
||||
|
||||
void rose_start_t3timer(struct sock *sk)
|
||||
{
|
||||
struct rose_sock *rose = rose_sk(sk);
|
||||
|
||||
del_timer(&rose->timer);
|
||||
|
||||
rose->timer.data = (unsigned long)sk;
|
||||
rose->timer.function = &rose_timer_expiry;
|
||||
rose->timer.expires = jiffies + rose->t3;
|
||||
|
||||
add_timer(&rose->timer);
|
||||
}
|
||||
|
||||
void rose_start_hbtimer(struct sock *sk)
|
||||
{
|
||||
struct rose_sock *rose = rose_sk(sk);
|
||||
|
||||
del_timer(&rose->timer);
|
||||
|
||||
rose->timer.data = (unsigned long)sk;
|
||||
rose->timer.function = &rose_timer_expiry;
|
||||
rose->timer.expires = jiffies + rose->hb;
|
||||
|
||||
add_timer(&rose->timer);
|
||||
}
|
||||
|
||||
void rose_start_idletimer(struct sock *sk)
|
||||
{
|
||||
struct rose_sock *rose = rose_sk(sk);
|
||||
|
||||
del_timer(&rose->idletimer);
|
||||
|
||||
if (rose->idle > 0) {
|
||||
rose->idletimer.data = (unsigned long)sk;
|
||||
rose->idletimer.function = &rose_idletimer_expiry;
|
||||
rose->idletimer.expires = jiffies + rose->idle;
|
||||
|
||||
add_timer(&rose->idletimer);
|
||||
}
|
||||
}
|
||||
|
||||
void rose_stop_heartbeat(struct sock *sk)
|
||||
{
|
||||
del_timer(&sk->sk_timer);
|
||||
}
|
||||
|
||||
void rose_stop_timer(struct sock *sk)
|
||||
{
|
||||
del_timer(&rose_sk(sk)->timer);
|
||||
}
|
||||
|
||||
void rose_stop_idletimer(struct sock *sk)
|
||||
{
|
||||
del_timer(&rose_sk(sk)->idletimer);
|
||||
}
|
||||
|
||||
static void rose_heartbeat_expiry(unsigned long param)
|
||||
{
|
||||
struct sock *sk = (struct sock *)param;
|
||||
struct rose_sock *rose = rose_sk(sk);
|
||||
|
||||
bh_lock_sock(sk);
|
||||
switch (rose->state) {
|
||||
case ROSE_STATE_0:
|
||||
/* Magic here: If we listen() and a new link dies before it
|
||||
is accepted() it isn't 'dead' so doesn't get removed. */
|
||||
if (sock_flag(sk, SOCK_DESTROY) ||
|
||||
(sk->sk_state == TCP_LISTEN && sock_flag(sk, SOCK_DEAD))) {
|
||||
bh_unlock_sock(sk);
|
||||
rose_destroy_socket(sk);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
case ROSE_STATE_3:
|
||||
/*
|
||||
* Check for the state of the receive buffer.
|
||||
*/
|
||||
if (atomic_read(&sk->sk_rmem_alloc) < (sk->sk_rcvbuf / 2) &&
|
||||
(rose->condition & ROSE_COND_OWN_RX_BUSY)) {
|
||||
rose->condition &= ~ROSE_COND_OWN_RX_BUSY;
|
||||
rose->condition &= ~ROSE_COND_ACK_PENDING;
|
||||
rose->vl = rose->vr;
|
||||
rose_write_internal(sk, ROSE_RR);
|
||||
rose_stop_timer(sk); /* HB */
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
rose_start_heartbeat(sk);
|
||||
bh_unlock_sock(sk);
|
||||
}
|
||||
|
||||
static void rose_timer_expiry(unsigned long param)
|
||||
{
|
||||
struct sock *sk = (struct sock *)param;
|
||||
struct rose_sock *rose = rose_sk(sk);
|
||||
|
||||
bh_lock_sock(sk);
|
||||
switch (rose->state) {
|
||||
case ROSE_STATE_1: /* T1 */
|
||||
case ROSE_STATE_4: /* T2 */
|
||||
rose_write_internal(sk, ROSE_CLEAR_REQUEST);
|
||||
rose->state = ROSE_STATE_2;
|
||||
rose_start_t3timer(sk);
|
||||
break;
|
||||
|
||||
case ROSE_STATE_2: /* T3 */
|
||||
rose->neighbour->use--;
|
||||
rose_disconnect(sk, ETIMEDOUT, -1, -1);
|
||||
break;
|
||||
|
||||
case ROSE_STATE_3: /* HB */
|
||||
if (rose->condition & ROSE_COND_ACK_PENDING) {
|
||||
rose->condition &= ~ROSE_COND_ACK_PENDING;
|
||||
rose_enquiry_response(sk);
|
||||
}
|
||||
break;
|
||||
}
|
||||
bh_unlock_sock(sk);
|
||||
}
|
||||
|
||||
static void rose_idletimer_expiry(unsigned long param)
|
||||
{
|
||||
struct sock *sk = (struct sock *)param;
|
||||
|
||||
bh_lock_sock(sk);
|
||||
rose_clear_queues(sk);
|
||||
|
||||
rose_write_internal(sk, ROSE_CLEAR_REQUEST);
|
||||
rose_sk(sk)->state = ROSE_STATE_2;
|
||||
|
||||
rose_start_t3timer(sk);
|
||||
|
||||
sk->sk_state = TCP_CLOSE;
|
||||
sk->sk_err = 0;
|
||||
sk->sk_shutdown |= SEND_SHUTDOWN;
|
||||
|
||||
if (!sock_flag(sk, SOCK_DEAD)) {
|
||||
sk->sk_state_change(sk);
|
||||
sock_set_flag(sk, SOCK_DEAD);
|
||||
}
|
||||
bh_unlock_sock(sk);
|
||||
}
|
129
net/rose/sysctl_net_rose.c
Normal file
129
net/rose/sysctl_net_rose.c
Normal file
|
@ -0,0 +1,129 @@
|
|||
/*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Copyright (C) 1996 Mike Shaver (shaver@zeroknowledge.com)
|
||||
*/
|
||||
#include <linux/mm.h>
|
||||
#include <linux/sysctl.h>
|
||||
#include <linux/init.h>
|
||||
#include <net/ax25.h>
|
||||
#include <net/rose.h>
|
||||
|
||||
static int min_timer[] = {1 * HZ};
|
||||
static int max_timer[] = {300 * HZ};
|
||||
static int min_idle[] = {0 * HZ};
|
||||
static int max_idle[] = {65535 * HZ};
|
||||
static int min_route[1], max_route[] = {1};
|
||||
static int min_ftimer[] = {60 * HZ};
|
||||
static int max_ftimer[] = {600 * HZ};
|
||||
static int min_maxvcs[] = {1}, max_maxvcs[] = {254};
|
||||
static int min_window[] = {1}, max_window[] = {7};
|
||||
|
||||
static struct ctl_table_header *rose_table_header;
|
||||
|
||||
static struct ctl_table rose_table[] = {
|
||||
{
|
||||
.procname = "restart_request_timeout",
|
||||
.data = &sysctl_rose_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_rose_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_rose_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_rose_clear_request_timeout,
|
||||
.maxlen = sizeof(int),
|
||||
.mode = 0644,
|
||||
.proc_handler = proc_dointvec_minmax,
|
||||
.extra1 = &min_timer,
|
||||
.extra2 = &max_timer
|
||||
},
|
||||
{
|
||||
.procname = "no_activity_timeout",
|
||||
.data = &sysctl_rose_no_activity_timeout,
|
||||
.maxlen = sizeof(int),
|
||||
.mode = 0644,
|
||||
.proc_handler = proc_dointvec_minmax,
|
||||
.extra1 = &min_idle,
|
||||
.extra2 = &max_idle
|
||||
},
|
||||
{
|
||||
.procname = "acknowledge_hold_back_timeout",
|
||||
.data = &sysctl_rose_ack_hold_back_timeout,
|
||||
.maxlen = sizeof(int),
|
||||
.mode = 0644,
|
||||
.proc_handler = proc_dointvec_minmax,
|
||||
.extra1 = &min_timer,
|
||||
.extra2 = &max_timer
|
||||
},
|
||||
{
|
||||
.procname = "routing_control",
|
||||
.data = &sysctl_rose_routing_control,
|
||||
.maxlen = sizeof(int),
|
||||
.mode = 0644,
|
||||
.proc_handler = proc_dointvec_minmax,
|
||||
.extra1 = &min_route,
|
||||
.extra2 = &max_route
|
||||
},
|
||||
{
|
||||
.procname = "link_fail_timeout",
|
||||
.data = &sysctl_rose_link_fail_timeout,
|
||||
.maxlen = sizeof(int),
|
||||
.mode = 0644,
|
||||
.proc_handler = proc_dointvec_minmax,
|
||||
.extra1 = &min_ftimer,
|
||||
.extra2 = &max_ftimer
|
||||
},
|
||||
{
|
||||
.procname = "maximum_virtual_circuits",
|
||||
.data = &sysctl_rose_maximum_vcs,
|
||||
.maxlen = sizeof(int),
|
||||
.mode = 0644,
|
||||
.proc_handler = proc_dointvec_minmax,
|
||||
.extra1 = &min_maxvcs,
|
||||
.extra2 = &max_maxvcs
|
||||
},
|
||||
{
|
||||
.procname = "window_size",
|
||||
.data = &sysctl_rose_window_size,
|
||||
.maxlen = sizeof(int),
|
||||
.mode = 0644,
|
||||
.proc_handler = proc_dointvec_minmax,
|
||||
.extra1 = &min_window,
|
||||
.extra2 = &max_window
|
||||
},
|
||||
{ }
|
||||
};
|
||||
|
||||
void __init rose_register_sysctl(void)
|
||||
{
|
||||
rose_table_header = register_net_sysctl(&init_net, "net/rose", rose_table);
|
||||
}
|
||||
|
||||
void rose_unregister_sysctl(void)
|
||||
{
|
||||
unregister_net_sysctl_table(rose_table_header);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue