OpenCores
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
 

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.