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/netrom
    from Rev 1275 to Rev 1765
    Reverse comparison

Rev 1275 → Rev 1765

/nr_timer.c
0,0 → 1,245
/*
* NET/ROM release 007
*
* 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
* NET/ROM 001 Jonathan(G4KLX) Cloned from ax25_timer.c
* NET/ROM 007 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/uaccess.h>
#include <asm/system.h>
#include <linux/fcntl.h>
#include <linux/mm.h>
#include <linux/interrupt.h>
#include <net/netrom.h>
 
static void nr_heartbeat_expiry(unsigned long);
static void nr_t1timer_expiry(unsigned long);
static void nr_t2timer_expiry(unsigned long);
static void nr_t4timer_expiry(unsigned long);
static void nr_idletimer_expiry(unsigned long);
 
void nr_start_t1timer(struct sock *sk)
{
del_timer(&sk->protinfo.nr->t1timer);
 
sk->protinfo.nr->t1timer.data = (unsigned long)sk;
sk->protinfo.nr->t1timer.function = &nr_t1timer_expiry;
sk->protinfo.nr->t1timer.expires = jiffies + sk->protinfo.nr->t1;
 
add_timer(&sk->protinfo.nr->t1timer);
}
 
void nr_start_t2timer(struct sock *sk)
{
del_timer(&sk->protinfo.nr->t2timer);
 
sk->protinfo.nr->t2timer.data = (unsigned long)sk;
sk->protinfo.nr->t2timer.function = &nr_t2timer_expiry;
sk->protinfo.nr->t2timer.expires = jiffies + sk->protinfo.nr->t2;
 
add_timer(&sk->protinfo.nr->t2timer);
}
 
void nr_start_t4timer(struct sock *sk)
{
del_timer(&sk->protinfo.nr->t4timer);
 
sk->protinfo.nr->t4timer.data = (unsigned long)sk;
sk->protinfo.nr->t4timer.function = &nr_t4timer_expiry;
sk->protinfo.nr->t4timer.expires = jiffies + sk->protinfo.nr->t4;
 
add_timer(&sk->protinfo.nr->t4timer);
}
 
void nr_start_idletimer(struct sock *sk)
{
del_timer(&sk->protinfo.nr->idletimer);
 
if (sk->protinfo.nr->idle > 0) {
sk->protinfo.nr->idletimer.data = (unsigned long)sk;
sk->protinfo.nr->idletimer.function = &nr_idletimer_expiry;
sk->protinfo.nr->idletimer.expires = jiffies + sk->protinfo.nr->idle;
 
add_timer(&sk->protinfo.nr->idletimer);
}
}
 
void nr_start_heartbeat(struct sock *sk)
{
del_timer(&sk->timer);
 
sk->timer.data = (unsigned long)sk;
sk->timer.function = &nr_heartbeat_expiry;
sk->timer.expires = jiffies + 5 * HZ;
 
add_timer(&sk->timer);
}
 
void nr_stop_t1timer(struct sock *sk)
{
del_timer(&sk->protinfo.nr->t1timer);
}
 
void nr_stop_t2timer(struct sock *sk)
{
del_timer(&sk->protinfo.nr->t2timer);
}
 
void nr_stop_t4timer(struct sock *sk)
{
del_timer(&sk->protinfo.nr->t4timer);
}
 
void nr_stop_idletimer(struct sock *sk)
{
del_timer(&sk->protinfo.nr->idletimer);
}
 
void nr_stop_heartbeat(struct sock *sk)
{
del_timer(&sk->timer);
}
 
int nr_t1timer_running(struct sock *sk)
{
return timer_pending(&sk->protinfo.nr->t1timer);
}
 
static void nr_heartbeat_expiry(unsigned long param)
{
struct sock *sk = (struct sock *)param;
 
switch (sk->protinfo.nr->state) {
 
case NR_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)) {
nr_destroy_socket(sk);
return;
}
break;
 
case NR_STATE_3:
/*
* Check for the state of the receive buffer.
*/
if (atomic_read(&sk->rmem_alloc) < (sk->rcvbuf / 2) &&
(sk->protinfo.nr->condition & NR_COND_OWN_RX_BUSY)) {
sk->protinfo.nr->condition &= ~NR_COND_OWN_RX_BUSY;
sk->protinfo.nr->condition &= ~NR_COND_ACK_PENDING;
sk->protinfo.nr->vl = sk->protinfo.nr->vr;
nr_write_internal(sk, NR_INFOACK);
break;
}
break;
}
 
nr_start_heartbeat(sk);
}
 
static void nr_t2timer_expiry(unsigned long param)
{
struct sock *sk = (struct sock *)param;
 
if (sk->protinfo.nr->condition & NR_COND_ACK_PENDING) {
sk->protinfo.nr->condition &= ~NR_COND_ACK_PENDING;
nr_enquiry_response(sk);
}
}
 
static void nr_t4timer_expiry(unsigned long param)
{
struct sock *sk = (struct sock *)param;
 
sk->protinfo.nr->condition &= ~NR_COND_PEER_RX_BUSY;
}
 
static void nr_idletimer_expiry(unsigned long param)
{
struct sock *sk = (struct sock *)param;
 
nr_clear_queues(sk);
 
sk->protinfo.nr->n2count = 0;
nr_write_internal(sk, NR_DISCREQ);
sk->protinfo.nr->state = NR_STATE_2;
 
nr_start_t1timer(sk);
nr_stop_t2timer(sk);
nr_stop_t4timer(sk);
 
sk->state = TCP_CLOSE;
sk->err = 0;
sk->shutdown |= SEND_SHUTDOWN;
 
if (!sk->dead)
sk->state_change(sk);
 
sk->dead = 1;
}
 
static void nr_t1timer_expiry(unsigned long param)
{
struct sock *sk = (struct sock *)param;
 
switch (sk->protinfo.nr->state) {
 
case NR_STATE_1:
if (sk->protinfo.nr->n2count == sk->protinfo.nr->n2) {
nr_disconnect(sk, ETIMEDOUT);
return;
} else {
sk->protinfo.nr->n2count++;
nr_write_internal(sk, NR_CONNREQ);
}
break;
 
case NR_STATE_2:
if (sk->protinfo.nr->n2count == sk->protinfo.nr->n2) {
nr_disconnect(sk, ETIMEDOUT);
return;
} else {
sk->protinfo.nr->n2count++;
nr_write_internal(sk, NR_DISCREQ);
}
break;
 
case NR_STATE_3:
if (sk->protinfo.nr->n2count == sk->protinfo.nr->n2) {
nr_disconnect(sk, ETIMEDOUT);
return;
} else {
sk->protinfo.nr->n2count++;
nr_requeue_frames(sk);
}
break;
}
 
nr_start_t1timer(sk);
}
/nr_in.c
0,0 → 1,304
/*
* NET/ROM release 007
*
* 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
* NET/ROM 001 Jonathan(G4KLX) Cloned from ax25_in.c
* NET/ROM 003 Jonathan(G4KLX) Added NET/ROM fragment reception.
* Darryl(G7LED) Added missing INFO with NAK case, optimized
* INFOACK handling, removed reconnect on error.
* NET/ROM 006 Jonathan(G4KLX) Hdrincl removal changes.
* NET/ROM 007 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 <net/ip.h> /* For ip_rcv */
#include <asm/uaccess.h>
#include <asm/system.h>
#include <linux/fcntl.h>
#include <linux/mm.h>
#include <linux/interrupt.h>
#include <net/netrom.h>
 
static int nr_queue_rx_frame(struct sock *sk, struct sk_buff *skb, int more)
{
struct sk_buff *skbo, *skbn = skb;
 
skb_pull(skb, NR_NETWORK_LEN + NR_TRANSPORT_LEN);
 
nr_start_idletimer(sk);
 
if (more) {
sk->protinfo.nr->fraglen += skb->len;
skb_queue_tail(&sk->protinfo.nr->frag_queue, skb);
return 0;
}
 
if (!more && sk->protinfo.nr->fraglen > 0) { /* End of fragment */
sk->protinfo.nr->fraglen += skb->len;
skb_queue_tail(&sk->protinfo.nr->frag_queue, skb);
 
if ((skbn = alloc_skb(sk->protinfo.nr->fraglen, GFP_ATOMIC)) == NULL)
return 1;
 
skbn->h.raw = skbn->data;
 
while ((skbo = skb_dequeue(&sk->protinfo.nr->frag_queue)) != NULL) {
memcpy(skb_put(skbn, skbo->len), skbo->data, skbo->len);
kfree_skb(skbo);
}
 
sk->protinfo.nr->fraglen = 0;
}
 
return sock_queue_rcv_skb(sk, skbn);
}
 
/*
* State machine for state 1, Awaiting Connection State.
* The handling of the timer(s) is in file nr_timer.c.
* Handling of state 0 and connection release is in netrom.c.
*/
static int nr_state1_machine(struct sock *sk, struct sk_buff *skb, int frametype)
{
switch (frametype) {
 
case NR_CONNACK:
nr_stop_t1timer(sk);
nr_start_idletimer(sk);
sk->protinfo.nr->your_index = skb->data[17];
sk->protinfo.nr->your_id = skb->data[18];
sk->protinfo.nr->vs = 0;
sk->protinfo.nr->va = 0;
sk->protinfo.nr->vr = 0;
sk->protinfo.nr->vl = 0;
sk->protinfo.nr->state = NR_STATE_3;
sk->protinfo.nr->n2count = 0;
sk->protinfo.nr->window = skb->data[20];
sk->state = TCP_ESTABLISHED;
if (!sk->dead)
sk->state_change(sk);
break;
 
case NR_CONNACK | NR_CHOKE_FLAG:
nr_disconnect(sk, ECONNREFUSED);
break;
 
default:
break;
}
 
return 0;
}
 
/*
* State machine for state 2, Awaiting Release State.
* The handling of the timer(s) is in file nr_timer.c
* Handling of state 0 and connection release is in netrom.c.
*/
static int nr_state2_machine(struct sock *sk, struct sk_buff *skb, int frametype)
{
switch (frametype) {
 
case NR_CONNACK | NR_CHOKE_FLAG:
nr_disconnect(sk, ECONNRESET);
break;
 
case NR_DISCREQ:
nr_write_internal(sk, NR_DISCACK);
 
case NR_DISCACK:
nr_disconnect(sk, 0);
break;
 
default:
break;
}
 
return 0;
}
 
