URL
https://opencores.org/ocsvn/or1k/or1k/trunk
Subversion Repositories or1k
Compare Revisions
- This comparison shows the changes necessary to convert path
/or1k/trunk/linux/linux-2.4/net/rose
- from Rev 1275 to Rev 1765
- ↔ Reverse comparison
Rev 1275 → Rev 1765
/rose_in.c
0,0 → 1,303
/* |
* ROSE release 003 |
* |
* This code REQUIRES 2.1.15 or higher/ NET3.038 |
* |
* This module: |
* This module is free software; you can redistribute it and/or |
* modify it under the terms of the GNU General Public License |
* as published by the Free Software Foundation; either version |
* 2 of the License, or (at your option) any later version. |
* |
* Most of this code is based on the SDL diagrams published in the 7th |
* ARRL Computer Networking Conference papers. The diagrams have mistakes |
* in them, but are mostly correct. Before you modify the code could you |
* read the SDL diagrams as the code is not obvious and probably very |
* easy to break; |
* |
* History |
* ROSE 001 Jonathan(G4KLX) Cloned from nr_in.c |
* ROSE 002 Jonathan(G4KLX) Return cause and diagnostic codes from Clear Requests. |
* ROSE 003 Jonathan(G4KLX) New timer architecture. |
* Removed M bit processing. |
*/ |
|
#include <linux/errno.h> |
#include <linux/types.h> |
#include <linux/socket.h> |
#include <linux/in.h> |
#include <linux/kernel.h> |
#include <linux/sched.h> |
#include <linux/timer.h> |
#include <linux/string.h> |
#include <linux/sockios.h> |
#include <linux/net.h> |
#include <net/ax25.h> |
#include <linux/inet.h> |
#include <linux/netdevice.h> |
#include <linux/skbuff.h> |
#include <net/sock.h> |
#include <net/ip.h> /* For ip_rcv */ |
#include <asm/segment.h> |
#include <asm/system.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) |
{ |
switch (frametype) { |
|
case ROSE_CALL_ACCEPTED: |
rose_stop_timer(sk); |
rose_start_idletimer(sk); |
sk->protinfo.rose->condition = 0x00; |
sk->protinfo.rose->vs = 0; |
sk->protinfo.rose->va = 0; |
sk->protinfo.rose->vr = 0; |
sk->protinfo.rose->vl = 0; |
sk->protinfo.rose->state = ROSE_STATE_3; |
sk->state = TCP_ESTABLISHED; |
if (!sk->dead) |
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]); |
sk->protinfo.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) |
{ |
switch (frametype) { |
|
case ROSE_CLEAR_REQUEST: |
rose_write_internal(sk, ROSE_CLEAR_CONFIRMATION); |
rose_disconnect(sk, 0, skb->data[3], skb->data[4]); |
sk->protinfo.rose->neighbour->use--; |
break; |
|
case ROSE_CLEAR_CONFIRMATION: |
rose_disconnect(sk, 0, -1, -1); |
sk->protinfo.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) |
{ |
int queued = 0; |
|
switch (frametype) { |
|
case ROSE_RESET_REQUEST: |
rose_stop_timer(sk); |
rose_start_idletimer(sk); |
rose_write_internal(sk, ROSE_RESET_CONFIRMATION); |
sk->protinfo.rose->condition = 0x00; |
sk->protinfo.rose->vs = 0; |
sk->protinfo.rose->vr = 0; |
sk->protinfo.rose->va = 0; |
sk->protinfo.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]); |
sk->protinfo.rose->neighbour->use--; |
break; |
|
case ROSE_RR: |
case ROSE_RNR: |
if (!rose_validate_nr(sk, nr)) { |
rose_write_internal(sk, ROSE_RESET_REQUEST); |
sk->protinfo.rose->condition = 0x00; |
sk->protinfo.rose->vs = 0; |
sk->protinfo.rose->vr = 0; |
sk->protinfo.rose->va = 0; |
sk->protinfo.rose->vl = 0; |
sk->protinfo.rose->state = ROSE_STATE_4; |
rose_start_t2timer(sk); |
rose_stop_idletimer(sk); |
} else { |
rose_frames_acked(sk, nr); |
if (frametype == ROSE_RNR) { |
sk->protinfo.rose->condition |= ROSE_COND_PEER_RX_BUSY; |
} else { |
sk->protinfo.rose->condition &= ~ROSE_COND_PEER_RX_BUSY; |
} |
} |
break; |
|
case ROSE_DATA: /* XXX */ |
sk->protinfo.rose->condition &= ~ROSE_COND_PEER_RX_BUSY; |
if (!rose_validate_nr(sk, nr)) { |
rose_write_internal(sk, ROSE_RESET_REQUEST); |
sk->protinfo.rose->condition = 0x00; |
sk->protinfo.rose->vs = 0; |
sk->protinfo.rose->vr = 0; |
sk->protinfo.rose->va = 0; |
sk->protinfo.rose->vl = 0; |
sk->protinfo.rose->state = ROSE_STATE_4; |
rose_start_t2timer(sk); |
rose_stop_idletimer(sk); |
break; |
} |
rose_frames_acked(sk, nr); |
if (ns == sk->protinfo.rose->vr) { |
rose_start_idletimer(sk); |
if (sock_queue_rcv_skb(sk, skb) == 0) { |
sk->protinfo.rose->vr = (sk->protinfo.rose->vr + 1) % ROSE_MODULUS; |
queued = 1; |
} else { |
/* Should never happen ! */ |
rose_write_internal(sk, ROSE_RESET_REQUEST); |
sk->protinfo.rose->condition = 0x00; |
sk->protinfo.rose->vs = 0; |
sk->protinfo.rose->vr = 0; |
sk->protinfo.rose->va = 0; |
sk->protinfo.rose->vl = 0; |
sk->protinfo.rose->state = ROSE_STATE_4; |
rose_start_t2timer(sk); |
rose_stop_idletimer(sk); |
break; |
} |
if (atomic_read(&sk->rmem_alloc) > (sk->rcvbuf / 2)) |
sk->protinfo.rose->condition |= ROSE_COND_OWN_RX_BUSY; |
} |
/* |
* If the window is full, ack the frame, else start the |
* acknowledge hold back timer. |
*/ |
if (((sk->protinfo.rose->vl + sysctl_rose_window_size) % ROSE_MODULUS) == sk->protinfo.rose->vr) { |
sk->protinfo.rose->condition &= ~ROSE_COND_ACK_PENDING; |
rose_stop_timer(sk); |
rose_enquiry_response(sk); |
} else { |
sk->protinfo.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) |
{ |
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); |
sk->protinfo.rose->condition = 0x00; |
sk->protinfo.rose->va = 0; |
sk->protinfo.rose->vr = 0; |
sk->protinfo.rose->vs = 0; |
sk->protinfo.rose->vl = 0; |
sk->protinfo.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]); |
sk->protinfo.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]); |
sk->protinfo.rose->neighbour->use--; |
} |
|
return 0; |
} |
|
/* Higher level upcall for a LAPB frame */ |
int rose_process_rx_frame(struct sock *sk, struct sk_buff *skb) |
{ |
int queued = 0, frametype, ns, nr, q, d, m; |
|
if (sk->protinfo.rose->state == ROSE_STATE_0) |
return 0; |
|
frametype = rose_decode(skb, &ns, &nr, &q, &d, &m); |
|
switch (sk->protinfo.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; |
} |
/af_rose.c
0,0 → 1,1531
/* |
* ROSE release 003 |
* |
* This code REQUIRES 2.1.15 or higher/ NET3.038 |
* |
* This module: |
* This module is free software; you can redistribute it and/or |
* modify it under the terms of the GNU General Public License |
* as published by the Free Software Foundation; either version |
* 2 of the License, or (at your option) any later version. |
* |
* History |
* ROSE 001 Jonathan(G4KLX) Cloned from af_netrom.c. |
* Alan(GW4PTS) Hacked up for newer API stuff |
* Terry (VK2KTJ) Added support for variable length |
* address masks. |
* ROSE 002 Jonathan(G4KLX) Changed hdrincl to qbitincl. |
* Added random number facilities entry. |
* Variable number of ROSE devices. |
* ROSE 003 Jonathan(G4KLX) New timer architecture. |
* Implemented idle timer. |
* Added use count to neighbour. |
* Tomi(OH2BNS) Fixed rose_getname(). |
* Arnaldo C. Melo s/suser/capable/ + micro cleanups |
* Joroen (PE1RXQ) Use sock_orphan() on release. |
* |
* ROSE 0.63 Jean-Paul(F6FBB) Fixed wrong length of L3 packets |
* Added CLEAR_REQUEST facilities |
* ROSE 0.64 Jean-Paul(F6FBB) Fixed null pointer in rose_kill_by_device |
*/ |
|
#include <linux/config.h> |
#include <linux/module.h> |
#include <linux/init.h> |
#include <linux/errno.h> |
#include <linux/types.h> |
#include <linux/socket.h> |
#include <linux/in.h> |
#include <linux/kernel.h> |
#include <linux/sched.h> |
#include <linux/timer.h> |
#include <linux/string.h> |
#include <linux/sockios.h> |
#include <linux/net.h> |
#include <linux/stat.h> |
#include <net/ax25.h> |
#include <linux/inet.h> |
#include <linux/netdevice.h> |
#include <linux/if_arp.h> |
#include <linux/skbuff.h> |
#include <net/sock.h> |
#include <asm/segment.h> |
#include <asm/system.h> |
#include <asm/uaccess.h> |
#include <linux/fcntl.h> |
#include <linux/termios.h> /* For TIOCINQ/OUTQ */ |
#include <linux/mm.h> |
#include <linux/interrupt.h> |
#include <linux/notifier.h> |
#include <net/rose.h> |
#include <linux/proc_fs.h> |
#include <net/ip.h> |
#include <net/arp.h> |
|
int rose_ndevs = 10; |
|
int sysctl_rose_restart_request_timeout = ROSE_DEFAULT_T0; |
int sysctl_rose_call_request_timeout = ROSE_DEFAULT_T1; |
int sysctl_rose_reset_request_timeout = ROSE_DEFAULT_T2; |
int sysctl_rose_clear_request_timeout = ROSE_DEFAULT_T3; |
int sysctl_rose_no_activity_timeout = ROSE_DEFAULT_IDLE; |
int sysctl_rose_ack_hold_back_timeout = ROSE_DEFAULT_HB; |
int sysctl_rose_routing_control = ROSE_DEFAULT_ROUTING; |
int sysctl_rose_link_fail_timeout = ROSE_DEFAULT_FAIL_TIMEOUT; |
int sysctl_rose_maximum_vcs = ROSE_DEFAULT_MAXVC; |
int sysctl_rose_window_size = ROSE_DEFAULT_WINDOW_SIZE; |
|
static struct sock *rose_list; |
|
static struct proto_ops rose_proto_ops; |
|
ax25_address rose_callsign; |
|
/* |
* Convert a ROSE address into text. |
*/ |
char *rose2asc(rose_address *addr) |
{ |
static char buffer[11]; |
|
if (addr->rose_addr[0] == 0x00 && addr->rose_addr[1] == 0x00 && |
addr->rose_addr[2] == 0x00 && addr->rose_addr[3] == 0x00 && |
addr->rose_addr[4] == 0x00) { |
strcpy(buffer, "*"); |
} else { |
sprintf(buffer, "%02X%02X%02X%02X%02X", addr->rose_addr[0] & 0xFF, |
addr->rose_addr[1] & 0xFF, |
addr->rose_addr[2] & 0xFF, |
addr->rose_addr[3] & 0xFF, |
addr->rose_addr[4] & 0xFF); |
} |
|
return buffer; |
} |
|
/* |
* Compare two ROSE addresses, 0 == equal. |
*/ |
int rosecmp(rose_address *addr1, rose_address *addr2) |
{ |
int i; |
|
for (i = 0; i < 5; i++) |
if (addr1->rose_addr[i] != addr2->rose_addr[i]) |
return 1; |
|
return 0; |
} |
|
/* |
* Compare two ROSE addresses for only mask digits, 0 == equal. |
*/ |
int rosecmpm(rose_address *addr1, rose_address *addr2, unsigned short mask) |
{ |
int i, j; |
|
if (mask > 10) |
return 1; |
|
for (i = 0; i < mask; i++) { |
j = i / 2; |
|
if ((i % 2) != 0) { |
if ((addr1->rose_addr[j] & 0x0F) != (addr2->rose_addr[j] & 0x0F)) |
return 1; |
} else { |
if ((addr1->rose_addr[j] & 0xF0) != (addr2->rose_addr[j] & 0xF0)) |
return 1; |
} |
} |
|
return 0; |
} |
|
static void rose_free_sock(struct sock *sk) |
{ |
sk_free(sk); |
|
MOD_DEC_USE_COUNT; |
} |
|
static struct sock *rose_alloc_sock(void) |
{ |
struct sock *sk; |
rose_cb *rose; |
|
if ((sk = sk_alloc(PF_ROSE, GFP_ATOMIC, 1)) == NULL) |
return NULL; |
|
if ((rose = kmalloc(sizeof(*rose), GFP_ATOMIC)) == NULL) { |
sk_free(sk); |
return NULL; |
} |
|
MOD_INC_USE_COUNT; |
|
memset(rose, 0x00, sizeof(*rose)); |
|
sk->protinfo.rose = rose; |
rose->sk = sk; |
|
return sk; |
} |
|
/* |
* Socket removal during an interrupt is now safe. |
*/ |
static void rose_remove_socket(struct sock *sk) |
{ |
struct sock *s; |
unsigned long flags; |
|
save_flags(flags); cli(); |
|
if ((s = rose_list) == sk) { |
rose_list = s->next; |
restore_flags(flags); |
return; |
} |
|
while (s != NULL && s->next != NULL) { |
if (s->next == sk) { |
s->next = sk->next; |
restore_flags(flags); |
return; |
} |
|
s = s->next; |
} |
|
restore_flags(flags); |
} |
|
/* |
* Kill all bound sockets on a broken link layer connection to a |
* particular neighbour. |
*/ |
void rose_kill_by_neigh(struct rose_neigh *neigh) |
{ |
struct sock *s; |
|
for (s = rose_list; s != NULL; s = s->next) { |
if (s->protinfo.rose->neighbour == neigh) { |
rose_disconnect(s, ENETUNREACH, ROSE_OUT_OF_ORDER, 0); |
s->protinfo.rose->neighbour->use--; |
s->protinfo.rose->neighbour = NULL; |
} |
} |
} |
|
/* |
* Kill all bound sockets on a dropped device. |
*/ |
static void rose_kill_by_device(struct net_device *dev) |
{ |
struct sock *s; |
|
for (s = rose_list; s != NULL; s = s->next) { |
if (s->protinfo.rose->device == dev) { |
rose_disconnect(s, ENETUNREACH, ROSE_OUT_OF_ORDER, 0); |
if (s->protinfo.rose->neighbour) |
s->protinfo.rose->neighbour->use--; |
s->protinfo.rose->device = NULL; |
} |
} |
} |
|
/* |
* Handle device status changes. |
*/ |
static int rose_device_event(struct notifier_block *this, unsigned long event, void *ptr) |
{ |
struct net_device *dev = (struct net_device *)ptr; |
|
if (event != NETDEV_DOWN) |
return NOTIFY_DONE; |
|
switch (dev->type) { |
case ARPHRD_ROSE: |
rose_kill_by_device(dev); |
break; |
case ARPHRD_AX25: |
rose_link_device_down(dev); |
rose_rt_device_down(dev); |
break; |
} |
|
return NOTIFY_DONE; |
} |
|
/* |
* Add a socket to the bound sockets list. |
*/ |
static void rose_insert_socket(struct sock *sk) |
{ |
unsigned long flags; |
|
save_flags(flags); cli(); |
|
sk->next = rose_list; |
rose_list = sk; |
|
restore_flags(flags); |
} |
|
/* |
* Find a socket that wants to accept the Call Request we just |
* received. |
*/ |
static struct sock *rose_find_listener(rose_address *addr, ax25_address *call) |
{ |
unsigned long flags; |
struct sock *s; |
|
save_flags(flags); cli(); |
|
for (s = rose_list; s != NULL; s = s->next) { |
if (rosecmp(&s->protinfo.rose->source_addr, addr) == 0 && ax25cmp(&s->protinfo.rose->source_call, call) == 0 && s->protinfo.rose->source_ndigis == 0 && s->state == TCP_LISTEN) { |
restore_flags(flags); |
return s; |
} |
} |
|
for (s = rose_list; s != NULL; s = s->next) { |
if (rosecmp(&s->protinfo.rose->source_addr, addr) == 0 && ax25cmp(&s->protinfo.rose->source_call, &null_ax25_address) == 0 && s->state == TCP_LISTEN) { |
restore_flags(flags); |
return s; |
} |
} |
|
restore_flags(flags); |
return NULL; |
} |
|
/* |
* Find a connected ROSE socket given my LCI and device. |
*/ |
struct sock *rose_find_socket(unsigned int lci, struct rose_neigh *neigh) |
{ |
struct sock *s; |
unsigned long flags; |
|
save_flags(flags); cli(); |
|
for (s = rose_list; s != NULL; s = s->next) { |
if (s->protinfo.rose->lci == lci && s->protinfo.rose->neighbour == neigh) { |
restore_flags(flags); |
return s; |
} |
} |
|
restore_flags(flags); |
|
return NULL; |
} |
|
/* |
* Find a unique LCI for a given device. |
*/ |
unsigned int rose_new_lci(struct rose_neigh *neigh) |
{ |
int lci; |
|
if (neigh->dce_mode) { |
for (lci = 1; lci <= sysctl_rose_maximum_vcs; lci++) |
if (rose_find_socket(lci, neigh) == NULL && rose_route_free_lci(lci, neigh) == NULL) |
return lci; |
} else { |
for (lci = sysctl_rose_maximum_vcs; lci > 0; lci--) |
if (rose_find_socket(lci, neigh) == NULL && rose_route_free_lci(lci, neigh) == NULL) |
return lci; |
} |
|
return 0; |
} |
|
/* |
* Deferred destroy. |
*/ |
void rose_destroy_socket(struct sock *); |
|
/* |
* Handler for deferred kills. |
*/ |
static void rose_destroy_timer(unsigned long data) |
{ |
rose_destroy_socket((struct sock *)data); |
} |
|
/* |
* This is called from user mode and the timers. Thus it protects itself against |
* interrupt users but doesn't worry about being called during work. |
* Once it is removed from the queue no interrupt or bottom half will |
* touch it and we are (fairly 8-) ) safe. |
*/ |
void rose_destroy_socket(struct sock *sk) /* Not static as it's used by the timer */ |
{ |
struct sk_buff *skb; |
unsigned long flags; |
|
save_flags(flags); cli(); |
|
rose_stop_heartbeat(sk); |
rose_stop_idletimer(sk); |
rose_stop_timer(sk); |
|
rose_remove_socket(sk); |
rose_clear_queues(sk); /* Flush the queues */ |
|
while ((skb = skb_dequeue(&sk->receive_queue)) != NULL) { |
if (skb->sk != sk) { /* A pending connection */ |
skb->sk->dead = 1; /* Queue the unaccepted socket for death */ |
rose_start_heartbeat(skb->sk); |
skb->sk->protinfo.rose->state = ROSE_STATE_0; |
} |
|
kfree_skb(skb); |
} |
|
if (atomic_read(&sk->wmem_alloc) != 0 || atomic_read(&sk->rmem_alloc) != 0) { |
/* Defer: outstanding buffers */ |
init_timer(&sk->timer); |
sk->timer.expires = jiffies + 10 * HZ; |
sk->timer.function = rose_destroy_timer; |
sk->timer.data = (unsigned long)sk; |
add_timer(&sk->timer); |
} else { |
rose_free_sock(sk); |
} |
|
restore_flags(flags); |
} |
|
/* |
* Handling for system calls applied via the various interfaces to a |
* ROSE socket object. |
*/ |
|
static int rose_setsockopt(struct socket *sock, int level, int optname, |
char *optval, int optlen) |
{ |
struct sock *sk = sock->sk; |
int opt; |
|
if (level != SOL_ROSE) |
return -ENOPROTOOPT; |
|
if (optlen < sizeof(int)) |
return -EINVAL; |
|
if (get_user(opt, (int *)optval)) |
return -EFAULT; |
|
switch (optname) { |
case ROSE_DEFER: |
sk->protinfo.rose->defer = opt ? 1 : 0; |
return 0; |
|
case ROSE_T1: |
if (opt < 1) |
return -EINVAL; |
sk->protinfo.rose->t1 = opt * HZ; |
return 0; |
|
case ROSE_T2: |
if (opt < 1) |
return -EINVAL; |
sk->protinfo.rose->t2 = opt * HZ; |
return 0; |
|
case ROSE_T3: |
if (opt < 1) |
return -EINVAL; |
sk->protinfo.rose->t3 = opt * HZ; |
return 0; |
|
case ROSE_HOLDBACK: |
if (opt < 1) |
return -EINVAL; |
sk->protinfo.rose->hb = opt * HZ; |
return 0; |
|
case ROSE_IDLE: |
if (opt < 0) |
return -EINVAL; |
sk->protinfo.rose->idle = opt * 60 * HZ; |
return 0; |
|
case ROSE_QBITINCL: |
sk->protinfo.rose->qbitincl = opt ? 1 : 0; |
return 0; |
|
default: |
return -ENOPROTOOPT; |
} |
} |
|
static int rose_getsockopt(struct socket *sock, int level, int optname, |
char *optval, int *optlen) |
{ |
struct sock *sk = sock->sk; |
int val = 0; |
int len; |
|
if (level != SOL_ROSE) |
return -ENOPROTOOPT; |
|
if (get_user(len, optlen)) |
return -EFAULT; |
|
if (len < 0) |
return -EINVAL; |
|
switch (optname) { |
case ROSE_DEFER: |
val = sk->protinfo.rose->defer; |
break; |
|
case ROSE_T1: |
val = sk->protinfo.rose->t1 / HZ; |
break; |
|
case ROSE_T2: |
val = sk->protinfo.rose->t2 / HZ; |
break; |
|
case ROSE_T3: |
val = sk->protinfo.rose->t3 / HZ; |
break; |
|
case ROSE_HOLDBACK: |
val = sk->protinfo.rose->hb / HZ; |
break; |
|
case ROSE_IDLE: |
val = sk->protinfo.rose->idle / (60 * HZ); |
break; |
|
case ROSE_QBITINCL: |
val = sk->protinfo.rose->qbitincl; |
break; |
|
default: |
return -ENOPROTOOPT; |
} |
|
len = min_t(unsigned int, len, sizeof(int)); |
|
if (put_user(len, optlen)) |
return -EFAULT; |
|
return copy_to_user(optval, &val, len) ? -EFAULT : 0; |
} |
|
static int rose_listen(struct socket *sock, int backlog) |
{ |
struct sock *sk = sock->sk; |
|
if (sk->state != TCP_LISTEN) { |
sk->protinfo.rose->dest_ndigis = 0; |
memset(&sk->protinfo.rose->dest_addr, '\0', ROSE_ADDR_LEN); |
memset(&sk->protinfo.rose->dest_call, '\0', AX25_ADDR_LEN); |
memset(sk->protinfo.rose->dest_digis, '\0', AX25_ADDR_LEN*ROSE_MAX_DIGIS); |
sk->max_ack_backlog = backlog; |
sk->state = TCP_LISTEN; |
return 0; |
} |
|
return -EOPNOTSUPP; |
} |
|
static int rose_create(struct socket *sock, int protocol) |
{ |
struct sock *sk; |
rose_cb *rose; |
|
if (sock->type != SOCK_SEQPACKET || protocol != 0) |
return -ESOCKTNOSUPPORT; |
|
if ((sk = rose_alloc_sock()) == NULL) |
return -ENOMEM; |
|
rose = sk->protinfo.rose; |
|
sock_init_data(sock, sk); |
|
skb_queue_head_init(&rose->ack_queue); |
#ifdef M_BIT |
skb_queue_head_init(&rose->frag_queue); |
rose->fraglen = 0; |
#endif |
|
sock->ops = &rose_proto_ops; |
sk->protocol = protocol; |
|
init_timer(&rose->timer); |
init_timer(&rose->idletimer); |
|
rose->t1 = sysctl_rose_call_request_timeout; |
rose->t2 = sysctl_rose_reset_request_timeout; |
rose->t3 = sysctl_rose_clear_request_timeout; |
rose->hb = sysctl_rose_ack_hold_back_timeout; |
rose->idle = sysctl_rose_no_activity_timeout; |
|
rose->state = ROSE_STATE_0; |
|
return 0; |
} |
|
static struct sock *rose_make_new(struct sock *osk) |
{ |
struct sock *sk; |
rose_cb *rose; |
|
if (osk->type != SOCK_SEQPACKET) |
return NULL; |
|
if ((sk = rose_alloc_sock()) == NULL) |
return NULL; |
|
rose = sk->protinfo.rose; |
|
sock_init_data(NULL, sk); |
|
skb_queue_head_init(&rose->ack_queue); |
#ifdef M_BIT |
skb_queue_head_init(&rose->frag_queue); |
rose->fraglen = 0; |
#endif |
|
sk->type = osk->type; |
sk->socket = osk->socket; |
sk->priority = osk->priority; |
sk->protocol = osk->protocol; |
sk->rcvbuf = osk->rcvbuf; |
sk->sndbuf = osk->sndbuf; |
sk->debug = osk->debug; |
sk->state = TCP_ESTABLISHED; |
sk->sleep = osk->sleep; |
sk->zapped = osk->zapped; |
|
init_timer(&rose->timer); |
init_timer(&rose->idletimer); |
|
rose->t1 = osk->protinfo.rose->t1; |
rose->t2 = osk->protinfo.rose->t2; |
rose->t3 = osk->protinfo.rose->t3; |
rose->hb = osk->protinfo.rose->hb; |
rose->idle = osk->protinfo.rose->idle; |
|
rose->defer = osk->protinfo.rose->defer; |
rose->device = osk->protinfo.rose->device; |
rose->qbitincl = osk->protinfo.rose->qbitincl; |
|
return sk; |
} |
|
static int rose_release(struct socket *sock) |
{ |
struct sock *sk = sock->sk; |
|
if (sk == NULL) return 0; |
|
switch (sk->protinfo.rose->state) { |
|
case ROSE_STATE_0: |
rose_disconnect(sk, 0, -1, -1); |
rose_destroy_socket(sk); |
break; |
|
case ROSE_STATE_2: |
sk->protinfo.rose->neighbour->use--; |
rose_disconnect(sk, 0, -1, -1); |
rose_destroy_socket(sk); |
break; |
|
case ROSE_STATE_1: |
case ROSE_STATE_3: |
case ROSE_STATE_4: |
case ROSE_STATE_5: |
rose_clear_queues(sk); |
rose_stop_idletimer(sk); |
rose_write_internal(sk, ROSE_CLEAR_REQUEST); |
rose_start_t3timer(sk); |
sk->protinfo.rose->state = ROSE_STATE_2; |
sk->state = TCP_CLOSE; |
sk->shutdown |= SEND_SHUTDOWN; |
sk->state_change(sk); |
sock_orphan(sk); |
sk->destroy = 1; |
break; |
|
default: |
sk->socket = NULL; |
break; |
} |
|
sock->sk = NULL; |
|
return 0; |
} |
|
static int rose_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) |
{ |
struct sock *sk = sock->sk; |
struct sockaddr_rose *addr = (struct sockaddr_rose *)uaddr; |
struct net_device *dev; |
ax25_address *user, *source; |
int n; |
|
if (sk->zapped == 0) |
return -EINVAL; |
|
if (addr_len != sizeof(struct sockaddr_rose) && addr_len != sizeof(struct full_sockaddr_rose)) |
return -EINVAL; |
|
if (addr->srose_family != AF_ROSE) |
return -EINVAL; |
|
if (addr_len == sizeof(struct sockaddr_rose) && addr->srose_ndigis > 1) |
return -EINVAL; |
|
if (addr->srose_ndigis > ROSE_MAX_DIGIS) |
return -EINVAL; |
|
if ((dev = rose_dev_get(&addr->srose_addr)) == NULL) { |
SOCK_DEBUG(sk, "ROSE: bind failed: invalid address\n"); |
return -EADDRNOTAVAIL; |
} |
|
source = &addr->srose_call; |
|
if ((user = ax25_findbyuid(current->euid)) == NULL) { |
if (ax25_uid_policy && !capable(CAP_NET_BIND_SERVICE)) |
return -EACCES; |
user = source; |
} |
|
sk->protinfo.rose->source_addr = addr->srose_addr; |
sk->protinfo.rose->source_call = *user; |
sk->protinfo.rose->device = dev; |
sk->protinfo.rose->source_ndigis = addr->srose_ndigis; |
|
if (addr_len == sizeof(struct full_sockaddr_rose)) { |
struct full_sockaddr_rose *full_addr = (struct full_sockaddr_rose *)uaddr; |
for (n = 0 ; n < addr->srose_ndigis ; n++) |
sk->protinfo.rose->source_digis[n] = full_addr->srose_digis[n]; |
} else { |
if (sk->protinfo.rose->source_ndigis == 1) { |
sk->protinfo.rose->source_digis[0] = addr->srose_digi; |
} |
} |
|
rose_insert_socket(sk); |
|
sk->zapped = 0; |
SOCK_DEBUG(sk, "ROSE: socket is bound\n"); |
return 0; |
} |
|
static int rose_connect(struct socket *sock, struct sockaddr *uaddr, int addr_len, int flags) |
{ |
struct sock *sk = sock->sk; |
struct sockaddr_rose *addr = (struct sockaddr_rose *)uaddr; |
unsigned char cause, diagnostic; |
ax25_address *user; |
struct net_device *dev; |
int n; |
|
if (sk->state == TCP_ESTABLISHED && sock->state == SS_CONNECTING) { |
sock->state = SS_CONNECTED; |
return 0; /* Connect completed during a ERESTARTSYS event */ |
} |
|
if (sk->state == TCP_CLOSE && sock->state == SS_CONNECTING) { |
sock->state = SS_UNCONNECTED; |
return -ECONNREFUSED; |
} |
|
if (sk->state == TCP_ESTABLISHED) |
return -EISCONN; /* No reconnect on a seqpacket socket */ |
|
sk->state = TCP_CLOSE; |
sock->state = SS_UNCONNECTED; |
|
if (addr_len != sizeof(struct sockaddr_rose) && addr_len != sizeof(struct full_sockaddr_rose)) |
return -EINVAL; |
|
if (addr->srose_family != AF_ROSE) |
return -EINVAL; |
|
if (addr_len == sizeof(struct sockaddr_rose) && addr->srose_ndigis > 1) |
return -EINVAL; |
|
if (addr->srose_ndigis > ROSE_MAX_DIGIS) |
return -EINVAL; |
|
/* Source + Destination digis should not exceed ROSE_MAX_DIGIS */ |
if ((sk->protinfo.rose->source_ndigis + addr->srose_ndigis) > ROSE_MAX_DIGIS) |
return -EINVAL; |
|
if ((sk->protinfo.rose->neighbour = rose_get_neigh(&addr->srose_addr, &cause, &diagnostic)) == NULL) |
return -ENETUNREACH; |
|
if ((sk->protinfo.rose->lci = rose_new_lci(sk->protinfo.rose->neighbour)) == 0) |
return -ENETUNREACH; |
|
if (sk->zapped) { /* Must bind first - autobinding in this may or may not work */ |
sk->zapped = 0; |
|
if ((dev = rose_dev_first()) == NULL) |
return -ENETUNREACH; |
|
if ((user = ax25_findbyuid(current->euid)) == NULL) |
return -EINVAL; |
|
memcpy(&sk->protinfo.rose->source_addr, dev->dev_addr, ROSE_ADDR_LEN); |
sk->protinfo.rose->source_call = *user; |
sk->protinfo.rose->device = dev; |
|
rose_insert_socket(sk); /* Finish the bind */ |
} |
|
sk->protinfo.rose->dest_addr = addr->srose_addr; |
sk->protinfo.rose->dest_call = addr->srose_call; |
sk->protinfo.rose->rand = ((int)sk->protinfo.rose & 0xFFFF) + sk->protinfo.rose->lci; |
sk->protinfo.rose->dest_ndigis = addr->srose_ndigis; |
|
if (addr_len == sizeof(struct full_sockaddr_rose)) { |
struct full_sockaddr_rose *full_addr = (struct full_sockaddr_rose *)uaddr; |
for (n = 0 ; n < addr->srose_ndigis ; n++) |
sk->protinfo.rose->dest_digis[n] = full_addr->srose_digis[n]; |
} else { |
if (sk->protinfo.rose->dest_ndigis == 1) { |
sk->protinfo.rose->dest_digis[0] = addr->srose_digi; |
} |
} |
|
/* Move to connecting socket, start sending Connect Requests */ |
sock->state = SS_CONNECTING; |
sk->state = TCP_SYN_SENT; |
|
sk->protinfo.rose->state = ROSE_STATE_1; |
|
sk->protinfo.rose->neighbour->use++; |
|
rose_write_internal(sk, ROSE_CALL_REQUEST); |
rose_start_heartbeat(sk); |
rose_start_t1timer(sk); |
|
/* Now the loop */ |
if (sk->state != TCP_ESTABLISHED && (flags & O_NONBLOCK)) |
return -EINPROGRESS; |
|
cli(); /* To avoid races on the sleep */ |
|
/* |
* A Connect Ack with Choke or timeout or failed routing will go to closed. |
*/ |
while (sk->state == TCP_SYN_SENT) { |
interruptible_sleep_on(sk->sleep); |
if (signal_pending(current)) { |
sti(); |
return -ERESTARTSYS; |
} |
} |
|
if (sk->state != TCP_ESTABLISHED) { |
sti(); |
sock->state = SS_UNCONNECTED; |
return sock_error(sk); /* Always set at this point */ |
} |
|
sock->state = SS_CONNECTED; |
|
sti(); |
|
return 0; |
} |
|
static int rose_accept(struct socket *sock, struct socket *newsock, int flags) |
{ |
struct sock *sk; |
struct sock *newsk; |
struct sk_buff *skb; |
|
if ((sk = sock->sk) == NULL) |
return -EINVAL; |
|
if (sk->type != SOCK_SEQPACKET) |
return -EOPNOTSUPP; |
|
if (sk->state != TCP_LISTEN) |
return -EINVAL; |
|
/* |
* The write queue this time is holding sockets ready to use |
* hooked into the SABM we saved |
*/ |
do { |
cli(); |
if ((skb = skb_dequeue(&sk->receive_queue)) == NULL) { |
if (flags & O_NONBLOCK) { |
sti(); |
return -EWOULDBLOCK; |
} |
interruptible_sleep_on(sk->sleep); |
if (signal_pending(current)) { |
sti(); |
return -ERESTARTSYS; |
} |
} |
} while (skb == NULL); |
|
newsk = skb->sk; |
newsk->pair = NULL; |
newsk->socket = newsock; |
newsk->sleep = &newsock->wait; |
sti(); |
|
/* Now attach up the new socket */ |
skb->sk = NULL; |
kfree_skb(skb); |
sk->ack_backlog--; |
newsock->sk = newsk; |
|
return 0; |
} |
|
static int rose_getname(struct socket *sock, struct sockaddr *uaddr, |
int *uaddr_len, int peer) |
{ |
struct full_sockaddr_rose *srose = (struct full_sockaddr_rose *)uaddr; |
struct sock *sk = sock->sk; |
int n; |
|
if (peer != 0) { |
if (sk->state != TCP_ESTABLISHED) |
return -ENOTCONN; |
srose->srose_family = AF_ROSE; |
srose->srose_addr = sk->protinfo.rose->dest_addr; |
srose->srose_call = sk->protinfo.rose->dest_call; |
srose->srose_ndigis = sk->protinfo.rose->dest_ndigis; |
for (n = 0 ; n < sk->protinfo.rose->dest_ndigis ; n++) |
srose->srose_digis[n] = sk->protinfo.rose->dest_digis[n]; |
} else { |
srose->srose_family = AF_ROSE; |
srose->srose_addr = sk->protinfo.rose->source_addr; |
srose->srose_call = sk->protinfo.rose->source_call; |
srose->srose_ndigis = sk->protinfo.rose->source_ndigis; |
for (n = 0 ; n < sk->protinfo.rose->source_ndigis ; n++) |
srose->srose_digis[n] = sk->protinfo.rose->source_digis[n]; |
} |
|
*uaddr_len = sizeof(struct full_sockaddr_rose); |
return 0; |
} |
|
int rose_rx_call_request(struct sk_buff *skb, struct net_device *dev, struct rose_neigh *neigh, unsigned int lci) |
{ |
struct sock *sk; |
struct sock *make; |
struct rose_facilities_struct facilities; |
int n, len; |
|
skb->sk = NULL; /* Initially we don't know who it's for */ |
|
/* |
* skb->data points to the rose frame start |
*/ |
memset(&facilities, 0x00, sizeof(struct rose_facilities_struct)); |
|
len = (((skb->data[3] >> 4) & 0x0F) + 1) / 2; |
len += (((skb->data[3] >> 0) & 0x0F) + 1) / 2; |
if (!rose_parse_facilities(skb->data + len + 4, &facilities)) { |
rose_transmit_clear_request(neigh, lci, ROSE_INVALID_FACILITY, 76); |
return 0; |
} |
|
sk = rose_find_listener(&facilities.source_addr, &facilities.source_call); |
|
/* |
* We can't accept the Call Request. |
*/ |
if (sk == NULL || sk->ack_backlog == sk->max_ack_backlog || (make = rose_make_new(sk)) == NULL) { |
rose_transmit_clear_request(neigh, lci, ROSE_NETWORK_CONGESTION, 120); |
return 0; |
} |
|
skb->sk = make; |
make->state = TCP_ESTABLISHED; |
|
make->protinfo.rose->lci = lci; |
make->protinfo.rose->dest_addr = facilities.dest_addr; |
make->protinfo.rose->dest_call = facilities.dest_call; |
make->protinfo.rose->dest_ndigis = facilities.dest_ndigis; |
for (n = 0 ; n < facilities.dest_ndigis ; n++) |
make->protinfo.rose->dest_digis[n] = facilities.dest_digis[n]; |
make->protinfo.rose->source_addr = facilities.source_addr; |
make->protinfo.rose->source_call = facilities.source_call; |
make->protinfo.rose->source_ndigis = facilities.source_ndigis; |
for (n = 0 ; n < facilities.source_ndigis ; n++) |
make->protinfo.rose->source_digis[n]= facilities.source_digis[n]; |
make->protinfo.rose->neighbour = neigh; |
make->protinfo.rose->device = dev; |
make->protinfo.rose->facilities = facilities; |
|
make->protinfo.rose->neighbour->use++; |
|
if (sk->protinfo.rose->defer) { |
make->protinfo.rose->state = ROSE_STATE_5; |
} else { |
rose_write_internal(make, ROSE_CALL_ACCEPTED); |
make->protinfo.rose->state = ROSE_STATE_3; |
rose_start_idletimer(make); |
} |
|
make->protinfo.rose->condition = 0x00; |
make->protinfo.rose->vs = 0; |
make->protinfo.rose->va = 0; |
make->protinfo.rose->vr = 0; |
make->protinfo.rose->vl = 0; |
sk->ack_backlog++; |
make->pair = sk; |
|
rose_insert_socket(make); |
|
skb_queue_head(&sk->receive_queue, skb); |
|
rose_start_heartbeat(make); |
|
if (!sk->dead) |
sk->data_ready(sk, skb->len); |
|
return 1; |
} |
|
static int rose_sendmsg(struct socket *sock, struct msghdr *msg, int len, |
struct scm_cookie *scm) |
{ |
struct sock *sk = sock->sk; |
struct sockaddr_rose *usrose = (struct sockaddr_rose *)msg->msg_name; |
int err; |
struct full_sockaddr_rose srose; |
struct sk_buff *skb; |
unsigned char *asmptr; |
int n, size, qbit = 0; |
|
if (msg->msg_flags & ~(MSG_DONTWAIT|MSG_EOR)) |
return -EINVAL; |
|
if (sk->zapped) |
return -EADDRNOTAVAIL; |
|
if (sk->shutdown & SEND_SHUTDOWN) { |
send_sig(SIGPIPE, current, 0); |
return -EPIPE; |
} |
|
if (sk->protinfo.rose->neighbour == NULL || sk->protinfo.rose->device == NULL) |
return -ENETUNREACH; |
|
if (usrose != NULL) { |
if (msg->msg_namelen != sizeof(struct sockaddr_rose) && msg->msg_namelen != sizeof(struct full_sockaddr_rose)) |
return -EINVAL; |
memset(&srose, 0, sizeof(struct full_sockaddr_rose)); |
memcpy(&srose, usrose, msg->msg_namelen); |
if (rosecmp(&sk->protinfo.rose->dest_addr, &srose.srose_addr) != 0 || |
ax25cmp(&sk->protinfo.rose->dest_call, &srose.srose_call) != 0) |
return -EISCONN; |
if (srose.srose_ndigis != sk->protinfo.rose->dest_ndigis) |
return -EISCONN; |
if (srose.srose_ndigis == sk->protinfo.rose->dest_ndigis) { |
for (n = 0 ; n < srose.srose_ndigis ; n++) |
if (ax25cmp(&sk->protinfo.rose->dest_digis[n], &srose.srose_digis[n]) != 0) |
return -EISCONN; |
} |
if (srose.srose_family != AF_ROSE) |
return -EINVAL; |
} else { |
if (sk->state != TCP_ESTABLISHED) |
return -ENOTCONN; |
|
srose.srose_family = AF_ROSE; |
srose.srose_addr = sk->protinfo.rose->dest_addr; |
srose.srose_call = sk->protinfo.rose->dest_call; |
srose.srose_ndigis = sk->protinfo.rose->dest_ndigis; |
for (n = 0 ; n < sk->protinfo.rose->dest_ndigis ; n++) |
srose.srose_digis[n] = sk->protinfo.rose->dest_digis[n]; |
} |
|
SOCK_DEBUG(sk, "ROSE: sendto: Addresses built.\n"); |
|
/* Build a packet */ |
SOCK_DEBUG(sk, "ROSE: sendto: building packet.\n"); |
size = len + AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN + ROSE_MIN_LEN; |
|
if ((skb = sock_alloc_send_skb(sk, size, msg->msg_flags & MSG_DONTWAIT, &err)) == NULL) |
return err; |
|
skb_reserve(skb, AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN + ROSE_MIN_LEN); |
|
/* |
* Put the data on the end |
*/ |
SOCK_DEBUG(sk, "ROSE: Appending user data\n"); |
|
asmptr = skb->h.raw = skb_put(skb, len); |
|
memcpy_fromiovec(asmptr, msg->msg_iov, len); |
|
/* |
* If the Q BIT Include socket option is in force, the first |
* byte of the user data is the logical value of the Q Bit. |
*/ |
if (sk->protinfo.rose->qbitincl) { |
qbit = skb->data[0]; |
skb_pull(skb, 1); |
} |
|
/* |
* Push down the ROSE header |
*/ |
asmptr = skb_push(skb, ROSE_MIN_LEN); |
|
SOCK_DEBUG(sk, "ROSE: Building Network Header.\n"); |
|
/* Build a ROSE Network header */ |
asmptr[0] = ((sk->protinfo.rose->lci >> 8) & 0x0F) | ROSE_GFI; |
asmptr[1] = (sk->protinfo.rose->lci >> 0) & 0xFF; |
asmptr[2] = ROSE_DATA; |
|
if (qbit) |
asmptr[0] |= ROSE_Q_BIT; |
|
SOCK_DEBUG(sk, "ROSE: Built header.\n"); |
|
SOCK_DEBUG(sk, "ROSE: Transmitting buffer\n"); |
|
if (sk->state != TCP_ESTABLISHED) { |
kfree_skb(skb); |
return -ENOTCONN; |
} |
|
#ifdef M_BIT |
#define ROSE_PACLEN (256-ROSE_MIN_LEN) |
if (skb->len - ROSE_MIN_LEN > ROSE_PACLEN) { |
unsigned char header[ROSE_MIN_LEN]; |
struct sk_buff *skbn; |
int frontlen; |
int lg; |
|
/* Save a copy of the Header */ |
memcpy(header, skb->data, ROSE_MIN_LEN); |
skb_pull(skb, ROSE_MIN_LEN); |
|
frontlen = skb_headroom(skb); |
|
while (skb->len > 0) { |
if ((skbn = sock_alloc_send_skb(sk, frontlen + ROSE_PACLEN, 0, &err)) == NULL) |
return err; |
|
skbn->sk = sk; |
skbn->free = 1; |
skbn->arp = 1; |
|
skb_reserve(skbn, frontlen); |
|
lg = (ROSE_PACLEN > skb->len) ? skb->len : ROSE_PACLEN; |
|
/* Copy the user data */ |
memcpy(skb_put(skbn, lg), skb->data, lg); |
skb_pull(skb, lg); |
|
/* Duplicate the Header */ |
skb_push(skbn, ROSE_MIN_LEN); |
memcpy(skbn->data, header, ROSE_MIN_LEN); |
|
if (skb->len > 0) |
skbn->data[2] |= M_BIT; |
|
skb_queue_tail(&sk->write_queue, skbn); /* Throw it on the queue */ |
} |
|
skb->free = 1; |
kfree_skb(skb, FREE_WRITE); |
} else { |
skb_queue_tail(&sk->write_queue, skb); /* Throw it on the queue */ |
} |
#else |
skb_queue_tail(&sk->write_queue, skb); /* Shove it onto the queue */ |
#endif |
|
rose_kick(sk); |
|
return len; |
} |
|
|
static int rose_recvmsg(struct socket *sock, struct msghdr *msg, int size, |
int flags, struct scm_cookie *scm) |
{ |
struct sock *sk = sock->sk; |
struct sockaddr_rose *srose = (struct sockaddr_rose *)msg->msg_name; |
int copied, qbit; |
unsigned char *asmptr; |
struct sk_buff *skb; |
int n, er; |
|
/* |
* This works for seqpacket too. The receiver has ordered the queue for |
* us! We do one quick check first though |
*/ |
if (sk->state != TCP_ESTABLISHED) |
return -ENOTCONN; |
|
/* Now we can treat all alike */ |
if ((skb = skb_recv_datagram(sk, flags & ~MSG_DONTWAIT, flags & MSG_DONTWAIT, &er)) == NULL) |
return er; |
|
qbit = (skb->data[0] & ROSE_Q_BIT) == ROSE_Q_BIT; |
|
skb_pull(skb, ROSE_MIN_LEN); |
|
if (sk->protinfo.rose->qbitincl) { |
asmptr = skb_push(skb, 1); |
*asmptr = qbit; |
} |
|
skb->h.raw = skb->data; |
copied = skb->len; |
|
if (copied > size) { |
copied = size; |
msg->msg_flags |= MSG_TRUNC; |
} |
|
skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied); |
|
if (srose != NULL) { |
srose->srose_family = AF_ROSE; |
srose->srose_addr = sk->protinfo.rose->dest_addr; |
srose->srose_call = sk->protinfo.rose->dest_call; |
srose->srose_ndigis = sk->protinfo.rose->dest_ndigis; |
if (msg->msg_namelen >= sizeof(struct full_sockaddr_rose)) { |
struct full_sockaddr_rose *full_srose = (struct full_sockaddr_rose *)msg->msg_name; |
for (n = 0 ; n < sk->protinfo.rose->dest_ndigis ; n++) |
full_srose->srose_digis[n] = sk->protinfo.rose->dest_digis[n]; |
msg->msg_namelen = sizeof(struct full_sockaddr_rose); |
} else { |
if (sk->protinfo.rose->dest_ndigis >= 1) { |
srose->srose_ndigis = 1; |
srose->srose_digi = sk->protinfo.rose->dest_digis[0]; |
} |
msg->msg_namelen = sizeof(struct sockaddr_rose); |
} |
} |
|
skb_free_datagram(sk, skb); |
|
return copied; |
} |
|
|
static int rose_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) |
{ |
struct sock *sk = sock->sk; |
|
switch (cmd) { |
case TIOCOUTQ: { |
long amount; |
amount = sk->sndbuf - atomic_read(&sk->wmem_alloc); |
if (amount < 0) |
amount = 0; |
return put_user(amount, (unsigned int *)arg); |
} |
|
case TIOCINQ: { |
struct sk_buff *skb; |
long amount = 0L; |
/* These two are safe on a single CPU system as only user tasks fiddle here */ |
if ((skb = skb_peek(&sk->receive_queue)) != NULL) |
amount = skb->len; |
return put_user(amount, (unsigned int *)arg); |
} |
|
case SIOCGSTAMP: |
if (sk != NULL) { |
if (sk->stamp.tv_sec == 0) |
return -ENOENT; |
return copy_to_user((void *)arg, &sk->stamp, sizeof(struct timeval)) ? -EFAULT : 0; |
} |
return -EINVAL; |
|
case SIOCGIFADDR: |
case SIOCSIFADDR: |
case SIOCGIFDSTADDR: |
case SIOCSIFDSTADDR: |
case SIOCGIFBRDADDR: |
case SIOCSIFBRDADDR: |
case SIOCGIFNETMASK: |
case SIOCSIFNETMASK: |
case SIOCGIFMETRIC: |
case SIOCSIFMETRIC: |
return -EINVAL; |
|
case SIOCADDRT: |
case SIOCDELRT: |
case SIOCRSCLRRT: |
if (!capable(CAP_NET_ADMIN)) return -EPERM; |
return rose_rt_ioctl(cmd, (void *)arg); |
|
case SIOCRSGCAUSE: { |
struct rose_cause_struct rose_cause; |
rose_cause.cause = sk->protinfo.rose->cause; |
rose_cause.diagnostic = sk->protinfo.rose->diagnostic; |
return copy_to_user((void *)arg, &rose_cause, sizeof(struct rose_cause_struct)) ? -EFAULT : 0; |
} |
|
case SIOCRSSCAUSE: { |
struct rose_cause_struct rose_cause; |
if (copy_from_user(&rose_cause, (void *)arg, sizeof(struct rose_cause_struct))) |
return -EFAULT; |
sk->protinfo.rose->cause = rose_cause.cause; |
sk->protinfo.rose->diagnostic = rose_cause.diagnostic; |
return 0; |
} |
|
case SIOCRSSL2CALL: |
if (!capable(CAP_NET_ADMIN)) return -EPERM; |
if (ax25cmp(&rose_callsign, &null_ax25_address) != 0) |
ax25_listen_release(&rose_callsign, NULL); |
if (copy_from_user(&rose_callsign, (void *)arg, sizeof(ax25_address))) |
return -EFAULT; |
if (ax25cmp(&rose_callsign, &null_ax25_address) != 0) |
ax25_listen_register(&rose_callsign, NULL); |
return 0; |
|
case SIOCRSGL2CALL: |
return copy_to_user((void *)arg, &rose_callsign, sizeof(ax25_address)) ? -EFAULT : 0; |
|
case SIOCRSACCEPT: |
if (sk->protinfo.rose->state == ROSE_STATE_5) { |
rose_write_internal(sk, ROSE_CALL_ACCEPTED); |
rose_start_idletimer(sk); |
sk->protinfo.rose->condition = 0x00; |
sk->protinfo.rose->vs = 0; |
sk->protinfo.rose->va = 0; |
sk->protinfo.rose->vr = 0; |
sk->protinfo.rose->vl = 0; |
sk->protinfo.rose->state = ROSE_STATE_3; |
} |
return 0; |
|
default: |
return dev_ioctl(cmd, (void *)arg); |
} |
|
/*NOTREACHED*/ |
return 0; |
} |
|
static int rose_get_info(char *buffer, char **start, off_t offset, int length) |
{ |
struct sock *s; |
struct net_device *dev; |
const char *devname, *callsign; |
int len = 0; |
off_t pos = 0; |
off_t begin = 0; |
|
cli(); |
|
len += sprintf(buffer, "dest_addr dest_call src_addr src_call dev lci neigh st vs vr va t t1 t2 t3 hb idle Snd-Q Rcv-Q inode\n"); |
|
for (s = rose_list; s != NULL; s = s->next) { |
if ((dev = s->protinfo.rose->device) == NULL) |
devname = "???"; |
else |
devname = dev->name; |
|
len += sprintf(buffer + len, "%-10s %-9s ", |
rose2asc(&s->protinfo.rose->dest_addr), |
ax2asc(&s->protinfo.rose->dest_call)); |
|
if (ax25cmp(&s->protinfo.rose->source_call, &null_ax25_address) == 0) |
callsign = "??????-?"; |
else |
callsign = ax2asc(&s->protinfo.rose->source_call); |
|
len += sprintf(buffer + len, "%-10s %-9s %-5s %3.3X %05d %d %d %d %d %3lu %3lu %3lu %3lu %3lu %3lu/%03lu %5d %5d %ld\n", |
rose2asc(&s->protinfo.rose->source_addr), |
callsign, |
devname, |
s->protinfo.rose->lci & 0x0FFF, |
(s->protinfo.rose->neighbour) ? s->protinfo.rose->neighbour->number : 0, |
s->protinfo.rose->state, |
s->protinfo.rose->vs, |
s->protinfo.rose->vr, |
s->protinfo.rose->va, |
ax25_display_timer(&s->protinfo.rose->timer) / HZ, |
s->protinfo.rose->t1 / HZ, |
s->protinfo.rose->t2 / HZ, |
s->protinfo.rose->t3 / HZ, |
s->protinfo.rose->hb / HZ, |
ax25_display_timer(&s->protinfo.rose->idletimer) / (60 * HZ), |
s->protinfo.rose->idle / (60 * HZ), |
atomic_read(&s->wmem_alloc), |
atomic_read(&s->rmem_alloc), |
s->socket != NULL ? s->socket->inode->i_ino : 0L); |
|
pos = begin + len; |
|
if (pos < offset) { |
len = 0; |
begin = pos; |
} |
|
if (pos > offset + length) |
break; |
} |
|
sti(); |
|
*start = buffer + (offset - begin); |
len -= (offset - begin); |
|
if (len > length) len = length; |
|
return(len); |
} |
|
static struct net_proto_family rose_family_ops = { |
family: PF_ROSE, |
create: rose_create, |
}; |
|
static struct proto_ops SOCKOPS_WRAPPED(rose_proto_ops) = { |
family: PF_ROSE, |
|
release: rose_release, |
bind: rose_bind, |
connect: rose_connect, |
socketpair: sock_no_socketpair, |
accept: rose_accept, |
getname: rose_getname, |
poll: datagram_poll, |
ioctl: rose_ioctl, |
listen: rose_listen, |
shutdown: sock_no_shutdown, |
setsockopt: rose_setsockopt, |
getsockopt: rose_getsockopt, |
sendmsg: rose_sendmsg, |
recvmsg: rose_recvmsg, |
mmap: sock_no_mmap, |
sendpage: sock_no_sendpage, |
}; |
|
#include <linux/smp_lock.h> |
SOCKOPS_WRAP(rose_proto, PF_ROSE); |
|
static struct notifier_block rose_dev_notifier = { |
notifier_call: rose_device_event, |
}; |
|
static struct net_device *dev_rose; |
|
static const char banner[] = KERN_INFO "F6FBB/G4KLX ROSE for Linux. Version 0.64 for AX25.037 Linux 2.4\n"; |
|
static int __init rose_proto_init(void) |
{ |
int i; |
|
rose_callsign = null_ax25_address; |
|
if (rose_ndevs > 0x7FFFFFFF/sizeof(struct net_device)) { |
printk(KERN_ERR "ROSE: rose_proto_init - rose_ndevs parameter to large\n"); |
return -1; |
} |
|
if ((dev_rose = kmalloc(rose_ndevs * sizeof(struct net_device), GFP_KERNEL)) == NULL) { |
printk(KERN_ERR "ROSE: rose_proto_init - unable to allocate device structure\n"); |
return -1; |
} |
|
memset(dev_rose, 0x00, rose_ndevs * sizeof(struct net_device)); |
|
for (i = 0; i < rose_ndevs; i++) { |
sprintf(dev_rose[i].name, "rose%d", i); |
dev_rose[i].init = rose_init; |
register_netdev(&dev_rose[i]); |
} |
|
sock_register(&rose_family_ops); |
register_netdevice_notifier(&rose_dev_notifier); |
printk(banner); |
|
ax25_protocol_register(AX25_P_ROSE, rose_route_frame); |
ax25_linkfail_register(rose_link_failed); |
|
#ifdef CONFIG_SYSCTL |
rose_register_sysctl(); |
#endif |
rose_loopback_init(); |
|
rose_add_loopback_neigh(); |
|
proc_net_create("rose", 0, rose_get_info); |
proc_net_create("rose_neigh", 0, rose_neigh_get_info); |
proc_net_create("rose_nodes", 0, rose_nodes_get_info); |
proc_net_create("rose_routes", 0, rose_routes_get_info); |
return 0; |
} |
module_init(rose_proto_init); |
|
EXPORT_NO_SYMBOLS; |
|
MODULE_PARM(rose_ndevs, "i"); |
MODULE_PARM_DESC(rose_ndevs, "number of ROSE devices"); |
|
MODULE_AUTHOR("Jonathan Naylor G4KLX <g4klx@g4klx.demon.co.uk>"); |
MODULE_DESCRIPTION("The amateur radio ROSE network layer protocol"); |
MODULE_LICENSE("GPL"); |
|
static void __exit rose_exit(void) |
{ |
int i; |
|
proc_net_remove("rose"); |
proc_net_remove("rose_neigh"); |
proc_net_remove("rose_nodes"); |
proc_net_remove("rose_routes"); |
rose_loopback_clear(); |
|
rose_rt_free(); |
|
ax25_protocol_release(AX25_P_ROSE); |
ax25_linkfail_release(rose_link_failed); |
|
if (ax25cmp(&rose_callsign, &null_ax25_address) != 0) |
ax25_listen_release(&rose_callsign, NULL); |
|
#ifdef CONFIG_SYSCTL |
rose_unregister_sysctl(); |
#endif |
unregister_netdevice_notifier(&rose_dev_notifier); |
|
sock_unregister(PF_ROSE); |
|
for (i = 0; i < rose_ndevs; i++) { |
if (dev_rose[i].priv != NULL) { |
kfree(dev_rose[i].priv); |
dev_rose[i].priv = NULL; |
unregister_netdev(&dev_rose[i]); |
} |
} |
|
kfree(dev_rose); |
} |
module_exit(rose_exit); |
|
/rose_out.c
0,0 → 1,130
/* |
* ROSE release 003 |
* |
* This code REQUIRES 2.1.15 or higher/ NET3.038 |
* |
* This module: |
* This module is free software; you can redistribute it and/or |
* modify it under the terms of the GNU General Public License |
* as published by the Free Software Foundation; either version |
* 2 of the License, or (at your option) any later version. |
* |
* History |
* ROSE 001 Jonathan(G4KLX) Cloned from nr_out.c |
* ROSE 003 Jonathan(G4KLX) New timer architecture. |
* Removed M bit processing. |
*/ |
|
#include <linux/errno.h> |
#include <linux/types.h> |
#include <linux/socket.h> |
#include <linux/in.h> |
#include <linux/kernel.h> |
#include <linux/sched.h> |
#include <linux/timer.h> |
#include <linux/string.h> |
#include <linux/sockios.h> |
#include <linux/net.h> |
#include <net/ax25.h> |
#include <linux/inet.h> |
#include <linux/netdevice.h> |
#include <linux/skbuff.h> |
#include <net/sock.h> |
#include <asm/segment.h> |
#include <asm/system.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) |
{ |
if (skb == NULL) |
return; |
|
skb->data[2] |= (sk->protinfo.rose->vr << 5) & 0xE0; |
skb->data[2] |= (sk->protinfo.rose->vs << 1) & 0x0E; |
|
rose_start_idletimer(sk); |
|
rose_transmit_link(skb, sk->protinfo.rose->neighbour); |
} |
|
void rose_kick(struct sock *sk) |
{ |
struct sk_buff *skb, *skbn; |
unsigned short start, end; |
|
if (sk->protinfo.rose->state != ROSE_STATE_3) |
return; |
|
if (sk->protinfo.rose->condition & ROSE_COND_PEER_RX_BUSY) |
return; |
|
if (skb_peek(&sk->write_queue) == NULL) |
return; |
|
start = (skb_peek(&sk->protinfo.rose->ack_queue) == NULL) ? sk->protinfo.rose->va : sk->protinfo.rose->vs; |
end = (sk->protinfo.rose->va + sysctl_rose_window_size) % ROSE_MODULUS; |
|
if (start == end) |
return; |
|
sk->protinfo.rose->vs = start; |
|
/* |
* Transmit data until either we're out of data to send or |
* the window is full. |
*/ |
|
skb = skb_dequeue(&sk->write_queue); |
|
do { |
if ((skbn = skb_clone(skb, GFP_ATOMIC)) == NULL) { |
skb_queue_head(&sk->write_queue, skb); |
break; |
} |
|
skb_set_owner_w(skbn, sk); |
|
/* |
* Transmit the frame copy. |
*/ |
rose_send_iframe(sk, skbn); |
|
sk->protinfo.rose->vs = (sk->protinfo.rose->vs + 1) % ROSE_MODULUS; |
|
/* |
* Requeue the original data frame. |
*/ |
skb_queue_tail(&sk->protinfo.rose->ack_queue, skb); |
|
} while (sk->protinfo.rose->vs != end && (skb = skb_dequeue(&sk->write_queue)) != NULL); |
|
sk->protinfo.rose->vl = sk->protinfo.rose->vr; |
sk->protinfo.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) |
{ |
if (sk->protinfo.rose->condition & ROSE_COND_OWN_RX_BUSY) |
rose_write_internal(sk, ROSE_RNR); |
else |
rose_write_internal(sk, ROSE_RR); |
|
sk->protinfo.rose->vl = sk->protinfo.rose->vr; |
sk->protinfo.rose->condition &= ~ROSE_COND_ACK_PENDING; |
|
rose_stop_timer(sk); |
} |
/sysctl_net_rose.c
0,0 → 1,78
/* -*- linux-c -*- |
* sysctl_net_rose.c: sysctl interface to net ROSE subsystem. |
* |
* Begun April 1, 1996, Mike Shaver. |
* Added /proc/sys/net/rose directory entry (empty =) ). [MS] |
*/ |
|
#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[] = {0}, 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 ctl_table rose_table[] = { |
{NET_ROSE_RESTART_REQUEST_TIMEOUT, "restart_request_timeout", |
&sysctl_rose_restart_request_timeout, sizeof(int), 0644, NULL, |
&proc_dointvec_minmax, &sysctl_intvec, NULL, &min_timer, &max_timer}, |
{NET_ROSE_CALL_REQUEST_TIMEOUT, "call_request_timeout", |
&sysctl_rose_call_request_timeout, sizeof(int), 0644, NULL, |
&proc_dointvec_minmax, &sysctl_intvec, NULL, &min_timer, &max_timer}, |
{NET_ROSE_RESET_REQUEST_TIMEOUT, "reset_request_timeout", |
&sysctl_rose_reset_request_timeout, sizeof(int), 0644, NULL, |
&proc_dointvec_minmax, &sysctl_intvec, NULL, &min_timer, &max_timer}, |
{NET_ROSE_CLEAR_REQUEST_TIMEOUT, "clear_request_timeout", |
&sysctl_rose_clear_request_timeout, sizeof(int), 0644, NULL, |
&proc_dointvec_minmax, &sysctl_intvec, NULL, &min_timer, &max_timer}, |
{NET_ROSE_NO_ACTIVITY_TIMEOUT, "no_activity_timeout", |
&sysctl_rose_no_activity_timeout, sizeof(int), 0644, NULL, |
&proc_dointvec_minmax, &sysctl_intvec, NULL, &min_idle, &max_idle}, |
{NET_ROSE_ACK_HOLD_BACK_TIMEOUT, "acknowledge_hold_back_timeout", |
&sysctl_rose_ack_hold_back_timeout, sizeof(int), 0644, NULL, |
&proc_dointvec_minmax, &sysctl_intvec, NULL, &min_timer, &max_timer}, |
{NET_ROSE_ROUTING_CONTROL, "routing_control", |
&sysctl_rose_routing_control, sizeof(int), 0644, NULL, |
&proc_dointvec_minmax, &sysctl_intvec, NULL, &min_route, &max_route}, |
{NET_ROSE_LINK_FAIL_TIMEOUT, "link_fail_timeout", |
&sysctl_rose_link_fail_timeout, sizeof(int), 0644, NULL, |
&proc_dointvec_minmax, &sysctl_intvec, NULL, &min_ftimer, &max_ftimer}, |
{NET_ROSE_MAX_VCS, "maximum_virtual_circuits", |
&sysctl_rose_maximum_vcs, sizeof(int), 0644, NULL, |
&proc_dointvec_minmax, &sysctl_intvec, NULL, &min_maxvcs, &max_maxvcs}, |
{NET_ROSE_WINDOW_SIZE, "window_size", |
&sysctl_rose_window_size, sizeof(int), 0644, NULL, |
&proc_dointvec_minmax, &sysctl_intvec, NULL, &min_window, &max_window}, |
{0} |
}; |
|
static ctl_table rose_dir_table[] = { |
{NET_ROSE, "rose", NULL, 0, 0555, rose_table}, |
{0} |
}; |
|
static ctl_table rose_root_table[] = { |
{CTL_NET, "net", NULL, 0, 0555, rose_dir_table}, |
{0} |
}; |
|
void __init rose_register_sysctl(void) |
{ |
rose_table_header = register_sysctl_table(rose_root_table, 1); |
} |
|
void rose_unregister_sysctl(void) |
{ |
unregister_sysctl_table(rose_table_header); |
} |
/rose_loopback.c
0,0 → 1,119
/* |
* ROSE release 003 |
* |
* This code REQUIRES 2.1.15 or higher/ NET3.038 |
* |
* This module: |
* This module is free software; you can redistribute it and/or |
* modify it under the terms of the GNU General Public License |
* as published by the Free Software Foundation; either version |
* 2 of the License, or (at your option) any later version. |
* |
* History |
* ROSE 003 Jonathan(G4KLX) Created this file from nr_loopback.c. |
* |
*/ |
|
#include <linux/types.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) { |
lci_i = ((skb->data[0] << 8) & 0xF00) + ((skb->data[1] << 0) & 0x0FF); |
frametype = skb->data[2]; |
dest = (rose_address *)(skb->data + 4); |
lci_o = 0xFFF - lci_i; |
|
skb->h.raw = skb->data; |
|
if ((sk = rose_find_socket(lci_o, rose_loopback_neigh)) != NULL) { |
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); |
} |
} |
/rose_subr.c
0,0 → 1,544
/* |
* ROSE release 003 |
* |
* This code REQUIRES 2.1.15 or higher/ NET3.038 |
* |
* This module: |
* This module is free software; you can redistribute it and/or |
* modify it under the terms of the GNU General Public License |
* as published by the Free Software Foundation; either version |
* 2 of the License, or (at your option) any later version. |
* |
* History |
* ROSE 001 Jonathan(G4KLX) Cloned from nr_subr.c |
* ROSE 002 Jonathan(G4KLX) Centralised disconnect processing. |
* ROSE 003 Jonathan(G4KLX) Added use count to neighbours. |
*/ |
|
#include <linux/errno.h> |
#include <linux/types.h> |
#include <linux/socket.h> |
#include <linux/in.h> |
#include <linux/kernel.h> |
#include <linux/sched.h> |
#include <linux/timer.h> |
#include <linux/string.h> |
#include <linux/sockios.h> |
#include <linux/net.h> |
#include <net/ax25.h> |
#include <linux/inet.h> |
#include <linux/netdevice.h> |
#include <linux/skbuff.h> |
#include <net/sock.h> |
#include <asm/segment.h> |
#include <asm/system.h> |
#include <linux/fcntl.h> |
#include <linux/mm.h> |
#include <linux/interrupt.h> |
#include <net/rose.h> |
|
/* |
* This routine purges all of the queues of frames. |
*/ |
void rose_clear_queues(struct sock *sk) |
{ |
skb_queue_purge(&sk->write_queue); |
skb_queue_purge(&sk->protinfo.rose->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; |
|
/* |
* Remove all the ack-ed frames from the ack queue. |
*/ |
if (sk->protinfo.rose->va != nr) { |
while (skb_peek(&sk->protinfo.rose->ack_queue) != NULL && sk->protinfo.rose->va != nr) { |
skb = skb_dequeue(&sk->protinfo.rose->ack_queue); |
kfree_skb(skb); |
sk->protinfo.rose->va = (sk->protinfo.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(&sk->protinfo.rose->ack_queue)) != NULL) { |
if (skb_prev == NULL) |
skb_queue_head(&sk->write_queue, skb); |
else |
skb_append(skb_prev, skb); |
skb_prev = skb; |
} |
} |
|
/* |
* Validate that the value of nr is between va and vs. Return true or |
* false for testing. |
*/ |
int rose_validate_nr(struct sock *sk, unsigned short nr) |
{ |
unsigned short vc = sk->protinfo.rose->va; |
|
while (vc != sk->protinfo.rose->vs) { |
if (nr == vc) return 1; |
vc = (vc + 1) % ROSE_MODULUS; |
} |
|
if (nr == sk->protinfo.rose->vs) return 1; |
|
return 0; |
} |
|
/* |
* This routine is called when the packet layer internally generates a |
* control frame. |
*/ |
void rose_write_internal(struct sock *sk, int frametype) |
{ |
struct sk_buff *skb; |
unsigned char *dptr; |
unsigned char lci1, lci2; |
char buffer[100]; |
int len, faclen = 0; |
int ax25_header_len = AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN + 1; |
|
len = ax25_header_len + ROSE_MIN_LEN; |
|
switch (frametype) { |
case ROSE_CALL_REQUEST: |
len += 1 + ROSE_ADDR_LEN + ROSE_ADDR_LEN; |
faclen = rose_create_facilities(buffer, sk->protinfo.rose); |
len += faclen; |
break; |
case ROSE_CALL_ACCEPTED: |
case ROSE_RESET_REQUEST: |
len += 2; |
break; |
case ROSE_CLEAR_REQUEST: |
len += 3; |
/* facilities */ |
faclen = 3 + 2 + AX25_ADDR_LEN + 3 + ROSE_ADDR_LEN; |
dptr = buffer; |
*dptr++ = faclen-1; /* Facilities length */ |
*dptr++ = 0; |
*dptr++ = FAC_NATIONAL; |
*dptr++ = FAC_NATIONAL_FAIL_CALL; |
*dptr++ = AX25_ADDR_LEN; |
memcpy(dptr, &rose_callsign, AX25_ADDR_LEN); |
dptr += AX25_ADDR_LEN; |
*dptr++ = FAC_NATIONAL_FAIL_ADD; |
*dptr++ = ROSE_ADDR_LEN + 1; |
*dptr++ = ROSE_ADDR_LEN * 2; |
memcpy(dptr, &sk->protinfo.rose->source_addr, ROSE_ADDR_LEN); |
len += faclen; |
break; |
} |
|
if ((skb = alloc_skb(len, GFP_ATOMIC)) == NULL) |
return; |
|
/* |
* Space for AX.25 header and PID. |
*/ |
skb_reserve(skb, ax25_header_len); |
|
dptr = skb_put(skb, len - ax25_header_len); |
|
lci1 = (sk->protinfo.rose->lci >> 8) & 0x0F; |
lci2 = (sk->protinfo.rose->lci >> 0) & 0xFF; |
|
switch (frametype) { |
|
case ROSE_CALL_REQUEST: |
*dptr++ = ROSE_GFI | lci1; |
*dptr++ = lci2; |
*dptr++ = frametype; |
*dptr++ = 0xAA; |
memcpy(dptr, &sk->protinfo.rose->dest_addr, ROSE_ADDR_LEN); |
dptr += ROSE_ADDR_LEN; |
memcpy(dptr, &sk->protinfo.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++ = sk->protinfo.rose->cause; |
*dptr++ = sk->protinfo.rose->diagnostic; |
*dptr++ = 0x00; /* Address length */ |
memcpy(dptr, buffer, faclen); |
dptr += faclen; |
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++ |= (sk->protinfo.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, sk->protinfo.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: |
p += 2; |
n += 2; |
len -= 2; |
break; |
|
case 0x40: |
if (*p == FAC_NATIONAL_RAND) |
facilities->rand = ((p[1] << 8) & 0xFF00) + ((p[2] << 0) & 0x00FF); |
p += 3; |
n += 3; |
len -= 3; |
break; |
|
case 0x80: |
p += 4; |
n += 4; |
len -= 4; |
break; |
|
case 0xC0: |
l = p[1]; |
if (*p == FAC_NATIONAL_DEST_DIGI) { |
if (!fac_national_digis_received) { |
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) { |
memcpy(&facilities->dest_digis[0], p + 2, AX25_ADDR_LEN); |
facilities->dest_ndigis = 1; |
} |
} |
else if (*p == FAC_NATIONAL_FAIL_CALL) { |
memcpy(&facilities->fail_call, p + 2, AX25_ADDR_LEN); |
} |
else if (*p == FAC_NATIONAL_FAIL_ADD) { |
memcpy(&facilities->fail_addr, p + 3, ROSE_ADDR_LEN); |
} |
else if (*p == FAC_NATIONAL_DIGIS) { |
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) |
memcpy(&facilities->dest_digis[facilities->dest_ndigis++], pt, AX25_ADDR_LEN); |
else |
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: |
p += 2; |
n += 2; |
len -= 2; |
break; |
|
case 0x40: |
p += 3; |
n += 3; |
len -= 3; |
break; |
|
case 0x80: |
p += 4; |
n += 4; |
len -= 4; |
break; |
|
case 0xC0: |
l = p[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'; |
facilities->source_call = *asc2ax(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'; |
facilities->dest_call = *asc2ax(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, struct rose_facilities_struct *facilities) |
{ |
int facilities_len, len; |
|
facilities_len = *p++; |
|
if (facilities_len == 0) |
return 0; |
|
while (facilities_len > 0) { |
if (*p == 0x00) { |
facilities_len--; |
p++; |
|
switch (*p) { |
case FAC_NATIONAL: /* National */ |
len = rose_parse_national(p + 1, facilities, facilities_len - 1); |
facilities_len -= len + 1; |
p += len + 1; |
break; |
|
case FAC_CCITT: /* CCITT */ |
len = rose_parse_ccitt(p + 1, facilities, facilities_len - 1); |
facilities_len -= len + 1; |
p += len + 1; |
break; |
|
default: |
printk(KERN_DEBUG "ROSE: rose_parse_facilities - unknown facilities family %02X\n", *p); |
facilities_len--; |
p++; |
break; |
} |
} |
else break; /* Error in facilities format */ |
} |
|
return 1; |
} |
|
int rose_create_facilities(unsigned char *buffer, rose_cb *rose) |
{ |
unsigned char *p = buffer + 1; |
char *callsign; |
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(&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(&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) |
{ |
rose_stop_timer(sk); |
rose_stop_idletimer(sk); |
|
rose_clear_queues(sk); |
|
sk->protinfo.rose->lci = 0; |
sk->protinfo.rose->state = ROSE_STATE_0; |
|
if (cause != -1) |
sk->protinfo.rose->cause = cause; |
|
if (diagnostic != -1) |
sk->protinfo.rose->diagnostic = diagnostic; |
|
sk->state = TCP_CLOSE; |
sk->err = reason; |
sk->shutdown |= SEND_SHUTDOWN; |
|
if (!sk->dead) |
sk->state_change(sk); |
|
sk->dead = 1; |
} |
/rose_link.c
0,0 → 1,343
/* |
* ROSE release 003 |
* |
* This code REQUIRES 2.1.15 or higher/ NET3.038 |
* |
* This module: |
* This module is free software; you can redistribute it and/or |
* modify it under the terms of the GNU General Public License |
* as published by the Free Software Foundation; either version |
* 2 of the License, or (at your option) any later version. |
* |
* History |
* ROSE 001 Jonathan(G4KLX) Cloned from rose_timer.c |
* ROSE 003 Jonathan(G4KLX) New timer architecture. |
*/ |
|
#include <linux/errno.h> |
#include <linux/types.h> |
#include <linux/socket.h> |
#include <linux/in.h> |
#include <linux/kernel.h> |
#include <linux/sched.h> |
#include <linux/timer.h> |
#include <linux/string.h> |
#include <linux/sockios.h> |
#include <linux/net.h> |
#include <net/ax25.h> |
#include <linux/inet.h> |
#include <linux/netdevice.h> |
#include <linux/skbuff.h> |
#include <net/sock.h> |
#include <asm/segment.h> |
#include <asm/system.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); |
|
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 + sysctl_rose_link_fail_timeout; |
|
add_timer(&neigh->ftimer); |
} |
|
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 + 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); |
} |
|
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; |
|
if (ax25cmp(&rose_callsign, &null_ax25_address) == 0) |
rose_call = (ax25_address *)neigh->dev->dev_addr; |
else |
rose_call = &rose_callsign; |
|
neigh->ax25 = ax25_send_frame(skb, 260, rose_call, &neigh->callsign, neigh->digipeat, neigh->dev); |
|
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; |
|
if (ax25cmp(&rose_callsign, &null_ax25_address) == 0) |
rose_call = (ax25_address *)neigh->dev->dev_addr; |
else |
rose_call = &rose_callsign; |
|
neigh->ax25 = ax25_find_cb(rose_call, &neigh->callsign, neigh->digipeat, neigh->dev); |
|
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: |
printk(KERN_WARNING "ROSE: received diagnostic #%d - %02X %02X %02X\n", skb->data[3], skb->data[4], skb->data[5], skb->data[6]); |
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 |
*/ |
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 |
*/ |
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 Diagnostic is required. |
*/ |
void rose_transmit_diagnostic(struct rose_neigh *neigh, unsigned char diag) |
{ |
struct sk_buff *skb; |
unsigned char *dptr; |
int len; |
|
len = AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN + ROSE_MIN_LEN + 2; |
|
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 + 2); |
|
*dptr++ = AX25_P_ROSE; |
*dptr++ = ROSE_GFI; |
*dptr++ = 0x00; |
*dptr++ = ROSE_DIAGNOSTIC; |
*dptr++ = diag; |
|
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; |
struct net_device *first; |
int faclen = 0; |
|
len = AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN + ROSE_MIN_LEN + 3; |
|
first = rose_dev_first(); |
if (first) |
faclen = 6 + AX25_ADDR_LEN + 3 + ROSE_ADDR_LEN; |
|
if ((skb = alloc_skb(len + faclen, GFP_ATOMIC)) == NULL) |
return; |
|
skb_reserve(skb, AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN); |
|
dptr = skb_put(skb, ROSE_MIN_LEN + 3 + faclen); |
|
*dptr++ = AX25_P_ROSE; |
*dptr++ = ((lci >> 8) & 0x0F) | ROSE_GFI; |
*dptr++ = ((lci >> 0) & 0xFF); |
*dptr++ = ROSE_CLEAR_REQUEST; |
*dptr++ = cause; |
*dptr++ = diagnostic; |
|
if (first) { |
*dptr++ = 0x00; /* Address length */ |
*dptr++ = 4 + AX25_ADDR_LEN + 3 + ROSE_ADDR_LEN; /* Facilities length */ |
*dptr++ = 0; |
*dptr++ = FAC_NATIONAL; |
*dptr++ = FAC_NATIONAL_FAIL_CALL; |
*dptr++ = AX25_ADDR_LEN; |
memcpy(dptr, &rose_callsign, AX25_ADDR_LEN); |
dptr += AX25_ADDR_LEN; |
*dptr++ = FAC_NATIONAL_FAIL_ADD; |
*dptr++ = ROSE_ADDR_LEN + 1; |
*dptr++ = ROSE_ADDR_LEN * 2; |
memcpy(dptr, first->dev_addr, ROSE_ADDR_LEN); |
} |
|
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 0 |
if (call_fw_firewall(PF_ROSE, skb->dev, skb->data, NULL, &skb) != FW_ACCEPT) { |
kfree_skb(skb); |
return; |
} |
#endif |
|
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); |
} |
} |
} |
/rose_dev.c
0,0 → 1,204
/* |
* ROSE release 003 |
* |
* This code REQUIRES 2.1.15 or higher/ NET3.038 |
* |
* This module: |
* This module is free software; you can redistribute it and/or |
* modify it under the terms of the GNU General Public License |
* as published by the Free Software Foundation; either version |
* 2 of the License, or (at your option) any later version. |
* |
* History |
* ROSE 001 Jonathan(G4KLX) Cloned from nr_dev.c. |
* Hans(PE1AYX) Fixed interface to IP layer. |
*/ |
|
#include <linux/config.h> |
#include <linux/module.h> |
#include <linux/proc_fs.h> |
#include <linux/kernel.h> |
#include <linux/sched.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> /* For the statistics structure. */ |
|
#include <asm/system.h> |
#include <asm/segment.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> |
|
/* |
* Only allow IP over ROSE frames through if the netrom device is up. |
*/ |
|
int rose_rx_ip(struct sk_buff *skb, struct net_device *dev) |
{ |
struct net_device_stats *stats = (struct net_device_stats *)dev->priv; |
|
#ifdef CONFIG_INET |
if (!netif_running(dev)) { |
stats->rx_errors++; |
return 0; |
} |
|
stats->rx_packets++; |
stats->rx_bytes += skb->len; |
|
skb->protocol = htons(ETH_P_IP); |
|
/* Spoof incoming device */ |
skb->dev = dev; |
skb->h.raw = skb->data; |
skb->nh.raw = skb->data; |
skb->pkt_type = PACKET_HOST; |
|
ip_rcv(skb, skb->dev, NULL); |
#else |
kfree_skb(skb); |
#endif |
return 1; |
} |
|
static int rose_header(struct sk_buff *skb, struct net_device *dev, unsigned short type, |
void *daddr, void *saddr, unsigned 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) |
{ |
struct net_device *dev = skb->dev; |
struct net_device_stats *stats = (struct net_device_stats *)dev->priv; |
unsigned char *bp = (unsigned char *)skb->data; |
struct sk_buff *skbn; |
|
#ifdef CONFIG_INET |
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); |
|
if (!rose_route_frame(skbn, NULL)) { |
kfree_skb(skbn); |
stats->tx_errors++; |
return 1; |
} |
|
stats->tx_packets++; |
stats->tx_bytes += skbn->len; |
#endif |
return 1; |
} |
|
static int rose_set_mac_address(struct net_device *dev, void *addr) |
{ |
struct sockaddr *sa = addr; |
|
rose_del_loopback_node((rose_address *)dev->dev_addr); |
|
memcpy(dev->dev_addr, sa->sa_data, dev->addr_len); |
|
rose_add_loopback_node((rose_address *)dev->dev_addr); |
|
return 0; |
} |
|
static int rose_open(struct net_device *dev) |
{ |
MOD_INC_USE_COUNT; |
netif_start_queue(dev); |
rose_add_loopback_node((rose_address *)dev->dev_addr); |
return 0; |
} |
|
static int rose_close(struct net_device *dev) |
{ |
netif_stop_queue(dev); |
rose_del_loopback_node((rose_address *)dev->dev_addr); |
MOD_DEC_USE_COUNT; |
return 0; |
} |
|
static int rose_xmit(struct sk_buff *skb, struct net_device *dev) |
{ |
struct net_device_stats *stats = (struct net_device_stats *)dev->priv; |
|
if (!netif_running(dev)) { |
printk(KERN_ERR "ROSE: rose_xmit - called when iface is down\n"); |
return 1; |
} |
dev_kfree_skb(skb); |
stats->tx_errors++; |
return 0; |
} |
|
static struct net_device_stats *rose_get_stats(struct net_device *dev) |
{ |
return (struct net_device_stats *)dev->priv; |
} |
|
int rose_init(struct net_device *dev) |
{ |
dev->mtu = ROSE_MAX_PACKET_SIZE - 2; |
dev->hard_start_xmit = rose_xmit; |
dev->open = rose_open; |
dev->stop = rose_close; |
|
dev->hard_header = rose_header; |
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; |
dev->rebuild_header = rose_rebuild_header; |
dev->set_mac_address = rose_set_mac_address; |
|
/* New-style flags. */ |
dev->flags = 0; |
|
if ((dev->priv = kmalloc(sizeof(struct net_device_stats), GFP_KERNEL)) == NULL) |
return -ENOMEM; |
|
memset(dev->priv, 0, sizeof(struct net_device_stats)); |
|
dev->get_stats = rose_get_stats; |
|
return 0; |
}; |
/rose_route.c
0,0 → 1,1153
/* |
* ROSE release 003 |
* |
* This code REQUIRES 2.1.15 or higher/ NET3.038 |
* |
* This module: |
* This module is free software; you can redistribute it and/or |
* modify it under the terms of the GNU General Public License |
* as published by the Free Software Foundation; either version |
* 2 of the License, or (at your option) any later version. |
* |
* History |
* ROSE 001 Jonathan(G4KLX) Cloned from nr_route.c. |
* Terry(VK2KTJ) Added support for variable length |
* address masks. |
* ROSE 002 Jonathan(G4KLX) Uprated through routing of packets. |
* Routing loop detection. |
* ROSE 003 Jonathan(G4KLX) New timer architecture. |
* Added use count to neighbours. |
*/ |
|
#include <linux/errno.h> |
#include <linux/types.h> |
#include <linux/socket.h> |
#include <linux/in.h> |
#include <linux/kernel.h> |
#include <linux/sched.h> |
#include <linux/timer.h> |
#include <linux/string.h> |
#include <linux/sockios.h> |
#include <linux/net.h> |
#include <net/ax25.h> |
#include <linux/inet.h> |
#include <linux/netdevice.h> |
#include <net/arp.h> |
#include <linux/if_arp.h> |
#include <linux/skbuff.h> |
#include <net/sock.h> |
#include <asm/segment.h> |
#include <asm/system.h> |
#include <asm/uaccess.h> |
#include <linux/fcntl.h> |
#include <linux/termios.h> /* For TIOCINQ/OUTQ */ |
#include <linux/mm.h> |
#include <linux/interrupt.h> |
#include <linux/notifier.h> |
#include <linux/netfilter.h> |
#include <linux/init.h> |
#include <net/rose.h> |
|
static unsigned int rose_neigh_no = 1; |
|
static struct rose_node *rose_node_list; |
static struct rose_neigh *rose_neigh_list; |
static struct rose_route *rose_route_list; |
|
struct rose_neigh *rose_loopback_neigh; |
|
static void rose_remove_neigh(struct rose_neigh *); |
|
/* |
* Add a new route to a node, and in the process add the node and the |
* neighbour if it is new. |
*/ |
static int rose_add_node(struct rose_route_struct *rose_route, struct net_device *dev) |
{ |
struct rose_node *rose_node, *rose_tmpn, *rose_tmpp; |
struct rose_neigh *rose_neigh; |
unsigned long flags; |
int i; |
|
for (rose_node = rose_node_list; rose_node != NULL; rose_node = rose_node->next) |
if ((rose_node->mask == rose_route->mask) && (rosecmpm(&rose_route->address, &rose_node->address, rose_route->mask) == 0)) |
break; |
|
if (rose_node != NULL && rose_node->loopback) |
return -EINVAL; |
|
for (rose_neigh = rose_neigh_list; rose_neigh != NULL; rose_neigh = rose_neigh->next) |
if (ax25cmp(&rose_route->neighbour, &rose_neigh->callsign) == 0 && rose_neigh->dev == dev) |
break; |
|
if (rose_neigh == NULL) { |
if ((rose_neigh = kmalloc(sizeof(*rose_neigh), GFP_ATOMIC)) == NULL) |
return -ENOMEM; |
|
rose_neigh->callsign = rose_route->neighbour; |
rose_neigh->digipeat = NULL; |
rose_neigh->ax25 = NULL; |
rose_neigh->dev = dev; |
rose_neigh->count = 0; |
rose_neigh->use = 0; |
rose_neigh->dce_mode = 0; |
rose_neigh->loopback = 0; |
rose_neigh->number = rose_neigh_no++; |
rose_neigh->restarted = 0; |
|
skb_queue_head_init(&rose_neigh->queue); |
|
init_timer(&rose_neigh->ftimer); |
init_timer(&rose_neigh->t0timer); |
|
if (rose_route->ndigis != 0) { |
if ((rose_neigh->digipeat = kmalloc(sizeof(ax25_digi), GFP_KERNEL)) == NULL) { |
kfree(rose_neigh); |
return -ENOMEM; |
} |
|
rose_neigh->digipeat->ndigi = rose_route->ndigis; |
rose_neigh->digipeat->lastrepeat = -1; |
|
for (i = 0; i < rose_route->ndigis; i++) { |
rose_neigh->digipeat->calls[i] = rose_route->digipeaters[i]; |
rose_neigh->digipeat->repeated[i] = 0; |
} |
} |
|
save_flags(flags); cli(); |
rose_neigh->next = rose_neigh_list; |
rose_neigh_list = rose_neigh; |
restore_flags(flags); |
} |
|
/* |
* This is a new node to be inserted into the list. Find where it needs |
* to be inserted into the list, and insert it. We want to be sure |
* to order the list in descending order of mask size to ensure that |
* later when we are searching this list the first match will be the |
* best match. |
*/ |
if (rose_node == NULL) { |
rose_tmpn = rose_node_list; |
rose_tmpp = NULL; |
|
while (rose_tmpn != NULL) { |
if (rose_tmpn->mask > rose_route->mask) { |
rose_tmpp = rose_tmpn; |
rose_tmpn = rose_tmpn->next; |
} else { |
break; |
} |
} |
|
/* create new node */ |
if ((rose_node = kmalloc(sizeof(*rose_node), GFP_ATOMIC)) == NULL) |
return -ENOMEM; |
|
rose_node->address = rose_route->address; |
rose_node->mask = rose_route->mask; |
rose_node->count = 1; |
rose_node->loopback = 0; |
rose_node->neighbour[0] = rose_neigh; |
|
save_flags(flags); cli(); |
|
if (rose_tmpn == NULL) { |
if (rose_tmpp == NULL) { /* Empty list */ |
rose_node_list = rose_node; |
rose_node->next = NULL; |
} else { |
rose_tmpp->next = rose_node; |
rose_node->next = NULL; |
} |
} else { |
if (rose_tmpp == NULL) { /* 1st node */ |
rose_node->next = rose_node_list; |
rose_node_list = rose_node; |
} else { |
rose_tmpp->next = rose_node; |
rose_node->next = rose_tmpn; |
} |
} |
|
restore_flags(flags); |
|
rose_neigh->count++; |
|
return 0; |
} |
|
/* We have space, slot it in */ |
if (rose_node->count < 3) { |
rose_node->neighbour[rose_node->count] = rose_neigh; |
rose_node->count++; |
rose_neigh->count++; |
} |
|
return 0; |
} |
|
static void rose_remove_node(struct rose_node *rose_node) |
{ |
struct rose_node *s; |
unsigned long flags; |
|
save_flags(flags); |
cli(); |
|
if ((s = rose_node_list) == rose_node) { |
rose_node_list = rose_node->next; |
restore_flags(flags); |
kfree(rose_node); |
return; |
} |
|
while (s != NULL && s->next != NULL) { |
if (s->next == rose_node) { |
s->next = rose_node->next; |
restore_flags(flags); |
kfree(rose_node); |
return; |
} |
|
s = s->next; |
} |
|
restore_flags(flags); |
} |
|
static void rose_remove_neigh(struct rose_neigh *rose_neigh) |
{ |
struct rose_neigh *s; |
unsigned long flags; |
|
rose_stop_ftimer(rose_neigh); |
rose_stop_t0timer(rose_neigh); |
|
skb_queue_purge(&rose_neigh->queue); |
|
save_flags(flags); cli(); |
|
if ((s = rose_neigh_list) == rose_neigh) { |
rose_neigh_list = rose_neigh->next; |
restore_flags(flags); |
if (rose_neigh->digipeat != NULL) |
kfree(rose_neigh->digipeat); |
kfree(rose_neigh); |
return; |
} |
|
while (s != NULL && s->next != NULL) { |
if (s->next == rose_neigh) { |
s->next = rose_neigh->next; |
restore_flags(flags); |
if (rose_neigh->digipeat != NULL) |
kfree(rose_neigh->digipeat); |
kfree(rose_neigh); |
return; |
} |
|
s = s->next; |
} |
|
restore_flags(flags); |
} |
|
static void rose_remove_route(struct rose_route *rose_route) |
{ |
struct rose_route *s; |
unsigned long flags; |
|
if (rose_route->neigh1 != NULL) |
rose_route->neigh1->use--; |
|
if (rose_route->neigh2 != NULL) |
rose_route->neigh2->use--; |
|
save_flags(flags); cli(); |
|
if ((s = rose_route_list) == rose_route) { |
rose_route_list = rose_route->next; |
restore_flags(flags); |
kfree(rose_route); |
return; |
} |
|
while (s != NULL && s->next != NULL) { |
if (s->next == rose_route) { |
s->next = rose_route->next; |
restore_flags(flags); |
kfree(rose_route); |
return; |
} |
|
s = s->next; |
} |
|
restore_flags(flags); |
} |
|
/* |
* "Delete" a node. Strictly speaking remove a route to a node. The node |
* is only deleted if no routes are left to it. |
*/ |
static int rose_del_node(struct rose_route_struct *rose_route, struct net_device *dev) |
{ |
struct rose_node *rose_node; |
struct rose_neigh *rose_neigh; |
int i; |
|
for (rose_node = rose_node_list; rose_node != NULL; rose_node = rose_node->next) |
if ((rose_node->mask == rose_route->mask) && (rosecmpm(&rose_route->address, &rose_node->address, rose_route->mask) == 0)) |
break; |
|
if (rose_node == NULL) return -EINVAL; |
|
if (rose_node->loopback) return -EINVAL; |
|
for (rose_neigh = rose_neigh_list; rose_neigh != NULL; rose_neigh = rose_neigh->next) |
if (ax25cmp(&rose_route->neighbour, &rose_neigh->callsign) == 0 && rose_neigh->dev == dev) |
break; |
|
if (rose_neigh == NULL) return -EINVAL; |
|
for (i = 0; i < rose_node->count; i++) { |
if (rose_node->neighbour[i] == rose_neigh) { |
rose_neigh->count--; |
|
if (rose_neigh->count == 0 && rose_neigh->use == 0) |
rose_remove_neigh(rose_neigh); |
|
rose_node->count--; |
|
if (rose_node->count == 0) { |
rose_remove_node(rose_node); |
} else { |
switch (i) { |
case 0: |
rose_node->neighbour[0] = rose_node->neighbour[1]; |
case 1: |
rose_node->neighbour[1] = rose_node->neighbour[2]; |
case 2: |
break; |
} |
} |
|
return 0; |
} |
} |
|
return -EINVAL; |
} |
|
/* |
* Add the loopback neighbour. |
*/ |
int rose_add_loopback_neigh(void) |
{ |
unsigned long flags; |
|
if ((rose_loopback_neigh = kmalloc(sizeof(struct rose_neigh), GFP_ATOMIC)) == NULL) |
return -ENOMEM; |
|
rose_loopback_neigh->callsign = null_ax25_address; |
rose_loopback_neigh->digipeat = NULL; |
rose_loopback_neigh->ax25 = NULL; |
rose_loopback_neigh->dev = NULL; |
rose_loopback_neigh->count = 0; |
rose_loopback_neigh->use = 0; |
rose_loopback_neigh->dce_mode = 1; |
rose_loopback_neigh->loopback = 1; |
rose_loopback_neigh->number = rose_neigh_no++; |
rose_loopback_neigh->restarted = 1; |
|
skb_queue_head_init(&rose_loopback_neigh->queue); |
|
init_timer(&rose_loopback_neigh->ftimer); |
init_timer(&rose_loopback_neigh->t0timer); |
|
save_flags(flags); cli(); |
rose_loopback_neigh->next = rose_neigh_list; |
rose_neigh_list = rose_loopback_neigh; |
restore_flags(flags); |
|
return 0; |
} |
|
/* |
* Add a loopback node. |
*/ |
int rose_add_loopback_node(rose_address *address) |
{ |
struct rose_node *rose_node; |
unsigned long flags; |
|
for (rose_node = rose_node_list; rose_node != NULL; rose_node = rose_node->next) |
if ((rose_node->mask == 10) && (rosecmpm(address, &rose_node->address, 10) == 0) && rose_node->loopback) |
break; |
|
if (rose_node != NULL) return 0; |
|
if ((rose_node = kmalloc(sizeof(*rose_node), GFP_ATOMIC)) == NULL) |
return -ENOMEM; |
|
rose_node->address = *address; |
rose_node->mask = 10; |
rose_node->count = 1; |
rose_node->loopback = 1; |
rose_node->neighbour[0] = rose_loopback_neigh; |
|
/* Insert at the head of list. Address is always mask=10 */ |
save_flags(flags); cli(); |
rose_node->next = rose_node_list; |
rose_node_list = rose_node; |
restore_flags(flags); |
|
rose_loopback_neigh->count++; |
|
return 0; |
} |
|
/* |
* Delete a loopback node. |
*/ |
void rose_del_loopback_node(rose_address *address) |
{ |
struct rose_node *rose_node; |
|
for (rose_node = rose_node_list; rose_node != NULL; rose_node = rose_node->next) |
if ((rose_node->mask == 10) && (rosecmpm(address, &rose_node->address, 10) == 0) && rose_node->loopback) |
break; |
|
if (rose_node == NULL) return; |
|
rose_remove_node(rose_node); |
|
rose_loopback_neigh->count--; |
} |
|
/* |
* A device has been removed. Remove its routes and neighbours. |
*/ |
void rose_rt_device_down(struct net_device *dev) |
{ |
struct rose_neigh *s, *rose_neigh = rose_neigh_list; |
struct rose_node *t, *rose_node; |
int i; |
|
while (rose_neigh != NULL) { |
s = rose_neigh; |
rose_neigh = rose_neigh->next; |
|
if (s->dev == dev) { |
rose_node = rose_node_list; |
|
while (rose_node != NULL) { |
t = rose_node; |
rose_node = rose_node->next; |
|
for (i = 0; i < t->count; i++) { |
if (t->neighbour[i] == s) { |
t->count--; |
|
switch (i) { |
case 0: |
t->neighbour[0] = t->neighbour[1]; |
case 1: |
t->neighbour[1] = t->neighbour[2]; |
case 2: |
break; |
} |
} |
} |
|
if (t->count <= 0) |
rose_remove_node(t); |
} |
|
rose_remove_neigh(s); |
} |
} |
} |
|
/* |
* A device has been removed. Remove its links. |
*/ |
void rose_route_device_down(struct net_device *dev) |
{ |
struct rose_route *s, *rose_route = rose_route_list; |
|
while (rose_route != NULL) { |
s = rose_route; |
rose_route = rose_route->next; |
|
if (s->neigh1->dev == dev || s->neigh2->dev == dev) |
rose_remove_route(s); |
} |
} |
|
/* |
* Clear all nodes and neighbours out, except for neighbours with |
* active connections going through them. |
* Do not clear loopback neighbour and nodes. |
*/ |
static int rose_clear_routes(void) |
{ |
struct rose_neigh *s, *rose_neigh = rose_neigh_list; |
struct rose_node *t, *rose_node = rose_node_list; |
|
while (rose_node != NULL) { |
t = rose_node; |
rose_node = rose_node->next; |
if (!t->loopback) |
rose_remove_node(t); |
} |
|
while (rose_neigh != NULL) { |
s = rose_neigh; |
rose_neigh = rose_neigh->next; |
|
if (s->use == 0 && !s->loopback) { |
s->count = 0; |
rose_remove_neigh(s); |
} |
} |
|
return 0; |
} |
|
/* |
* Check that the device given is a valid AX.25 interface that is "up". |
*/ |
struct net_device *rose_ax25_dev_get(char *devname) |
{ |
struct net_device *dev; |
|
if ((dev = dev_get_by_name(devname)) == NULL) |
return NULL; |
|
if ((dev->flags & IFF_UP) && dev->type == ARPHRD_AX25) |
return dev; |
|
dev_put(dev); |
return NULL; |
} |
|
/* |
* Find the first active ROSE device, usually "rose0". |
*/ |
struct net_device *rose_dev_first(void) |
{ |
struct net_device *dev, *first = NULL; |
|
read_lock(&dev_base_lock); |
for (dev = dev_base; dev != NULL; dev = dev->next) { |
if ((dev->flags & IFF_UP) && dev->type == ARPHRD_ROSE) |
if (first == NULL || strncmp(dev->name, first->name, 3) < 0) |
first = dev; |
} |
read_unlock(&dev_base_lock); |
|
return first; |
} |
|
/* |
* Find the ROSE device for the given address. |
*/ |
struct net_device *rose_dev_get(rose_address *addr) |
{ |
struct net_device *dev; |
|
read_lock(&dev_base_lock); |
for (dev = dev_base; dev != NULL; dev = dev->next) { |
if ((dev->flags & IFF_UP) && dev->type == ARPHRD_ROSE && rosecmp(addr, (rose_address *)dev->dev_addr) == 0) { |
dev_hold(dev); |
goto out; |
} |
} |
out: |
read_unlock(&dev_base_lock); |
return dev; |
} |
|
static int rose_dev_exists(rose_address *addr) |
{ |
struct net_device *dev; |
|
read_lock(&dev_base_lock); |
for (dev = dev_base; dev != NULL; dev = dev->next) { |
if ((dev->flags & IFF_UP) && dev->type == ARPHRD_ROSE && rosecmp(addr, (rose_address *)dev->dev_addr) == 0) |
goto out; |
} |
out: |
read_unlock(&dev_base_lock); |
return dev != NULL; |
} |
|
|
|
|
struct rose_route *rose_route_free_lci(unsigned int lci, struct rose_neigh *neigh) |
{ |
struct rose_route *rose_route; |
|
for (rose_route = rose_route_list; rose_route != NULL; rose_route = rose_route->next) |
if ((rose_route->neigh1 == neigh && rose_route->lci1 == lci) || |
(rose_route->neigh2 == neigh && rose_route->lci2 == lci)) |
return rose_route; |
|
return NULL; |
} |
|
/* |
* Find a neighbour given a ROSE address. |
*/ |
struct rose_neigh *rose_get_neigh(rose_address *addr, unsigned char *cause, unsigned char *diagnostic) |
{ |
struct rose_node *node; |
int failed = 0; |
int i; |
|
for (node = rose_node_list; node != NULL; node = node->next) { |
if (rosecmpm(addr, &node->address, node->mask) == 0) { |
for (i = 0; i < node->count; i++) { |
if (!rose_ftimer_running(node->neighbour[i])) { |
return node->neighbour[i]; } |
else |
failed = 1; |
} |
break; |
} |
} |
|
if (failed) { |
*cause = ROSE_OUT_OF_ORDER; |
*diagnostic = 0; |
} else { |
*cause = ROSE_NOT_OBTAINABLE; |
*diagnostic = 0; |
} |
|
return NULL; |
} |
|
/* |
* Handle the ioctls that control the routing functions. |
*/ |
int rose_rt_ioctl(unsigned int cmd, void *arg) |
{ |
struct rose_route_struct rose_route; |
struct net_device *dev; |
int err; |
|
switch (cmd) { |
|
case SIOCADDRT: |
if (copy_from_user(&rose_route, arg, sizeof(struct rose_route_struct))) |
return -EFAULT; |
if ((dev = rose_ax25_dev_get(rose_route.device)) == NULL) |
return -EINVAL; |
if (rose_dev_exists(&rose_route.address)) { /* Can't add routes to ourself */ |
dev_put(dev); |
return -EINVAL; |
} |
if (rose_route.mask > 10) /* Mask can't be more than 10 digits */ |
return -EINVAL; |
|
err = rose_add_node(&rose_route, dev); |
dev_put(dev); |
return err; |
|
case SIOCDELRT: |
if (copy_from_user(&rose_route, arg, sizeof(struct rose_route_struct))) |
return -EFAULT; |
if ((dev = rose_ax25_dev_get(rose_route.device)) == NULL) |
return -EINVAL; |
err = rose_del_node(&rose_route, dev); |
dev_put(dev); |
return err; |
|
|
case SIOCRSCLRRT: |
return rose_clear_routes(); |
|
default: |
return -EINVAL; |
} |
|
return 0; |
} |
|
static void rose_del_route_by_neigh(struct rose_neigh *rose_neigh) |
{ |
struct rose_route *rose_route, *s; |
|
rose_neigh->restarted = 0; |
|
rose_stop_t0timer(rose_neigh); |
rose_start_ftimer(rose_neigh); |
|
skb_queue_purge(&rose_neigh->queue); |
|
rose_route = rose_route_list; |
|
while (rose_route != NULL) { |
if ((rose_route->neigh1 == rose_neigh && rose_route->neigh2 == rose_neigh) || |
(rose_route->neigh1 == rose_neigh && rose_route->neigh2 == NULL) || |
(rose_route->neigh2 == rose_neigh && rose_route->neigh1 == NULL)) { |
s = rose_route->next; |
rose_remove_route(rose_route); |
rose_route = s; |
continue; |
} |
|
if (rose_route->neigh1 == rose_neigh) { |
rose_route->neigh1->use--; |
rose_route->neigh1 = NULL; |
rose_transmit_clear_request(rose_route->neigh2, rose_route->lci2, ROSE_OUT_OF_ORDER, 0); |
} |
|
if (rose_route->neigh2 == rose_neigh) { |
rose_route->neigh2->use--; |
rose_route->neigh2 = NULL; |
rose_transmit_clear_request(rose_route->neigh1, rose_route->lci1, ROSE_OUT_OF_ORDER, 0); |
} |
|
rose_route = rose_route->next; |
} |
} |
|
/* |
* A level 2 link has timed out, therefore it appears to be a poor link, |
* then don't use that neighbour until it is reset. Blow away all through |
* routes and connections using this route. |
*/ |
void rose_link_failed(ax25_cb *ax25, int reason) |
{ |
struct rose_neigh *rose_neigh; |
|
for (rose_neigh = rose_neigh_list; rose_neigh != NULL; rose_neigh = rose_neigh->next) |
if (rose_neigh->ax25 == ax25) |
break; |
|
if (rose_neigh == NULL) return; |
|
rose_neigh->ax25 = NULL; |
|
rose_del_route_by_neigh(rose_neigh); |
rose_kill_by_neigh(rose_neigh); |
} |
|
/* |
* A device has been "downed" remove its link status. Blow away all |
* through routes and connections that use this device. |
*/ |
void rose_link_device_down(struct net_device *dev) |
{ |
struct rose_neigh *rose_neigh; |
|
for (rose_neigh = rose_neigh_list; rose_neigh != NULL; rose_neigh = rose_neigh->next) { |
if (rose_neigh->dev == dev) { |
rose_del_route_by_neigh(rose_neigh); |
rose_kill_by_neigh(rose_neigh); |
} |
} |
} |
|
/* |
* Route a frame to an appropriate AX.25 connection. |
*/ |
int rose_route_frame(struct sk_buff *skb, ax25_cb *ax25) |
{ |
struct rose_neigh *rose_neigh, *new_neigh; |
struct rose_route *rose_route; |
struct rose_facilities_struct facilities; |
rose_address *src_addr, *dest_addr; |
struct sock *sk; |
unsigned short frametype; |
unsigned int lci, new_lci; |
unsigned char cause, diagnostic; |
struct net_device *dev; |
unsigned long flags; |
int len; |
|
#if 0 |
if (call_in_firewall(PF_ROSE, skb->dev, skb->data, NULL, &skb) != FW_ACCEPT) |
return 0; |
#endif |
|
frametype = skb->data[2]; |
lci = ((skb->data[0] << 8) & 0xF00) + ((skb->data[1] << 0) & 0x0FF); |
src_addr = (rose_address *)(skb->data + 9); |
dest_addr = (rose_address *)(skb->data + 4); |
|
for (rose_neigh = rose_neigh_list; rose_neigh != NULL; rose_neigh = rose_neigh->next) |
if (ax25cmp(&ax25->dest_addr, &rose_neigh->callsign) == 0 && ax25->ax25_dev->dev == rose_neigh->dev) |
break; |
|
if (rose_neigh == NULL) { |
printk("rose_route : unknown neighbour or device %s\n", ax2asc(&ax25->dest_addr)); |
return 0; |
} |
|
/* |
* Obviously the link is working, halt the ftimer. |
*/ |
rose_stop_ftimer(rose_neigh); |
|
/* |
* LCI of zero is always for us, and its always a restart |
* frame. |
*/ |
if (lci == 0) { |
rose_link_rx_restart(skb, rose_neigh, frametype); |
return 0; |
} |
|
/* |
* Find an existing socket. |
*/ |
if ((sk = rose_find_socket(lci, rose_neigh)) != NULL) { |
if (frametype == ROSE_CALL_REQUEST) { |
/* Remove an existing unused socket */ |
rose_clear_queues(sk); |
sk->protinfo.rose->cause = ROSE_NETWORK_CONGESTION; |
sk->protinfo.rose->diagnostic = 0; |
sk->protinfo.rose->neighbour->use--; |
sk->protinfo.rose->neighbour = NULL; |
sk->protinfo.rose->lci = 0; |
sk->protinfo.rose->state = ROSE_STATE_0; |
sk->state = TCP_CLOSE; |
sk->err = 0; |
sk->shutdown |= SEND_SHUTDOWN; |
if (!sk->dead) |
sk->state_change(sk); |
sk->dead = 1; |
} |
else { |
skb->h.raw = skb->data; |
return rose_process_rx_frame(sk, skb); |
} |
} |
|
/* |
* Is is a Call Request and is it for us ? |
*/ |
if (frametype == ROSE_CALL_REQUEST) |
if ((dev = rose_dev_get(dest_addr)) != NULL) { |
int err = rose_rx_call_request(skb, dev, rose_neigh, lci); |
dev_put(dev); |
return err; |
} |
|
if (!sysctl_rose_routing_control) { |
rose_transmit_clear_request(rose_neigh, lci, ROSE_NOT_OBTAINABLE, 0); |
return 0; |
} |
|
/* |
* Route it to the next in line if we have an entry for it. |
*/ |
for (rose_route = rose_route_list; rose_route != NULL; rose_route = rose_route->next) { |
if (rose_route->lci1 == lci && rose_route->neigh1 == rose_neigh) { |
if (frametype == ROSE_CALL_REQUEST) { |
/* F6FBB - Remove an existing unused route */ |
rose_remove_route(rose_route); |
break; |
} else if (rose_route->neigh2 != NULL) { |
skb->data[0] &= 0xF0; |
skb->data[0] |= (rose_route->lci2 >> 8) & 0x0F; |
skb->data[1] = (rose_route->lci2 >> 0) & 0xFF; |
rose_transmit_link(skb, rose_route->neigh2); |
if (frametype == ROSE_CLEAR_CONFIRMATION) |
rose_remove_route(rose_route); |
return 1; |
} else { |
if (frametype == ROSE_CLEAR_CONFIRMATION) |
rose_remove_route(rose_route); |
return 0; |
} |
} |
if (rose_route->lci2 == lci && rose_route->neigh2 == rose_neigh) { |
if (frametype == ROSE_CALL_REQUEST) { |
/* F6FBB - Remove an existing unused route */ |
rose_remove_route(rose_route); |
break; |
} else if (rose_route->neigh1 != NULL) { |
skb->data[0] &= 0xF0; |
skb->data[0] |= (rose_route->lci1 >> 8) & 0x0F; |
skb->data[1] = (rose_route->lci1 >> 0) & 0xFF; |
rose_transmit_link(skb, rose_route->neigh1); |
if (frametype == ROSE_CLEAR_CONFIRMATION) |
rose_remove_route(rose_route); |
return 1; |
} else { |
if (frametype == ROSE_CLEAR_CONFIRMATION) |
rose_remove_route(rose_route); |
return 0; |
} |
} |
} |
|
/* |
* We know that: |
* 1. The frame isn't for us, |
* 2. It isn't "owned" by any existing route. |
*/ |
if (frametype != ROSE_CALL_REQUEST) /* XXX */ |
return 0; |
|
len = (((skb->data[3] >> 4) & 0x0F) + 1) / 2; |
len += (((skb->data[3] >> 0) & 0x0F) + 1) / 2; |
|
memset(&facilities, 0x00, sizeof(struct rose_facilities_struct)); |
|
if (!rose_parse_facilities(skb->data + len + 4, &facilities)) { |
rose_transmit_clear_request(rose_neigh, lci, ROSE_INVALID_FACILITY, 76); |
return 0; |
} |
|
/* |
* Check for routing loops. |
*/ |
for (rose_route = rose_route_list; rose_route != NULL; rose_route = rose_route->next) { |
if (rose_route->rand == facilities.rand && |
rosecmp(src_addr, &rose_route->src_addr) == 0 && |
ax25cmp(&facilities.dest_call, &rose_route->src_call) == 0 && |
ax25cmp(&facilities.source_call, &rose_route->dest_call) == 0) { |
rose_transmit_clear_request(rose_neigh, lci, ROSE_NOT_OBTAINABLE, 120); |
return 0; |
} |
} |
|
if ((new_neigh = rose_get_neigh(dest_addr, &cause, &diagnostic)) == NULL) { |
rose_transmit_clear_request(rose_neigh, lci, cause, diagnostic); |
return 0; |
} |
|
if ((new_lci = rose_new_lci(new_neigh)) == 0) { |
rose_transmit_clear_request(rose_neigh, lci, ROSE_NETWORK_CONGESTION, 71); |
return 0; |
} |
|
if ((rose_route = kmalloc(sizeof(*rose_route), GFP_ATOMIC)) == NULL) { |
rose_transmit_clear_request(rose_neigh, lci, ROSE_NETWORK_CONGESTION, 120); |
return 0; |
} |
|
rose_route->lci1 = lci; |
rose_route->src_addr = *src_addr; |
rose_route->dest_addr = *dest_addr; |
rose_route->src_call = facilities.dest_call; |
rose_route->dest_call = facilities.source_call; |
rose_route->rand = facilities.rand; |
rose_route->neigh1 = rose_neigh; |
rose_route->lci2 = new_lci; |
rose_route->neigh2 = new_neigh; |
|
rose_route->neigh1->use++; |
rose_route->neigh2->use++; |
|
save_flags(flags); cli(); |
rose_route->next = rose_route_list; |
rose_route_list = rose_route; |
restore_flags(flags); |
|
skb->data[0] &= 0xF0; |
skb->data[0] |= (rose_route->lci2 >> 8) & 0x0F; |
skb->data[1] = (rose_route->lci2 >> 0) & 0xFF; |
|
rose_transmit_link(skb, rose_route->neigh2); |
|
return 1; |
} |
|
int rose_nodes_get_info(char *buffer, char **start, off_t offset, int length) |
{ |
struct rose_node *rose_node; |
int len = 0; |
off_t pos = 0; |
off_t begin = 0; |
int i; |
|
cli(); |
|
len += sprintf(buffer, "address mask n neigh neigh neigh\n"); |
|
for (rose_node = rose_node_list; rose_node != NULL; rose_node = rose_node->next) { |
/* if (rose_node->loopback) { |
len += sprintf(buffer + len, "%-10s %04d 1 loopback\n", |
rose2asc(&rose_node->address), |
rose_node->mask); |
} else { */ |
len += sprintf(buffer + len, "%-10s %04d %d", |
rose2asc(&rose_node->address), |
rose_node->mask, |
rose_node->count); |
|
for (i = 0; i < rose_node->count; i++) |
len += sprintf(buffer + len, " %05d", |
rose_node->neighbour[i]->number); |
|
len += sprintf(buffer + len, "\n"); |
/* } */ |
|
pos = begin + len; |
|
if (pos < offset) { |
len = 0; |
begin = pos; |
} |
|
if (pos > offset + length) |
break; |
} |
|
sti(); |
|
*start = buffer + (offset - begin); |
len -= (offset - begin); |
|
if (len > length) len = length; |
|
return len; |
} |
|
int rose_neigh_get_info(char *buffer, char **start, off_t offset, int length) |
{ |
struct rose_neigh *rose_neigh; |
int len = 0; |
off_t pos = 0; |
off_t begin = 0; |
int i; |
|
cli(); |
|
len += sprintf(buffer, "addr callsign dev count use mode restart t0 tf digipeaters\n"); |
|
for (rose_neigh = rose_neigh_list; rose_neigh != NULL; rose_neigh = rose_neigh->next) { |
/* if (!rose_neigh->loopback) { */ |
len += sprintf(buffer + len, "%05d %-9s %-4s %3d %3d %3s %3s %3lu %3lu", |
rose_neigh->number, |
(rose_neigh->loopback) ? "RSLOOP-0" : ax2asc(&rose_neigh->callsign), |
rose_neigh->dev ? rose_neigh->dev->name : "???", |
rose_neigh->count, |
rose_neigh->use, |
(rose_neigh->dce_mode) ? "DCE" : "DTE", |
(rose_neigh->restarted) ? "yes" : "no", |
ax25_display_timer(&rose_neigh->t0timer) / HZ, |
ax25_display_timer(&rose_neigh->ftimer) / HZ); |
|
if (rose_neigh->digipeat != NULL) { |
for (i = 0; i < rose_neigh->digipeat->ndigi; i++) |
len += sprintf(buffer + len, " %s", ax2asc(&rose_neigh->digipeat->calls[i])); |
} |
|
len += sprintf(buffer + len, "\n"); |
|
pos = begin + len; |
|
if (pos < offset) { |
len = 0; |
begin = pos; |
} |
|
if (pos > offset + length) |
break; |
/* } */ |
} |
|
sti(); |
|
*start = buffer + (offset - begin); |
len -= (offset - begin); |
|
if (len > length) len = length; |
|
return len; |
} |
|
int rose_routes_get_info(char *buffer, char **start, off_t offset, int length) |
{ |
struct rose_route *rose_route; |
int len = 0; |
off_t pos = 0; |
off_t begin = 0; |
|
cli(); |
|
len += sprintf(buffer, "lci address callsign neigh <-> lci address callsign neigh\n"); |
|
for (rose_route = rose_route_list; rose_route != NULL; rose_route = rose_route->next) { |
if (rose_route->neigh1 != NULL) { |
len += sprintf(buffer + len, "%3.3X %-10s %-9s %05d ", |
rose_route->lci1, |
rose2asc(&rose_route->src_addr), |
ax2asc(&rose_route->src_call), |
rose_route->neigh1->number); |
} else { |
len += sprintf(buffer + len, "000 * * 00000 "); |
} |
|
if (rose_route->neigh2 != NULL) { |
len += sprintf(buffer + len, "%3.3X %-10s %-9s %05d\n", |
rose_route->lci2, |
rose2asc(&rose_route->dest_addr), |
ax2asc(&rose_route->dest_call), |
rose_route->neigh2->number); |
} else { |
len += sprintf(buffer + len, "000 * * 00000\n"); |
} |
|
pos = begin + len; |
|
if (pos < offset) { |
len = 0; |
begin = pos; |
} |
|
if (pos > offset + length) |
break; |
} |
|
sti(); |
|
*start = buffer + (offset - begin); |
len -= (offset - begin); |
|
if (len > length) len = length; |
|
return len; |
} |
|
/* |
* Release all memory associated with ROSE routing structures. |
*/ |
void __exit rose_rt_free(void) |
{ |
struct rose_neigh *s, *rose_neigh = rose_neigh_list; |
struct rose_node *t, *rose_node = rose_node_list; |
struct rose_route *u, *rose_route = rose_route_list; |
|
while (rose_neigh != NULL) { |
s = rose_neigh; |
rose_neigh = rose_neigh->next; |
|
rose_remove_neigh(s); |
} |
|
while (rose_node != NULL) { |
t = rose_node; |
rose_node = rose_node->next; |
|
rose_remove_node(t); |
} |
|
while (rose_route != NULL) { |
u = rose_route; |
rose_route = rose_route->next; |
|
rose_remove_route(u); |
} |
} |
/rose_timer.c
0,0 → 1,207
/* |
* ROSE release 003 |
* |
* This code REQUIRES 2.1.15 or higher/ NET3.038 |
* |
* This module: |
* This module is free software; you can redistribute it and/or |
* modify it under the terms of the GNU General Public License |
* as published by the Free Software Foundation; either version |
* 2 of the License, or (at your option) any later version. |
* |
* History |
* ROSE 001 Jonathan(G4KLX) Cloned from nr_timer.c |
* ROSE 003 Jonathan(G4KLX) New timer architecture. |
* Implemented idle timer. |
*/ |
|
#include <linux/errno.h> |
#include <linux/types.h> |
#include <linux/socket.h> |
#include <linux/in.h> |
#include <linux/kernel.h> |
#include <linux/sched.h> |
#include <linux/timer.h> |
#include <linux/string.h> |
#include <linux/sockios.h> |
#include <linux/net.h> |
#include <net/ax25.h> |
#include <linux/inet.h> |
#include <linux/netdevice.h> |
#include <linux/skbuff.h> |
#include <net/sock.h> |
#include <asm/segment.h> |
#include <asm/system.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->timer); |
|
sk->timer.data = (unsigned long)sk; |
sk->timer.function = &rose_heartbeat_expiry; |
sk->timer.expires = jiffies + 5 * HZ; |
|
add_timer(&sk->timer); |
} |
|
void rose_start_t1timer(struct sock *sk) |
{ |
del_timer(&sk->protinfo.rose->timer); |
|
sk->protinfo.rose->timer.data = (unsigned long)sk; |
sk->protinfo.rose->timer.function = &rose_timer_expiry; |
sk->protinfo.rose->timer.expires = jiffies + sk->protinfo.rose->t1; |
|
add_timer(&sk->protinfo.rose->timer); |
} |
|
void rose_start_t2timer(struct sock *sk) |
{ |
del_timer(&sk->protinfo.rose->timer); |
|
sk->protinfo.rose->timer.data = (unsigned long)sk; |
sk->protinfo.rose->timer.function = &rose_timer_expiry; |
sk->protinfo.rose->timer.expires = jiffies + sk->protinfo.rose->t2; |
|
add_timer(&sk->protinfo.rose->timer); |
} |
|
void rose_start_t3timer(struct sock *sk) |
{ |
del_timer(&sk->protinfo.rose->timer); |
|
sk->protinfo.rose->timer.data = (unsigned long)sk; |
sk->protinfo.rose->timer.function = &rose_timer_expiry; |
sk->protinfo.rose->timer.expires = jiffies + sk->protinfo.rose->t3; |
|
add_timer(&sk->protinfo.rose->timer); |
} |
|
void rose_start_hbtimer(struct sock *sk) |
{ |
del_timer(&sk->protinfo.rose->timer); |
|
sk->protinfo.rose->timer.data = (unsigned long)sk; |
sk->protinfo.rose->timer.function = &rose_timer_expiry; |
sk->protinfo.rose->timer.expires = jiffies + sk->protinfo.rose->hb; |
|
add_timer(&sk->protinfo.rose->timer); |
} |
|
void rose_start_idletimer(struct sock *sk) |
{ |
del_timer(&sk->protinfo.rose->idletimer); |
|
if (sk->protinfo.rose->idle > 0) { |
sk->protinfo.rose->idletimer.data = (unsigned long)sk; |
sk->protinfo.rose->idletimer.function = &rose_idletimer_expiry; |
sk->protinfo.rose->idletimer.expires = jiffies + sk->protinfo.rose->idle; |
|
add_timer(&sk->protinfo.rose->idletimer); |
} |
} |
|
void rose_stop_heartbeat(struct sock *sk) |
{ |
del_timer(&sk->timer); |
} |
|
void rose_stop_timer(struct sock *sk) |
{ |
del_timer(&sk->protinfo.rose->timer); |
} |
|
void rose_stop_idletimer(struct sock *sk) |
{ |
del_timer(&sk->protinfo.rose->idletimer); |
} |
|
static void rose_heartbeat_expiry(unsigned long param) |
{ |
struct sock *sk = (struct sock *)param; |
|
switch (sk->protinfo.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 (sk->destroy || (sk->state == TCP_LISTEN && sk->dead)) { |
rose_destroy_socket(sk); |
return; |
} |
break; |
|
case ROSE_STATE_3: |
/* |
* Check for the state of the receive buffer. |
*/ |
if (atomic_read(&sk->rmem_alloc) < (sk->rcvbuf / 2) && |
(sk->protinfo.rose->condition & ROSE_COND_OWN_RX_BUSY)) { |
sk->protinfo.rose->condition &= ~ROSE_COND_OWN_RX_BUSY; |
sk->protinfo.rose->condition &= ~ROSE_COND_ACK_PENDING; |
sk->protinfo.rose->vl = sk->protinfo.rose->vr; |
rose_write_internal(sk, ROSE_RR); |
rose_stop_timer(sk); /* HB */ |
break; |
} |
break; |
} |
|
rose_start_heartbeat(sk); |
} |
|
static void rose_timer_expiry(unsigned long param) |
{ |
struct sock *sk = (struct sock *)param; |
|
switch (sk->protinfo.rose->state) { |
|
case ROSE_STATE_1: /* T1 */ |
case ROSE_STATE_4: /* T2 */ |
rose_write_internal(sk, ROSE_CLEAR_REQUEST); |
sk->protinfo.rose->state = ROSE_STATE_2; |
rose_start_t3timer(sk); |
break; |
|
case ROSE_STATE_2: /* T3 */ |
sk->protinfo.rose->neighbour->use--; |
rose_disconnect(sk, ETIMEDOUT, -1, -1); |
break; |
|
case ROSE_STATE_3: /* HB */ |
if (sk->protinfo.rose->condition & ROSE_COND_ACK_PENDING) { |
sk->protinfo.rose->condition &= ~ROSE_COND_ACK_PENDING; |
rose_enquiry_response(sk); |
} |
break; |
} |
} |
|
static void rose_idletimer_expiry(unsigned long param) |
{ |
struct sock *sk = (struct sock *)param; |
|
rose_clear_queues(sk); |
|
rose_write_internal(sk, ROSE_CLEAR_REQUEST); |
sk->protinfo.rose->state = ROSE_STATE_2; |
|
rose_start_t3timer(sk); |
|
sk->state = TCP_CLOSE; |
sk->err = 0; |
sk->shutdown |= SEND_SHUTDOWN; |
|
if (!sk->dead) |
sk->state_change(sk); |
|
sk->dead = 1; |
} |
/Makefile
0,0 → 1,19
# |
# Makefile for the Linux Rose (X.25 PLP) layer. |
# |
# Note! Dependencies are done automagically by 'make dep', which also |
# removes any old dependencies. DON'T put your own dependencies here |
# unless it's something special (ie not a .c file). |
# |
# Note 2! The CFLAGS definition is now in the main makefile... |
|
O_TARGET := rose.o |
|
obj-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 |
obj-m := $(O_TARGET) |
|
obj-$(CONFIG_SYSCTL) += sysctl_net_rose.o |
|
include $(TOPDIR)/Rules.make |
|