/*
* State machine for state 3, Connected State.
* The handling of the timer(s) is in file nr_timer.c
* Handling of state 0 and connection release is in netrom.c.
*/
static int nr_state3_machine(struct sock *sk, struct sk_buff *skb, int frametype)
{
struct sk_buff_head temp_queue;
struct sk_buff *skbn;
unsigned short save_vr;
unsigned short nr, ns;
int queued = 0;
 
nr = skb->data[18];
ns = skb->data[17];
 
switch (frametype) {
 
case NR_CONNREQ:
nr_write_internal(sk, NR_CONNACK);
break;
 
case NR_DISCREQ:
nr_write_internal(sk, NR_DISCACK);
nr_disconnect(sk, 0);
break;
 
case NR_CONNACK | NR_CHOKE_FLAG:
case NR_DISCACK:
nr_disconnect(sk, ECONNRESET);
break;
 
case NR_INFOACK:
case NR_INFOACK | NR_CHOKE_FLAG:
case NR_INFOACK | NR_NAK_FLAG:
case NR_INFOACK | NR_NAK_FLAG | NR_CHOKE_FLAG:
if (frametype & NR_CHOKE_FLAG) {
sk->protinfo.nr->condition |= NR_COND_PEER_RX_BUSY;
nr_start_t4timer(sk);
} else {
sk->protinfo.nr->condition &= ~NR_COND_PEER_RX_BUSY;
nr_stop_t4timer(sk);
}
if (!nr_validate_nr(sk, nr)) {
break;
}
if (frametype & NR_NAK_FLAG) {
nr_frames_acked(sk, nr);
nr_send_nak_frame(sk);
} else {
if (sk->protinfo.nr->condition & NR_COND_PEER_RX_BUSY) {
nr_frames_acked(sk, nr);
} else {
nr_check_iframes_acked(sk, nr);
}
}
break;
 
case NR_INFO:
case NR_INFO | NR_NAK_FLAG:
case NR_INFO | NR_CHOKE_FLAG:
case NR_INFO | NR_MORE_FLAG:
case NR_INFO | NR_NAK_FLAG | NR_CHOKE_FLAG:
case NR_INFO | NR_CHOKE_FLAG | NR_MORE_FLAG:
case NR_INFO | NR_NAK_FLAG | NR_MORE_FLAG:
case NR_INFO | NR_NAK_FLAG | NR_CHOKE_FLAG | NR_MORE_FLAG:
if (frametype & NR_CHOKE_FLAG) {
sk->protinfo.nr->condition |= NR_COND_PEER_RX_BUSY;
nr_start_t4timer(sk);
} else {
sk->protinfo.nr->condition &= ~NR_COND_PEER_RX_BUSY;
nr_stop_t4timer(sk);
}
if (nr_validate_nr(sk, nr)) {
if (frametype & NR_NAK_FLAG) {
nr_frames_acked(sk, nr);
nr_send_nak_frame(sk);
} else {
if (sk->protinfo.nr->condition & NR_COND_PEER_RX_BUSY) {
nr_frames_acked(sk, nr);
} else {
nr_check_iframes_acked(sk, nr);
}
}
}
queued = 1;
skb_queue_head(&sk->protinfo.nr->reseq_queue, skb);
if (sk->protinfo.nr->condition & NR_COND_OWN_RX_BUSY)
break;
skb_queue_head_init(&temp_queue);
do {
save_vr = sk->protinfo.nr->vr;
while ((skbn = skb_dequeue(&sk->protinfo.nr->reseq_queue)) != NULL) {
ns = skbn->data[17];
if (ns == sk->protinfo.nr->vr) {
if (nr_queue_rx_frame(sk, skbn, frametype & NR_MORE_FLAG) == 0) {
sk->protinfo.nr->vr = (sk->protinfo.nr->vr + 1) % NR_MODULUS;
} else {
sk->protinfo.nr->condition |= NR_COND_OWN_RX_BUSY;
skb_queue_tail(&temp_queue, skbn);
}
} else if (nr_in_rx_window(sk, ns)) {
skb_queue_tail(&temp_queue, skbn);
} else {
kfree_skb(skbn);
}
}
while ((skbn = skb_dequeue(&temp_queue)) != NULL) {
skb_queue_tail(&sk->protinfo.nr->reseq_queue, skbn);
}
} while (save_vr != sk->protinfo.nr->vr);
/*
* Window is full, ack it immediately.
*/
if (((sk->protinfo.nr->vl + sk->protinfo.nr->window) % NR_MODULUS) == sk->protinfo.nr->vr) {
nr_enquiry_response(sk);
} else {
if (!(sk->protinfo.nr->condition & NR_COND_ACK_PENDING)) {
sk->protinfo.nr->condition |= NR_COND_ACK_PENDING;
nr_start_t2timer(sk);
}
}
break;
 
default:
break;
}
 
return queued;
}
 
/* Higher level upcall for a LAPB frame */
int nr_process_rx_frame(struct sock *sk, struct sk_buff *skb)
{
int queued = 0, frametype;
 
if (sk->protinfo.nr->state == NR_STATE_0)
return 0;
 
frametype = skb->data[19];
 
switch (sk->protinfo.nr->state) {
case NR_STATE_1:
queued = nr_state1_machine(sk, skb, frametype);
break;
case NR_STATE_2:
queued = nr_state2_machine(sk, skb, frametype);
break;
case NR_STATE_3:
queued = nr_state3_machine(sk, skb, frametype);
break;
}
 
nr_kick(sk);
 
return queued;
}
/nr_out.c
0,0 → 1,272
/*
* NET/ROM release 007
*
* 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
* NET/ROM 001 Jonathan(G4KLX) Cloned from ax25_out.c
* NET/ROM 003 Jonathan(G4KLX) Added NET/ROM fragmentation.
* Darryl(G7LED) Fixed NAK, to give out correct reponse.
* NET/ROM 007 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/uaccess.h>
#include <asm/system.h>
#include <linux/fcntl.h>
#include <linux/mm.h>
#include <linux/interrupt.h>
#include <net/netrom.h>
 
/*
* This is where all NET/ROM frames pass, except for IP-over-NET/ROM which
* cannot be fragmented in this manner.
*/
void nr_output(struct sock *sk, struct sk_buff *skb)
{
struct sk_buff *skbn;
unsigned char transport[NR_TRANSPORT_LEN];
int err, frontlen, len;
 
if (skb->len - NR_TRANSPORT_LEN > NR_MAX_PACKET_SIZE) {
/* Save a copy of the Transport Header */
memcpy(transport, skb->data, NR_TRANSPORT_LEN);
skb_pull(skb, NR_TRANSPORT_LEN);
 
frontlen = skb_headroom(skb);
 
while (skb->len > 0) {
if ((skbn = sock_alloc_send_skb(sk, frontlen + NR_MAX_PACKET_SIZE, 0, &err)) == NULL)
return;
 
skb_reserve(skbn, frontlen);
 
len = (NR_MAX_PACKET_SIZE > skb->len) ? skb->len : NR_MAX_PACKET_SIZE;
 
/* Copy the user data */
memcpy(skb_put(skbn, len), skb->data, len);
skb_pull(skb, len);
 
/* Duplicate the Transport Header */
skb_push(skbn, NR_TRANSPORT_LEN);
memcpy(skbn->data, transport, NR_TRANSPORT_LEN);
 
if (skb->len > 0)
skbn->data[4] |= NR_MORE_FLAG;
 
skb_queue_tail(&sk->write_queue, skbn); /* Throw it on the queue */
}
 
kfree_skb(skb);
} else {
skb_queue_tail(&sk->write_queue, skb); /* Throw it on the queue */
}
 
nr_kick(sk);
}
 
/*
* 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 nr_send_iframe(struct sock *sk, struct sk_buff *skb)
{
if (skb == NULL)
return;
 
skb->data[2] = sk->protinfo.nr->vs;
skb->data[3] = sk->protinfo.nr->vr;
 
if (sk->protinfo.nr->condition & NR_COND_OWN_RX_BUSY)
skb->data[4] |= NR_CHOKE_FLAG;
 
nr_start_idletimer(sk);
 
nr_transmit_buffer(sk, skb);
}
 
void nr_send_nak_frame(struct sock *sk)
{
struct sk_buff *skb, *skbn;
 
if ((skb = skb_peek(&sk->protinfo.nr->ack_queue)) == NULL)
return;
 
if ((skbn = skb_clone(skb, GFP_ATOMIC)) == NULL)
return;
 
skbn->data[2] = sk->protinfo.nr->va;
skbn->data[3] = sk->protinfo.nr->vr;
 
if (sk->protinfo.nr->condition & NR_COND_OWN_RX_BUSY)
skbn->data[4] |= NR_CHOKE_FLAG;
 
nr_transmit_buffer(sk, skbn);
 
sk->protinfo.nr->condition &= ~NR_COND_ACK_PENDING;
sk->protinfo.nr->vl = sk->protinfo.nr->vr;
 
nr_stop_t1timer(sk);
}
 
void nr_kick(struct sock *sk)
{
struct sk_buff *skb, *skbn;
unsigned short start, end;
 
if (sk->protinfo.nr->state != NR_STATE_3)
return;
 
if (sk->protinfo.nr->condition & NR_COND_PEER_RX_BUSY)
return;
 
if (skb_peek(&sk->write_queue) == NULL)
return;
 
start = (skb_peek(&sk->protinfo.nr->ack_queue) == NULL) ? sk->protinfo.nr->va : sk->protinfo.nr->vs;
end = (sk->protinfo.nr->va + sk->protinfo.nr->window) % NR_MODULUS;
 
if (start == end)
return;
 
sk->protinfo.nr->vs = start;
 
/*
* Transmit data until either we're out of data to send or
* the window is full.
*/
 
/*
* Dequeue the frame and copy it.
*/
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.
*/
nr_send_iframe(sk, skbn);
 
sk->protinfo.nr->vs = (sk->protinfo.nr->vs + 1) % NR_MODULUS;
 
/*
* Requeue the original data frame.
*/
skb_queue_tail(&sk->protinfo.nr->ack_queue, skb);
 
} while (sk->protinfo.nr->vs != end && (skb = skb_dequeue(&sk->write_queue)) != NULL);
 
sk->protinfo.nr->vl = sk->protinfo.nr->vr;
sk->protinfo.nr->condition &= ~NR_COND_ACK_PENDING;
 
if (!nr_t1timer_running(sk))
nr_start_t1timer(sk);
}
 
void nr_transmit_buffer(struct sock *sk, struct sk_buff *skb)
{
unsigned char *dptr;
 
/*
* Add the protocol byte and network header.
*/
dptr = skb_push(skb, NR_NETWORK_LEN);
 
memcpy(dptr, &sk->protinfo.nr->source_addr, AX25_ADDR_LEN);
dptr[6] &= ~AX25_CBIT;
dptr[6] &= ~AX25_EBIT;
dptr[6] |= AX25_SSSID_SPARE;
dptr += AX25_ADDR_LEN;
 
memcpy(dptr, &sk->protinfo.nr->dest_addr, AX25_ADDR_LEN);
dptr[6] &= ~AX25_CBIT;
dptr[6] |= AX25_EBIT;
dptr[6] |= AX25_SSSID_SPARE;
dptr += AX25_ADDR_LEN;
 
*dptr++ = sysctl_netrom_network_ttl_initialiser;
 
if (!nr_route_frame(skb, NULL)) {
kfree_skb(skb);
nr_disconnect(sk, ENETUNREACH);
}
}
 
/*
* The following routines are taken from page 170 of the 7th ARRL Computer
* Networking Conference paper, as is the whole state machine.
*/
 
void nr_establish_data_link(struct sock *sk)
{
sk->protinfo.nr->condition = 0x00;
sk->protinfo.nr->n2count = 0;
 
nr_write_internal(sk, NR_CONNREQ);
 
nr_stop_t2timer(sk);
nr_stop_t4timer(sk);
nr_stop_idletimer(sk);
nr_start_t1timer(sk);
}
 
/*
* Never send a NAK when we are CHOKEd.
*/
void nr_enquiry_response(struct sock *sk)
{
int frametype = NR_INFOACK;
 
if (sk->protinfo.nr->condition & NR_COND_OWN_RX_BUSY) {
frametype |= NR_CHOKE_FLAG;
} else {
if (skb_peek(&sk->protinfo.nr->reseq_queue) != NULL)
frametype |= NR_NAK_FLAG;
}
 
nr_write_internal(sk, frametype);
 
sk->protinfo.nr->vl = sk->protinfo.nr->vr;
sk->protinfo.nr->condition &= ~NR_COND_ACK_PENDING;
}
 
void nr_check_iframes_acked(struct sock *sk, unsigned short nr)
{
if (sk->protinfo.nr->vs == nr) {
nr_frames_acked(sk, nr);
nr_stop_t1timer(sk);
sk->protinfo.nr->n2count = 0;
} else {
if (sk->protinfo.nr->va != nr) {
nr_frames_acked(sk, nr);
nr_start_t1timer(sk);
}
}
}
/nr_loopback.c
0,0 → 1,100
/*
* NET/ROM release 007
*
* 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
* NET/ROM 007 Tomi(OH2BNS) Created this file.
* Small change in nr_loopback_queue().
*
*/
 
#include <linux/types.h>
#include <linux/socket.h>
#include <linux/timer.h>
#include <net/ax25.h>
#include <linux/skbuff.h>
#include <net/netrom.h>
#include <linux/init.h>
 
static struct sk_buff_head loopback_queue;
static struct timer_list loopback_timer;
 
static void nr_set_loopback_timer(void);
 
void nr_loopback_init(void)
{
skb_queue_head_init(&loopback_queue);
 
init_timer(&loopback_timer);
}
 
static int nr_loopback_running(void)
{
return timer_pending(&loopback_timer);
}
 
int nr_loopback_queue(struct sk_buff *skb)
{
struct sk_buff *skbn;
 
if ((skbn = alloc_skb(skb->len, GFP_ATOMIC)) != NULL) {
memcpy(skb_put(skbn, skb->len), skb->data, skb->len);
skbn->h.raw = skbn->data;
 
skb_queue_tail(&loopback_queue, skbn);
 
if (!nr_loopback_running())
nr_set_loopback_timer();
}
 
kfree_skb(skb);
return 1;
}
 
static void nr_loopback_timer(unsigned long);
 
static void nr_set_loopback_timer(void)
{
del_timer(&loopback_timer);
 
loopback_timer.data = 0;
loopback_timer.function = &nr_loopback_timer;
loopback_timer.expires = jiffies + 10;
 
add_timer(&loopback_timer);
}
 
static void nr_loopback_timer(unsigned long param)
{
struct sk_buff *skb;
ax25_address *nr_dest;
struct net_device *dev;
 
if ((skb = skb_dequeue(&loopback_queue)) != NULL) {
nr_dest = (ax25_address *)(skb->data + 7);
 
dev = nr_dev_get(nr_dest);
 
if (dev == NULL || nr_rx_frame(skb, dev) == 0)
kfree_skb(skb);
 
if (dev != NULL)
dev_put(dev);
 
if (!skb_queue_empty(&loopback_queue) && !nr_loopback_running())
nr_set_loopback_timer();
}
}
 
void __exit nr_loopback_clear(void)
{
del_timer(&loopback_timer);
skb_queue_purge(&loopback_queue);
}
/nr_subr.c
0,0 → 1,288
/*
* NET/ROM release 007
*
* 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
* NET/ROM 001 Jonathan(G4KLX) Cloned from ax25_subr.c
* NET/ROM 003 Jonathan(G4KLX) Added G8BPQ NET/ROM extensions.
* NET/ROM 007 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/uaccess.h>
#include <asm/system.h>
#include <linux/fcntl.h>
#include <linux/mm.h>
#include <linux/interrupt.h>
#include <net/netrom.h>
 
/*
* This routine purges all of the queues of frames.
*/
void nr_clear_queues(struct sock *sk)
{
skb_queue_purge(&sk->write_queue);
skb_queue_purge(&sk->protinfo.nr->ack_queue);
skb_queue_purge(&sk->protinfo.nr->reseq_queue);
skb_queue_purge(&sk->protinfo.nr->frag_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 nr_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.nr->va != nr) {
while (skb_peek(&sk->protinfo.nr->ack_queue) != NULL && sk->protinfo.nr->va != nr) {
skb = skb_dequeue(&sk->protinfo.nr->ack_queue);
kfree_skb(skb);
sk->protinfo.nr->va = (sk->protinfo.nr->va + 1) % NR_MODULUS;
}
}
}
 
/*
* Requeue all the un-ack-ed frames on the output queue to be picked
* up by nr_kick called from the timer. This arrangement handles the
* possibility of an empty output queue.
*/
void nr_requeue_frames(struct sock *sk)
{
struct sk_buff *skb, *skb_prev = NULL;
 
while ((skb = skb_dequeue(&sk->protinfo.nr->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 nr_validate_nr(struct sock *sk, unsigned short nr)
{
unsigned short vc = sk->protinfo.nr->va;
 
while (vc != sk->protinfo.nr->vs) {
if (nr == vc) return 1;
vc = (vc + 1) % NR_MODULUS;
}
 
if (nr == sk->protinfo.nr->vs) return 1;
 
return 0;
}
 
/*
* Check that ns is within the receive window.
*/
int nr_in_rx_window(struct sock *sk, unsigned short ns)
{
unsigned short vc = sk->protinfo.nr->vr;
unsigned short vt = (sk->protinfo.nr->vl + sk->protinfo.nr->window) % NR_MODULUS;
 
while (vc != vt) {
if (ns == vc) return 1;
vc = (vc + 1) % NR_MODULUS;
}
 
return 0;
}
 
/*
* This routine is called when the HDLC layer internally generates a
* control frame.
*/
void nr_write_internal(struct sock *sk, int frametype)
{
struct sk_buff *skb;
unsigned char *dptr;
int len, timeout;
 
len = AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN + NR_NETWORK_LEN + NR_TRANSPORT_LEN;
 
switch (frametype & 0x0F) {
case NR_CONNREQ:
len += 17;
break;
case NR_CONNACK:
len += (sk->protinfo.nr->bpqext) ? 2 : 1;
break;
case NR_DISCREQ:
case NR_DISCACK:
case NR_INFOACK:
break;
default:
printk(KERN_ERR "NET/ROM: nr_write_internal - invalid frame type %d\n", frametype);
return;
}
 
if ((skb = alloc_skb(len, GFP_ATOMIC)) == NULL)
return;
 
/*
* Space for AX.25 and NET/ROM network header
*/
skb_reserve(skb, AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN + NR_NETWORK_LEN);
dptr = skb_put(skb, skb_tailroom(skb));
 
switch (frametype & 0x0F) {
 
case NR_CONNREQ:
timeout = sk->protinfo.nr->t1 / HZ;
*dptr++ = sk->protinfo.nr->my_index;
*dptr++ = sk->protinfo.nr->my_id;
*dptr++ = 0;
*dptr++ = 0;
*dptr++ = frametype;
*dptr++ = sk->protinfo.nr->window;
memcpy(dptr, &sk->protinfo.nr->user_addr, AX25_ADDR_LEN);
dptr[6] &= ~AX25_CBIT;
dptr[6] &= ~AX25_EBIT;
dptr[6] |= AX25_SSSID_SPARE;
dptr += AX25_ADDR_LEN;
memcpy(dptr, &sk->protinfo.nr->source_addr, AX25_ADDR_LEN);
dptr[6] &= ~AX25_CBIT;
dptr[6] &= ~AX25_EBIT;
dptr[6] |= AX25_SSSID_SPARE;
dptr += AX25_ADDR_LEN;
*dptr++ = timeout % 256;
*dptr++ = timeout / 256;
break;
 
case NR_CONNACK:
*dptr++ = sk->protinfo.nr->your_index;
*dptr++ = sk->protinfo.nr->your_id;
*dptr++ = sk->protinfo.nr->my_index;
*dptr++ = sk->protinfo.nr->my_id;
*dptr++ = frametype;
*dptr++ = sk->protinfo.nr->window;
if (sk->protinfo.nr->bpqext) *dptr++ = sysctl_netrom_network_ttl_initialiser;
break;
 
case NR_DISCREQ:
case NR_DISCACK:
*dptr++ = sk->protinfo.nr->your_index;
*dptr++ = sk->protinfo.nr->your_id;
*dptr++ = 0;
*dptr++ = 0;
*dptr++ = frametype;
break;
 
case NR_INFOACK:
*dptr++ = sk->protinfo.nr->your_index;
*dptr++ = sk->protinfo.nr->your_id;
*dptr++ = 0;
*dptr++ = sk->protinfo.nr->vr;
*dptr++ = frametype;
break;
}
 
nr_transmit_buffer(sk, skb);
}
 
/*
* This routine is called when a Connect Acknowledge with the Choke Flag
* set is needed to refuse a connection.
*/
void nr_transmit_refusal(struct sk_buff *skb, int mine)
{
struct sk_buff *skbn;
unsigned char *dptr;
int len;
 
len = AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN + NR_NETWORK_LEN + NR_TRANSPORT_LEN + 1;
 
if ((skbn = alloc_skb(len, GFP_ATOMIC)) == NULL)
return;
 
skb_reserve(skbn, AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN);
 
dptr = skb_put(skbn, NR_NETWORK_LEN + NR_TRANSPORT_LEN);
 
memcpy(dptr, skb->data + 7, AX25_ADDR_LEN);
dptr[6] &= ~AX25_CBIT;
dptr[6] &= ~AX25_EBIT;
dptr[6] |= AX25_SSSID_SPARE;
dptr += AX25_ADDR_LEN;
memcpy(dptr, skb->data + 0, AX25_ADDR_LEN);
dptr[6] &= ~AX25_CBIT;
dptr[6] |= AX25_EBIT;
dptr[6] |= AX25_SSSID_SPARE;
dptr += AX25_ADDR_LEN;
 
*dptr++ = sysctl_netrom_network_ttl_initialiser;
 
if (mine) {
*dptr++ = 0;
*dptr++ = 0;
*dptr++ = skb->data[15];
*dptr++ = skb->data[16];
} else {
*dptr++ = skb->data[15];
*dptr++ = skb->data[16];
*dptr++ = 0;
*dptr++ = 0;
}
 
*dptr++ = NR_CONNACK | NR_CHOKE_FLAG;
*dptr++ = 0;
 
if (!nr_route_frame(skbn, NULL))
kfree_skb(skbn);
}
 
void nr_disconnect(struct sock *sk, int reason)
{
nr_stop_t1timer(sk);
nr_stop_t2timer(sk);
nr_stop_t4timer(sk);
nr_stop_idletimer(sk);
 
nr_clear_queues(sk);
 
sk->protinfo.nr->state = NR_STATE_0;
 
sk->state = TCP_CLOSE;
sk->err = reason;
sk->shutdown |= SEND_SHUTDOWN;
 
if (!sk->dead)
sk->state_change(sk);
 
sk->dead = 1;
}
/af_netrom.c
0,0 → 1,1384
/*
* NET/ROM release 007
*
* 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
* NET/ROM 001 Jonathan(G4KLX) Cloned from the AX25 code.
* NET/ROM 002 Darryl(G7LED) Fixes and address enhancement.
* Jonathan(G4KLX) Complete bind re-think.
* Alan(GW4PTS) Trivial tweaks into new format.
* NET/ROM 003 Jonathan(G4KLX) Added G8BPQ extensions.
* Added NET/ROM routing ioctl.
* Darryl(G7LED) Fix autobinding (on connect).
* Fixed nr_release(), set TCP_CLOSE, wakeup app
* context, THEN make the sock dead.
* Circuit ID check before allocating it on
* a connection.
* Alan(GW4PTS) sendmsg/recvmsg only. Fixed connect clear bug
* inherited from AX.25
* NET/ROM 004 Jonathan(G4KLX) Converted to module.
* NET/ROM 005 Jonathan(G4KLX) Linux 2.1
* Alan(GW4PTS) Started POSIXisms
* NET/ROM 006 Alan(GW4PTS) Brought in line with the ANK changes
* Jonathan(G4KLX) Removed hdrincl.
* NET/ROM 007 Jonathan(G4KLX) New timer architecture.
* Impmented Idle timer.
* Arnaldo C. Melo s/suser/capable/, micro cleanups
* Jeroen(PE1RXQ) Use sock_orphan() on release.
* Tomi(OH2BNS) Better frame type checking.
* Device refcnt fixes.
*/
 
#include <linux/config.h>
#include <linux/module.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/uaccess.h>
#include <asm/system.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/netrom.h>
#include <linux/proc_fs.h>
#include <net/ip.h>
#include <net/arp.h>
#include <linux/init.h>
 
int nr_ndevs = 4;
 
int sysctl_netrom_default_path_quality = NR_DEFAULT_QUAL;
int sysctl_netrom_obsolescence_count_initialiser = NR_DEFAULT_OBS;
int sysctl_netrom_network_ttl_initialiser = NR_DEFAULT_TTL;
int sysctl_netrom_transport_timeout = NR_DEFAULT_T1;
int sysctl_netrom_transport_maximum_tries = NR_DEFAULT_N2;
int sysctl_netrom_transport_acknowledge_delay = NR_DEFAULT_T2;
int sysctl_netrom_transport_busy_delay = NR_DEFAULT_T4;
int sysctl_netrom_transport_requested_window_size = NR_DEFAULT_WINDOW;
int sysctl_netrom_transport_no_activity_timeout = NR_DEFAULT_IDLE;
int sysctl_netrom_routing_control = NR_DEFAULT_ROUTING;
int sysctl_netrom_link_fails_count = NR_DEFAULT_FAILS;
 
static unsigned short circuit = 0x101;
 
static struct sock *volatile nr_list;
 
static struct proto_ops nr_proto_ops;
 
static void nr_free_sock(struct sock *sk)
{
sk_free(sk);
 
MOD_DEC_USE_COUNT;
}
 
static struct sock *nr_alloc_sock(void)
{
struct sock *sk;
nr_cb *nr;
 
if ((sk = sk_alloc(PF_NETROM, GFP_ATOMIC, 1)) == NULL)
return NULL;
 
if ((nr = kmalloc(sizeof(*nr), GFP_ATOMIC)) == NULL) {
sk_free(sk);
return NULL;
}
 
MOD_INC_USE_COUNT;
 
memset(nr, 0x00, sizeof(*nr));
 
sk->protinfo.nr = nr;
nr->sk = sk;
 
return sk;
}
 
/*
* Socket removal during an interrupt is now safe.
*/
static void nr_remove_socket(struct sock *sk)
{
struct sock *s;
unsigned long flags;
 
save_flags(flags); cli();
 
if ((s = nr_list) == sk) {
nr_list = s->next;
dev_put(sk->protinfo.nr->device);
restore_flags(flags);
return;
}
 
while (s != NULL && s->next != NULL) {
if (s->next == sk) {
s->next = sk->next;
dev_put(sk->protinfo.nr->device);
restore_flags(flags);
return;
}
 
s = s->next;
}
 
restore_flags(flags);
}
 
/*
* Kill all bound sockets on a dropped device.
*/
static void nr_kill_by_device(struct net_device *dev)
{
struct sock *s;
 
for (s = nr_list; s != NULL; s = s->next) {
if (s->protinfo.nr->device == dev)
nr_disconnect(s, ENETUNREACH);
}
}
 
/*
* Handle device status changes.
*/
static int nr_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;
 
nr_kill_by_device(dev);
nr_rt_device_down(dev);
return NOTIFY_DONE;
}
 
/*
* Add a socket to the bound sockets list.
*/
static void nr_insert_socket(struct sock *sk)
{
unsigned long flags;
 
save_flags(flags); cli();
 
sk->next = nr_list;
nr_list = sk;
 
restore_flags(flags);
}
 
/*
* Find a socket that wants to accept the Connect Request we just
* received.
*/
static struct sock *nr_find_listener(ax25_address *addr)
{
unsigned long flags;
struct sock *s;
 
save_flags(flags);
cli();
 
for (s = nr_list; s != NULL; s = s->next) {
if (ax25cmp(&s->protinfo.nr->source_addr, addr) == 0 && s->state == TCP_LISTEN) {
restore_flags(flags);
return s;
}
}
 
restore_flags(flags);
return NULL;
}
 
/*
* Find a connected NET/ROM socket given my circuit IDs.
*/
static struct sock *nr_find_socket(unsigned char index, unsigned char id)
{
struct sock *s;
unsigned long flags;
 
save_flags(flags);
cli();
 
for (s = nr_list; s != NULL; s = s->next) {
if (s->protinfo.nr->my_index == index && s->protinfo.nr->my_id == id) {
restore_flags(flags);
return s;
}
}
 
restore_flags(flags);
 
return NULL;
}
 
/*
* Find a connected NET/ROM socket given their circuit IDs.
*/
static struct sock *nr_find_peer(unsigned char index, unsigned char id, ax25_address *dest)
{
struct sock *s;
unsigned long flags;
 
save_flags(flags);
cli();
 
for (s = nr_list; s != NULL; s = s->next) {
if (s->protinfo.nr->your_index == index && s->protinfo.nr->your_id == id && ax25cmp(&s->protinfo.nr->dest_addr, dest) == 0) {
restore_flags(flags);
return s;
}
}
 
restore_flags(flags);
 
return NULL;
}
 
/*
* Find next free circuit ID.
*/
static unsigned short nr_find_next_circuit(void)
{
unsigned short id = circuit;
unsigned char i, j;
 
for (;;) {
i = id / 256;
j = id % 256;
 
if (i != 0 && j != 0)
if (nr_find_socket(i, j) == NULL)
break;
 
id++;
}
 
return id;
}
 
/*
* Deferred destroy.
*/
void nr_destroy_socket(struct sock *);
 
/*
* Handler for deferred kills.
*/
static void nr_destroy_timer(unsigned long data)
{
nr_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 nr_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();
 
nr_stop_heartbeat(sk);
nr_stop_t1timer(sk);
nr_stop_t2timer(sk);
nr_stop_t4timer(sk);
nr_stop_idletimer(sk);
 
nr_remove_socket(sk);
nr_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 */
nr_start_heartbeat(skb->sk);
skb->sk->protinfo.nr->state = NR_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 = nr_destroy_timer;
sk->timer.data = (unsigned long)sk;
add_timer(&sk->timer);
} else {
nr_free_sock(sk);
}
 
restore_flags(flags);
}
 
/*
* Handling for system calls applied via the various interfaces to a
* NET/ROM socket object.
*/
 
static int nr_setsockopt(struct socket *sock, int level, int optname,
char *optval, int optlen)
{
struct sock *sk = sock->sk;
int opt;
 
if (level != SOL_NETROM)
return -ENOPROTOOPT;
 
if (optlen < sizeof(int))
return -EINVAL;
 
if (get_user(opt, (int *)optval))
return -EFAULT;
 
switch (optname) {
case NETROM_T1:
if (opt < 1)
return -EINVAL;
sk->protinfo.nr->t1 = opt * HZ;
return 0;
 
case NETROM_T2:
if (opt < 1)
return -EINVAL;
sk->protinfo.nr->t2 = opt * HZ;
return 0;
 
case NETROM_N2:
if (opt < 1 || opt > 31)
return -EINVAL;
sk->protinfo.nr->n2 = opt;
return 0;
 
case NETROM_T4:
if (opt < 1)
return -EINVAL;
sk->protinfo.nr->t4 = opt * HZ;
return 0;
 
case NETROM_IDLE:
if (opt < 0)
return -EINVAL;
sk->protinfo.nr->idle = opt * 60 * HZ;
return 0;
 
default:
return -ENOPROTOOPT;
}
}
 
static int nr_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_NETROM)
return -ENOPROTOOPT;
if (get_user(len, optlen))
return -EFAULT;
 
if (len < 0)
return -EINVAL;
switch (optname) {
case NETROM_T1:
val = sk->protinfo.nr->t1 / HZ;
break;
 
case NETROM_T2:
val = sk->protinfo.nr->t2 / HZ;
break;
 
case NETROM_N2:
val = sk->protinfo.nr->n2;
break;
 
case NETROM_T4:
val = sk->protinfo.nr->t4 / HZ;
break;
 
case NETROM_IDLE:
val = sk->protinfo.nr->idle / (60 * HZ);
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 nr_listen(struct socket *sock, int backlog)
{
struct sock *sk = sock->sk;
 
if (sk->state != TCP_LISTEN) {
memset(&sk->protinfo.nr->user_addr, '\0', AX25_ADDR_LEN);
sk->max_ack_backlog = backlog;
sk->state = TCP_LISTEN;
return 0;
}
 
return -EOPNOTSUPP;
}
 
static int nr_create(struct socket *sock, int protocol)
{
struct sock *sk;
nr_cb *nr;
 
if (sock->type != SOCK_SEQPACKET || protocol != 0)
return -ESOCKTNOSUPPORT;
 
if ((sk = nr_alloc_sock()) == NULL)
return -ENOMEM;
 
nr = sk->protinfo.nr;
 
sock_init_data(sock, sk);
 
sock->ops = &nr_proto_ops;
sk->protocol = protocol;
 
skb_queue_head_init(&nr->ack_queue);
skb_queue_head_init(&nr->reseq_queue);
skb_queue_head_init(&nr->frag_queue);
 
init_timer(&nr->t1timer);
init_timer(&nr->t2timer);
init_timer(&nr->t4timer);
init_timer(&nr->idletimer);
 
nr->t1 = sysctl_netrom_transport_timeout;
nr->t2 = sysctl_netrom_transport_acknowledge_delay;
nr->n2 = sysctl_netrom_transport_maximum_tries;
nr->t4 = sysctl_netrom_transport_busy_delay;
nr->idle = sysctl_netrom_transport_no_activity_timeout;
nr->window = sysctl_netrom_transport_requested_window_size;
 
nr->bpqext = 1;
nr->state = NR_STATE_0;
 
return 0;
}
 
static struct sock *nr_make_new(struct sock *osk)
{
struct sock *sk;
nr_cb *nr;
 
if (osk->type != SOCK_SEQPACKET)
return NULL;
 
if ((sk = nr_alloc_sock()) == NULL)
return NULL;
 
nr = sk->protinfo.nr;
 
sock_init_data(NULL, sk);
 
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;
 
skb_queue_head_init(&nr->ack_queue);
skb_queue_head_init(&nr->reseq_queue);
skb_queue_head_init(&nr->frag_queue);
 
init_timer(&nr->t1timer);
init_timer(&nr->t2timer);
init_timer(&nr->t4timer);
init_timer(&nr->idletimer);
 
nr->t1 = osk->protinfo.nr->t1;
nr->t2 = osk->protinfo.nr->t2;
nr->n2 = osk->protinfo.nr->n2;
nr->t4 = osk->protinfo.nr->t4;
nr->idle = osk->protinfo.nr->idle;
nr->window = osk->protinfo.nr->window;
 
nr->device = osk->protinfo.nr->device;
nr->bpqext = osk->protinfo.nr->bpqext;
 
return sk;
}
 
static int nr_release(struct socket *sock)
{
struct sock *sk = sock->sk;
 
if (sk == NULL) return 0;
 
switch (sk->protinfo.nr->state) {
 
case NR_STATE_0:
case NR_STATE_1:
case NR_STATE_2:
nr_disconnect(sk, 0);
nr_destroy_socket(sk);
break;
 
case NR_STATE_3:
nr_clear_queues(sk);
sk->protinfo.nr->n2count = 0;
nr_write_internal(sk, NR_DISCREQ);
nr_start_t1timer(sk);
nr_stop_t2timer(sk);
nr_stop_t4timer(sk);
nr_stop_idletimer(sk);
sk->protinfo.nr->state = NR_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 nr_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
{
struct sock *sk = sock->sk;
struct full_sockaddr_ax25 *addr = (struct full_sockaddr_ax25 *)uaddr;
struct net_device *dev;
ax25_address *user, *source;
 
if (sk->zapped == 0)
return -EINVAL;
 
if (addr_len < sizeof(struct sockaddr_ax25) || addr_len > sizeof(struct
full_sockaddr_ax25))
return -EINVAL;
 
if (addr_len < (addr->fsa_ax25.sax25_ndigis * sizeof(ax25_address) + sizeof(struct sockaddr_ax25)))
return -EINVAL;
 
if (addr->fsa_ax25.sax25_family != AF_NETROM)
return -EINVAL;
 
if ((dev = nr_dev_get(&addr->fsa_ax25.sax25_call)) == NULL) {
SOCK_DEBUG(sk, "NET/ROM: bind failed: invalid node callsign\n");
return -EADDRNOTAVAIL;
}
 
/*
* Only the super user can set an arbitrary user callsign.
*/
if (addr->fsa_ax25.sax25_ndigis == 1) {
if (!capable(CAP_NET_BIND_SERVICE)) {
dev_put(dev);
return -EACCES;
}
sk->protinfo.nr->user_addr = addr->fsa_digipeater[0];
sk->protinfo.nr->source_addr = addr->fsa_ax25.sax25_call;
} else {
source = &addr->fsa_ax25.sax25_call;
 
if ((user = ax25_findbyuid(current->euid)) == NULL) {
if (ax25_uid_policy && !capable(CAP_NET_BIND_SERVICE)) {
dev_put(dev);
return -EPERM;
}
user = source;
}
 
sk->protinfo.nr->user_addr = *user;
sk->protinfo.nr->source_addr = *source;
}
 
sk->protinfo.nr->device = dev;
nr_insert_socket(sk);
 
sk->zapped = 0;
SOCK_DEBUG(sk, "NET/ROM: socket is bound\n");
return 0;
}
 
static int nr_connect(struct socket *sock, struct sockaddr *uaddr,
int addr_len, int flags)
{
struct sock *sk = sock->sk;
struct sockaddr_ax25 *addr = (struct sockaddr_ax25 *)uaddr;
ax25_address *user, *source = NULL;
struct net_device *dev;
 
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_ax25) && addr_len != sizeof(struct full_sockaddr_ax25))
return -EINVAL;
 
if (addr->sax25_family != AF_NETROM)
return -EINVAL;
 
if (sk->zapped) { /* Must bind first - autobinding in this may or may not work */
sk->zapped = 0;
 
if ((dev = nr_dev_first()) == NULL)
return -ENETUNREACH;
 
source = (ax25_address *)dev->dev_addr;
 
if ((user = ax25_findbyuid(current->euid)) == NULL) {
if (ax25_uid_policy && !capable(CAP_NET_ADMIN)) {
dev_put(dev);
return -EPERM;
}
user = source;
}
 
sk->protinfo.nr->user_addr = *user;
sk->protinfo.nr->source_addr = *source;
sk->protinfo.nr->device = dev;
 
nr_insert_socket(sk); /* Finish the bind */
}
 
sk->protinfo.nr->dest_addr = addr->sax25_call;
 
circuit = nr_find_next_circuit();
 
sk->protinfo.nr->my_index = circuit / 256;
sk->protinfo.nr->my_id = circuit % 256;
 
circuit++;
 
/* Move to connecting socket, start sending Connect Requests */
sock->state = SS_CONNECTING;
sk->state = TCP_SYN_SENT;
 
nr_establish_data_link(sk);
 
sk->protinfo.nr->state = NR_STATE_1;
 
nr_start_heartbeat(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 nr_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 */
kfree_skb(skb);
sk->ack_backlog--;
newsock->sk = newsk;
 
return 0;
}
 
static int nr_getname(struct socket *sock, struct sockaddr *uaddr,
int *uaddr_len, int peer)
{
struct full_sockaddr_ax25 *sax = (struct full_sockaddr_ax25 *)uaddr;
struct sock *sk = sock->sk;
 
if (peer != 0) {
if (sk->state != TCP_ESTABLISHED)
return -ENOTCONN;
sax->fsa_ax25.sax25_family = AF_NETROM;
sax->fsa_ax25.sax25_ndigis = 1;
sax->fsa_ax25.sax25_call = sk->protinfo.nr->user_addr;
sax->fsa_digipeater[0] = sk->protinfo.nr->dest_addr;
*uaddr_len = sizeof(struct full_sockaddr_ax25);
} else {
sax->fsa_ax25.sax25_family = AF_NETROM;
sax->fsa_ax25.sax25_ndigis = 0;
sax->fsa_ax25.sax25_call = sk->protinfo.nr->source_addr;
*uaddr_len = sizeof(struct sockaddr_ax25);
}
 
return 0;
}
 
int nr_rx_frame(struct sk_buff *skb, struct net_device *dev)
{
struct sock *sk;
struct sock *make;
ax25_address *src, *dest, *user;
unsigned short circuit_index, circuit_id;
unsigned short peer_circuit_index, peer_circuit_id;
unsigned short frametype, flags, window, timeout;
 
skb->sk = NULL; /* Initially we don't know who it's for */
 
/*
* skb->data points to the netrom frame start
*/
 
src = (ax25_address *)(skb->data + 0);
dest = (ax25_address *)(skb->data + 7);
 
circuit_index = skb->data[15];
circuit_id = skb->data[16];
peer_circuit_index = skb->data[17];
peer_circuit_id = skb->data[18];
frametype = skb->data[19] & 0x0F;
flags = skb->data[19] & 0xF0;
 
switch (frametype) {
case NR_PROTOEXT:
#ifdef CONFIG_INET
/*
* Check for an incoming IP over NET/ROM frame.
*/
if (circuit_index == NR_PROTO_IP && circuit_id == NR_PROTO_IP) {
skb_pull(skb, NR_NETWORK_LEN + NR_TRANSPORT_LEN);
skb->h.raw = skb->data;
 
return nr_rx_ip(skb, dev);
}
#endif
return 0;
 
case NR_CONNREQ:
case NR_CONNACK:
case NR_DISCREQ:
case NR_DISCACK:
case NR_INFO:
case NR_INFOACK:
/*
* These frame types we understand.
*/
break;
 
default:
/*
* Everything else is ignored.
*/
return 0;
}
 
/*
* Find an existing socket connection, based on circuit ID, if it's
* a Connect Request base it on their circuit ID.
*
* Circuit ID 0/0 is not valid but it could still be a "reset" for a
* circuit that no longer exists at the other end ...
*/
 
sk = NULL;
 
if (circuit_index == 0 && circuit_id == 0) {
if (frametype == NR_CONNACK && flags == NR_CHOKE_FLAG)
sk = nr_find_peer(peer_circuit_index, peer_circuit_id, src);
} else {
if (frametype == NR_CONNREQ)
sk = nr_find_peer(circuit_index, circuit_id, src);
else
sk = nr_find_socket(circuit_index, circuit_id);
}
 
if (sk != NULL) {
skb->h.raw = skb->data;
 
if (frametype == NR_CONNACK && skb->len == 22)
sk->protinfo.nr->bpqext = 1;
else
sk->protinfo.nr->bpqext = 0;
 
return nr_process_rx_frame(sk, skb);
}
 
/*
* Now it should be a CONNREQ.
*/
if (frametype != NR_CONNREQ) {
/*
* Here it would be nice to be able to send a reset but
* NET/ROM doesn't have one. The following hack would
* have been a way to extend the protocol but apparently
* it kills BPQ boxes... :-(
*/
#if 0
/*
* Never reply to a CONNACK/CHOKE.
*/
if (frametype != NR_CONNACK || flags != NR_CHOKE_FLAG)
nr_transmit_refusal(skb, 1);
#endif
return 0;
}
 
sk = nr_find_listener(dest);
 
user = (ax25_address *)(skb->data + 21);
 
if (sk == NULL || sk->ack_backlog == sk->max_ack_backlog || (make = nr_make_new(sk)) == NULL) {
nr_transmit_refusal(skb, 0);
return 0;
}
 
window = skb->data[20];
 
skb->sk = make;
make->state = TCP_ESTABLISHED;
 
/* Fill in his circuit details */
make->protinfo.nr->source_addr = *dest;
make->protinfo.nr->dest_addr = *src;
make->protinfo.nr->user_addr = *user;
 
make->protinfo.nr->your_index = circuit_index;
make->protinfo.nr->your_id = circuit_id;
 
circuit = nr_find_next_circuit();
 
make->protinfo.nr->my_index = circuit / 256;
make->protinfo.nr->my_id = circuit % 256;
 
circuit++;
 
/* Window negotiation */
if (window < make->protinfo.nr->window)
make->protinfo.nr->window = window;
 
/* L4 timeout negotiation */
if (skb->len == 37) {
timeout = skb->data[36] * 256 + skb->data[35];
if (timeout * HZ < make->protinfo.nr->t1)
make->protinfo.nr->t1 = timeout * HZ;
make->protinfo.nr->bpqext = 1;
} else {
make->protinfo.nr->bpqext = 0;
}
 
nr_write_internal(make, NR_CONNACK);
 
make->protinfo.nr->condition = 0x00;
make->protinfo.nr->vs = 0;
make->protinfo.nr->va = 0;
make->protinfo.nr->vr = 0;
make->protinfo.nr->vl = 0;
make->protinfo.nr->state = NR_STATE_3;
sk->ack_backlog++;
make->pair = sk;
 
dev_hold(make->protinfo.nr->device);
 
nr_insert_socket(make);
 
skb_queue_head(&sk->receive_queue, skb);
 
nr_start_heartbeat(make);
nr_start_idletimer(make);
 
if (!sk->dead)
sk->data_ready(sk, skb->len);
 
return 1;
}
 
static int nr_sendmsg(struct socket *sock, struct msghdr *msg, int len, struct scm_cookie *scm)
{
struct sock *sk = sock->sk;
struct sockaddr_ax25 *usax = (struct sockaddr_ax25 *)msg->msg_name;
int err;
struct sockaddr_ax25 sax;
struct sk_buff *skb;
unsigned char *asmptr;
int size;
 
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.nr->device == NULL)
return -ENETUNREACH;
 
if (usax) {
if (msg->msg_namelen < sizeof(sax))
return -EINVAL;
sax = *usax;
if (ax25cmp(&sk->protinfo.nr->dest_addr, &sax.sax25_call) != 0)
return -EISCONN;
if (sax.sax25_family != AF_NETROM)
return -EINVAL;
} else {
if (sk->state != TCP_ESTABLISHED)
return -ENOTCONN;
sax.sax25_family = AF_NETROM;
sax.sax25_call = sk->protinfo.nr->dest_addr;
}
 
SOCK_DEBUG(sk, "NET/ROM: sendto: Addresses built.\n");
 
/* Build a packet */
SOCK_DEBUG(sk, "NET/ROM: sendto: building packet.\n");
size = len + AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN + NR_NETWORK_LEN + NR_TRANSPORT_LEN;
 
if ((skb = sock_alloc_send_skb(sk, size, msg->msg_flags & MSG_DONTWAIT, &err)) == NULL)
return err;
 
skb_reserve(skb, size - len);
 
/*
* Push down the NET/ROM header
*/
 
asmptr = skb_push(skb, NR_TRANSPORT_LEN);
SOCK_DEBUG(sk, "Building NET/ROM Header.\n");
 
/* Build a NET/ROM Transport header */
 
*asmptr++ = sk->protinfo.nr->your_index;
*asmptr++ = sk->protinfo.nr->your_id;
*asmptr++ = 0; /* To be filled in later */
*asmptr++ = 0; /* Ditto */
*asmptr++ = NR_INFO;
SOCK_DEBUG(sk, "Built header.\n");
 
/*
* Put the data on the end
*/
 
skb->h.raw = skb_put(skb, len);
 
asmptr = skb->h.raw;
SOCK_DEBUG(sk, "NET/ROM: Appending user data\n");
 
/* User data follows immediately after the NET/ROM transport header */
memcpy_fromiovec(asmptr, msg->msg_iov, len);
SOCK_DEBUG(sk, "NET/ROM: Transmitting buffer\n");
 
if (sk->state != TCP_ESTABLISHED) {
kfree_skb(skb);
return -ENOTCONN;
}
 
nr_output(sk, skb); /* Shove it onto the queue */
 
return len;
}
 
static int nr_recvmsg(struct socket *sock, struct msghdr *msg, int size,
int flags, struct scm_cookie *scm)
{
struct sock *sk = sock->sk;
struct sockaddr_ax25 *sax = (struct sockaddr_ax25 *)msg->msg_name;
int copied;
struct sk_buff *skb;
int 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;
 
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 (sax != NULL) {
sax->sax25_family = AF_NETROM;
memcpy(sax->sax25_call.ax25_call, skb->data + 7, AX25_ADDR_LEN);
}
 
msg->msg_namelen = sizeof(*sax);
 
skb_free_datagram(sk, skb);
 
return copied;
}
 
 
static int nr_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, (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, (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 SIOCNRDECOBS:
if (!capable(CAP_NET_ADMIN)) return -EPERM;
return nr_rt_ioctl(cmd, (void *)arg);
 
default:
return dev_ioctl(cmd, (void *)arg);
}
 
/*NOTREACHED*/
return 0;
}
 
static int nr_get_info(char *buffer, char **start, off_t offset, int length)
{
struct sock *s;
struct net_device *dev;
const char *devname;
int len = 0;
off_t pos = 0;
off_t begin = 0;
 
cli();
 
len += sprintf(buffer, "user_addr dest_node src_node dev my your st vs vr va t1 t2 t4 idle n2 wnd Snd-Q Rcv-Q inode\n");
 
for (s = nr_list; s != NULL; s = s->next) {
if ((dev = s->protinfo.nr->device) == NULL)
devname = "???";
else
devname = dev->name;
 
len += sprintf(buffer + len, "%-9s ",
ax2asc(&s->protinfo.nr->user_addr));
len += sprintf(buffer + len, "%-9s ",
ax2asc(&s->protinfo.nr->dest_addr));
len += sprintf(buffer + len, "%-9s %-3s %02X/%02X %02X/%02X %2d %3d %3d %3d %3lu/%03lu %2lu/%02lu %3lu/%03lu %3lu/%03lu %2d/%02d %3d %5d %5d %ld\n",
ax2asc(&s->protinfo.nr->source_addr),
devname,
s->protinfo.nr->my_index,
s->protinfo.nr->my_id,
s->protinfo.nr->your_index,
s->protinfo.nr->your_id,
s->protinfo.nr->state,
s->protinfo.nr->vs,
s->protinfo.nr->vr,
s->protinfo.nr->va,
ax25_display_timer(&s->protinfo.nr->t1timer) / HZ,
s->protinfo.nr->t1 / HZ,
ax25_display_timer(&s->protinfo.nr->t2timer) / HZ,
s->protinfo.nr->t2 / HZ,
ax25_display_timer(&s->protinfo.nr->t4timer) / HZ,
s->protinfo.nr->t4 / HZ,
ax25_display_timer(&s->protinfo.nr->idletimer) / (60 * HZ),
s->protinfo.nr->idle / (60 * HZ),
s->protinfo.nr->n2count,
s->protinfo.nr->n2,
s->protinfo.nr->window,
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 nr_family_ops = {
family: PF_NETROM,
create: nr_create,
};
 
static struct proto_ops SOCKOPS_WRAPPED(nr_proto_ops) = {
family: PF_NETROM,
 
release: nr_release,
bind: nr_bind,
connect: nr_connect,
socketpair: sock_no_socketpair,
accept: nr_accept,
getname: nr_getname,
poll: datagram_poll,
ioctl: nr_ioctl,
listen: nr_listen,
shutdown: sock_no_shutdown,
setsockopt: nr_setsockopt,
getsockopt: nr_getsockopt,
sendmsg: nr_sendmsg,
recvmsg: nr_recvmsg,
mmap: sock_no_mmap,
sendpage: sock_no_sendpage,
};
 
#include <linux/smp_lock.h>
SOCKOPS_WRAP(nr_proto, PF_NETROM);
 
static struct notifier_block nr_dev_notifier = {
notifier_call: nr_device_event,
};
 
static struct net_device *dev_nr;
 
static char banner[] __initdata = KERN_INFO "G4KLX NET/ROM for Linux. Version 0.7 for AX25.037 Linux 2.4\n";
 
static int __init nr_proto_init(void)
{
int i;
 
if (nr_ndevs > 0x7fffffff/sizeof(struct net_device)) {
printk(KERN_ERR "NET/ROM: nr_proto_init - nr_ndevs parameter to large\n");
return -1;
}
 
if ((dev_nr = kmalloc(nr_ndevs * sizeof(struct net_device), GFP_KERNEL)) == NULL) {
printk(KERN_ERR "NET/ROM: nr_proto_init - unable to allocate device structure\n");
return -1;
}
 
memset(dev_nr, 0x00, nr_ndevs * sizeof(struct net_device));
 
for (i = 0; i < nr_ndevs; i++) {
sprintf(dev_nr[i].name, "nr%d", i);
dev_nr[i].init = nr_init;
register_netdev(&dev_nr[i]);
}
 
sock_register(&nr_family_ops);
register_netdevice_notifier(&nr_dev_notifier);
printk(banner);
 
ax25_protocol_register(AX25_P_NETROM, nr_route_frame);
ax25_linkfail_register(nr_link_failed);
 
#ifdef CONFIG_SYSCTL
nr_register_sysctl();
#endif
 
nr_loopback_init();
 
proc_net_create("nr", 0, nr_get_info);
proc_net_create("nr_neigh", 0, nr_neigh_get_info);
proc_net_create("nr_nodes", 0, nr_nodes_get_info);
return 0;
}
 
module_init(nr_proto_init);
 
 
EXPORT_NO_SYMBOLS;
 
MODULE_PARM(nr_ndevs, "i");
MODULE_PARM_DESC(nr_ndevs, "number of NET/ROM devices");
 
MODULE_AUTHOR("Jonathan Naylor G4KLX <g4klx@g4klx.demon.co.uk>");
MODULE_DESCRIPTION("The amateur radio NET/ROM network and transport layer protocol");
MODULE_LICENSE("GPL");
 
static void __exit nr_exit(void)
{
int i;
 
proc_net_remove("nr");
proc_net_remove("nr_neigh");
proc_net_remove("nr_nodes");
nr_loopback_clear();
 
nr_rt_free();
 
ax25_protocol_release(AX25_P_NETROM);
ax25_linkfail_release(nr_link_failed);
 
unregister_netdevice_notifier(&nr_dev_notifier);
 
#ifdef CONFIG_SYSCTL
nr_unregister_sysctl();
#endif
sock_unregister(PF_NETROM);
 
for (i = 0; i < nr_ndevs; i++) {
if (dev_nr[i].priv != NULL) {
kfree(dev_nr[i].priv);
dev_nr[i].priv = NULL;
unregister_netdev(&dev_nr[i]);
}
}
 
kfree(dev_nr);
}
module_exit(nr_exit);
/sysctl_net_netrom.c
0,0 → 1,90
/* -*- linux-c -*-
* sysctl_net_netrom.c: sysctl interface to net NET/ROM subsystem.
*
* Begun April 1, 1996, Mike Shaver.
* Added /proc/sys/net/netrom directory entry (empty =) ). [MS]
*/
 
#include <linux/mm.h>
#include <linux/sysctl.h>
#include <linux/init.h>
#include <net/ax25.h>
#include <net/netrom.h>
 
/*
* Values taken from NET/ROM documentation.
*/
static int min_quality[] = {0}, max_quality[] = {255};
static int min_obs[] = {0}, max_obs[] = {255};
static int min_ttl[] = {0}, max_ttl[] = {255};
static int min_t1[] = {5 * HZ};
static int max_t1[] = {600 * HZ};
static int min_n2[] = {2}, max_n2[] = {127};
static int min_t2[] = {1 * HZ};
static int max_t2[] = {60 * HZ};
static int min_t4[] = {1 * HZ};
static int max_t4[] = {1000 * HZ};
static int min_window[] = {1}, max_window[] = {127};
static int min_idle[] = {0 * HZ};
static int max_idle[] = {65535 * HZ};
static int min_route[] = {0}, max_route[] = {1};
static int min_fails[] = {1}, max_fails[] = {10};
 
static struct ctl_table_header *nr_table_header;
 
static ctl_table nr_table[] = {
{NET_NETROM_DEFAULT_PATH_QUALITY, "default_path_quality",
&sysctl_netrom_default_path_quality, sizeof(int), 0644, NULL,
&proc_dointvec_minmax, &sysctl_intvec, NULL, &min_quality, &max_quality},
{NET_NETROM_OBSOLESCENCE_COUNT_INITIALISER, "obsolescence_count_initialiser",
&sysctl_netrom_obsolescence_count_initialiser, sizeof(int), 0644, NULL,
&proc_dointvec_minmax, &sysctl_intvec, NULL, &min_obs, &max_obs},
{NET_NETROM_NETWORK_TTL_INITIALISER, "network_ttl_initialiser",
&sysctl_netrom_network_ttl_initialiser, sizeof(int), 0644, NULL,
&proc_dointvec_minmax, &sysctl_intvec, NULL, &min_ttl, &max_ttl},
{NET_NETROM_TRANSPORT_TIMEOUT, "transport_timeout",
&sysctl_netrom_transport_timeout, sizeof(int), 0644, NULL,
&proc_dointvec_minmax, &sysctl_intvec, NULL, &min_t1, &max_t1},
{NET_NETROM_TRANSPORT_MAXIMUM_TRIES, "transport_maximum_tries",
&sysctl_netrom_transport_maximum_tries, sizeof(int), 0644, NULL,
&proc_dointvec_minmax, &sysctl_intvec, NULL, &min_n2, &max_n2},
{NET_NETROM_TRANSPORT_ACKNOWLEDGE_DELAY, "transport_acknowledge_delay",
&sysctl_netrom_transport_acknowledge_delay, sizeof(int), 0644, NULL,
&proc_dointvec_minmax, &sysctl_intvec, NULL, &min_t2, &max_t2},
{NET_NETROM_TRANSPORT_BUSY_DELAY, "transport_busy_delay",
&sysctl_netrom_transport_busy_delay, sizeof(int), 0644, NULL,
&proc_dointvec_minmax, &sysctl_intvec, NULL, &min_t4, &max_t4},
{NET_NETROM_TRANSPORT_REQUESTED_WINDOW_SIZE, "transport_requested_window_size",
&sysctl_netrom_transport_requested_window_size, sizeof(int), 0644, NULL,
&proc_dointvec_minmax, &sysctl_intvec, NULL, &min_window, &max_window},
{NET_NETROM_TRANSPORT_NO_ACTIVITY_TIMEOUT, "transport_no_activity_timeout",
&sysctl_netrom_transport_no_activity_timeout, sizeof(int), 0644, NULL,
&proc_dointvec_minmax, &sysctl_intvec, NULL, &min_idle, &max_idle},
{NET_NETROM_ROUTING_CONTROL, "routing_control",
&sysctl_netrom_routing_control, sizeof(int), 0644, NULL,
&proc_dointvec_minmax, &sysctl_intvec, NULL, &min_route, &max_route},
{NET_NETROM_LINK_FAILS_COUNT, "link_fails_count",
&sysctl_netrom_link_fails_count, sizeof(int), 0644, NULL,
&proc_dointvec_minmax, &sysctl_intvec, NULL, &min_fails, &max_fails},
{0}
};
 
static ctl_table nr_dir_table[] = {
{NET_NETROM, "netrom", NULL, 0, 0555, nr_table},
{0}
};
 
static ctl_table nr_root_table[] = {
{CTL_NET, "net", NULL, 0, 0555, nr_dir_table},
{0}
};
 
void __init nr_register_sysctl(void)
{
nr_table_header = register_sysctl_table(nr_root_table, 1);
}
 
void nr_unregister_sysctl(void)
{
unregister_sysctl_table(nr_table_header);
}
/Makefile
0,0 → 1,19
#
# Makefile for the Linux NET/ROM 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 := netrom.o
 
obj-y := af_netrom.o nr_dev.o nr_in.o nr_loopback.o nr_out.o nr_route.o \
nr_subr.o nr_timer.o
obj-m := $(O_TARGET)
 
obj-$(CONFIG_SYSCTL) += sysctl_net_netrom.o
 
include $(TOPDIR)/Rules.make
 
/nr_dev.c
0,0 → 1,237
/*
* NET/ROM release 007
*
* 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
* NET/ROM 001 Jonathan(G4KLX) Cloned from loopback.c
* NET/ROM 002 Steve Whitehouse(GW7RRM) fixed the set_mac_address
* NET/ROM 003 Jonathan(G4KLX) Put nr_rebuild_header into line with
* ax25_rebuild_header
* NET/ROM 004 Jonathan(G4KLX) Callsign registration with AX.25.
* NET/ROM 006 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/uaccess.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/netrom.h>
 
#ifdef CONFIG_INET
 
/*
* Only allow IP over NET/ROM frames through if the netrom device is up.
*/
 
int nr_rx_ip(struct sk_buff *skb, struct net_device *dev)
{
struct net_device_stats *stats = (struct net_device_stats *)dev->priv;
 
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);
 
return 1;
}
 
 
static int nr_rebuild_header(struct sk_buff *skb)
{
struct net_device *dev = skb->dev;
struct net_device_stats *stats = (struct net_device_stats *)dev->priv;
struct sk_buff *skbn;
unsigned char *bp = skb->data;
int len;
 
if (arp_find(bp + 7, skb)) {
return 1;
}
 
bp[6] &= ~AX25_CBIT;
bp[6] &= ~AX25_EBIT;
bp[6] |= AX25_SSSID_SPARE;
bp += AX25_ADDR_LEN;
 
bp[6] &= ~AX25_CBIT;
bp[6] |= AX25_EBIT;
bp[6] |= AX25_SSSID_SPARE;
 
if ((skbn = skb_clone(skb, GFP_ATOMIC)) == NULL) {
kfree_skb(skb);
return 1;
}
 
if (skb->sk != NULL)
skb_set_owner_w(skbn, skb->sk);
 
kfree_skb(skb);
 
len = skbn->len;
if (!nr_route_frame(skbn, NULL)) {
kfree_skb(skbn);
stats->tx_errors++;
}
 
stats->tx_packets++;
stats->tx_bytes += len;
 
return 1;
}
 
#else
 
static int nr_rebuild_header(struct sk_buff *skb)
{
return 1;
}
 
#endif
 
static int nr_header(struct sk_buff *skb, struct net_device *dev, unsigned short type,
void *daddr, void *saddr, unsigned len)
{
unsigned char *buff = skb_push(skb, NR_NETWORK_LEN + NR_TRANSPORT_LEN);
 
memcpy(buff, (saddr != NULL) ? saddr : dev->dev_addr, dev->addr_len);
buff[6] &= ~AX25_CBIT;
buff[6] &= ~AX25_EBIT;
buff[6] |= AX25_SSSID_SPARE;
buff += AX25_ADDR_LEN;
 
if (daddr != NULL)
memcpy(buff, daddr, dev->addr_len);
buff[6] &= ~AX25_CBIT;
buff[6] |= AX25_EBIT;
buff[6] |= AX25_SSSID_SPARE;
buff += AX25_ADDR_LEN;
 
*buff++ = sysctl_netrom_network_ttl_initialiser;
 
*buff++ = NR_PROTO_IP;
*buff++ = NR_PROTO_IP;
*buff++ = 0;
*buff++ = 0;
*buff++ = NR_PROTOEXT;
 
if (daddr != NULL)
return 37;
 
return -37;
}
 
static int nr_set_mac_address(struct net_device *dev, void *addr)
{
struct sockaddr *sa = addr;
 
ax25_listen_release((ax25_address *)dev->dev_addr, NULL);
 
memcpy(dev->dev_addr, sa->sa_data, dev->addr_len);
 
ax25_listen_register((ax25_address *)dev->dev_addr, NULL);
 
return 0;
}
 
static int nr_open(struct net_device *dev)
{
MOD_INC_USE_COUNT;
netif_start_queue(dev);
ax25_listen_register((ax25_address *)dev->dev_addr, NULL);
return 0;
}
 
static int nr_close(struct net_device *dev)
{
netif_stop_queue(dev);
ax25_listen_release((ax25_address *)dev->dev_addr, NULL);
MOD_DEC_USE_COUNT;
return 0;
}
 
static int nr_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct net_device_stats *stats = (struct net_device_stats *)dev->priv;
dev_kfree_skb(skb);
stats->tx_errors++;
return 0;
}
 
static struct net_device_stats *nr_get_stats(struct net_device *dev)
{
return (struct net_device_stats *)dev->priv;
}
 
int nr_init(struct net_device *dev)
{
dev->mtu = NR_MAX_PACKET_SIZE;
dev->hard_start_xmit = nr_xmit;
dev->open = nr_open;
dev->stop = nr_close;
 
dev->hard_header = nr_header;
dev->hard_header_len = AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN + NR_NETWORK_LEN + NR_TRANSPORT_LEN;
dev->addr_len = AX25_ADDR_LEN;
dev->type = ARPHRD_NETROM;
dev->rebuild_header = nr_rebuild_header;
dev->set_mac_address = nr_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 = nr_get_stats;
 
return 0;
};
/nr_route.c
0,0 → 1,908
/*
* NET/ROM release 007
*
* 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
* NET/ROM 001 Jonathan(G4KLX) First attempt.
* NET/ROM 003 Jonathan(G4KLX) Use SIOCADDRT/SIOCDELRT ioctl values
* for NET/ROM routes.
* Use '*' for a blank mnemonic in /proc/net/nr_nodes.
* Change default quality for new neighbour when same
* as node callsign.
* Alan Cox(GW4PTS) Added the firewall hooks.
* NET/ROM 006 Jonathan(G4KLX) Added the setting of digipeated neighbours.
* Tomi(OH2BNS) Routing quality and link failure changes.
* Device refcnt fixes.
*/
 
#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/uaccess.h>
#include <asm/system.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/netrom.h>
 
static unsigned int nr_neigh_no = 1;
 
static struct nr_node *nr_node_list;
static struct nr_neigh *nr_neigh_list;
 
static void nr_remove_neigh(struct nr_neigh *);
 
/*
* Add a new route to a node, and in the process add the node and the
* neighbour if it is new.
*/
static int nr_add_node(ax25_address *nr, const char *mnemonic, ax25_address *ax25,
ax25_digi *ax25_digi, struct net_device *dev, int quality, int obs_count)
{
struct nr_node *nr_node;
struct nr_neigh *nr_neigh;
struct nr_route nr_route;
struct net_device *tdev;
unsigned long flags;
int i, found;
 
/* Can't add routes to ourself */
if ((tdev = nr_dev_get(nr)) != NULL) {
dev_put(tdev);
return -EINVAL;
}
 
for (nr_node = nr_node_list; nr_node != NULL; nr_node = nr_node->next)
if (ax25cmp(nr, &nr_node->callsign) == 0)
break;
 
for (nr_neigh = nr_neigh_list; nr_neigh != NULL; nr_neigh = nr_neigh->next)
if (ax25cmp(ax25, &nr_neigh->callsign) == 0 && nr_neigh->dev == dev)
break;
 
/*
* The L2 link to a neighbour has failed in the past
* and now a frame comes from this neighbour. We assume
* it was a temporary trouble with the link and reset the
* routes now (and not wait for a node broadcast).
*/
if (nr_neigh != NULL && nr_neigh->failed != 0 && quality == 0) {
struct nr_node *node;
 
for (node = nr_node_list; node != NULL; node = node->next)
for (i = 0; i < node->count; i++)
if (node->routes[i].neighbour == nr_neigh)
if (i < node->which)
node->which = i;
}
 
if (nr_neigh != NULL)
nr_neigh->failed = 0;
 
if (quality == 0 && nr_neigh != NULL && nr_node != NULL)
return 0;
 
if (nr_neigh == NULL) {
if ((nr_neigh = kmalloc(sizeof(*nr_neigh), GFP_ATOMIC)) == NULL)
return -ENOMEM;
 
nr_neigh->callsign = *ax25;
nr_neigh->digipeat = NULL;
nr_neigh->ax25 = NULL;
nr_neigh->dev = dev;
nr_neigh->quality = sysctl_netrom_default_path_quality;
nr_neigh->locked = 0;
nr_neigh->count = 0;
nr_neigh->number = nr_neigh_no++;
nr_neigh->failed = 0;
 
if (ax25_digi != NULL && ax25_digi->ndigi > 0) {
if ((nr_neigh->digipeat = kmalloc(sizeof(*ax25_digi), GFP_KERNEL)) == NULL) {
kfree(nr_neigh);
return -ENOMEM;
}
memcpy(nr_neigh->digipeat, ax25_digi, sizeof(ax25_digi));
}
 
dev_hold(nr_neigh->dev);
 
save_flags(flags);
cli();
 
nr_neigh->next = nr_neigh_list;
nr_neigh_list = nr_neigh;
 
restore_flags(flags);
}
 
if (quality != 0 && ax25cmp(nr, ax25) == 0 && !nr_neigh->locked)
nr_neigh->quality = quality;
 
if (nr_node == NULL) {
if ((nr_node = kmalloc(sizeof(*nr_node), GFP_ATOMIC)) == NULL)
return -ENOMEM;
 
nr_node->callsign = *nr;
strcpy(nr_node->mnemonic, mnemonic);
 
nr_node->which = 0;
nr_node->count = 1;
 
nr_node->routes[0].quality = quality;
nr_node->routes[0].obs_count = obs_count;
nr_node->routes[0].neighbour = nr_neigh;
 
save_flags(flags);
cli();
 
nr_node->next = nr_node_list;
nr_node_list = nr_node;
 
restore_flags(flags);
 
nr_neigh->count++;
 
return 0;
}
 
if (quality != 0)
strcpy(nr_node->mnemonic, mnemonic);
 
for (found = 0, i = 0; i < nr_node->count; i++) {
if (nr_node->routes[i].neighbour == nr_neigh) {
nr_node->routes[i].quality = quality;
nr_node->routes[i].obs_count = obs_count;
found = 1;
break;
}
}
 
if (!found) {
/* We have space at the bottom, slot it in */
if (nr_node->count < 3) {
nr_node->routes[2] = nr_node->routes[1];
nr_node->routes[1] = nr_node->routes[0];
 
nr_node->routes[0].quality = quality;
nr_node->routes[0].obs_count = obs_count;
nr_node->routes[0].neighbour = nr_neigh;
 
nr_node->which++;
nr_node->count++;
nr_neigh->count++;
} else {
/* It must be better than the worst */
if (quality > nr_node->routes[2].quality) {
nr_node->routes[2].neighbour->count--;
 
if (nr_node->routes[2].neighbour->count == 0 && !nr_node->routes[2].neighbour->locked)
nr_remove_neigh(nr_node->routes[2].neighbour);
 
nr_node->routes[2].quality = quality;
nr_node->routes[2].obs_count = obs_count;
nr_node->routes[2].neighbour = nr_neigh;
 
nr_neigh->count++;
}
}
}
 
/* Now re-sort the routes in quality order */
switch (nr_node->count) {
case 3:
if (nr_node->routes[1].quality > nr_node->routes[0].quality) {
switch (nr_node->which) {
case 0: nr_node->which = 1; break;
case 1: nr_node->which = 0; break;
default: break;
}
nr_route = nr_node->routes[0];
nr_node->routes[0] = nr_node->routes[1];
nr_node->routes[1] = nr_route;
}
if (nr_node->routes[2].quality > nr_node->routes[1].quality) {
switch (nr_node->which) {
case 1: nr_node->which = 2; break;
case 2: nr_node->which = 1; break;
default: break;
}
nr_route = nr_node->routes[1];
nr_node->routes[1] = nr_node->routes[2];
nr_node->routes[2] = nr_route;
}
case 2:
if (nr_node->routes[1].quality > nr_node->routes[0].quality) {
switch (nr_node->which) {
case 0: nr_node->which = 1; break;
case 1: nr_node->which = 0; break;
default: break;
}
nr_route = nr_node->routes[0];
nr_node->routes[0] = nr_node->routes[1];
nr_node->routes[1] = nr_route;
}
case 1:
break;
}
 
for (i = 0; i < nr_node->count; i++) {
if (nr_node->routes[i].neighbour == nr_neigh) {
if (i < nr_node->which)
nr_node->which = i;
break;
}
}
 
return 0;
}
 
static void nr_remove_node(struct nr_node *nr_node)
{
struct nr_node *s;
unsigned long flags;
 
save_flags(flags);
cli();
 
if ((s = nr_node_list) == nr_node) {
nr_node_list = nr_node->next;
restore_flags(flags);
kfree(nr_node);
return;
}
 
while (s != NULL && s->next != NULL) {
if (s->next == nr_node) {
s->next = nr_node->next;
restore_flags(flags);
kfree(nr_node);
return;
}
 
s = s->next;
}
 
restore_flags(flags);
}
 
static void nr_remove_neigh(struct nr_neigh *nr_neigh)
{
struct nr_neigh *s;
unsigned long flags;
save_flags(flags);
cli();
 
if ((s = nr_neigh_list) == nr_neigh) {
nr_neigh_list = nr_neigh->next;
restore_flags(flags);
dev_put(nr_neigh->dev);
if (nr_neigh->digipeat != NULL)
kfree(nr_neigh->digipeat);
kfree(nr_neigh);
return;
}
 
while (s != NULL && s->next != NULL) {
if (s->next == nr_neigh) {
s->next = nr_neigh->next;
restore_flags(flags);
dev_put(nr_neigh->dev);
if (nr_neigh->digipeat != NULL)
kfree(nr_neigh->digipeat);
kfree(nr_neigh);
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 nr_del_node(ax25_address *callsign, ax25_address *neighbour, struct net_device *dev)
{
struct nr_node *nr_node;
struct nr_neigh *nr_neigh;
int i;
 
for (nr_node = nr_node_list; nr_node != NULL; nr_node = nr_node->next)
if (ax25cmp(callsign, &nr_node->callsign) == 0)
break;
 
if (nr_node == NULL) return -EINVAL;
 
for (nr_neigh = nr_neigh_list; nr_neigh != NULL; nr_neigh = nr_neigh->next)
if (ax25cmp(neighbour, &nr_neigh->callsign) == 0 && nr_neigh->dev == dev)
break;
 
if (nr_neigh == NULL) return -EINVAL;
 
for (i = 0; i < nr_node->count; i++) {
if (nr_node->routes[i].neighbour == nr_neigh) {
nr_neigh->count--;
 
if (nr_neigh->count == 0 && !nr_neigh->locked)
nr_remove_neigh(nr_neigh);
 
nr_node->count--;
 
if (nr_node->count == 0) {
nr_remove_node(nr_node);
} else {
switch (i) {
case 0:
nr_node->routes[0] = nr_node->routes[1];
case 1:
nr_node->routes[1] = nr_node->routes[2];
case 2:
break;
}
}
 
return 0;
}
}
 
return -EINVAL;
}
 
/*
* Lock a neighbour with a quality.
*/
static int nr_add_neigh(ax25_address *callsign, ax25_digi *ax25_digi, struct net_device *dev, unsigned int quality)
{
struct nr_neigh *nr_neigh;
unsigned long flags;
 
for (nr_neigh = nr_neigh_list; nr_neigh != NULL; nr_neigh = nr_neigh->next) {
if (ax25cmp(callsign, &nr_neigh->callsign) == 0 && nr_neigh->dev == dev) {
nr_neigh->quality = quality;
nr_neigh->locked = 1;
return 0;
}
}
 
if ((nr_neigh = kmalloc(sizeof(*nr_neigh), GFP_ATOMIC)) == NULL)
return -ENOMEM;
 
nr_neigh->callsign = *callsign;
nr_neigh->digipeat = NULL;
nr_neigh->ax25 = NULL;
nr_neigh->dev = dev;
nr_neigh->quality = quality;
nr_neigh->locked = 1;
nr_neigh->count = 0;
nr_neigh->number = nr_neigh_no++;
nr_neigh->failed = 0;
 
if (ax25_digi != NULL && ax25_digi->ndigi > 0) {
if ((nr_neigh->digipeat = kmalloc(sizeof(*ax25_digi), GFP_KERNEL)) == NULL) {
kfree(nr_neigh);
return -ENOMEM;
}
memcpy(nr_neigh->digipeat, ax25_digi, sizeof(ax25_digi));
}
 
dev_hold(nr_neigh->dev);
 
save_flags(flags);
cli();
 
nr_neigh->next = nr_neigh_list;
nr_neigh_list = nr_neigh;
 
restore_flags(flags);
 
return 0;
}
 
/*
* "Delete" a neighbour. The neighbour is only removed if the number
* of nodes that may use it is zero.
*/
static int nr_del_neigh(ax25_address *callsign, struct net_device *dev, unsigned int quality)
{
struct nr_neigh *nr_neigh;
 
for (nr_neigh = nr_neigh_list; nr_neigh != NULL; nr_neigh = nr_neigh->next)
if (ax25cmp(callsign, &nr_neigh->callsign) == 0 && nr_neigh->dev == dev)
break;
 
if (nr_neigh == NULL) return -EINVAL;
 
nr_neigh->quality = quality;
nr_neigh->locked = 0;
 
if (nr_neigh->count == 0)
nr_remove_neigh(nr_neigh);
 
return 0;
}
 
/*
* Decrement the obsolescence count by one. If a route is reduced to a
* count of zero, remove it. Also remove any unlocked neighbours with
* zero nodes routing via it.
*/
static int nr_dec_obs(void)
{
struct nr_neigh *nr_neigh;
struct nr_node *s, *nr_node;
int i;
 
nr_node = nr_node_list;
 
while (nr_node != NULL) {
s = nr_node;
nr_node = nr_node->next;
 
for (i = 0; i < s->count; i++) {
switch (s->routes[i].obs_count) {
 
case 0: /* A locked entry */
break;
 
case 1: /* From 1 -> 0 */
nr_neigh = s->routes[i].neighbour;
 
nr_neigh->count--;
 
if (nr_neigh->count == 0 && !nr_neigh->locked)
nr_remove_neigh(nr_neigh);
 
s->count--;
 
switch (i) {
case 0:
s->routes[0] = s->routes[1];
case 1:
s->routes[1] = s->routes[2];
case 2:
break;
}
break;
 
default:
s->routes[i].obs_count--;
break;
 
}
}
 
if (s->count <= 0)
nr_remove_node(s);
}
 
return 0;
}
 
/*
* A device has been removed. Remove its routes and neighbours.
*/
void nr_rt_device_down(struct net_device *dev)
{
struct nr_neigh *s, *nr_neigh = nr_neigh_list;
struct nr_node *t, *nr_node;
int i;
 
while (nr_neigh != NULL) {
s = nr_neigh;
nr_neigh = nr_neigh->next;
 
if (s->dev == dev) {
nr_node = nr_node_list;
 
while (nr_node != NULL) {
t = nr_node;
nr_node = nr_node->next;
 
for (i = 0; i < t->count; i++) {
if (t->routes[i].neighbour == s) {
t->count--;
 
switch (i) {
case 0:
t->routes[0] = t->routes[1];
case 1:
t->routes[1] = t->routes[2];
case 2:
break;
}
}
}
 
if (t->count <= 0)
nr_remove_node(t);
}
 
nr_remove_neigh(s);
}
}
}
 
/*
* Check that the device given is a valid AX.25 interface that is "up".
* Or a valid ethernet interface with an AX.25 callsign binding.
*/
static struct net_device *nr_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 NET/ROM device, usually "nr0".
*/
struct net_device *nr_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_NETROM)
if (first == NULL || strncmp(dev->name, first->name, 3) < 0)
first = dev;
}
 
if (first != NULL)
dev_hold(first);
 
read_unlock(&dev_base_lock);
 
return first;
}
 
/*
* Find the NET/ROM device for the given callsign.
*/
struct net_device *nr_dev_get(ax25_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_NETROM && ax25cmp(addr, (ax25_address *)dev->dev_addr) == 0) {
dev_hold(dev);
goto out;
}
}
out:
read_unlock(&dev_base_lock);
return dev;
}
 
static ax25_digi *nr_call_to_digi(int ndigis, ax25_address *digipeaters)
{
static ax25_digi ax25_digi;
int i;
 
if (ndigis == 0)
return NULL;
 
for (i = 0; i < ndigis; i++) {
ax25_digi.calls[i] = digipeaters[i];
ax25_digi.repeated[i] = 0;
}
 
ax25_digi.ndigi = ndigis;
ax25_digi.lastrepeat = -1;
 
return &ax25_digi;
}
 
/*
* Handle the ioctls that control the routing functions.
*/
int nr_rt_ioctl(unsigned int cmd, void *arg)
{
struct nr_route_struct nr_route;
struct net_device *dev;
int ret;
 
switch (cmd) {
 
case SIOCADDRT:
if (copy_from_user(&nr_route, arg, sizeof(struct nr_route_struct)))
return -EFAULT;
if ((dev = nr_ax25_dev_get(nr_route.device)) == NULL)
return -EINVAL;
if (nr_route.ndigis < 0 || nr_route.ndigis > AX25_MAX_DIGIS) {
dev_put(dev);
return -EINVAL;
}
switch (nr_route.type) {
case NETROM_NODE:
ret = nr_add_node(&nr_route.callsign,
nr_route.mnemonic,
&nr_route.neighbour,
nr_call_to_digi(nr_route.ndigis, nr_route.digipeaters),
dev, nr_route.quality,
nr_route.obs_count);
break;
case NETROM_NEIGH:
ret = nr_add_neigh(&nr_route.callsign,
nr_call_to_digi(nr_route.ndigis, nr_route.digipeaters),
dev, nr_route.quality);
break;
default:
ret = -EINVAL;
break;
}
dev_put(dev);
return ret;
 
case SIOCDELRT:
if (copy_from_user(&nr_route, arg, sizeof(struct nr_route_struct)))
return -EFAULT;
if ((dev = nr_ax25_dev_get(nr_route.device)) == NULL)
return -EINVAL;
switch (nr_route.type) {
case NETROM_NODE:
ret = nr_del_node(&nr_route.callsign,
&nr_route.neighbour, dev);
break;
case NETROM_NEIGH:
ret = nr_del_neigh(&nr_route.callsign,
dev, nr_route.quality);
break;
default:
ret = -EINVAL;
break;
}
dev_put(dev);
return ret;
 
case SIOCNRDECOBS:
return nr_dec_obs();
 
default:
return -EINVAL;
}
 
return 0;
}
 
/*
* 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.
*/
void nr_link_failed(ax25_cb *ax25, int reason)
{
struct nr_neigh *nr_neigh;
struct nr_node *nr_node;
 
for (nr_neigh = nr_neigh_list; nr_neigh != NULL; nr_neigh = nr_neigh->next)
if (nr_neigh->ax25 == ax25)
break;
 
if (nr_neigh == NULL) return;
 
nr_neigh->ax25 = NULL;
 
if (++nr_neigh->failed < sysctl_netrom_link_fails_count) return;
 
for (nr_node = nr_node_list; nr_node != NULL; nr_node = nr_node->next)
if (nr_node->which < nr_node->count && nr_node->routes[nr_node->which].neighbour == nr_neigh)
nr_node->which++;
}
 
/*
* Route a frame to an appropriate AX.25 connection. A NULL ax25_cb
* indicates an internally generated frame.
*/
int nr_route_frame(struct sk_buff *skb, ax25_cb *ax25)
{
ax25_address *nr_src, *nr_dest;
struct nr_neigh *nr_neigh;
struct nr_node *nr_node;
struct net_device *dev;
unsigned char *dptr;
 
 
nr_src = (ax25_address *)(skb->data + 0);
nr_dest = (ax25_address *)(skb->data + 7);
 
if (ax25 != NULL)
nr_add_node(nr_src, "", &ax25->dest_addr, ax25->digipeat,
ax25->ax25_dev->dev, 0, sysctl_netrom_obsolescence_count_initialiser);
 
if ((dev = nr_dev_get(nr_dest)) != NULL) { /* Its for me */
int ret;
 
if (ax25 == NULL) /* Its from me */
ret = nr_loopback_queue(skb);
else
ret = nr_rx_frame(skb, dev);
 
dev_put(dev);
return ret;
}
 
if (!sysctl_netrom_routing_control && ax25 != NULL)
return 0;
 
/* Its Time-To-Live has expired */
if (--skb->data[14] == 0)
return 0;
 
for (nr_node = nr_node_list; nr_node != NULL; nr_node = nr_node->next)
if (ax25cmp(nr_dest, &nr_node->callsign) == 0)
break;
 
if (nr_node == NULL || nr_node->which >= nr_node->count)
return 0;
 
nr_neigh = nr_node->routes[nr_node->which].neighbour;
 
if ((dev = nr_dev_first()) == NULL)
return 0;
 
dptr = skb_push(skb, 1);
*dptr = AX25_P_NETROM;
 
nr_neigh->ax25 = ax25_send_frame(skb, 256, (ax25_address *)dev->dev_addr, &nr_neigh->callsign, nr_neigh->digipeat, nr_neigh->dev);
 
dev_put(dev);
 
return (nr_neigh->ax25 != NULL);
}
 
int nr_nodes_get_info(char *buffer, char **start, off_t offset, int length)
{
struct nr_node *nr_node;
int len = 0;
off_t pos = 0;
off_t begin = 0;
int i;
 
cli();
 
len += sprintf(buffer, "callsign mnemonic w n qual obs neigh qual obs neigh qual obs neigh\n");
 
for (nr_node = nr_node_list; nr_node != NULL; nr_node = nr_node->next) {
len += sprintf(buffer + len, "%-9s %-7s %d %d",
ax2asc(&nr_node->callsign),
(nr_node->mnemonic[0] == '\0') ? "*" : nr_node->mnemonic,
nr_node->which + 1,
nr_node->count);
 
for (i = 0; i < nr_node->count; i++) {
len += sprintf(buffer + len, " %3d %d %05d",
nr_node->routes[i].quality,
nr_node->routes[i].obs_count,
nr_node->routes[i].neighbour->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 nr_neigh_get_info(char *buffer, char **start, off_t offset, int length)
{
struct nr_neigh *nr_neigh;
int len = 0;
off_t pos = 0;
off_t begin = 0;
int i;
 
cli();
 
len += sprintf(buffer, "addr callsign dev qual lock count failed digipeaters\n");
 
for (nr_neigh = nr_neigh_list; nr_neigh != NULL; nr_neigh = nr_neigh->next) {
len += sprintf(buffer + len, "%05d %-9s %-4s %3d %d %3d %3d",
nr_neigh->number,
ax2asc(&nr_neigh->callsign),
nr_neigh->dev ? nr_neigh->dev->name : "???",
nr_neigh->quality,
nr_neigh->locked,
nr_neigh->count,
nr_neigh->failed);
 
if (nr_neigh->digipeat != NULL) {
for (i = 0; i < nr_neigh->digipeat->ndigi; i++)
len += sprintf(buffer + len, " %s", ax2asc(&nr_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;
}
 
/*
* Free all memory associated with the nodes and routes lists.
*/
void __exit nr_rt_free(void)
{
struct nr_neigh *s, *nr_neigh = nr_neigh_list;
struct nr_node *t, *nr_node = nr_node_list;
 
while (nr_node != NULL) {
t = nr_node;
nr_node = nr_node->next;
 
nr_remove_node(t);
}
 
while (nr_neigh != NULL) {
s = nr_neigh;
nr_neigh = nr_neigh->next;
 
nr_remove_neigh(s);
}
}

powered by: WebSVN 2.1.0

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