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/ax25
- from Rev 1275 to Rev 1765
- ↔ Reverse comparison
Rev 1275 → Rev 1765
/ax25_ds_subr.c
0,0 → 1,217
/* |
* AX.25 release 037 |
* |
* 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 |
* AX.25 036 Jonathan(G4KLX) Cloned from ax25_out.c and ax25_subr.c. |
* Joerg(DL1BKE) Changed ax25_ds_enquiry_response(), |
* fixed ax25_dama_on() and ax25_dama_off(). |
* AX.25 037 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> |
|
void ax25_ds_nr_error_recovery(ax25_cb *ax25) |
{ |
ax25_ds_establish_data_link(ax25); |
} |
|
/* |
* dl1bke 960114: transmit I frames on DAMA poll |
*/ |
void ax25_ds_enquiry_response(ax25_cb *ax25) |
{ |
ax25_cb *ax25o; |
|
|
* DAMA spec mention the following behaviour as seen |
* with TheFirmware: |
* |
* DB0ACH->DL1BKE <RR C P R0> [DAMA] |
* DL1BKE->DB0ACH <I NR=0 NS=0> |
* DL1BKE-7->DB0PRA-6 DB0ACH <I C S3 R5> |
* DL1BKE->DB0ACH <RR R F R0> |
* |
* The Flexnet DAMA Master implementation apparently |
* insists on the "proper" AX.25 behaviour: |
* |
* DB0ACH->DL1BKE <RR C P R0> [DAMA] |
* DL1BKE->DB0ACH <RR R F R0> |
* DL1BKE->DB0ACH <I NR=0 NS=0> |
* DL1BKE-7->DB0PRA-6 DB0ACH <I C S3 R5> |
* |
* Flexnet refuses to send us *any* I frame if we send |
* a REJ in case AX25_COND_REJECT is set. It is superfluous in |
* this mode anyway (a RR or RNR invokes the retransmission). |
* Is this a Flexnet bug? |
*/ |
|
ax25_std_enquiry_response(ax25); |
|
if (!(ax25->condition & AX25_COND_PEER_RX_BUSY)) { |
ax25_requeue_frames(ax25); |
ax25_kick(ax25); |
} |
|
if (ax25->state == AX25_STATE_1 || ax25->state == AX25_STATE_2 || skb_peek(&ax25->ack_queue) != NULL) |
ax25_ds_t1_timeout(ax25); |
else |
ax25->n2count = 0; |
|
ax25_start_t3timer(ax25); |
ax25_ds_set_timer(ax25->ax25_dev); |
|
for (ax25o = ax25_list; ax25o != NULL; ax25o = ax25o->next) { |
if (ax25o == ax25) |
continue; |
|
if (ax25o->ax25_dev != ax25->ax25_dev) |
continue; |
|
if (ax25o->state == AX25_STATE_1 || ax25o->state == AX25_STATE_2) { |
ax25_ds_t1_timeout(ax25o); |
continue; |
} |
|
if (!(ax25o->condition & AX25_COND_PEER_RX_BUSY) && ax25o->state == AX25_STATE_3) { |
ax25_requeue_frames(ax25o); |
ax25_kick(ax25o); |
} |
|
if (ax25o->state == AX25_STATE_1 || ax25o->state == AX25_STATE_2 || skb_peek(&ax25o->ack_queue) != NULL) |
ax25_ds_t1_timeout(ax25o); |
|
/* do not start T3 for listening sockets (tnx DD8NE) */ |
|
if (ax25o->state != AX25_STATE_0) |
ax25_start_t3timer(ax25o); |
} |
} |
|
void ax25_ds_establish_data_link(ax25_cb *ax25) |
{ |
ax25->condition &= AX25_COND_DAMA_MODE; |
ax25->n2count = 0; |
ax25_calculate_t1(ax25); |
ax25_start_t1timer(ax25); |
ax25_stop_t2timer(ax25); |
ax25_start_t3timer(ax25); |
} |
|
/* |
* :::FIXME::: |
* This is a kludge. Not all drivers recognize kiss commands. |
* We need a driver level request to switch duplex mode, that does |
* either SCC changing, PI config or KISS as required. Currently |
* this request isn't reliable. |
*/ |
static void ax25_kiss_cmd(ax25_dev *ax25_dev, unsigned char cmd, unsigned char param) |
{ |
struct sk_buff *skb; |
unsigned char *p; |
|
if (ax25_dev->dev == NULL) |
return; |
|
if ((skb = alloc_skb(2, GFP_ATOMIC)) == NULL) |
return; |
|
skb->nh.raw = skb->data; |
p = skb_put(skb, 2); |
|
*p++ = cmd; |
*p++ = param; |
|
skb->dev = ax25_dev->dev; |
skb->protocol = htons(ETH_P_AX25); |
|
dev_queue_xmit(skb); |
} |
|
/* |
* A nasty problem arises if we count the number of DAMA connections |
* wrong, especially when connections on the device already existed |
* and our network node (or the sysop) decides to turn on DAMA Master |
* mode. We thus flag the 'real' slave connections with |
* ax25->dama_slave=1 and look on every disconnect if still slave |
* connections exist. |
*/ |
static int ax25_check_dama_slave(ax25_dev *ax25_dev) |
{ |
ax25_cb *ax25; |
|
for (ax25 = ax25_list; ax25 != NULL ; ax25 = ax25->next) |
if (ax25->ax25_dev == ax25_dev && (ax25->condition & AX25_COND_DAMA_MODE) && ax25->state > AX25_STATE_1) |
return 1; |
|
return 0; |
} |
|
void ax25_dev_dama_on(ax25_dev *ax25_dev) |
{ |
if (ax25_dev == NULL) |
return; |
|
if (ax25_dev->dama.slave == 0) |
ax25_kiss_cmd(ax25_dev, 5, 1); |
|
ax25_dev->dama.slave = 1; |
ax25_ds_set_timer(ax25_dev); |
} |
|
void ax25_dev_dama_off(ax25_dev *ax25_dev) |
{ |
if (ax25_dev == NULL) |
return; |
|
if (ax25_dev->dama.slave && !ax25_check_dama_slave(ax25_dev)) { |
ax25_kiss_cmd(ax25_dev, 5, 0); |
ax25_dev->dama.slave = 0; |
ax25_ds_del_timer(ax25_dev); |
} |
} |
|
void ax25_dama_on(ax25_cb *ax25) |
{ |
ax25_dev_dama_on(ax25->ax25_dev); |
ax25->condition |= AX25_COND_DAMA_MODE; |
} |
|
void ax25_dama_off(ax25_cb *ax25) |
{ |
ax25->condition &= ~AX25_COND_DAMA_MODE; |
ax25_dev_dama_off(ax25->ax25_dev); |
} |
|
/ax25_ds_timer.c
0,0 → 1,224
/* |
* AX.25 release 037 |
* |
* 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 |
* AX.25 036 Jonathan(G4KLX) Cloned from ax25_timer.c. |
* Joerg(DL1BKE) Added DAMA Slave Timeout timer |
* AX.25 037 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> |
|
static void ax25_ds_timeout(unsigned long); |
|
/* |
* Add DAMA slave timeout timer to timer list. |
* Unlike the connection based timers the timeout function gets |
* triggered every second. Please note that NET_AX25_DAMA_SLAVE_TIMEOUT |
* (aka /proc/sys/net/ax25/{dev}/dama_slave_timeout) is still in |
* 1/10th of a second. |
*/ |
|
static void ax25_ds_add_timer(ax25_dev *ax25_dev) |
{ |
struct timer_list *t = &ax25_dev->dama.slave_timer; |
t->data = (unsigned long) ax25_dev; |
t->function = &ax25_ds_timeout; |
t->expires = jiffies + HZ; |
add_timer(t); |
} |
|
void ax25_ds_del_timer(ax25_dev *ax25_dev) |
{ |
if (ax25_dev) del_timer(&ax25_dev->dama.slave_timer); |
} |
|
void ax25_ds_set_timer(ax25_dev *ax25_dev) |
{ |
if (ax25_dev == NULL) /* paranoia */ |
return; |
|
del_timer(&ax25_dev->dama.slave_timer); |
ax25_dev->dama.slave_timeout = ax25_dev->values[AX25_VALUES_DS_TIMEOUT] / 10; |
ax25_ds_add_timer(ax25_dev); |
} |
|
/* |
* DAMA Slave Timeout |
* Silently discard all (slave) connections in case our master forgot us... |
*/ |
|
static void ax25_ds_timeout(unsigned long arg) |
{ |
ax25_dev *ax25_dev = (struct ax25_dev *) arg; |
ax25_cb *ax25; |
|
if (ax25_dev == NULL || !ax25_dev->dama.slave) |
return; /* Yikes! */ |
|
if (!ax25_dev->dama.slave_timeout || --ax25_dev->dama.slave_timeout) { |
ax25_ds_set_timer(ax25_dev); |
return; |
} |
|
for (ax25=ax25_list; ax25 != NULL; ax25 = ax25->next) { |
if (ax25->ax25_dev != ax25_dev || !(ax25->condition & AX25_COND_DAMA_MODE)) |
continue; |
|
ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND); |
ax25_disconnect(ax25, ETIMEDOUT); |
} |
|
ax25_dev_dama_off(ax25_dev); |
} |
|
void ax25_ds_heartbeat_expiry(ax25_cb *ax25) |
{ |
switch (ax25->state) { |
|
case AX25_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 (ax25->sk == NULL || ax25->sk->destroy || (ax25->sk->state == TCP_LISTEN && ax25->sk->dead)) { |
ax25_destroy_socket(ax25); |
return; |
} |
break; |
|
case AX25_STATE_3: |
/* |
* Check the state of the receive buffer. |
*/ |
if (ax25->sk != NULL) { |
if (atomic_read(&ax25->sk->rmem_alloc) < (ax25->sk->rcvbuf / 2) && |
(ax25->condition & AX25_COND_OWN_RX_BUSY)) { |
ax25->condition &= ~AX25_COND_OWN_RX_BUSY; |
ax25->condition &= ~AX25_COND_ACK_PENDING; |
break; |
} |
} |
break; |
} |
|
ax25_start_heartbeat(ax25); |
} |
|
/* dl1bke 960114: T3 works much like the IDLE timeout, but |
* gets reloaded with every frame for this |
* connection. |
*/ |
void ax25_ds_t3timer_expiry(ax25_cb *ax25) |
{ |
ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND); |
ax25_dama_off(ax25); |
ax25_disconnect(ax25, ETIMEDOUT); |
} |
|
/* dl1bke 960228: close the connection when IDLE expires. |
* unlike T3 this timer gets reloaded only on |
* I frames. |
*/ |
void ax25_ds_idletimer_expiry(ax25_cb *ax25) |
{ |
ax25_clear_queues(ax25); |
|
ax25->n2count = 0; |
ax25->state = AX25_STATE_2; |
|
ax25_calculate_t1(ax25); |
ax25_start_t1timer(ax25); |
ax25_stop_t3timer(ax25); |
|
if (ax25->sk != NULL) { |
ax25->sk->state = TCP_CLOSE; |
ax25->sk->err = 0; |
ax25->sk->shutdown |= SEND_SHUTDOWN; |
if (!ax25->sk->dead) |
ax25->sk->state_change(ax25->sk); |
ax25->sk->dead = 1; |
} |
} |
|
/* dl1bke 960114: The DAMA protocol requires to send data and SABM/DISC |
* within the poll of any connected channel. Remember |
* that we are not allowed to send anything unless we |
* get polled by the Master. |
* |
* Thus we'll have to do parts of our T1 handling in |
* ax25_enquiry_response(). |
*/ |
void ax25_ds_t1_timeout(ax25_cb *ax25) |
{ |
switch (ax25->state) { |
|
case AX25_STATE_1: |
if (ax25->n2count == ax25->n2) { |
if (ax25->modulus == AX25_MODULUS) { |
ax25_disconnect(ax25, ETIMEDOUT); |
return; |
} else { |
ax25->modulus = AX25_MODULUS; |
ax25->window = ax25->ax25_dev->values[AX25_VALUES_WINDOW]; |
ax25->n2count = 0; |
ax25_send_control(ax25, AX25_SABM, AX25_POLLOFF, AX25_COMMAND); |
} |
} else { |
ax25->n2count++; |
if (ax25->modulus == AX25_MODULUS) |
ax25_send_control(ax25, AX25_SABM, AX25_POLLOFF, AX25_COMMAND); |
else |
ax25_send_control(ax25, AX25_SABME, AX25_POLLOFF, AX25_COMMAND); |
} |
break; |
|
case AX25_STATE_2: |
if (ax25->n2count == ax25->n2) { |
ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND); |
ax25_disconnect(ax25, ETIMEDOUT); |
return; |
} else { |
ax25->n2count++; |
} |
break; |
|
case AX25_STATE_3: |
if (ax25->n2count == ax25->n2) { |
ax25_send_control(ax25, AX25_DM, AX25_POLLON, AX25_RESPONSE); |
ax25_disconnect(ax25, ETIMEDOUT); |
return; |
} else { |
ax25->n2count++; |
} |
break; |
} |
|
ax25_calculate_t1(ax25); |
ax25_start_t1timer(ax25); |
} |
/ax25_in.c
0,0 → 1,488
/* |
* AX.25 release 037 |
* |
* 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 |
* AX.25 028a Jonathan(G4KLX) New state machine based on SDL diagrams. |
* AX.25 028b Jonathan(G4KLX) Extracted AX25 control block from |
* the sock structure. |
* AX.25 029 Alan(GW4PTS) Switched to KA9Q constant names. |
* Jonathan(G4KLX) Added IP mode registration. |
* AX.25 030 Jonathan(G4KLX) Added AX.25 fragment reception. |
* Upgraded state machine for SABME. |
* Added arbitrary protocol id support. |
* AX.25 031 Joerg(DL1BKE) Added DAMA support |
* HaJo(DD8NE) Added Idle Disc Timer T5 |
* Joerg(DL1BKE) Renamed it to "IDLE" with a slightly |
* different behaviour. Fixed defrag |
* routine (I hope) |
* AX.25 032 Darryl(G7LED) AX.25 segmentation fixed. |
* AX.25 033 Jonathan(G4KLX) Remove auto-router. |
* Modularisation changes. |
* AX.25 035 Hans(PE1AYX) Fixed interface to IP layer. |
* AX.25 036 Jonathan(G4KLX) Move DAMA code into own file. |
* Joerg(DL1BKE) Fixed DAMA Slave. |
* AX.25 037 Jonathan(G4KLX) New timer architecture. |
* Thomas(DL9SAU) Fixed missing initialization of skb->protocol. |
*/ |
|
#include <linux/config.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 <net/ax25.h> |
#include <linux/inet.h> |
#include <linux/netdevice.h> |
#include <linux/skbuff.h> |
#include <linux/netfilter.h> |
#include <net/sock.h> |
#include <net/ip.h> /* For ip_rcv */ |
#include <net/arp.h> /* For arp_rcv */ |
#include <asm/uaccess.h> |
#include <asm/system.h> |
#include <linux/fcntl.h> |
#include <linux/mm.h> |
#include <linux/interrupt.h> |
|
/* |
* Given a fragment, queue it on the fragment queue and if the fragment |
* is complete, send it back to ax25_rx_iframe. |
*/ |
static int ax25_rx_fragment(ax25_cb *ax25, struct sk_buff *skb) |
{ |
struct sk_buff *skbn, *skbo; |
|
if (ax25->fragno != 0) { |
if (!(*skb->data & AX25_SEG_FIRST)) { |
if ((ax25->fragno - 1) == (*skb->data & AX25_SEG_REM)) { |
/* Enqueue fragment */ |
ax25->fragno = *skb->data & AX25_SEG_REM; |
skb_pull(skb, 1); /* skip fragno */ |
ax25->fraglen += skb->len; |
skb_queue_tail(&ax25->frag_queue, skb); |
|
/* Last fragment received ? */ |
if (ax25->fragno == 0) { |
skbn = alloc_skb(AX25_MAX_HEADER_LEN + |
ax25->fraglen, |
GFP_ATOMIC); |
if (!skbn) { |
skb_queue_purge(&ax25->frag_queue); |
return 1; |
} |
|
skb_reserve(skbn, AX25_MAX_HEADER_LEN); |
|
skbn->dev = ax25->ax25_dev->dev; |
skbn->h.raw = skbn->data; |
skbn->nh.raw = skbn->data; |
|
/* Copy data from the fragments */ |
while ((skbo = skb_dequeue(&ax25->frag_queue)) != NULL) { |
memcpy(skb_put(skbn, skbo->len), skbo->data, skbo->len); |
kfree_skb(skbo); |
} |
|
ax25->fraglen = 0; |
|
if (ax25_rx_iframe(ax25, skbn) == 0) |
kfree_skb(skbn); |
} |
|
return 1; |
} |
} |
} else { |
/* First fragment received */ |
if (*skb->data & AX25_SEG_FIRST) { |
skb_queue_purge(&ax25->frag_queue); |
ax25->fragno = *skb->data & AX25_SEG_REM; |
skb_pull(skb, 1); /* skip fragno */ |
ax25->fraglen = skb->len; |
skb_queue_tail(&ax25->frag_queue, skb); |
return 1; |
} |
} |
|
return 0; |
} |
|
/* |
* This is where all valid I frames are sent to, to be dispatched to |
* whichever protocol requires them. |
*/ |
int ax25_rx_iframe(ax25_cb *ax25, struct sk_buff *skb) |
{ |
int (*func)(struct sk_buff *, ax25_cb *); |
volatile int queued = 0; |
unsigned char pid; |
|
if (skb == NULL) return 0; |
|
ax25_start_idletimer(ax25); |
|
pid = *skb->data; |
|
#ifdef CONFIG_INET |
if (pid == AX25_P_IP) { |
/* working around a TCP bug to keep additional listeners |
* happy. TCP re-uses the buffer and destroys the original |
* content. |
*/ |
struct sk_buff *skbn = skb_copy(skb, GFP_ATOMIC); |
if (skbn != NULL) { |
kfree_skb(skb); |
skb = skbn; |
} |
|
skb_pull(skb, 1); /* Remove PID */ |
skb->h.raw = skb->data; |
skb->nh.raw = skb->data; |
skb->dev = ax25->ax25_dev->dev; |
skb->pkt_type = PACKET_HOST; |
skb->protocol = htons(ETH_P_IP); |
ip_rcv(skb, skb->dev, NULL); /* Wrong ptype */ |
return 1; |
} |
#endif |
if (pid == AX25_P_SEGMENT) { |
skb_pull(skb, 1); /* Remove PID */ |
return ax25_rx_fragment(ax25, skb); |
} |
|
if ((func = ax25_protocol_function(pid)) != NULL) { |
skb_pull(skb, 1); /* Remove PID */ |
return (*func)(skb, ax25); |
} |
|
if (ax25->sk != NULL && ax25->ax25_dev->values[AX25_VALUES_CONMODE] == 2) { |
if ((!ax25->pidincl && ax25->sk->protocol == pid) || ax25->pidincl) { |
if (sock_queue_rcv_skb(ax25->sk, skb) == 0) |
queued = 1; |
else |
ax25->condition |= AX25_COND_OWN_RX_BUSY; |
} |
} |
|
return queued; |
} |
|
/* |
* Higher level upcall for a LAPB frame |
*/ |
static int ax25_process_rx_frame(ax25_cb *ax25, struct sk_buff *skb, int type, int dama) |
{ |
int queued = 0; |
|
if (ax25->state == AX25_STATE_0) |
return 0; |
|
switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) { |
case AX25_PROTO_STD_SIMPLEX: |
case AX25_PROTO_STD_DUPLEX: |
queued = ax25_std_frame_in(ax25, skb, type); |
break; |
|
#ifdef CONFIG_AX25_DAMA_SLAVE |
case AX25_PROTO_DAMA_SLAVE: |
if (dama || ax25->ax25_dev->dama.slave) |
queued = ax25_ds_frame_in(ax25, skb, type); |
else |
queued = ax25_std_frame_in(ax25, skb, type); |
break; |
#endif |
} |
|
return queued; |
} |
|
static int ax25_rcv(struct sk_buff *skb, struct net_device *dev, ax25_address *dev_addr, struct packet_type *ptype) |
{ |
struct sock *make; |
struct sock *sk; |
int type = 0; |
ax25_digi dp, reverse_dp; |
ax25_cb *ax25; |
ax25_address src, dest; |
ax25_address *next_digi = NULL; |
ax25_dev *ax25_dev; |
struct sock *raw; |
int mine = 0; |
int dama; |
|
/* |
* Process the AX.25/LAPB frame. |
*/ |
|
skb->h.raw = skb->data; |
|
if ((ax25_dev = ax25_dev_ax25dev(dev)) == NULL) { |
kfree_skb(skb); |
return 0; |
} |
|
/* |
* Parse the address header. |
*/ |
|
if (ax25_addr_parse(skb->data, skb->len, &src, &dest, &dp, &type, &dama) == NULL) { |
kfree_skb(skb); |
return 0; |
} |
|
/* |
* Ours perhaps ? |
*/ |
if (dp.lastrepeat + 1 < dp.ndigi) /* Not yet digipeated completely */ |
next_digi = &dp.calls[dp.lastrepeat + 1]; |
|
/* |
* Pull of the AX.25 headers leaving the CTRL/PID bytes |
*/ |
skb_pull(skb, ax25_addr_size(&dp)); |
|
/* For our port addresses ? */ |
if (ax25cmp(&dest, dev_addr) == 0 && dp.lastrepeat + 1 == dp.ndigi) |
mine = 1; |
|
/* Also match on any registered callsign from L3/4 */ |
if (!mine && ax25_listen_mine(&dest, dev) && dp.lastrepeat + 1 == dp.ndigi) |
mine = 1; |
|
/* UI frame - bypass LAPB processing */ |
if ((*skb->data & ~0x10) == AX25_UI && dp.lastrepeat + 1 == dp.ndigi) { |
skb->h.raw = skb->data + 2; /* skip control and pid */ |
|
if ((raw = ax25_addr_match(&dest)) != NULL) |
ax25_send_to_raw(raw, skb, skb->data[1]); |
|
if (!mine && ax25cmp(&dest, (ax25_address *)dev->broadcast) != 0) { |
kfree_skb(skb); |
return 0; |
} |
|
/* Now we are pointing at the pid byte */ |
switch (skb->data[1]) { |
#ifdef CONFIG_INET |
case AX25_P_IP: |
skb_pull(skb,2); /* drop PID/CTRL */ |
skb->h.raw = skb->data; |
skb->nh.raw = skb->data; |
skb->dev = dev; |
skb->pkt_type = PACKET_HOST; |
skb->protocol = htons(ETH_P_IP); |
ip_rcv(skb, dev, ptype); /* Note ptype here is the wrong one, fix me later */ |
break; |
|
case AX25_P_ARP: |
skb_pull(skb,2); |
skb->h.raw = skb->data; |
skb->nh.raw = skb->data; |
skb->dev = dev; |
skb->pkt_type = PACKET_HOST; |
skb->protocol = htons(ETH_P_ARP); |
arp_rcv(skb, dev, ptype); /* Note ptype here is wrong... */ |
break; |
#endif |
case AX25_P_TEXT: |
/* Now find a suitable dgram socket */ |
if ((sk = ax25_find_socket(&dest, &src, SOCK_DGRAM)) != NULL) { |
if (atomic_read(&sk->rmem_alloc) >= sk->rcvbuf) { |
kfree_skb(skb); |
} else { |
/* |
* Remove the control and PID. |
*/ |
skb_pull(skb, 2); |
if (sock_queue_rcv_skb(sk, skb) != 0) |
kfree_skb(skb); |
} |
} else { |
kfree_skb(skb); |
} |
break; |
|
default: |
kfree_skb(skb); /* Will scan SOCK_AX25 RAW sockets */ |
break; |
} |
|
return 0; |
} |
|
/* |
* Is connected mode supported on this device ? |
* If not, should we DM the incoming frame (except DMs) or |
* silently ignore them. For now we stay quiet. |
*/ |
if (ax25_dev->values[AX25_VALUES_CONMODE] == 0) { |
kfree_skb(skb); |
return 0; |
} |
|
/* LAPB */ |
|
/* AX.25 state 1-4 */ |
|
ax25_digi_invert(&dp, &reverse_dp); |
|
if ((ax25 = ax25_find_cb(&dest, &src, &reverse_dp, dev)) != NULL) { |
/* |
* Process the frame. If it is queued up internally it returns one otherwise we |
* free it immediately. This routine itself wakes the user context layers so we |
* do no further work |
*/ |
if (ax25_process_rx_frame(ax25, skb, type, dama) == 0) |
kfree_skb(skb); |
|
return 0; |
} |
|
/* AX.25 state 0 (disconnected) */ |
|
/* a) received not a SABM(E) */ |
|
if ((*skb->data & ~AX25_PF) != AX25_SABM && (*skb->data & ~AX25_PF) != AX25_SABME) { |
/* |
* Never reply to a DM. Also ignore any connects for |
* addresses that are not our interfaces and not a socket. |
*/ |
if ((*skb->data & ~AX25_PF) != AX25_DM && mine) |
ax25_return_dm(dev, &src, &dest, &dp); |
|
kfree_skb(skb); |
return 0; |
} |
|
/* b) received SABM(E) */ |
|
if (dp.lastrepeat + 1 == dp.ndigi) |
sk = ax25_find_listener(&dest, 0, dev, SOCK_SEQPACKET); |
else |
sk = ax25_find_listener(next_digi, 1, dev, SOCK_SEQPACKET); |
|
if (sk != NULL) { |
if (sk->ack_backlog == sk->max_ack_backlog || (make = ax25_make_new(sk, ax25_dev)) == NULL) { |
if (mine) ax25_return_dm(dev, &src, &dest, &dp); |
kfree_skb(skb); |
return 0; |
} |
|
ax25 = make->protinfo.ax25; |
skb_set_owner_r(skb, make); |
skb_queue_head(&sk->receive_queue, skb); |
|
make->state = TCP_ESTABLISHED; |
make->pair = sk; |
|
sk->ack_backlog++; |
} else { |
if (!mine) { |
kfree_skb(skb); |
return 0; |
} |
|
if ((ax25 = ax25_create_cb()) == NULL) { |
ax25_return_dm(dev, &src, &dest, &dp); |
kfree_skb(skb); |
return 0; |
} |
|
ax25_fillin_cb(ax25, ax25_dev); |
} |
|
ax25->source_addr = dest; |
ax25->dest_addr = src; |
|
/* |
* Sort out any digipeated paths. |
*/ |
if (dp.ndigi && !ax25->digipeat && |
(ax25->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL) { |
kfree_skb(skb); |
ax25_destroy_socket(ax25); |
return 0; |
} |
|
if (dp.ndigi == 0) { |
if (ax25->digipeat != NULL) { |
kfree(ax25->digipeat); |
ax25->digipeat = NULL; |
} |
} else { |
/* Reverse the source SABM's path */ |
memcpy(ax25->digipeat, &reverse_dp, sizeof(ax25_digi)); |
} |
|
if ((*skb->data & ~AX25_PF) == AX25_SABME) { |
ax25->modulus = AX25_EMODULUS; |
ax25->window = ax25_dev->values[AX25_VALUES_EWINDOW]; |
} else { |
ax25->modulus = AX25_MODULUS; |
ax25->window = ax25_dev->values[AX25_VALUES_WINDOW]; |
} |
|
ax25_send_control(ax25, AX25_UA, AX25_POLLON, AX25_RESPONSE); |
|
#ifdef CONFIG_AX25_DAMA_SLAVE |
if (dama && ax25->ax25_dev->values[AX25_VALUES_PROTOCOL] == AX25_PROTO_DAMA_SLAVE) |
ax25_dama_on(ax25); |
#endif |
|
ax25->state = AX25_STATE_3; |
|
ax25_insert_socket(ax25); |
|
ax25_start_heartbeat(ax25); |
ax25_start_t3timer(ax25); |
ax25_start_idletimer(ax25); |
|
if (sk != NULL) { |
if (!sk->dead) |
sk->data_ready(sk, skb->len); |
} else { |
kfree_skb(skb); |
} |
|
return 0; |
} |
|
/* |
* Receive an AX.25 frame via a SLIP interface. |
*/ |
int ax25_kiss_rcv(struct sk_buff *skb, struct net_device *dev, |
struct packet_type *ptype) |
{ |
skb->sk = NULL; /* Initially we don't know who it's for */ |
skb->destructor = NULL; /* Who initializes this, dammit?! */ |
|
if ((*skb->data & 0x0F) != 0) { |
kfree_skb(skb); /* Not a KISS data frame */ |
return 0; |
} |
|
skb_pull(skb, AX25_KISS_HEADER_LEN); /* Remove the KISS byte */ |
|
return ax25_rcv(skb, dev, (ax25_address *)dev->dev_addr, ptype); |
} |
|
/ax25_out.c
0,0 → 1,409
/* |
* AX.25 release 037 |
* |
* 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 |
* AX.25 028a Jonathan(G4KLX) New state machine based on SDL diagrams. |
* AX.25 029 Alan(GW4PTS) Switched to KA9Q constant names. |
* Jonathan(G4KLX) Only poll when window is full. |
* AX.25 030 Jonathan(G4KLX) Added fragmentation to ax25_output. |
* Added support for extended AX.25. |
* AX.25 031 Joerg(DL1BKE) Added DAMA support |
* Joerg(DL1BKE) Modified fragmenter to fragment vanilla |
* AX.25 I-Frames. Added PACLEN parameter. |
* Joerg(DL1BKE) Fixed a problem with buffer allocation |
* for fragments. |
* AX.25 037 Jonathan(G4KLX) New timer architecture. |
* Joerg(DL1BKE) Fixed DAMA Slave mode: will work |
* on non-DAMA interfaces like AX25L2V2 |
* again (this behaviour is _required_). |
* Joerg(DL1BKE) ax25_check_iframes_acked() returns a |
* value now (for DAMA n2count handling) |
*/ |
|
#include <linux/config.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 <net/ax25.h> |
#include <linux/inet.h> |
#include <linux/netdevice.h> |
#include <linux/skbuff.h> |
#include <linux/netfilter.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> |
|
ax25_cb *ax25_send_frame(struct sk_buff *skb, int paclen, ax25_address *src, ax25_address *dest, ax25_digi *digi, struct net_device *dev) |
{ |
ax25_dev *ax25_dev; |
ax25_cb *ax25; |
|
/* |
* Take the default packet length for the device if zero is |
* specified. |
*/ |
if (paclen == 0) { |
if ((ax25_dev = ax25_dev_ax25dev(dev)) == NULL) |
return NULL; |
|
paclen = ax25_dev->values[AX25_VALUES_PACLEN]; |
} |
|
/* |
* Look for an existing connection. |
*/ |
if ((ax25 = ax25_find_cb(src, dest, digi, dev)) != NULL) { |
ax25_output(ax25, paclen, skb); |
return ax25; /* It already existed */ |
} |
|
if ((ax25_dev = ax25_dev_ax25dev(dev)) == NULL) |
return NULL; |
|
if ((ax25 = ax25_create_cb()) == NULL) |
return NULL; |
|
ax25_fillin_cb(ax25, ax25_dev); |
|
ax25->source_addr = *src; |
ax25->dest_addr = *dest; |
|
if (digi != NULL) { |
if ((ax25->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL) { |
ax25_free_cb(ax25); |
return NULL; |
} |
memcpy(ax25->digipeat, digi, sizeof(ax25_digi)); |
} |
|
switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) { |
case AX25_PROTO_STD_SIMPLEX: |
case AX25_PROTO_STD_DUPLEX: |
ax25_std_establish_data_link(ax25); |
break; |
|
#ifdef CONFIG_AX25_DAMA_SLAVE |
case AX25_PROTO_DAMA_SLAVE: |
if (ax25_dev->dama.slave) |
ax25_ds_establish_data_link(ax25); |
else |
ax25_std_establish_data_link(ax25); |
break; |
#endif |
} |
|
ax25_insert_socket(ax25); |
|
ax25->state = AX25_STATE_1; |
|
ax25_start_heartbeat(ax25); |
|
ax25_output(ax25, paclen, skb); |
|
return ax25; /* We had to create it */ |
} |
|
/* |
* All outgoing AX.25 I frames pass via this routine. Therefore this is |
* where the fragmentation of frames takes place. If fragment is set to |
* zero then we are not allowed to do fragmentation, even if the frame |
* is too large. |
*/ |
void ax25_output(ax25_cb *ax25, int paclen, struct sk_buff *skb) |
{ |
struct sk_buff *skbn; |
unsigned char *p; |
int frontlen, len, fragno, ka9qfrag, first = 1; |
unsigned long flags; |
|
if ((skb->len - 1) > paclen) { |
if (*skb->data == AX25_P_TEXT) { |
skb_pull(skb, 1); /* skip PID */ |
ka9qfrag = 0; |
} else { |
paclen -= 2; /* Allow for fragment control info */ |
ka9qfrag = 1; |
} |
|
fragno = skb->len / paclen; |
if (skb->len % paclen == 0) fragno--; |
|
frontlen = skb_headroom(skb); /* Address space + CTRL */ |
|
while (skb->len > 0) { |
save_flags(flags); |
cli(); |
|
if ((skbn = alloc_skb(paclen + 2 + frontlen, GFP_ATOMIC)) == NULL) { |
restore_flags(flags); |
printk(KERN_CRIT "AX.25: ax25_output - out of memory\n"); |
return; |
} |
|
if (skb->sk != NULL) |
skb_set_owner_w(skbn, skb->sk); |
|
restore_flags(flags); |
|
len = (paclen > skb->len) ? skb->len : paclen; |
|
if (ka9qfrag == 1) { |
skb_reserve(skbn, frontlen + 2); |
skbn->nh.raw = skbn->data + (skb->nh.raw - skb->data); |
memcpy(skb_put(skbn, len), skb->data, len); |
p = skb_push(skbn, 2); |
|
*p++ = AX25_P_SEGMENT; |
|
*p = fragno--; |
if (first) { |
*p |= AX25_SEG_FIRST; |
first = 0; |
} |
} else { |
skb_reserve(skbn, frontlen + 1); |
skbn->nh.raw = skbn->data + (skb->nh.raw - skb->data); |
memcpy(skb_put(skbn, len), skb->data, len); |
p = skb_push(skbn, 1); |
*p = AX25_P_TEXT; |
} |
|
skb_pull(skb, len); |
skb_queue_tail(&ax25->write_queue, skbn); /* Throw it on the queue */ |
} |
|
kfree_skb(skb); |
} else { |
skb_queue_tail(&ax25->write_queue, skb); /* Throw it on the queue */ |
} |
|
switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) { |
case AX25_PROTO_STD_SIMPLEX: |
case AX25_PROTO_STD_DUPLEX: |
ax25_kick(ax25); |
break; |
|
#ifdef CONFIG_AX25_DAMA_SLAVE |
/* |
* A DAMA slave is _required_ to work as normal AX.25L2V2 |
* if no DAMA master is available. |
*/ |
case AX25_PROTO_DAMA_SLAVE: |
if (!ax25->ax25_dev->dama.slave) ax25_kick(ax25); |
break; |
#endif |
} |
} |
|
/* |
* 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 ax25_send_iframe(ax25_cb *ax25, struct sk_buff *skb, int poll_bit) |
{ |
unsigned char *frame; |
|
if (skb == NULL) |
return; |
|
skb->nh.raw = skb->data; |
|
if (ax25->modulus == AX25_MODULUS) { |
frame = skb_push(skb, 1); |
|
*frame = AX25_I; |
*frame |= (poll_bit) ? AX25_PF : 0; |
*frame |= (ax25->vr << 5); |
*frame |= (ax25->vs << 1); |
} else { |
frame = skb_push(skb, 2); |
|
frame[0] = AX25_I; |
frame[0] |= (ax25->vs << 1); |
frame[1] = (poll_bit) ? AX25_EPF : 0; |
frame[1] |= (ax25->vr << 1); |
} |
|
ax25_start_idletimer(ax25); |
|
ax25_transmit_buffer(ax25, skb, AX25_COMMAND); |
} |
|
void ax25_kick(ax25_cb *ax25) |
{ |
struct sk_buff *skb, *skbn; |
int last = 1; |
unsigned short start, end, next; |
|
if (ax25->state != AX25_STATE_3 && ax25->state != AX25_STATE_4) |
return; |
|
if (ax25->condition & AX25_COND_PEER_RX_BUSY) |
return; |
|
if (skb_peek(&ax25->write_queue) == NULL) |
return; |
|
start = (skb_peek(&ax25->ack_queue) == NULL) ? ax25->va : ax25->vs; |
end = (ax25->va + ax25->window) % ax25->modulus; |
|
if (start == end) |
return; |
|
ax25->vs = start; |
|
/* |
* Transmit data until either we're out of data to send or |
* the window is full. Send a poll on the final I frame if |
* the window is filled. |
*/ |
|
/* |
* Dequeue the frame and copy it. |
*/ |
skb = skb_dequeue(&ax25->write_queue); |
|
do { |
if ((skbn = skb_clone(skb, GFP_ATOMIC)) == NULL) { |
skb_queue_head(&ax25->write_queue, skb); |
break; |
} |
|
if (skb->sk != NULL) |
skb_set_owner_w(skbn, skb->sk); |
|
next = (ax25->vs + 1) % ax25->modulus; |
last = (next == end); |
|
/* |
* Transmit the frame copy. |
* bke 960114: do not set the Poll bit on the last frame |
* in DAMA mode. |
*/ |
switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) { |
case AX25_PROTO_STD_SIMPLEX: |
case AX25_PROTO_STD_DUPLEX: |
ax25_send_iframe(ax25, skbn, (last) ? AX25_POLLON : AX25_POLLOFF); |
break; |
|
#ifdef CONFIG_AX25_DAMA_SLAVE |
case AX25_PROTO_DAMA_SLAVE: |
ax25_send_iframe(ax25, skbn, AX25_POLLOFF); |
break; |
#endif |
} |
|
ax25->vs = next; |
|
/* |
* Requeue the original data frame. |
*/ |
skb_queue_tail(&ax25->ack_queue, skb); |
|
} while (!last && (skb = skb_dequeue(&ax25->write_queue)) != NULL); |
|
ax25->condition &= ~AX25_COND_ACK_PENDING; |
|
if (!ax25_t1timer_running(ax25)) { |
ax25_stop_t3timer(ax25); |
ax25_calculate_t1(ax25); |
ax25_start_t1timer(ax25); |
} |
} |
|
void ax25_transmit_buffer(ax25_cb *ax25, struct sk_buff *skb, int type) |
{ |
struct sk_buff *skbn; |
unsigned char *ptr; |
int headroom; |
|
if (ax25->ax25_dev == NULL) { |
ax25_disconnect(ax25, ENETUNREACH); |
return; |
} |
|
headroom = ax25_addr_size(ax25->digipeat); |
|
if (skb_headroom(skb) < headroom) { |
if ((skbn = skb_realloc_headroom(skb, headroom)) == NULL) { |
printk(KERN_CRIT "AX.25: ax25_transmit_buffer - out of memory\n"); |
kfree_skb(skb); |
return; |
} |
|
if (skb->sk != NULL) |
skb_set_owner_w(skbn, skb->sk); |
|
kfree_skb(skb); |
skb = skbn; |
} |
|
ptr = skb_push(skb, headroom); |
|
ax25_addr_build(ptr, &ax25->source_addr, &ax25->dest_addr, ax25->digipeat, type, ax25->modulus); |
|
skb->dev = ax25->ax25_dev->dev; |
|
ax25_queue_xmit(skb); |
} |
|
/* |
* A small shim to dev_queue_xmit to add the KISS control byte, and do |
* any packet forwarding in operation. |
*/ |
void ax25_queue_xmit(struct sk_buff *skb) |
{ |
unsigned char *ptr; |
|
skb->protocol = htons(ETH_P_AX25); |
skb->dev = ax25_fwd_dev(skb->dev); |
|
ptr = skb_push(skb, 1); |
*ptr = 0x00; /* KISS */ |
|
dev_queue_xmit(skb); |
} |
|
int ax25_check_iframes_acked(ax25_cb *ax25, unsigned short nr) |
{ |
if (ax25->vs == nr) { |
ax25_frames_acked(ax25, nr); |
ax25_calculate_rtt(ax25); |
ax25_stop_t1timer(ax25); |
ax25_start_t3timer(ax25); |
return 1; |
} else { |
if (ax25->va != nr) { |
ax25_frames_acked(ax25, nr); |
ax25_calculate_t1(ax25); |
ax25_start_t1timer(ax25); |
return 1; |
} |
} |
return 0; |
} |
|
/ax25_iface.c
0,0 → 1,268
/* |
* AX.25 release 037 |
* |
* 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 |
* AX.25 036 Jonathan(G4KLX) Split from ax25_timer.c. |
*/ |
|
#include <linux/config.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 <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> |
|
static struct protocol_struct { |
struct protocol_struct *next; |
unsigned int pid; |
int (*func)(struct sk_buff *, ax25_cb *); |
} *protocol_list; |
|
static struct linkfail_struct { |
struct linkfail_struct *next; |
void (*func)(ax25_cb *, int); |
} *linkfail_list; |
|
static struct listen_struct { |
struct listen_struct *next; |
ax25_address callsign; |
struct net_device *dev; |
} *listen_list; |
|
int ax25_protocol_register(unsigned int pid, int (*func)(struct sk_buff *, ax25_cb *)) |
{ |
struct protocol_struct *protocol; |
unsigned long flags; |
|
if (pid == AX25_P_TEXT || pid == AX25_P_SEGMENT) |
return 0; |
#ifdef CONFIG_INET |
if (pid == AX25_P_IP || pid == AX25_P_ARP) |
return 0; |
#endif |
if ((protocol = kmalloc(sizeof(*protocol), GFP_ATOMIC)) == NULL) |
return 0; |
|
protocol->pid = pid; |
protocol->func = func; |
|
save_flags(flags); |
cli(); |
|
protocol->next = protocol_list; |
protocol_list = protocol; |
|
restore_flags(flags); |
|
return 1; |
} |
|
void ax25_protocol_release(unsigned int pid) |
{ |
struct protocol_struct *s, *protocol = protocol_list; |
unsigned long flags; |
|
if (protocol == NULL) |
return; |
|
save_flags(flags); |
cli(); |
|
if (protocol->pid == pid) { |
protocol_list = protocol->next; |
restore_flags(flags); |
kfree(protocol); |
return; |
} |
|
while (protocol != NULL && protocol->next != NULL) { |
if (protocol->next->pid == pid) { |
s = protocol->next; |
protocol->next = protocol->next->next; |
restore_flags(flags); |
kfree(s); |
return; |
} |
|
protocol = protocol->next; |
} |
|
restore_flags(flags); |
} |
|
int ax25_linkfail_register(void (*func)(ax25_cb *, int)) |
{ |
struct linkfail_struct *linkfail; |
unsigned long flags; |
|
if ((linkfail = kmalloc(sizeof(*linkfail), GFP_ATOMIC)) == NULL) |
return 0; |
|
linkfail->func = func; |
|
save_flags(flags); |
cli(); |
|
linkfail->next = linkfail_list; |
linkfail_list = linkfail; |
|
restore_flags(flags); |
|
return 1; |
} |
|
void ax25_linkfail_release(void (*func)(ax25_cb *, int)) |
{ |
struct linkfail_struct *s, *linkfail = linkfail_list; |
unsigned long flags; |
|
if (linkfail == NULL) |
return; |
|
save_flags(flags); |
cli(); |
|
if (linkfail->func == func) { |
linkfail_list = linkfail->next; |
restore_flags(flags); |
kfree(linkfail); |
return; |
} |
|
while (linkfail != NULL && linkfail->next != NULL) { |
if (linkfail->next->func == func) { |
s = linkfail->next; |
linkfail->next = linkfail->next->next; |
restore_flags(flags); |
kfree(s); |
return; |
} |
|
linkfail = linkfail->next; |
} |
|
restore_flags(flags); |
} |
|
int ax25_listen_register(ax25_address *callsign, struct net_device *dev) |
{ |
struct listen_struct *listen; |
unsigned long flags; |
|
if (ax25_listen_mine(callsign, dev)) |
return 0; |
|
if ((listen = kmalloc(sizeof(*listen), GFP_ATOMIC)) == NULL) |
return 0; |
|
listen->callsign = *callsign; |
listen->dev = dev; |
|
save_flags(flags); |
cli(); |
|
listen->next = listen_list; |
listen_list = listen; |
|
restore_flags(flags); |
|
return 1; |
} |
|
void ax25_listen_release(ax25_address *callsign, struct net_device *dev) |
{ |
struct listen_struct *s, *listen = listen_list; |
unsigned long flags; |
|
if (listen == NULL) |
return; |
|
save_flags(flags); |
cli(); |
|
if (ax25cmp(&listen->callsign, callsign) == 0 && listen->dev == dev) { |
listen_list = listen->next; |
restore_flags(flags); |
kfree(listen); |
return; |
} |
|
while (listen != NULL && listen->next != NULL) { |
if (ax25cmp(&listen->next->callsign, callsign) == 0 && listen->next->dev == dev) { |
s = listen->next; |
listen->next = listen->next->next; |
restore_flags(flags); |
kfree(s); |
return; |
} |
|
listen = listen->next; |
} |
|
restore_flags(flags); |
} |
|
int (*ax25_protocol_function(unsigned int pid))(struct sk_buff *, ax25_cb *) |
{ |
struct protocol_struct *protocol; |
|
for (protocol = protocol_list; protocol != NULL; protocol = protocol->next) |
if (protocol->pid == pid) |
return protocol->func; |
|
return NULL; |
} |
|
int ax25_listen_mine(ax25_address *callsign, struct net_device *dev) |
{ |
struct listen_struct *listen; |
|
for (listen = listen_list; listen != NULL; listen = listen->next) |
if (ax25cmp(&listen->callsign, callsign) == 0 && (listen->dev == dev || listen->dev == NULL)) |
return 1; |
|
return 0; |
} |
|
void ax25_link_failed(ax25_cb *ax25, int reason) |
{ |
struct linkfail_struct *linkfail; |
|
for (linkfail = linkfail_list; linkfail != NULL; linkfail = linkfail->next) |
(linkfail->func)(ax25, reason); |
} |
|
int ax25_protocol_is_registered(unsigned int pid) |
{ |
struct protocol_struct *protocol; |
|
for (protocol = protocol_list; protocol != NULL; protocol = protocol->next) |
if (protocol->pid == pid) |
return 1; |
|
return 0; |
} |
|
/ax25_ip.c
0,0 → 1,222
/* |
* AX.25 release 037 |
* |
* 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 |
* AX.25 036 Jonathan(G4KLX) Split from af_ax25.c. |
*/ |
|
#include <linux/config.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 <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 <linux/proc_fs.h> |
#include <linux/stat.h> |
#include <linux/netfilter.h> |
#include <linux/sysctl.h> |
#include <net/ip.h> |
#include <net/arp.h> |
|
/* |
* IP over AX.25 encapsulation. |
*/ |
|
/* |
* Shove an AX.25 UI header on an IP packet and handle ARP |
*/ |
|
#ifdef CONFIG_INET |
|
int ax25_encapsulate(struct sk_buff *skb, struct net_device *dev, unsigned short type, void *daddr, void *saddr, unsigned len) |
{ |
unsigned char *buff; |
|
/* they sometimes come back to us... */ |
if (type == ETH_P_AX25) |
return 0; |
|
/* header is an AX.25 UI frame from us to them */ |
buff = skb_push(skb, AX25_HEADER_LEN); |
*buff++ = 0x00; /* KISS DATA */ |
|
if (daddr != NULL) |
memcpy(buff, daddr, dev->addr_len); /* Address specified */ |
|
buff[6] &= ~AX25_CBIT; |
buff[6] &= ~AX25_EBIT; |
buff[6] |= AX25_SSSID_SPARE; |
buff += AX25_ADDR_LEN; |
|
if (saddr != NULL) |
memcpy(buff, saddr, dev->addr_len); |
else |
memcpy(buff, dev->dev_addr, dev->addr_len); |
|
buff[6] &= ~AX25_CBIT; |
buff[6] |= AX25_EBIT; |
buff[6] |= AX25_SSSID_SPARE; |
buff += AX25_ADDR_LEN; |
|
*buff++ = AX25_UI; /* UI */ |
|
/* Append a suitable AX.25 PID */ |
switch (type) { |
case ETH_P_IP: |
*buff++ = AX25_P_IP; |
break; |
case ETH_P_ARP: |
*buff++ = AX25_P_ARP; |
break; |
default: |
printk(KERN_ERR "AX.25: ax25_encapsulate - wrong protocol type 0x%2.2x\n", type); |
*buff++ = 0; |
break; |
} |
|
if (daddr != NULL) |
return AX25_HEADER_LEN; |
|
return -AX25_HEADER_LEN; /* Unfinished header */ |
} |
|
int ax25_rebuild_header(struct sk_buff *skb) |
{ |
struct sk_buff *ourskb; |
unsigned char *bp = skb->data; |
struct net_device *dev; |
ax25_address *src, *dst; |
ax25_route *route; |
ax25_dev *ax25_dev; |
|
dst = (ax25_address *)(bp + 1); |
src = (ax25_address *)(bp + 8); |
|
if (arp_find(bp + 1, skb)) |
return 1; |
|
route = ax25_rt_find_route(dst, NULL); |
dev = route->dev; |
|
if (dev == NULL) |
dev = skb->dev; |
|
if ((ax25_dev = ax25_dev_ax25dev(dev)) == NULL) |
return 1; |
|
if (bp[16] == AX25_P_IP) { |
if (route->ip_mode == 'V' || (route->ip_mode == ' ' && ax25_dev->values[AX25_VALUES_IPDEFMODE])) { |
/* |
* We copy the buffer and release the original thereby |
* keeping it straight |
* |
* Note: we report 1 back so the caller will |
* not feed the frame direct to the physical device |
* We don't want that to happen. (It won't be upset |
* as we have pulled the frame from the queue by |
* freeing it). |
* |
* NB: TCP modifies buffers that are still |
* on a device queue, thus we use skb_copy() |
* instead of using skb_clone() unless this |
* gets fixed. |
*/ |
|
ax25_address src_c; |
ax25_address dst_c; |
|
if ((ourskb = skb_copy(skb, GFP_ATOMIC)) == NULL) { |
kfree_skb(skb); |
return 1; |
} |
|
if (skb->sk != NULL) |
skb_set_owner_w(ourskb, skb->sk); |
|
kfree_skb(skb); |
/* dl9sau: bugfix |
* after kfree_skb(), dst and src which were pointer |
* to bp which is part of skb->data would not be valid |
* anymore hope that after skb_pull(ourskb, ..) our |
* dsc_c and src_c will not become invalid |
*/ |
bp = ourskb->data; |
dst_c = *(ax25_address *)(bp + 1); |
src_c = *(ax25_address *)(bp + 8); |
|
skb_pull(ourskb, AX25_HEADER_LEN - 1); /* Keep PID */ |
ourskb->nh.raw = ourskb->data; |
|
ax25_send_frame(ourskb, ax25_dev->values[AX25_VALUES_PACLEN], &src_c, |
&dst_c, route->digipeat, dev); |
|
return 1; |
} |
} |
|
bp[7] &= ~AX25_CBIT; |
bp[7] &= ~AX25_EBIT; |
bp[7] |= AX25_SSSID_SPARE; |
|
bp[14] &= ~AX25_CBIT; |
bp[14] |= AX25_EBIT; |
bp[14] |= AX25_SSSID_SPARE; |
|
skb_pull(skb, AX25_KISS_HEADER_LEN); |
|
if (route->digipeat != NULL) { |
if ((ourskb = ax25_rt_build_path(skb, src, dst, route->digipeat)) == NULL) { |
kfree_skb(skb); |
return 1; |
} |
|
skb = ourskb; |
} |
|
skb->dev = dev; |
|
ax25_queue_xmit(skb); |
|
return 1; |
} |
|
#else /* INET */ |
|
int ax25_encapsulate(struct sk_buff *skb, struct net_device *dev, unsigned short type, void *daddr, void *saddr, unsigned len) |
{ |
return -AX25_HEADER_LEN; |
} |
|
int ax25_rebuild_header(struct sk_buff *skb) |
{ |
return 1; |
} |
|
#endif |
|
/ax25_addr.c
0,0 → 1,303
/* |
* AX.25 release 037 |
* |
* 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 |
* AX.25 036 Jonathan(G4KLX) Split from ax25_subr.c. |
*/ |
|
#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> |
|
/* |
* The null address is defined as a callsign of all spaces with an |
* SSID of zero. |
*/ |
ax25_address null_ax25_address = {{0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00}}; |
|
/* |
* ax25 -> ascii conversion |
*/ |
char *ax2asc(ax25_address *a) |
{ |
static char buf[11]; |
char c, *s; |
int n; |
|
for (n = 0, s = buf; n < 6; n++) { |
c = (a->ax25_call[n] >> 1) & 0x7F; |
|
if (c != ' ') *s++ = c; |
} |
|
*s++ = '-'; |
|
if ((n = ((a->ax25_call[6] >> 1) & 0x0F)) > 9) { |
*s++ = '1'; |
n -= 10; |
} |
|
*s++ = n + '0'; |
*s++ = '\0'; |
|
if (*buf == '\0' || *buf == '-') |
return "*"; |
|
return buf; |
|
} |
|
/* |
* ascii -> ax25 conversion |
*/ |
ax25_address *asc2ax(char *callsign) |
{ |
static ax25_address addr; |
char *s; |
int n; |
|
for (s = callsign, n = 0; n < 6; n++) { |
if (*s != '\0' && *s != '-') |
addr.ax25_call[n] = *s++; |
else |
addr.ax25_call[n] = ' '; |
addr.ax25_call[n] <<= 1; |
addr.ax25_call[n] &= 0xFE; |
} |
|
if (*s++ == '\0') { |
addr.ax25_call[6] = 0x00; |
return &addr; |
} |
|
addr.ax25_call[6] = *s++ - '0'; |
|
if (*s != '\0') { |
addr.ax25_call[6] *= 10; |
addr.ax25_call[6] += *s++ - '0'; |
} |
|
addr.ax25_call[6] <<= 1; |
addr.ax25_call[6] &= 0x1E; |
|
return &addr; |
} |
|
/* |
* Compare two ax.25 addresses |
*/ |
int ax25cmp(ax25_address *a, ax25_address *b) |
{ |
int ct = 0; |
|
while (ct < 6) { |
if ((a->ax25_call[ct] & 0xFE) != (b->ax25_call[ct] & 0xFE)) /* Clean off repeater bits */ |
return 1; |
ct++; |
} |
|
if ((a->ax25_call[ct] & 0x1E) == (b->ax25_call[ct] & 0x1E)) /* SSID without control bit */ |
return 0; |
|
return 2; /* Partial match */ |
} |
|
/* |
* Compare two AX.25 digipeater paths. |
*/ |
int ax25digicmp(ax25_digi *digi1, ax25_digi *digi2) |
{ |
int i; |
|
if (digi1->ndigi != digi2->ndigi) |
return 1; |
|
if (digi1->lastrepeat != digi2->lastrepeat) |
return 1; |
|
for (i = 0; i < digi1->ndigi; i++) |
if (ax25cmp(&digi1->calls[i], &digi2->calls[i]) != 0) |
return 1; |
|
return 0; |
} |
|
/* |
* Given an AX.25 address pull of to, from, digi list, command/response and the start of data |
* |
*/ |
unsigned char *ax25_addr_parse(unsigned char *buf, int len, ax25_address *src, ax25_address *dest, ax25_digi *digi, int *flags, int *dama) |
{ |
int d = 0; |
|
if (len < 14) return NULL; |
|
if (flags != NULL) { |
*flags = 0; |
|
if (buf[6] & AX25_CBIT) |
*flags = AX25_COMMAND; |
if (buf[13] & AX25_CBIT) |
*flags = AX25_RESPONSE; |
} |
|
if (dama != NULL) |
*dama = ~buf[13] & AX25_DAMA_FLAG; |
|
/* Copy to, from */ |
if (dest != NULL) |
memcpy(dest, buf + 0, AX25_ADDR_LEN); |
if (src != NULL) |
memcpy(src, buf + 7, AX25_ADDR_LEN); |
|
buf += 2 * AX25_ADDR_LEN; |
len -= 2 * AX25_ADDR_LEN; |
|
digi->lastrepeat = -1; |
digi->ndigi = 0; |
|
while (!(buf[-1] & AX25_EBIT)) { |
if (d >= AX25_MAX_DIGIS) return NULL; /* Max of 6 digis */ |
if (len < 7) return NULL; /* Short packet */ |
|
memcpy(&digi->calls[d], buf, AX25_ADDR_LEN); |
digi->ndigi = d + 1; |
|
if (buf[6] & AX25_HBIT) { |
digi->repeated[d] = 1; |
digi->lastrepeat = d; |
} else { |
digi->repeated[d] = 0; |
} |
|
buf += AX25_ADDR_LEN; |
len -= AX25_ADDR_LEN; |
d++; |
} |
|
return buf; |
} |
|
/* |
* Assemble an AX.25 header from the bits |
*/ |
int ax25_addr_build(unsigned char *buf, ax25_address *src, ax25_address *dest, ax25_digi *d, int flag, int modulus) |
{ |
int len = 0; |
int ct = 0; |
|
memcpy(buf, dest, AX25_ADDR_LEN); |
buf[6] &= ~(AX25_EBIT | AX25_CBIT); |
buf[6] |= AX25_SSSID_SPARE; |
|
if (flag == AX25_COMMAND) buf[6] |= AX25_CBIT; |
|
buf += AX25_ADDR_LEN; |
len += AX25_ADDR_LEN; |
|
memcpy(buf, src, AX25_ADDR_LEN); |
buf[6] &= ~(AX25_EBIT | AX25_CBIT); |
buf[6] &= ~AX25_SSSID_SPARE; |
|
if (modulus == AX25_MODULUS) |
buf[6] |= AX25_SSSID_SPARE; |
else |
buf[6] |= AX25_ESSID_SPARE; |
|
if (flag == AX25_RESPONSE) buf[6] |= AX25_CBIT; |
|
/* |
* Fast path the normal digiless path |
*/ |
if (d == NULL || d->ndigi == 0) { |
buf[6] |= AX25_EBIT; |
return 2 * AX25_ADDR_LEN; |
} |
|
buf += AX25_ADDR_LEN; |
len += AX25_ADDR_LEN; |
|
while (ct < d->ndigi) { |
memcpy(buf, &d->calls[ct], AX25_ADDR_LEN); |
|
if (d->repeated[ct]) |
buf[6] |= AX25_HBIT; |
else |
buf[6] &= ~AX25_HBIT; |
|
buf[6] &= ~AX25_EBIT; |
buf[6] |= AX25_SSSID_SPARE; |
|
buf += AX25_ADDR_LEN; |
len += AX25_ADDR_LEN; |
ct++; |
} |
|
buf[-1] |= AX25_EBIT; |
|
return len; |
} |
|
int ax25_addr_size(ax25_digi *dp) |
{ |
if (dp == NULL) |
return 2 * AX25_ADDR_LEN; |
|
return AX25_ADDR_LEN * (2 + dp->ndigi); |
} |
|
/* |
* Reverse Digipeat List. May not pass both parameters as same struct |
*/ |
void ax25_digi_invert(ax25_digi *in, ax25_digi *out) |
{ |
int ct; |
|
out->ndigi = in->ndigi; |
out->lastrepeat = in->ndigi - in->lastrepeat - 2; |
|
/* Invert the digipeaters */ |
for (ct = 0; ct < in->ndigi; ct++) { |
out->calls[ct] = in->calls[in->ndigi - ct - 1]; |
|
if (ct <= out->lastrepeat) { |
out->calls[ct].ax25_call[6] |= AX25_HBIT; |
out->repeated[ct] = 1; |
} else { |
out->calls[ct].ax25_call[6] &= ~AX25_HBIT; |
out->repeated[ct] = 0; |
} |
} |
} |
|
/ax25_subr.c
0,0 → 1,315
/* |
* AX.25 release 037 |
* |
* 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 |
* AX.25 029 Alan(GW4PTS) Switched to KA9Q constant names. Removed |
* old BSD code. |
* AX.25 030 Jonathan(G4KLX) Added support for extended AX.25. |
* Added fragmentation support. |
* Darryl(G7LED) Added function ax25_requeue_frames() to split |
* it up from ax25_frames_acked(). |
* AX.25 031 Joerg(DL1BKE) DAMA needs KISS Fullduplex ON/OFF. |
* Thus we have ax25_kiss_cmd() now... ;-) |
* Dave Brown(N2RJT) |
* Killed a silly bug in the DAMA code. |
* Joerg(DL1BKE) Found the real bug in ax25.h, sri. |
* AX.25 032 Joerg(DL1BKE) Added ax25_queue_length to count the number of |
* enqueued buffers of a socket.. |
* AX.25 035 Frederic(F1OAT) Support for pseudo-digipeating. |
* AX.25 037 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> |
|
/* |
* This routine purges all the queues of frames. |
*/ |
void ax25_clear_queues(ax25_cb *ax25) |
{ |
skb_queue_purge(&ax25->write_queue); |
skb_queue_purge(&ax25->ack_queue); |
skb_queue_purge(&ax25->reseq_queue); |
skb_queue_purge(&ax25->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 ax25_frames_acked(ax25_cb *ax25, unsigned short nr) |
{ |
struct sk_buff *skb; |
|
/* |
* Remove all the ack-ed frames from the ack queue. |
*/ |
if (ax25->va != nr) { |
while (skb_peek(&ax25->ack_queue) != NULL && ax25->va != nr) { |
skb = skb_dequeue(&ax25->ack_queue); |
kfree_skb(skb); |
ax25->va = (ax25->va + 1) % ax25->modulus; |
} |
} |
} |
|
void ax25_requeue_frames(ax25_cb *ax25) |
{ |
struct sk_buff *skb, *skb_prev = NULL; |
|
/* |
* Requeue all the un-ack-ed frames on the output queue to be picked |
* up by ax25_kick called from the timer. This arrangement handles the |
* possibility of an empty output queue. |
*/ |
while ((skb = skb_dequeue(&ax25->ack_queue)) != NULL) { |
if (skb_prev == NULL) |
skb_queue_head(&ax25->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 ax25_validate_nr(ax25_cb *ax25, unsigned short nr) |
{ |
unsigned short vc = ax25->va; |
|
while (vc != ax25->vs) { |
if (nr == vc) return 1; |
vc = (vc + 1) % ax25->modulus; |
} |
|
if (nr == ax25->vs) return 1; |
|
return 0; |
} |
|
/* |
* This routine is the centralised routine for parsing the control |
* information for the different frame formats. |
*/ |
int ax25_decode(ax25_cb *ax25, struct sk_buff *skb, int *ns, int *nr, int *pf) |
{ |
unsigned char *frame; |
int frametype = AX25_ILLEGAL; |
|
frame = skb->data; |
*ns = *nr = *pf = 0; |
|
if (ax25->modulus == AX25_MODULUS) { |
if ((frame[0] & AX25_S) == 0) { |
frametype = AX25_I; /* I frame - carries NR/NS/PF */ |
*ns = (frame[0] >> 1) & 0x07; |
*nr = (frame[0] >> 5) & 0x07; |
*pf = frame[0] & AX25_PF; |
} else if ((frame[0] & AX25_U) == 1) { /* S frame - take out PF/NR */ |
frametype = frame[0] & 0x0F; |
*nr = (frame[0] >> 5) & 0x07; |
*pf = frame[0] & AX25_PF; |
} else if ((frame[0] & AX25_U) == 3) { /* U frame - take out PF */ |
frametype = frame[0] & ~AX25_PF; |
*pf = frame[0] & AX25_PF; |
} |
skb_pull(skb, 1); |
} else { |
if ((frame[0] & AX25_S) == 0) { |
frametype = AX25_I; /* I frame - carries NR/NS/PF */ |
*ns = (frame[0] >> 1) & 0x7F; |
*nr = (frame[1] >> 1) & 0x7F; |
*pf = frame[1] & AX25_EPF; |
skb_pull(skb, 2); |
} else if ((frame[0] & AX25_U) == 1) { /* S frame - take out PF/NR */ |
frametype = frame[0] & 0x0F; |
*nr = (frame[1] >> 1) & 0x7F; |
*pf = frame[1] & AX25_EPF; |
skb_pull(skb, 2); |
} else if ((frame[0] & AX25_U) == 3) { /* U frame - take out PF */ |
frametype = frame[0] & ~AX25_PF; |
*pf = frame[0] & AX25_PF; |
skb_pull(skb, 1); |
} |
} |
|
return frametype; |
} |
|
/* |
* This routine is called when the HDLC layer internally generates a |
* command or response for the remote machine ( eg. RR, UA etc. ). |
* Only supervisory or unnumbered frames are processed. |
*/ |
void ax25_send_control(ax25_cb *ax25, int frametype, int poll_bit, int type) |
{ |
struct sk_buff *skb; |
unsigned char *dptr; |
|
if ((skb = alloc_skb(AX25_BPQ_HEADER_LEN + ax25_addr_size(ax25->digipeat) + 2, GFP_ATOMIC)) == NULL) |
return; |
|
skb_reserve(skb, AX25_BPQ_HEADER_LEN + ax25_addr_size(ax25->digipeat)); |
|
skb->nh.raw = skb->data; |
|
/* Assume a response - address structure for DTE */ |
if (ax25->modulus == AX25_MODULUS) { |
dptr = skb_put(skb, 1); |
*dptr = frametype; |
*dptr |= (poll_bit) ? AX25_PF : 0; |
if ((frametype & AX25_U) == AX25_S) /* S frames carry NR */ |
*dptr |= (ax25->vr << 5); |
} else { |
if ((frametype & AX25_U) == AX25_U) { |
dptr = skb_put(skb, 1); |
*dptr = frametype; |
*dptr |= (poll_bit) ? AX25_PF : 0; |
} else { |
dptr = skb_put(skb, 2); |
dptr[0] = frametype; |
dptr[1] = (ax25->vr << 1); |
dptr[1] |= (poll_bit) ? AX25_EPF : 0; |
} |
} |
|
ax25_transmit_buffer(ax25, skb, type); |
} |
|
/* |
* Send a 'DM' to an unknown connection attempt, or an invalid caller. |
* |
* Note: src here is the sender, thus it's the target of the DM |
*/ |
void ax25_return_dm(struct net_device *dev, ax25_address *src, ax25_address *dest, ax25_digi *digi) |
{ |
struct sk_buff *skb; |
char *dptr; |
ax25_digi retdigi; |
|
if (dev == NULL) |
return; |
|
if ((skb = alloc_skb(AX25_BPQ_HEADER_LEN + ax25_addr_size(digi) + 1, GFP_ATOMIC)) == NULL) |
return; /* Next SABM will get DM'd */ |
|
skb_reserve(skb, AX25_BPQ_HEADER_LEN + ax25_addr_size(digi)); |
skb->nh.raw = skb->data; |
|
ax25_digi_invert(digi, &retdigi); |
|
dptr = skb_put(skb, 1); |
|
*dptr = AX25_DM | AX25_PF; |
|
/* |
* Do the address ourselves |
*/ |
dptr = skb_push(skb, ax25_addr_size(digi)); |
dptr += ax25_addr_build(dptr, dest, src, &retdigi, AX25_RESPONSE, AX25_MODULUS); |
|
skb->dev = dev; |
|
ax25_queue_xmit(skb); |
} |
|
/* |
* Exponential backoff for AX.25 |
*/ |
void ax25_calculate_t1(ax25_cb *ax25) |
{ |
int n, t = 2; |
|
switch (ax25->backoff) { |
case 0: |
break; |
|
case 1: |
t += 2 * ax25->n2count; |
break; |
|
case 2: |
for (n = 0; n < ax25->n2count; n++) |
t *= 2; |
if (t > 8) t = 8; |
break; |
} |
|
ax25->t1 = t * ax25->rtt; |
} |
|
/* |
* Calculate the Round Trip Time |
*/ |
void ax25_calculate_rtt(ax25_cb *ax25) |
{ |
if (ax25->backoff == 0) |
return; |
|
if (ax25_t1timer_running(ax25) && ax25->n2count == 0) |
ax25->rtt = (9 * ax25->rtt + ax25->t1 - ax25_display_timer(&ax25->t1timer)) / 10; |
|
if (ax25->rtt < AX25_T1CLAMPLO) |
ax25->rtt = AX25_T1CLAMPLO; |
|
if (ax25->rtt > AX25_T1CLAMPHI) |
ax25->rtt = AX25_T1CLAMPHI; |
} |
|
void ax25_disconnect(ax25_cb *ax25, int reason) |
{ |
ax25_clear_queues(ax25); |
|
ax25_stop_t1timer(ax25); |
ax25_stop_t2timer(ax25); |
ax25_stop_t3timer(ax25); |
ax25_stop_idletimer(ax25); |
|
ax25->state = AX25_STATE_0; |
|
ax25_link_failed(ax25, reason); |
|
if (ax25->sk != NULL) { |
ax25->sk->state = TCP_CLOSE; |
ax25->sk->err = reason; |
ax25->sk->shutdown |= SEND_SHUTDOWN; |
if (!ax25->sk->dead) |
ax25->sk->state_change(ax25->sk); |
ax25->sk->dead = 1; |
} |
} |
/ax25_dev.c
0,0 → 1,213
/* |
* AX.25 release 037 |
* |
* 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. |
* |
* Other kernels modules in this kit are generally BSD derived. See the copyright headers. |
* |
* |
* History |
* AX.25 036 Jonathan(G4KLX) Split from ax25_route.c. |
*/ |
|
#include <linux/config.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 <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/mm.h> |
#include <linux/interrupt.h> |
#include <linux/init.h> |
|
ax25_dev *ax25_dev_list; |
|
ax25_dev *ax25_dev_ax25dev(struct net_device *dev) |
{ |
ax25_dev *ax25_dev; |
|
for (ax25_dev = ax25_dev_list; ax25_dev != NULL; ax25_dev = ax25_dev->next) |
if (ax25_dev->dev == dev) |
return ax25_dev; |
|
return NULL; |
} |
|
ax25_dev *ax25_addr_ax25dev(ax25_address *addr) |
{ |
ax25_dev *ax25_dev; |
|
for (ax25_dev = ax25_dev_list; ax25_dev != NULL; ax25_dev = ax25_dev->next) |
if (ax25cmp(addr, (ax25_address *)ax25_dev->dev->dev_addr) == 0) |
return ax25_dev; |
|
return NULL; |
} |
|
/* |
* This is called when an interface is brought up. These are |
* reasonable defaults. |
*/ |
void ax25_dev_device_up(struct net_device *dev) |
{ |
ax25_dev *ax25_dev; |
unsigned long flags; |
|
if ((ax25_dev = kmalloc(sizeof(*ax25_dev), GFP_ATOMIC)) == NULL) { |
printk(KERN_ERR "AX.25: ax25_dev_device_up - out of memory\n"); |
return; |
} |
|
ax25_unregister_sysctl(); |
|
memset(ax25_dev, 0x00, sizeof(*ax25_dev)); |
|
ax25_dev->dev = dev; |
ax25_dev->forward = NULL; |
|
ax25_dev->values[AX25_VALUES_IPDEFMODE] = AX25_DEF_IPDEFMODE; |
ax25_dev->values[AX25_VALUES_AXDEFMODE] = AX25_DEF_AXDEFMODE; |
ax25_dev->values[AX25_VALUES_BACKOFF] = AX25_DEF_BACKOFF; |
ax25_dev->values[AX25_VALUES_CONMODE] = AX25_DEF_CONMODE; |
ax25_dev->values[AX25_VALUES_WINDOW] = AX25_DEF_WINDOW; |
ax25_dev->values[AX25_VALUES_EWINDOW] = AX25_DEF_EWINDOW; |
ax25_dev->values[AX25_VALUES_T1] = AX25_DEF_T1; |
ax25_dev->values[AX25_VALUES_T2] = AX25_DEF_T2; |
ax25_dev->values[AX25_VALUES_T3] = AX25_DEF_T3; |
ax25_dev->values[AX25_VALUES_IDLE] = AX25_DEF_IDLE; |
ax25_dev->values[AX25_VALUES_N2] = AX25_DEF_N2; |
ax25_dev->values[AX25_VALUES_PACLEN] = AX25_DEF_PACLEN; |
ax25_dev->values[AX25_VALUES_PROTOCOL] = AX25_DEF_PROTOCOL; |
ax25_dev->values[AX25_VALUES_DS_TIMEOUT]= AX25_DEF_DS_TIMEOUT; |
|
save_flags(flags); cli(); |
ax25_dev->next = ax25_dev_list; |
ax25_dev_list = ax25_dev; |
restore_flags(flags); |
|
ax25_register_sysctl(); |
} |
|
void ax25_dev_device_down(struct net_device *dev) |
{ |
ax25_dev *s, *ax25_dev; |
unsigned long flags; |
|
if ((ax25_dev = ax25_dev_ax25dev(dev)) == NULL) |
return; |
|
ax25_unregister_sysctl(); |
|
save_flags(flags); cli(); |
|
#ifdef CONFIG_AX25_DAMA_SLAVE |
ax25_ds_del_timer(ax25_dev); |
#endif |
|
/* |
* Remove any packet forwarding that points to this device. |
*/ |
for (s = ax25_dev_list; s != NULL; s = s->next) |
if (s->forward == dev) |
s->forward = NULL; |
|
if ((s = ax25_dev_list) == ax25_dev) { |
ax25_dev_list = s->next; |
restore_flags(flags); |
kfree(ax25_dev); |
ax25_register_sysctl(); |
return; |
} |
|
while (s != NULL && s->next != NULL) { |
if (s->next == ax25_dev) { |
s->next = ax25_dev->next; |
restore_flags(flags); |
kfree(ax25_dev); |
ax25_register_sysctl(); |
return; |
} |
|
s = s->next; |
} |
|
restore_flags(flags); |
ax25_register_sysctl(); |
} |
|
int ax25_fwd_ioctl(unsigned int cmd, struct ax25_fwd_struct *fwd) |
{ |
ax25_dev *ax25_dev, *fwd_dev; |
|
if ((ax25_dev = ax25_addr_ax25dev(&fwd->port_from)) == NULL) |
return -EINVAL; |
|
switch (cmd) { |
case SIOCAX25ADDFWD: |
if ((fwd_dev = ax25_addr_ax25dev(&fwd->port_to)) == NULL) |
return -EINVAL; |
if (ax25_dev->forward != NULL) |
return -EINVAL; |
ax25_dev->forward = fwd_dev->dev; |
break; |
|
case SIOCAX25DELFWD: |
if (ax25_dev->forward == NULL) |
return -EINVAL; |
ax25_dev->forward = NULL; |
break; |
|
default: |
return -EINVAL; |
} |
|
return 0; |
} |
|
struct net_device *ax25_fwd_dev(struct net_device *dev) |
{ |
ax25_dev *ax25_dev; |
|
if ((ax25_dev = ax25_dev_ax25dev(dev)) == NULL) |
return dev; |
|
if (ax25_dev->forward == NULL) |
return dev; |
|
return ax25_dev->forward; |
} |
|
/* |
* Free all memory associated with device structures. |
*/ |
void __exit ax25_dev_free(void) |
{ |
ax25_dev *s, *ax25_dev = ax25_dev_list; |
|
while (ax25_dev != NULL) { |
s = ax25_dev; |
ax25_dev = ax25_dev->next; |
|
kfree(s); |
} |
} |
/ax25_std_in.c
0,0 → 1,467
/* |
* AX.25 release 037 |
* |
* 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 |
* AX.25 028a Jonathan(G4KLX) New state machine based on SDL diagrams. |
* AX.25 028b Jonathan(G4KLX) Extracted AX25 control block from |
* the sock structure. |
* AX.25 029 Alan(GW4PTS) Switched to KA9Q constant names. |
* Jonathan(G4KLX) Added IP mode registration. |
* AX.25 030 Jonathan(G4KLX) Added AX.25 fragment reception. |
* Upgraded state machine for SABME. |
* Added arbitrary protocol id support. |
* AX.25 031 Joerg(DL1BKE) Added DAMA support |
* HaJo(DD8NE) Added Idle Disc Timer T5 |
* Joerg(DL1BKE) Renamed it to "IDLE" with a slightly |
* different behaviour. Fixed defrag |
* routine (I hope) |
* AX.25 032 Darryl(G7LED) AX.25 segmentation fixed. |
* AX.25 033 Jonathan(G4KLX) Remove auto-router. |
* Modularisation changes. |
* AX.25 035 Hans(PE1AYX) Fixed interface to IP layer. |
* AX.25 036 Jonathan(G4KLX) Cloned from ax25_in.c. |
* AX.25 037 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> |
|
/* |
* State machine for state 1, Awaiting Connection State. |
* The handling of the timer(s) is in file ax25_std_timer.c. |
* Handling of state 0 and connection release is in ax25.c. |
*/ |
static int ax25_std_state1_machine(ax25_cb *ax25, struct sk_buff *skb, int frametype, int pf, int type) |
{ |
switch (frametype) { |
case AX25_SABM: |
ax25->modulus = AX25_MODULUS; |
ax25->window = ax25->ax25_dev->values[AX25_VALUES_WINDOW]; |
ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE); |
break; |
|
case AX25_SABME: |
ax25->modulus = AX25_EMODULUS; |
ax25->window = ax25->ax25_dev->values[AX25_VALUES_EWINDOW]; |
ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE); |
break; |
|
case AX25_DISC: |
ax25_send_control(ax25, AX25_DM, pf, AX25_RESPONSE); |
break; |
|
case AX25_UA: |
if (pf) { |
ax25_calculate_rtt(ax25); |
ax25_stop_t1timer(ax25); |
ax25_start_t3timer(ax25); |
ax25_start_idletimer(ax25); |
ax25->vs = 0; |
ax25->va = 0; |
ax25->vr = 0; |
ax25->state = AX25_STATE_3; |
ax25->n2count = 0; |
if (ax25->sk != NULL) { |
ax25->sk->state = TCP_ESTABLISHED; |
/* For WAIT_SABM connections we will produce an accept ready socket here */ |
if (!ax25->sk->dead) |
ax25->sk->state_change(ax25->sk); |
} |
} |
break; |
|
case AX25_DM: |
if (pf) { |
if (ax25->modulus == AX25_MODULUS) { |
ax25_disconnect(ax25, ECONNREFUSED); |
} else { |
ax25->modulus = AX25_MODULUS; |
ax25->window = ax25->ax25_dev->values[AX25_VALUES_WINDOW]; |
} |
} |
break; |
|
default: |
break; |
} |
|
return 0; |
} |
|
/* |
* State machine for state 2, Awaiting Release State. |
* The handling of the timer(s) is in file ax25_std_timer.c |
* Handling of state 0 and connection release is in ax25.c. |
*/ |
static int ax25_std_state2_machine(ax25_cb *ax25, struct sk_buff *skb, int frametype, int pf, int type) |
{ |
switch (frametype) { |
case AX25_SABM: |
case AX25_SABME: |
ax25_send_control(ax25, AX25_DM, pf, AX25_RESPONSE); |
break; |
|
case AX25_DISC: |
ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE); |
ax25_disconnect(ax25, 0); |
break; |
|
case AX25_DM: |
case AX25_UA: |
if (pf) ax25_disconnect(ax25, 0); |
break; |
|
case AX25_I: |
case AX25_REJ: |
case AX25_RNR: |
case AX25_RR: |
if (pf) ax25_send_control(ax25, AX25_DM, AX25_POLLON, AX25_RESPONSE); |
break; |
|
default: |
break; |
} |
|
return 0; |
} |
|
/* |
* State machine for state 3, Connected State. |
* The handling of the timer(s) is in file ax25_std_timer.c |
* Handling of state 0 and connection release is in ax25.c. |
*/ |
static int ax25_std_state3_machine(ax25_cb *ax25, struct sk_buff *skb, int frametype, int ns, int nr, int pf, int type) |
{ |
int queued = 0; |
|
switch (frametype) { |
case AX25_SABM: |
case AX25_SABME: |
if (frametype == AX25_SABM) { |
ax25->modulus = AX25_MODULUS; |
ax25->window = ax25->ax25_dev->values[AX25_VALUES_WINDOW]; |
} else { |
ax25->modulus = AX25_EMODULUS; |
ax25->window = ax25->ax25_dev->values[AX25_VALUES_EWINDOW]; |
} |
ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE); |
ax25_stop_t1timer(ax25); |
ax25_stop_t2timer(ax25); |
ax25_start_t3timer(ax25); |
ax25_start_idletimer(ax25); |
ax25->condition = 0x00; |
ax25->vs = 0; |
ax25->va = 0; |
ax25->vr = 0; |
ax25_requeue_frames(ax25); |
break; |
|
case AX25_DISC: |
ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE); |
ax25_disconnect(ax25, 0); |
break; |
|
case AX25_DM: |
ax25_disconnect(ax25, ECONNRESET); |
break; |
|
case AX25_RR: |
case AX25_RNR: |
if (frametype == AX25_RR) |
ax25->condition &= ~AX25_COND_PEER_RX_BUSY; |
else |
ax25->condition |= AX25_COND_PEER_RX_BUSY; |
if (type == AX25_COMMAND && pf) |
ax25_std_enquiry_response(ax25); |
if (ax25_validate_nr(ax25, nr)) { |
ax25_check_iframes_acked(ax25, nr); |
} else { |
ax25_std_nr_error_recovery(ax25); |
ax25->state = AX25_STATE_1; |
} |
break; |
|
case AX25_REJ: |
ax25->condition &= ~AX25_COND_PEER_RX_BUSY; |
if (type == AX25_COMMAND && pf) |
ax25_std_enquiry_response(ax25); |
if (ax25_validate_nr(ax25, nr)) { |
ax25_frames_acked(ax25, nr); |
ax25_calculate_rtt(ax25); |
ax25_stop_t1timer(ax25); |
ax25_start_t3timer(ax25); |
ax25_requeue_frames(ax25); |
} else { |
ax25_std_nr_error_recovery(ax25); |
ax25->state = AX25_STATE_1; |
} |
break; |
|
case AX25_I: |
if (!ax25_validate_nr(ax25, nr)) { |
ax25_std_nr_error_recovery(ax25); |
ax25->state = AX25_STATE_1; |
break; |
} |
if (ax25->condition & AX25_COND_PEER_RX_BUSY) { |
ax25_frames_acked(ax25, nr); |
} else { |
ax25_check_iframes_acked(ax25, nr); |
} |
if (ax25->condition & AX25_COND_OWN_RX_BUSY) { |
if (pf) ax25_std_enquiry_response(ax25); |
break; |
} |
if (ns == ax25->vr) { |
ax25->vr = (ax25->vr + 1) % ax25->modulus; |
queued = ax25_rx_iframe(ax25, skb); |
if (ax25->condition & AX25_COND_OWN_RX_BUSY) |
ax25->vr = ns; /* ax25->vr - 1 */ |
ax25->condition &= ~AX25_COND_REJECT; |
if (pf) { |
ax25_std_enquiry_response(ax25); |
} else { |
if (!(ax25->condition & AX25_COND_ACK_PENDING)) { |
ax25->condition |= AX25_COND_ACK_PENDING; |
ax25_start_t2timer(ax25); |
} |
} |
} else { |
if (ax25->condition & AX25_COND_REJECT) { |
if (pf) ax25_std_enquiry_response(ax25); |
} else { |
ax25->condition |= AX25_COND_REJECT; |
ax25_send_control(ax25, AX25_REJ, pf, AX25_RESPONSE); |
ax25->condition &= ~AX25_COND_ACK_PENDING; |
} |
} |
break; |
|
case AX25_FRMR: |
case AX25_ILLEGAL: |
ax25_std_establish_data_link(ax25); |
ax25->state = AX25_STATE_1; |
break; |
|
default: |
break; |
} |
|
return queued; |
} |
|
/* |
* State machine for state 4, Timer Recovery State. |
* The handling of the timer(s) is in file ax25_std_timer.c |
* Handling of state 0 and connection release is in ax25.c. |
*/ |
static int ax25_std_state4_machine(ax25_cb *ax25, struct sk_buff *skb, int frametype, int ns, int nr, int pf, int type) |
{ |
int queued = 0; |
|
switch (frametype) { |
case AX25_SABM: |
case AX25_SABME: |
if (frametype == AX25_SABM) { |
ax25->modulus = AX25_MODULUS; |
ax25->window = ax25->ax25_dev->values[AX25_VALUES_WINDOW]; |
} else { |
ax25->modulus = AX25_EMODULUS; |
ax25->window = ax25->ax25_dev->values[AX25_VALUES_EWINDOW]; |
} |
ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE); |
ax25_stop_t1timer(ax25); |
ax25_stop_t2timer(ax25); |
ax25_start_t3timer(ax25); |
ax25_start_idletimer(ax25); |
ax25->condition = 0x00; |
ax25->vs = 0; |
ax25->va = 0; |
ax25->vr = 0; |
ax25->state = AX25_STATE_3; |
ax25->n2count = 0; |
ax25_requeue_frames(ax25); |
break; |
|
case AX25_DISC: |
ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE); |
ax25_disconnect(ax25, 0); |
break; |
|
case AX25_DM: |
ax25_disconnect(ax25, ECONNRESET); |
break; |
|
case AX25_RR: |
case AX25_RNR: |
if (frametype == AX25_RR) |
ax25->condition &= ~AX25_COND_PEER_RX_BUSY; |
else |
ax25->condition |= AX25_COND_PEER_RX_BUSY; |
if (type == AX25_RESPONSE && pf) { |
ax25_stop_t1timer(ax25); |
ax25->n2count = 0; |
if (ax25_validate_nr(ax25, nr)) { |
ax25_frames_acked(ax25, nr); |
if (ax25->vs == ax25->va) { |
ax25_start_t3timer(ax25); |
ax25->state = AX25_STATE_3; |
} else { |
ax25_requeue_frames(ax25); |
} |
} else { |
ax25_std_nr_error_recovery(ax25); |
ax25->state = AX25_STATE_1; |
} |
break; |
} |
if (type == AX25_COMMAND && pf) |
ax25_std_enquiry_response(ax25); |
if (ax25_validate_nr(ax25, nr)) { |
ax25_frames_acked(ax25, nr); |
} else { |
ax25_std_nr_error_recovery(ax25); |
ax25->state = AX25_STATE_1; |
} |
break; |
|
case AX25_REJ: |
ax25->condition &= ~AX25_COND_PEER_RX_BUSY; |
if (pf && type == AX25_RESPONSE) { |
ax25_stop_t1timer(ax25); |
ax25->n2count = 0; |
if (ax25_validate_nr(ax25, nr)) { |
ax25_frames_acked(ax25, nr); |
if (ax25->vs == ax25->va) { |
ax25_start_t3timer(ax25); |
ax25->state = AX25_STATE_3; |
} else { |
ax25_requeue_frames(ax25); |
} |
} else { |
ax25_std_nr_error_recovery(ax25); |
ax25->state = AX25_STATE_1; |
} |
break; |
} |
if (type == AX25_COMMAND && pf) |
ax25_std_enquiry_response(ax25); |
if (ax25_validate_nr(ax25, nr)) { |
ax25_frames_acked(ax25, nr); |
ax25_requeue_frames(ax25); |
} else { |
ax25_std_nr_error_recovery(ax25); |
ax25->state = AX25_STATE_1; |
} |
break; |
|
case AX25_I: |
if (!ax25_validate_nr(ax25, nr)) { |
ax25_std_nr_error_recovery(ax25); |
ax25->state = AX25_STATE_1; |
break; |
} |
ax25_frames_acked(ax25, nr); |
if (ax25->condition & AX25_COND_OWN_RX_BUSY) { |
if (pf) ax25_std_enquiry_response(ax25); |
break; |
} |
if (ns == ax25->vr) { |
ax25->vr = (ax25->vr + 1) % ax25->modulus; |
queued = ax25_rx_iframe(ax25, skb); |
if (ax25->condition & AX25_COND_OWN_RX_BUSY) |
ax25->vr = ns; /* ax25->vr - 1 */ |
ax25->condition &= ~AX25_COND_REJECT; |
if (pf) { |
ax25_std_enquiry_response(ax25); |
} else { |
if (!(ax25->condition & AX25_COND_ACK_PENDING)) { |
ax25->condition |= AX25_COND_ACK_PENDING; |
ax25_start_t2timer(ax25); |
} |
} |
} else { |
if (ax25->condition & AX25_COND_REJECT) { |
if (pf) ax25_std_enquiry_response(ax25); |
} else { |
ax25->condition |= AX25_COND_REJECT; |
ax25_send_control(ax25, AX25_REJ, pf, AX25_RESPONSE); |
ax25->condition &= ~AX25_COND_ACK_PENDING; |
} |
} |
break; |
|
case AX25_FRMR: |
case AX25_ILLEGAL: |
ax25_std_establish_data_link(ax25); |
ax25->state = AX25_STATE_1; |
break; |
|
default: |
break; |
} |
|
return queued; |
} |
|
/* |
* Higher level upcall for a LAPB frame |
*/ |
int ax25_std_frame_in(ax25_cb *ax25, struct sk_buff *skb, int type) |
{ |
int queued = 0, frametype, ns, nr, pf; |
|
frametype = ax25_decode(ax25, skb, &ns, &nr, &pf); |
|
switch (ax25->state) { |
case AX25_STATE_1: |
queued = ax25_std_state1_machine(ax25, skb, frametype, pf, type); |
break; |
case AX25_STATE_2: |
queued = ax25_std_state2_machine(ax25, skb, frametype, pf, type); |
break; |
case AX25_STATE_3: |
queued = ax25_std_state3_machine(ax25, skb, frametype, ns, nr, pf, type); |
break; |
case AX25_STATE_4: |
queued = ax25_std_state4_machine(ax25, skb, frametype, ns, nr, pf, type); |
break; |
} |
|
ax25_kick(ax25); |
|
return queued; |
} |
/ax25_timer.c
0,0 → 1,256
/* |
* AX.25 release 037 |
* |
* 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 |
* AX.25 028a Jonathan(G4KLX) New state machine based on SDL diagrams. |
* AX.25 028b Jonathan(G4KLX) Extracted AX25 control block from the |
* sock structure. |
* AX.25 029 Alan(GW4PTS) Switched to KA9Q constant names. |
* AX.25 031 Joerg(DL1BKE) Added DAMA support |
* AX.25 032 Joerg(DL1BKE) Fixed DAMA timeout bug |
* AX.25 033 Jonathan(G4KLX) Modularisation functions. |
* AX.25 035 Frederic(F1OAT) Support for pseudo-digipeating. |
* AX.25 036 Jonathan(G4KLX) Split Standard and DAMA code into separate files. |
* Joerg(DL1BKE) Fixed DAMA Slave. We are *required* to start with |
* standard AX.25 mode. |
* AX.25 037 Jonathan(G4KLX) New timer architecture. |
* Tomi(OH2BNS) Fixed heartbeat expiry (check ax25_dev). |
*/ |
|
#include <linux/config.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 <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> |
|
static void ax25_heartbeat_expiry(unsigned long); |
static void ax25_t1timer_expiry(unsigned long); |
static void ax25_t2timer_expiry(unsigned long); |
static void ax25_t3timer_expiry(unsigned long); |
static void ax25_idletimer_expiry(unsigned long); |
|
void ax25_start_heartbeat(ax25_cb *ax25) |
{ |
del_timer(&ax25->timer); |
|
ax25->timer.data = (unsigned long)ax25; |
ax25->timer.function = &ax25_heartbeat_expiry; |
ax25->timer.expires = jiffies + 5 * HZ; |
|
add_timer(&ax25->timer); |
} |
|
void ax25_start_t1timer(ax25_cb *ax25) |
{ |
del_timer(&ax25->t1timer); |
|
ax25->t1timer.data = (unsigned long)ax25; |
ax25->t1timer.function = &ax25_t1timer_expiry; |
ax25->t1timer.expires = jiffies + ax25->t1; |
|
add_timer(&ax25->t1timer); |
} |
|
void ax25_start_t2timer(ax25_cb *ax25) |
{ |
del_timer(&ax25->t2timer); |
|
ax25->t2timer.data = (unsigned long)ax25; |
ax25->t2timer.function = &ax25_t2timer_expiry; |
ax25->t2timer.expires = jiffies + ax25->t2; |
|
add_timer(&ax25->t2timer); |
} |
|
void ax25_start_t3timer(ax25_cb *ax25) |
{ |
del_timer(&ax25->t3timer); |
|
if (ax25->t3 > 0) { |
ax25->t3timer.data = (unsigned long)ax25; |
ax25->t3timer.function = &ax25_t3timer_expiry; |
ax25->t3timer.expires = jiffies + ax25->t3; |
|
add_timer(&ax25->t3timer); |
} |
} |
|
void ax25_start_idletimer(ax25_cb *ax25) |
{ |
del_timer(&ax25->idletimer); |
|
if (ax25->idle > 0) { |
ax25->idletimer.data = (unsigned long)ax25; |
ax25->idletimer.function = &ax25_idletimer_expiry; |
ax25->idletimer.expires = jiffies + ax25->idle; |
|
add_timer(&ax25->idletimer); |
} |
} |
|
void ax25_stop_heartbeat(ax25_cb *ax25) |
{ |
del_timer(&ax25->timer); |
} |
|
void ax25_stop_t1timer(ax25_cb *ax25) |
{ |
del_timer(&ax25->t1timer); |
} |
|
void ax25_stop_t2timer(ax25_cb *ax25) |
{ |
del_timer(&ax25->t2timer); |
} |
|
void ax25_stop_t3timer(ax25_cb *ax25) |
{ |
del_timer(&ax25->t3timer); |
} |
|
void ax25_stop_idletimer(ax25_cb *ax25) |
{ |
del_timer(&ax25->idletimer); |
} |
|
int ax25_t1timer_running(ax25_cb *ax25) |
{ |
return timer_pending(&ax25->t1timer); |
} |
|
unsigned long ax25_display_timer(struct timer_list *timer) |
{ |
if (!timer_pending(timer)) |
return 0; |
|
return timer->expires - jiffies; |
} |
|
static void ax25_heartbeat_expiry(unsigned long param) |
{ |
ax25_cb *ax25 = (ax25_cb *)param; |
int proto = AX25_PROTO_STD_SIMPLEX; |
|
if (ax25->ax25_dev) |
proto = ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]; |
|
switch (proto) { |
case AX25_PROTO_STD_SIMPLEX: |
case AX25_PROTO_STD_DUPLEX: |
ax25_std_heartbeat_expiry(ax25); |
break; |
|
#ifdef CONFIG_AX25_DAMA_SLAVE |
case AX25_PROTO_DAMA_SLAVE: |
if (ax25->ax25_dev->dama.slave) |
ax25_ds_heartbeat_expiry(ax25); |
else |
ax25_std_heartbeat_expiry(ax25); |
break; |
#endif |
} |
} |
|
static void ax25_t1timer_expiry(unsigned long param) |
{ |
ax25_cb *ax25 = (ax25_cb *)param; |
|
switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) { |
case AX25_PROTO_STD_SIMPLEX: |
case AX25_PROTO_STD_DUPLEX: |
ax25_std_t1timer_expiry(ax25); |
break; |
|
#ifdef CONFIG_AX25_DAMA_SLAVE |
case AX25_PROTO_DAMA_SLAVE: |
if (!ax25->ax25_dev->dama.slave) |
ax25_std_t1timer_expiry(ax25); |
break; |
#endif |
} |
} |
|
static void ax25_t2timer_expiry(unsigned long param) |
{ |
ax25_cb *ax25 = (ax25_cb *)param; |
|
switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) { |
case AX25_PROTO_STD_SIMPLEX: |
case AX25_PROTO_STD_DUPLEX: |
ax25_std_t2timer_expiry(ax25); |
break; |
|
#ifdef CONFIG_AX25_DAMA_SLAVE |
case AX25_PROTO_DAMA_SLAVE: |
if (!ax25->ax25_dev->dama.slave) |
ax25_std_t2timer_expiry(ax25); |
break; |
#endif |
} |
} |
|
static void ax25_t3timer_expiry(unsigned long param) |
{ |
ax25_cb *ax25 = (ax25_cb *)param; |
|
switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) { |
case AX25_PROTO_STD_SIMPLEX: |
case AX25_PROTO_STD_DUPLEX: |
ax25_std_t3timer_expiry(ax25); |
break; |
|
#ifdef CONFIG_AX25_DAMA_SLAVE |
case AX25_PROTO_DAMA_SLAVE: |
if (ax25->ax25_dev->dama.slave) |
ax25_ds_t3timer_expiry(ax25); |
else |
ax25_std_t3timer_expiry(ax25); |
break; |
#endif |
} |
} |
|
static void ax25_idletimer_expiry(unsigned long param) |
{ |
ax25_cb *ax25 = (ax25_cb *)param; |
|
switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) { |
case AX25_PROTO_STD_SIMPLEX: |
case AX25_PROTO_STD_DUPLEX: |
ax25_std_idletimer_expiry(ax25); |
break; |
|
#ifdef CONFIG_AX25_DAMA_SLAVE |
case AX25_PROTO_DAMA_SLAVE: |
if (ax25->ax25_dev->dama.slave) |
ax25_ds_idletimer_expiry(ax25); |
else |
ax25_std_idletimer_expiry(ax25); |
break; |
#endif |
} |
} |
/ax25_uid.c
0,0 → 1,178
/* |
* AX.25 release 037 |
* |
* 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 |
* AX.25 036 Jonathan(G4KLX) Split from af_ax25.c. |
*/ |
|
#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/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/mm.h> |
#include <linux/interrupt.h> |
#include <linux/notifier.h> |
#include <linux/proc_fs.h> |
#include <linux/stat.h> |
#include <linux/netfilter.h> |
#include <linux/sysctl.h> |
#include <net/ip.h> |
#include <net/arp.h> |
|
/* |
* Callsign/UID mapper. This is in kernel space for security on multi-amateur machines. |
*/ |
|
static ax25_uid_assoc *ax25_uid_list; |
|
int ax25_uid_policy = 0; |
|
ax25_address *ax25_findbyuid(uid_t uid) |
{ |
ax25_uid_assoc *ax25_uid; |
|
for (ax25_uid = ax25_uid_list; ax25_uid != NULL; ax25_uid = ax25_uid->next) { |
if (ax25_uid->uid == uid) |
return &ax25_uid->call; |
} |
|
return NULL; |
} |
|
int ax25_uid_ioctl(int cmd, struct sockaddr_ax25 *sax) |
{ |
ax25_uid_assoc *s, *ax25_uid; |
unsigned long flags; |
|
switch (cmd) { |
case SIOCAX25GETUID: |
for (ax25_uid = ax25_uid_list; ax25_uid != NULL; ax25_uid = ax25_uid->next) { |
if (ax25cmp(&sax->sax25_call, &ax25_uid->call) == 0) |
return ax25_uid->uid; |
} |
return -ENOENT; |
|
case SIOCAX25ADDUID: |
if (!capable(CAP_NET_ADMIN)) |
return -EPERM; |
if (ax25_findbyuid(sax->sax25_uid)) |
return -EEXIST; |
if (sax->sax25_uid == 0) |
return -EINVAL; |
if ((ax25_uid = kmalloc(sizeof(*ax25_uid), GFP_KERNEL)) == NULL) |
return -ENOMEM; |
ax25_uid->uid = sax->sax25_uid; |
ax25_uid->call = sax->sax25_call; |
save_flags(flags); cli(); |
ax25_uid->next = ax25_uid_list; |
ax25_uid_list = ax25_uid; |
restore_flags(flags); |
return 0; |
|
case SIOCAX25DELUID: |
if (!capable(CAP_NET_ADMIN)) |
return -EPERM; |
for (ax25_uid = ax25_uid_list; ax25_uid != NULL; ax25_uid = ax25_uid->next) { |
if (ax25cmp(&sax->sax25_call, &ax25_uid->call) == 0) |
break; |
} |
if (ax25_uid == NULL) |
return -ENOENT; |
save_flags(flags); cli(); |
if ((s = ax25_uid_list) == ax25_uid) { |
ax25_uid_list = s->next; |
restore_flags(flags); |
kfree(ax25_uid); |
return 0; |
} |
while (s != NULL && s->next != NULL) { |
if (s->next == ax25_uid) { |
s->next = ax25_uid->next; |
restore_flags(flags); |
kfree(ax25_uid); |
return 0; |
} |
s = s->next; |
} |
restore_flags(flags); |
return -ENOENT; |
|
default: |
return -EINVAL; |
} |
|
return -EINVAL; /*NOTREACHED */ |
} |
|
int ax25_uid_get_info(char *buffer, char **start, off_t offset, int length) |
{ |
ax25_uid_assoc *pt; |
int len = 0; |
off_t pos = 0; |
off_t begin = 0; |
|
cli(); |
|
len += sprintf(buffer, "Policy: %d\n", ax25_uid_policy); |
|
for (pt = ax25_uid_list; pt != NULL; pt = pt->next) { |
len += sprintf(buffer + len, "%6d %s\n", pt->uid, ax2asc(&pt->call)); |
|
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 UID/Callsign structures. |
*/ |
void __exit ax25_uid_free(void) |
{ |
ax25_uid_assoc *s, *ax25_uid = ax25_uid_list; |
|
while (ax25_uid != NULL) { |
s = ax25_uid; |
ax25_uid = ax25_uid->next; |
|
kfree(s); |
} |
} |
/ax25_std_subr.c
0,0 → 1,102
/* |
* AX.25 release 037 |
* |
* 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 |
* AX.25 036 Jonathan(G4KLX) Split from ax25_out.c. |
* AX.25 037 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> |
|
/* |
* The following routines are taken from page 170 of the 7th ARRL Computer |
* Networking Conference paper, as is the whole state machine. |
*/ |
|
void ax25_std_nr_error_recovery(ax25_cb *ax25) |
{ |
ax25_std_establish_data_link(ax25); |
} |
|
void ax25_std_establish_data_link(ax25_cb *ax25) |
{ |
ax25->condition = 0x00; |
ax25->n2count = 0; |
|
if (ax25->modulus == AX25_MODULUS) |
ax25_send_control(ax25, AX25_SABM, AX25_POLLON, AX25_COMMAND); |
else |
ax25_send_control(ax25, AX25_SABME, AX25_POLLON, AX25_COMMAND); |
|
ax25_calculate_t1(ax25); |
ax25_stop_idletimer(ax25); |
ax25_stop_t3timer(ax25); |
ax25_stop_t2timer(ax25); |
ax25_start_t1timer(ax25); |
} |
|
void ax25_std_transmit_enquiry(ax25_cb *ax25) |
{ |
if (ax25->condition & AX25_COND_OWN_RX_BUSY) |
ax25_send_control(ax25, AX25_RNR, AX25_POLLON, AX25_COMMAND); |
else |
ax25_send_control(ax25, AX25_RR, AX25_POLLON, AX25_COMMAND); |
|
ax25->condition &= ~AX25_COND_ACK_PENDING; |
|
ax25_calculate_t1(ax25); |
ax25_start_t1timer(ax25); |
} |
|
void ax25_std_enquiry_response(ax25_cb *ax25) |
{ |
if (ax25->condition & AX25_COND_OWN_RX_BUSY) |
ax25_send_control(ax25, AX25_RNR, AX25_POLLON, AX25_RESPONSE); |
else |
ax25_send_control(ax25, AX25_RR, AX25_POLLON, AX25_RESPONSE); |
|
ax25->condition &= ~AX25_COND_ACK_PENDING; |
} |
|
void ax25_std_timeout_response(ax25_cb *ax25) |
{ |
if (ax25->condition & AX25_COND_OWN_RX_BUSY) |
ax25_send_control(ax25, AX25_RNR, AX25_POLLOFF, AX25_RESPONSE); |
else |
ax25_send_control(ax25, AX25_RR, AX25_POLLOFF, AX25_RESPONSE); |
|
ax25->condition &= ~AX25_COND_ACK_PENDING; |
} |
/af_ax25.c
0,0 → 1,1884
/* |
* AX.25 release 038 |
* |
* 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 |
* AX.25 006 Alan(GW4PTS) Nearly died of shock - it's working 8-) |
* AX.25 007 Alan(GW4PTS) Removed the silliest bugs |
* AX.25 008 Alan(GW4PTS) Cleaned up, fixed a few state machine problems, added callbacks |
* AX.25 009 Alan(GW4PTS) Emergency patch kit to fix memory corruption |
* AX.25 010 Alan(GW4PTS) Added RAW sockets/Digipeat. |
* AX.25 011 Alan(GW4PTS) RAW socket and datagram fixes (thanks) - Raw sendto now gets PID right |
* datagram sendto uses correct target address. |
* AX.25 012 Alan(GW4PTS) Correct incoming connection handling, send DM to failed connects. |
* Use skb->data not skb+1. Support sk->priority correctly. |
* Correct receive on SOCK_DGRAM. |
* AX.25 013 Alan(GW4PTS) Send DM to all unknown frames, missing initialiser fixed |
* Leave spare SSID bits set (DAMA etc) - thanks for bug report, |
* removed device registration (it's not used or needed). Clean up for |
* gcc 2.5.8. PID to AX25_P_ |
* AX.25 014 Alan(GW4PTS) Cleanup and NET3 merge |
* AX.25 015 Alan(GW4PTS) Internal test version. |
* AX.25 016 Alan(GW4PTS) Semi Internal version for PI card |
* work. |
* AX.25 017 Alan(GW4PTS) Fixed some small bugs reported by |
* G4KLX |
* AX.25 018 Alan(GW4PTS) Fixed a small error in SOCK_DGRAM |
* AX.25 019 Alan(GW4PTS) Clean ups for the non INET kernel and device ioctls in AX.25 |
* AX.25 020 Jonathan(G4KLX) /proc support and other changes. |
* AX.25 021 Alan(GW4PTS) Added AX25_T1, AX25_N2, AX25_T3 as requested. |
* AX.25 022 Jonathan(G4KLX) More work on the ax25 auto router and /proc improved (again)! |
* Alan(GW4PTS) Added TIOCINQ/OUTQ |
* AX.25 023 Alan(GW4PTS) Fixed shutdown bug |
* AX.25 023 Alan(GW4PTS) Linus changed timers |
* AX.25 024 Alan(GW4PTS) Small bug fixes |
* AX.25 025 Alan(GW4PTS) More fixes, Linux 1.1.51 compatibility stuff, timers again! |
* AX.25 026 Alan(GW4PTS) Small state fix. |
* AX.25 027 Alan(GW4PTS) Socket close crash fixes. |
* AX.25 028 Alan(GW4PTS) Callsign control including settings per uid. |
* Small bug fixes. |
* Protocol set by sockets only. |
* Small changes to allow for start of NET/ROM layer. |
* AX.25 028a Jonathan(G4KLX) Changes to state machine. |
* AX.25 028b Jonathan(G4KLX) Extracted ax25 control block |
* from sock structure. |
* AX.25 029 Alan(GW4PTS) Combined 028b and some KA9Q code |
* Jonathan(G4KLX) and removed all the old Berkeley, added IP mode registration. |
* Darryl(G7LED) stuff. Cross-port digipeating. Minor fixes and enhancements. |
* Alan(GW4PTS) Missed suser() on axassociate checks |
* AX.25 030 Alan(GW4PTS) Added variable length headers. |
* Jonathan(G4KLX) Added BPQ Ethernet interface. |
* Steven(GW7RRM) Added digi-peating control ioctl. |
* Added extended AX.25 support. |
* Added AX.25 frame segmentation. |
* Darryl(G7LED) Changed connect(), recvfrom(), sendto() sockaddr/addrlen to |
* fall inline with bind() and new policy. |
* Moved digipeating ctl to new ax25_dev structs. |
* Fixed ax25_release(), set TCP_CLOSE, wakeup app |
* context, THEN make the sock dead. |
* Alan(GW4PTS) Cleaned up for single recvmsg methods. |
* Alan(GW4PTS) Fixed not clearing error on connect failure. |
* AX.25 031 Jonathan(G4KLX) Added binding to any device. |
* Joerg(DL1BKE) Added DAMA support, fixed (?) digipeating, fixed buffer locking |
* for "virtual connect" mode... Result: Probably the |
* "Most Buggiest Code You've Ever Seen" (TM) |
* HaJo(DD8NE) Implementation of a T5 (idle) timer |
* Joerg(DL1BKE) Renamed T5 to IDLE and changed behaviour: |
* the timer gets reloaded on every received or transmitted |
* I frame for IP or NETROM. The idle timer is not active |
* on "vanilla AX.25" connections. Furthermore added PACLEN |
* to provide AX.25-layer based fragmentation (like WAMPES) |
* AX.25 032 Joerg(DL1BKE) Fixed DAMA timeout error. |
* ax25_send_frame() limits the number of enqueued |
* datagrams per socket. |
* AX.25 033 Jonathan(G4KLX) Removed auto-router. |
* Hans(PE1AYX) Converted to Module. |
* Joerg(DL1BKE) Moved BPQ Ethernet to separate driver. |
* AX.25 034 Jonathan(G4KLX) 2.1 changes |
* Alan(GW4PTS) Small POSIXisations |
* AX.25 035 Alan(GW4PTS) Started fixing to the new |
* format. |
* Hans(PE1AYX) Fixed interface to IP layer. |
* Alan(GW4PTS) Added asynchronous support. |
* Frederic(F1OAT) Support for pseudo-digipeating. |
* Jonathan(G4KLX) Support for packet forwarding. |
* AX.25 036 Jonathan(G4KLX) Major restructuring. |
* Joerg(DL1BKE) Fixed DAMA Slave. |
* Jonathan(G4KLX) Fix wildcard listen parameter setting. |
* AX.25 037 Jonathan(G4KLX) New timer architecture. |
* AX.25 038 Matthias(DG2FEF) Small fixes to the syscall interface to make kernel |
* independent of AX25_MAX_DIGIS used by applications. |
* Tomi(OH2BNS) Fixed ax25_getname(). |
* Joerg(DL1BKE) Starting to phase out the support for full_sockaddr_ax25 |
* with only 6 digipeaters and sockaddr_ax25 in ax25_bind(), |
* ax25_connect() and ax25_sendmsg() |
* Joerg(DL1BKE) Added support for SO_BINDTODEVICE |
* Arnaldo C. Melo s/suser/capable(CAP_NET_ADMIN)/, some more cleanups |
* Michal Ostrowski Module initialization cleanup. |
* Jeroen(PE1RXQ) Use sock_orphan() on release. |
*/ |
|
#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 <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 <linux/proc_fs.h> |
#include <linux/stat.h> |
#include <linux/netfilter.h> |
#include <linux/sysctl.h> |
#include <linux/init.h> |
#include <net/ip.h> |
#include <net/arp.h> |
|
|
|
ax25_cb *volatile ax25_list; |
|
static struct proto_ops ax25_proto_ops; |
|
/* |
* Free an allocated ax25 control block. This is done to centralise |
* the MOD count code. |
*/ |
void ax25_free_cb(ax25_cb *ax25) |
{ |
if (ax25->digipeat != NULL) { |
kfree(ax25->digipeat); |
ax25->digipeat = NULL; |
} |
|
kfree(ax25); |
|
MOD_DEC_USE_COUNT; |
} |
|
static void ax25_free_sock(struct sock *sk) |
{ |
ax25_free_cb(sk->protinfo.ax25); |
} |
|
/* |
* Socket removal during an interrupt is now safe. |
*/ |
static void ax25_remove_socket(ax25_cb *ax25) |
{ |
ax25_cb *s; |
unsigned long flags; |
|
save_flags(flags); cli(); |
|
if ((s = ax25_list) == ax25) { |
ax25_list = s->next; |
restore_flags(flags); |
return; |
} |
|
while (s != NULL && s->next != NULL) { |
if (s->next == ax25) { |
s->next = ax25->next; |
restore_flags(flags); |
return; |
} |
|
s = s->next; |
} |
|
restore_flags(flags); |
} |
|
/* |
* Kill all bound sockets on a dropped device. |
*/ |
static void ax25_kill_by_device(struct net_device *dev) |
{ |
ax25_dev *ax25_dev; |
ax25_cb *s; |
|
if ((ax25_dev = ax25_dev_ax25dev(dev)) == NULL) |
return; |
|
for (s = ax25_list; s != NULL; s = s->next) { |
if (s->ax25_dev == ax25_dev) { |
s->ax25_dev = NULL; |
ax25_disconnect(s, ENETUNREACH); |
} |
} |
} |
|
/* |
* Handle device status changes. |
*/ |
static int ax25_device_event(struct notifier_block *this,unsigned long event, void *ptr) |
{ |
struct net_device *dev = (struct net_device *)ptr; |
|
/* Reject non AX.25 devices */ |
if (dev->type != ARPHRD_AX25) |
return NOTIFY_DONE; |
|
switch (event) { |
case NETDEV_UP: |
ax25_dev_device_up(dev); |
break; |
case NETDEV_DOWN: |
ax25_kill_by_device(dev); |
ax25_rt_device_down(dev); |
ax25_dev_device_down(dev); |
break; |
default: |
break; |
} |
|
return NOTIFY_DONE; |
} |
|
/* |
* Add a socket to the bound sockets list. |
*/ |
void ax25_insert_socket(ax25_cb *ax25) |
{ |
unsigned long flags; |
|
save_flags(flags); |
cli(); |
|
ax25->next = ax25_list; |
ax25_list = ax25; |
|
restore_flags(flags); |
} |
|
/* |
* Find a socket that wants to accept the SABM we have just |
* received. |
*/ |
struct sock *ax25_find_listener(ax25_address *addr, int digi, struct net_device *dev, int type) |
{ |
unsigned long flags; |
ax25_cb *s; |
|
save_flags(flags); |
cli(); |
|
for (s = ax25_list; s != NULL; s = s->next) { |
if ((s->iamdigi && !digi) || (!s->iamdigi && digi)) |
continue; |
if (s->sk != NULL && ax25cmp(&s->source_addr, addr) == 0 && s->sk->type == type && s->sk->state == TCP_LISTEN) { |
/* If device is null we match any device */ |
if (s->ax25_dev == NULL || s->ax25_dev->dev == dev) { |
restore_flags(flags); |
return s->sk; |
} |
} |
} |
|
restore_flags(flags); |
return NULL; |
} |
|
/* |
* Find an AX.25 socket given both ends. |
*/ |
struct sock *ax25_find_socket(ax25_address *my_addr, ax25_address *dest_addr, int type) |
{ |
ax25_cb *s; |
unsigned long flags; |
|
save_flags(flags); |
cli(); |
|
for (s = ax25_list; s != NULL; s = s->next) { |
if (s->sk != NULL && ax25cmp(&s->source_addr, my_addr) == 0 && ax25cmp(&s->dest_addr, dest_addr) == 0 && s->sk->type == type) { |
restore_flags(flags); |
return s->sk; |
} |
} |
|
restore_flags(flags); |
|
return NULL; |
} |
|
/* |
* Find an AX.25 control block given both ends. It will only pick up |
* floating AX.25 control blocks or non Raw socket bound control blocks. |
*/ |
ax25_cb *ax25_find_cb(ax25_address *src_addr, ax25_address *dest_addr, ax25_digi *digi, struct net_device *dev) |
{ |
ax25_cb *s; |
unsigned long flags; |
|
save_flags(flags); |
cli(); |
|
for (s = ax25_list; s != NULL; s = s->next) { |
if (s->sk != NULL && s->sk->type != SOCK_SEQPACKET) |
continue; |
if (s->ax25_dev == NULL) |
continue; |
if (ax25cmp(&s->source_addr, src_addr) == 0 && ax25cmp(&s->dest_addr, dest_addr) == 0 && s->ax25_dev->dev == dev) { |
if (digi != NULL && digi->ndigi != 0) { |
if (s->digipeat == NULL) |
continue; |
if (ax25digicmp(s->digipeat, digi) != 0) |
continue; |
} else { |
if (s->digipeat != NULL && s->digipeat->ndigi != 0) |
continue; |
} |
restore_flags(flags); |
return s; |
} |
} |
|
restore_flags(flags); |
|
return NULL; |
} |
|
/* |
* Look for any matching address - RAW sockets can bind to arbitrary names |
*/ |
struct sock *ax25_addr_match(ax25_address *addr) |
{ |
unsigned long flags; |
ax25_cb *s; |
|
save_flags(flags); |
cli(); |
|
for (s = ax25_list; s != NULL; s = s->next) { |
if (s->sk != NULL && ax25cmp(&s->source_addr, addr) == 0 && s->sk->type == SOCK_RAW) { |
restore_flags(flags); |
return s->sk; |
} |
} |
|
restore_flags(flags); |
|
return NULL; |
} |
|
void ax25_send_to_raw(struct sock *sk, struct sk_buff *skb, int proto) |
{ |
struct sk_buff *copy; |
|
while (sk != NULL) { |
if (sk->type == SOCK_RAW && |
sk->protocol == proto && |
atomic_read(&sk->rmem_alloc) <= sk->rcvbuf) { |
if ((copy = skb_clone(skb, GFP_ATOMIC)) == NULL) |
return; |
|
if (sock_queue_rcv_skb(sk, copy) != 0) |
kfree_skb(copy); |
} |
|
sk = sk->next; |
} |
} |
|
/* |
* Deferred destroy. |
*/ |
void ax25_destroy_socket(ax25_cb *); |
|
/* |
* Handler for deferred kills. |
*/ |
static void ax25_destroy_timer(unsigned long data) |
{ |
ax25_destroy_socket((ax25_cb *)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 ax25_destroy_socket(ax25_cb *ax25) /* Not static as it's used by the timer */ |
{ |
struct sk_buff *skb; |
unsigned long flags; |
|
save_flags(flags); cli(); |
|
ax25_stop_heartbeat(ax25); |
ax25_stop_t1timer(ax25); |
ax25_stop_t2timer(ax25); |
ax25_stop_t3timer(ax25); |
ax25_stop_idletimer(ax25); |
|
ax25_remove_socket(ax25); |
ax25_clear_queues(ax25); /* Flush the queues */ |
|
if (ax25->sk != NULL) { |
while ((skb = skb_dequeue(&ax25->sk->receive_queue)) != NULL) { |
if (skb->sk != ax25->sk) { /* A pending connection */ |
skb->sk->dead = 1; /* Queue the unaccepted socket for death */ |
ax25_start_heartbeat(skb->sk->protinfo.ax25); |
skb->sk->protinfo.ax25->state = AX25_STATE_0; |
} |
|
kfree_skb(skb); |
} |
} |
|
if (ax25->sk != NULL) { |
if (atomic_read(&ax25->sk->wmem_alloc) != 0 || |
atomic_read(&ax25->sk->rmem_alloc) != 0) { |
/* Defer: outstanding buffers */ |
init_timer(&ax25->timer); |
ax25->timer.expires = jiffies + 10 * HZ; |
ax25->timer.function = ax25_destroy_timer; |
ax25->timer.data = (unsigned long)ax25; |
add_timer(&ax25->timer); |
} else { |
sk_free(ax25->sk); |
} |
} else { |
ax25_free_cb(ax25); |
} |
|
restore_flags(flags); |
} |
|
/* |
* dl1bke 960311: set parameters for existing AX.25 connections, |
* includes a KILL command to abort any connection. |
* VERY useful for debugging ;-) |
*/ |
static int ax25_ctl_ioctl(const unsigned int cmd, void *arg) |
{ |
struct ax25_ctl_struct ax25_ctl; |
ax25_digi digi; |
ax25_dev *ax25_dev; |
ax25_cb *ax25; |
unsigned int k; |
|
if (copy_from_user(&ax25_ctl, arg, sizeof(ax25_ctl))) |
return -EFAULT; |
|
if ((ax25_dev = ax25_addr_ax25dev(&ax25_ctl.port_addr)) == NULL) |
return -ENODEV; |
|
if (ax25_ctl.digi_count > AX25_MAX_DIGIS) |
return -EINVAL; |
|
digi.ndigi = ax25_ctl.digi_count; |
for (k = 0; k < digi.ndigi; k++) |
digi.calls[k] = ax25_ctl.digi_addr[k]; |
|
if ((ax25 = ax25_find_cb(&ax25_ctl.source_addr, &ax25_ctl.dest_addr, &digi, ax25_dev->dev)) == NULL) |
return -ENOTCONN; |
|
switch (ax25_ctl.cmd) { |
case AX25_KILL: |
ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND); |
#ifdef CONFIG_AX25_DAMA_SLAVE |
if (ax25_dev->dama.slave && ax25->ax25_dev->values[AX25_VALUES_PROTOCOL] == AX25_PROTO_DAMA_SLAVE) |
ax25_dama_off(ax25); |
#endif |
ax25_disconnect(ax25, ENETRESET); |
break; |
|
case AX25_WINDOW: |
if (ax25->modulus == AX25_MODULUS) { |
if (ax25_ctl.arg < 1 || ax25_ctl.arg > 7) |
return -EINVAL; |
} else { |
if (ax25_ctl.arg < 1 || ax25_ctl.arg > 63) |
return -EINVAL; |
} |
ax25->window = ax25_ctl.arg; |
break; |
|
case AX25_T1: |
if (ax25_ctl.arg < 1) |
return -EINVAL; |
ax25->rtt = (ax25_ctl.arg * HZ) / 2; |
ax25->t1 = ax25_ctl.arg * HZ; |
break; |
|
case AX25_T2: |
if (ax25_ctl.arg < 1) |
return -EINVAL; |
ax25->t2 = ax25_ctl.arg * HZ; |
break; |
|
case AX25_N2: |
if (ax25_ctl.arg < 1 || ax25_ctl.arg > 31) |
return -EINVAL; |
ax25->n2count = 0; |
ax25->n2 = ax25_ctl.arg; |
break; |
|
case AX25_T3: |
if (ax25_ctl.arg < 0) |
return -EINVAL; |
ax25->t3 = ax25_ctl.arg * HZ; |
break; |
|
case AX25_IDLE: |
if (ax25_ctl.arg < 0) |
return -EINVAL; |
ax25->idle = ax25_ctl.arg * 60 * HZ; |
break; |
|
case AX25_PACLEN: |
if (ax25_ctl.arg < 16 || ax25_ctl.arg > 65535) |
return -EINVAL; |
ax25->paclen = ax25_ctl.arg; |
break; |
|
default: |
return -EINVAL; |
} |
|
return 0; |
} |
|
/* |
* Fill in a created AX.25 created control block with the default |
* values for a particular device. |
*/ |
void ax25_fillin_cb(ax25_cb *ax25, ax25_dev *ax25_dev) |
{ |
ax25->ax25_dev = ax25_dev; |
|
if (ax25->ax25_dev != NULL) { |
ax25->rtt = ax25_dev->values[AX25_VALUES_T1] / 2; |
ax25->t1 = ax25_dev->values[AX25_VALUES_T1]; |
ax25->t2 = ax25_dev->values[AX25_VALUES_T2]; |
ax25->t3 = ax25_dev->values[AX25_VALUES_T3]; |
ax25->n2 = ax25_dev->values[AX25_VALUES_N2]; |
ax25->paclen = ax25_dev->values[AX25_VALUES_PACLEN]; |
ax25->idle = ax25_dev->values[AX25_VALUES_IDLE]; |
ax25->backoff = ax25_dev->values[AX25_VALUES_BACKOFF]; |
|
if (ax25_dev->values[AX25_VALUES_AXDEFMODE]) { |
ax25->modulus = AX25_EMODULUS; |
ax25->window = ax25_dev->values[AX25_VALUES_EWINDOW]; |
} else { |
ax25->modulus = AX25_MODULUS; |
ax25->window = ax25_dev->values[AX25_VALUES_WINDOW]; |
} |
} else { |
ax25->rtt = AX25_DEF_T1 / 2; |
ax25->t1 = AX25_DEF_T1; |
ax25->t2 = AX25_DEF_T2; |
ax25->t3 = AX25_DEF_T3; |
ax25->n2 = AX25_DEF_N2; |
ax25->paclen = AX25_DEF_PACLEN; |
ax25->idle = AX25_DEF_IDLE; |
ax25->backoff = AX25_DEF_BACKOFF; |
|
if (AX25_DEF_AXDEFMODE) { |
ax25->modulus = AX25_EMODULUS; |
ax25->window = AX25_DEF_EWINDOW; |
} else { |
ax25->modulus = AX25_MODULUS; |
ax25->window = AX25_DEF_WINDOW; |
} |
} |
} |
|
/* |
* Create an empty AX.25 control block. |
*/ |
ax25_cb *ax25_create_cb(void) |
{ |
ax25_cb *ax25; |
|
if ((ax25 = kmalloc(sizeof(*ax25), GFP_ATOMIC)) == NULL) |
return NULL; |
|
MOD_INC_USE_COUNT; |
|
memset(ax25, 0x00, sizeof(*ax25)); |
|
skb_queue_head_init(&ax25->write_queue); |
skb_queue_head_init(&ax25->frag_queue); |
skb_queue_head_init(&ax25->ack_queue); |
skb_queue_head_init(&ax25->reseq_queue); |
|
init_timer(&ax25->timer); |
init_timer(&ax25->t1timer); |
init_timer(&ax25->t2timer); |
init_timer(&ax25->t3timer); |
init_timer(&ax25->idletimer); |
|
ax25_fillin_cb(ax25, NULL); |
|
ax25->state = AX25_STATE_0; |
|
return ax25; |
} |
|
/* |
* Handling for system calls applied via the various interfaces to an |
* AX25 socket object |
*/ |
|
static int ax25_setsockopt(struct socket *sock, int level, int optname, char *optval, int optlen) |
{ |
struct sock *sk = sock->sk; |
struct net_device *dev; |
char devname[IFNAMSIZ]; |
int opt; |
|
if (level != SOL_AX25) |
return -ENOPROTOOPT; |
|
if (optlen < sizeof(int)) |
return -EINVAL; |
|
if (get_user(opt, (int *)optval)) |
return -EFAULT; |
|
switch (optname) { |
case AX25_WINDOW: |
if (sk->protinfo.ax25->modulus == AX25_MODULUS) { |
if (opt < 1 || opt > 7) |
return -EINVAL; |
} else { |
if (opt < 1 || opt > 63) |
return -EINVAL; |
} |
sk->protinfo.ax25->window = opt; |
return 0; |
|
case AX25_T1: |
if (opt < 1) |
return -EINVAL; |
sk->protinfo.ax25->rtt = (opt * HZ) / 2; |
sk->protinfo.ax25->t1 = opt * HZ; |
return 0; |
|
case AX25_T2: |
if (opt < 1) |
return -EINVAL; |
sk->protinfo.ax25->t2 = opt * HZ; |
return 0; |
|
case AX25_N2: |
if (opt < 1 || opt > 31) |
return -EINVAL; |
sk->protinfo.ax25->n2 = opt; |
return 0; |
|
case AX25_T3: |
if (opt < 1) |
return -EINVAL; |
sk->protinfo.ax25->t3 = opt * HZ; |
return 0; |
|
case AX25_IDLE: |
if (opt < 0) |
return -EINVAL; |
sk->protinfo.ax25->idle = opt * 60 * HZ; |
return 0; |
|
case AX25_BACKOFF: |
if (opt < 0 || opt > 2) |
return -EINVAL; |
sk->protinfo.ax25->backoff = opt; |
return 0; |
|
case AX25_EXTSEQ: |
sk->protinfo.ax25->modulus = opt ? AX25_EMODULUS : AX25_MODULUS; |
return 0; |
|
case AX25_PIDINCL: |
sk->protinfo.ax25->pidincl = opt ? 1 : 0; |
return 0; |
|
case AX25_IAMDIGI: |
sk->protinfo.ax25->iamdigi = opt ? 1 : 0; |
return 0; |
|
case AX25_PACLEN: |
if (opt < 16 || opt > 65535) |
return -EINVAL; |
sk->protinfo.ax25->paclen = opt; |
return 0; |
|
case SO_BINDTODEVICE: |
if (optlen > IFNAMSIZ) optlen=IFNAMSIZ; |
if (copy_from_user(devname, optval, optlen)) |
return -EFAULT; |
|
dev = dev_get_by_name(devname); |
if (dev == NULL) return -ENODEV; |
|
if (sk->type == SOCK_SEQPACKET && |
(sock->state != SS_UNCONNECTED || sk->state == TCP_LISTEN)) |
return -EADDRNOTAVAIL; |
|
sk->protinfo.ax25->ax25_dev = ax25_dev_ax25dev(dev); |
ax25_fillin_cb(sk->protinfo.ax25, sk->protinfo.ax25->ax25_dev); |
return 0; |
|
default: |
return -ENOPROTOOPT; |
} |
} |
|
static int ax25_getsockopt(struct socket *sock, int level, int optname, char *optval, int *optlen) |
{ |
struct sock *sk = sock->sk; |
struct ax25_dev *ax25_dev; |
char devname[IFNAMSIZ]; |
void *valptr; |
int val = 0; |
int maxlen, length; |
|
if (level != SOL_AX25) |
return -ENOPROTOOPT; |
|
if (get_user(maxlen, optlen)) |
return -EFAULT; |
|
if (maxlen < 1) |
return -EFAULT; |
|
valptr = (void *) &val; |
length = min_t(unsigned int, maxlen, sizeof(int)); |
|
switch (optname) { |
case AX25_WINDOW: |
val = sk->protinfo.ax25->window; |
break; |
|
case AX25_T1: |
val = sk->protinfo.ax25->t1 / HZ; |
break; |
|
case AX25_T2: |
val = sk->protinfo.ax25->t2 / HZ; |
break; |
|
case AX25_N2: |
val = sk->protinfo.ax25->n2; |
break; |
|
case AX25_T3: |
val = sk->protinfo.ax25->t3 / HZ; |
break; |
|
case AX25_IDLE: |
val = sk->protinfo.ax25->idle / (60 * HZ); |
break; |
|
case AX25_BACKOFF: |
val = sk->protinfo.ax25->backoff; |
break; |
|
case AX25_EXTSEQ: |
val = (sk->protinfo.ax25->modulus == AX25_EMODULUS); |
break; |
|
case AX25_PIDINCL: |
val = sk->protinfo.ax25->pidincl; |
break; |
|
case AX25_IAMDIGI: |
val = sk->protinfo.ax25->iamdigi; |
break; |
|
case AX25_PACLEN: |
val = sk->protinfo.ax25->paclen; |
break; |
|
case SO_BINDTODEVICE: |
ax25_dev = sk->protinfo.ax25->ax25_dev; |
|
if (ax25_dev != NULL && ax25_dev->dev != NULL) { |
strncpy(devname, ax25_dev->dev->name, IFNAMSIZ); |
length = min_t(unsigned int, strlen(ax25_dev->dev->name)+1, maxlen); |
devname[length-1] = '\0'; |
} else { |
*devname = '\0'; |
length = 1; |
} |
|
valptr = (void *) devname; |
break; |
|
default: |
return -ENOPROTOOPT; |
} |
|
if (put_user(length, optlen)) |
return -EFAULT; |
|
return copy_to_user(optval, valptr, length) ? -EFAULT : 0; |
} |
|
static int ax25_listen(struct socket *sock, int backlog) |
{ |
struct sock *sk = sock->sk; |
|
if (sk->type == SOCK_SEQPACKET && sk->state != TCP_LISTEN) { |
sk->max_ack_backlog = backlog; |
sk->state = TCP_LISTEN; |
return 0; |
} |
|
return -EOPNOTSUPP; |
} |
|
int ax25_create(struct socket *sock, int protocol) |
{ |
struct sock *sk; |
ax25_cb *ax25; |
|
switch (sock->type) { |
case SOCK_DGRAM: |
if (protocol == 0 || protocol == PF_AX25) |
protocol = AX25_P_TEXT; |
break; |
case SOCK_SEQPACKET: |
switch (protocol) { |
case 0: |
case PF_AX25: /* For CLX */ |
protocol = AX25_P_TEXT; |
break; |
case AX25_P_SEGMENT: |
#ifdef CONFIG_INET |
case AX25_P_ARP: |
case AX25_P_IP: |
#endif |
#ifdef CONFIG_NETROM |
case AX25_P_NETROM: |
#endif |
#ifdef CONFIG_ROSE |
case AX25_P_ROSE: |
#endif |
return -ESOCKTNOSUPPORT; |
#ifdef CONFIG_NETROM_MODULE |
case AX25_P_NETROM: |
if (ax25_protocol_is_registered(AX25_P_NETROM)) |
return -ESOCKTNOSUPPORT; |
#endif |
#ifdef CONFIG_ROSE_MODULE |
case AX25_P_ROSE: |
if (ax25_protocol_is_registered(AX25_P_ROSE)) |
return -ESOCKTNOSUPPORT; |
#endif |
default: |
break; |
} |
break; |
case SOCK_RAW: |
break; |
default: |
return -ESOCKTNOSUPPORT; |
} |
|
if ((sk = sk_alloc(PF_AX25, GFP_ATOMIC, 1)) == NULL) |
return -ENOMEM; |
|
if ((ax25 = ax25_create_cb()) == NULL) { |
sk_free(sk); |
return -ENOMEM; |
} |
|
sock_init_data(sock, sk); |
|
sk->destruct = ax25_free_sock; |
sock->ops = &ax25_proto_ops; |
sk->protocol = protocol; |
|
ax25->sk = sk; |
sk->protinfo.ax25 = ax25; |
|
return 0; |
} |
|
struct sock *ax25_make_new(struct sock *osk, struct ax25_dev *ax25_dev) |
{ |
struct sock *sk; |
ax25_cb *ax25; |
|
if ((sk = sk_alloc(PF_AX25, GFP_ATOMIC, 1)) == NULL) |
return NULL; |
|
if ((ax25 = ax25_create_cb()) == NULL) { |
sk_free(sk); |
return NULL; |
} |
|
switch (osk->type) { |
case SOCK_DGRAM: |
break; |
case SOCK_SEQPACKET: |
break; |
default: |
sk_free(sk); |
ax25_free_cb(ax25); |
return NULL; |
} |
|
sock_init_data(NULL, sk); |
|
sk->destruct = ax25_free_sock; |
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; |
|
ax25->modulus = osk->protinfo.ax25->modulus; |
ax25->backoff = osk->protinfo.ax25->backoff; |
ax25->pidincl = osk->protinfo.ax25->pidincl; |
ax25->iamdigi = osk->protinfo.ax25->iamdigi; |
ax25->rtt = osk->protinfo.ax25->rtt; |
ax25->t1 = osk->protinfo.ax25->t1; |
ax25->t2 = osk->protinfo.ax25->t2; |
ax25->t3 = osk->protinfo.ax25->t3; |
ax25->n2 = osk->protinfo.ax25->n2; |
ax25->idle = osk->protinfo.ax25->idle; |
ax25->paclen = osk->protinfo.ax25->paclen; |
ax25->window = osk->protinfo.ax25->window; |
|
ax25->ax25_dev = ax25_dev; |
ax25->source_addr = osk->protinfo.ax25->source_addr; |
|
if (osk->protinfo.ax25->digipeat != NULL) { |
if ((ax25->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL) { |
sk_free(sk); |
return NULL; |
} |
|
memcpy(ax25->digipeat, osk->protinfo.ax25->digipeat, sizeof(ax25_digi)); |
} |
|
sk->protinfo.ax25 = ax25; |
ax25->sk = sk; |
|
return sk; |
} |
|
static int ax25_release(struct socket *sock) |
{ |
struct sock *sk = sock->sk; |
|
if (sk == NULL) return 0; |
|
if (sk->type == SOCK_SEQPACKET) { |
switch (sk->protinfo.ax25->state) { |
case AX25_STATE_0: |
ax25_disconnect(sk->protinfo.ax25, 0); |
ax25_destroy_socket(sk->protinfo.ax25); |
break; |
|
case AX25_STATE_1: |
case AX25_STATE_2: |
ax25_send_control(sk->protinfo.ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND); |
ax25_disconnect(sk->protinfo.ax25, 0); |
ax25_destroy_socket(sk->protinfo.ax25); |
break; |
|
case AX25_STATE_3: |
case AX25_STATE_4: |
ax25_clear_queues(sk->protinfo.ax25); |
sk->protinfo.ax25->n2count = 0; |
switch (sk->protinfo.ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) { |
case AX25_PROTO_STD_SIMPLEX: |
case AX25_PROTO_STD_DUPLEX: |
ax25_send_control(sk->protinfo.ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND); |
ax25_stop_t2timer(sk->protinfo.ax25); |
ax25_stop_t3timer(sk->protinfo.ax25); |
ax25_stop_idletimer(sk->protinfo.ax25); |
break; |
#ifdef CONFIG_AX25_DAMA_SLAVE |
case AX25_PROTO_DAMA_SLAVE: |
ax25_stop_t3timer(sk->protinfo.ax25); |
ax25_stop_idletimer(sk->protinfo.ax25); |
break; |
#endif |
} |
ax25_calculate_t1(sk->protinfo.ax25); |
ax25_start_t1timer(sk->protinfo.ax25); |
sk->protinfo.ax25->state = AX25_STATE_2; |
sk->state = TCP_CLOSE; |
sk->shutdown |= SEND_SHUTDOWN; |
sk->state_change(sk); |
sock_orphan(sk); |
sk->destroy = 1; |
break; |
|
default: |
break; |
} |
} else { |
sk->state = TCP_CLOSE; |
sk->shutdown |= SEND_SHUTDOWN; |
sk->state_change(sk); |
sock_orphan(sk); |
ax25_destroy_socket(sk->protinfo.ax25); |
} |
|
sock->sk = NULL; |
sk->socket = NULL; /* Not used, but we should do this */ |
|
return 0; |
} |
|
/* |
* We support a funny extension here so you can (as root) give any callsign |
* digipeated via a local address as source. This hack is obsolete now |
* that we've implemented support for SO_BINDTODEVICE. It is however small |
* and trivially backward compatible. |
*/ |
static int ax25_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; |
ax25_address *call; |
ax25_dev *ax25_dev = NULL; |
|
if (sk->zapped == 0) |
return -EINVAL; |
|
if (addr_len != sizeof(struct sockaddr_ax25) && |
addr_len != sizeof(struct full_sockaddr_ax25)) { |
/* support for old structure may go away some time */ |
if ((addr_len < sizeof(struct sockaddr_ax25) + sizeof(ax25_address) * 6) || |
(addr_len > sizeof(struct full_sockaddr_ax25))) |
return -EINVAL; |
|
printk(KERN_WARNING "ax25_bind(): %s uses old (6 digipeater) socket structure.\n", |
current->comm); |
} |
|
if (addr->fsa_ax25.sax25_family != AF_AX25) |
return -EINVAL; |
|
call = ax25_findbyuid(current->euid); |
if (call == NULL && ax25_uid_policy && !capable(CAP_NET_ADMIN)) |
return -EACCES; |
|
if (call == NULL) |
sk->protinfo.ax25->source_addr = addr->fsa_ax25.sax25_call; |
else |
sk->protinfo.ax25->source_addr = *call; |
|
/* |
* User already set interface with SO_BINDTODEVICE |
*/ |
|
if (sk->protinfo.ax25->ax25_dev != NULL) |
goto done; |
|
if (addr_len > sizeof(struct sockaddr_ax25) && addr->fsa_ax25.sax25_ndigis == 1) { |
if (ax25cmp(&addr->fsa_digipeater[0], &null_ax25_address) != 0 && |
(ax25_dev = ax25_addr_ax25dev(&addr->fsa_digipeater[0])) == NULL) |
return -EADDRNOTAVAIL; |
} else { |
if ((ax25_dev = ax25_addr_ax25dev(&addr->fsa_ax25.sax25_call)) == NULL) |
return -EADDRNOTAVAIL; |
} |
|
if (ax25_dev != NULL) |
ax25_fillin_cb(sk->protinfo.ax25, ax25_dev); |
|
done: |
ax25_insert_socket(sk->protinfo.ax25); |
sk->zapped = 0; |
return 0; |
} |
|
/* |
* FIXME: nonblock behaviour looks like it may have a bug. |
*/ |
static int ax25_connect(struct socket *sock, struct sockaddr *uaddr, int addr_len, int flags) |
{ |
struct sock *sk = sock->sk; |
struct full_sockaddr_ax25 *fsa = (struct full_sockaddr_ax25 *)uaddr; |
ax25_digi *digi = NULL; |
int ct = 0, err; |
|
/* deal with restarts */ |
if (sock->state == SS_CONNECTING) { |
switch (sk->state) { |
case TCP_SYN_SENT: /* still trying */ |
return -EINPROGRESS; |
|
case TCP_ESTABLISHED: /* connection established */ |
sock->state = SS_CONNECTED; |
return 0; |
|
case TCP_CLOSE: /* connection refused */ |
sock->state = SS_UNCONNECTED; |
return -ECONNREFUSED; |
} |
} |
|
if (sk->state == TCP_ESTABLISHED && sk->type == SOCK_SEQPACKET) |
return -EISCONN; /* No reconnect on a seqpacket socket */ |
|
sk->state = TCP_CLOSE; |
sock->state = SS_UNCONNECTED; |
|
/* |
* some sanity checks. code further down depends on this |
*/ |
|
if (addr_len == sizeof(struct sockaddr_ax25)) { |
/* support for this will go away in early 2.5.x */ |
printk(KERN_WARNING "ax25_connect(): %s uses obsolete socket structure\n", |
current->comm); |
} |
else if (addr_len != sizeof(struct full_sockaddr_ax25)) { |
/* support for old structure may go away some time */ |
if ((addr_len < sizeof(struct sockaddr_ax25) + sizeof(ax25_address) * 6) || |
(addr_len > sizeof(struct full_sockaddr_ax25))) |
return -EINVAL; |
|
printk(KERN_WARNING "ax25_connect(): %s uses old (6 digipeater) socket structure.\n", |
current->comm); |
} |
|
if (fsa->fsa_ax25.sax25_family != AF_AX25) |
return -EINVAL; |
|
if (sk->protinfo.ax25->digipeat != NULL) { |
kfree(sk->protinfo.ax25->digipeat); |
sk->protinfo.ax25->digipeat = NULL; |
} |
|
/* |
* Handle digi-peaters to be used. |
*/ |
if (addr_len > sizeof(struct sockaddr_ax25) && fsa->fsa_ax25.sax25_ndigis != 0) { |
/* Valid number of digipeaters ? */ |
if (fsa->fsa_ax25.sax25_ndigis < 1 || fsa->fsa_ax25.sax25_ndigis > AX25_MAX_DIGIS) |
return -EINVAL; |
|
if ((digi = kmalloc(sizeof(ax25_digi), GFP_KERNEL)) == NULL) |
return -ENOBUFS; |
|
digi->ndigi = fsa->fsa_ax25.sax25_ndigis; |
digi->lastrepeat = -1; |
|
while (ct < fsa->fsa_ax25.sax25_ndigis) { |
if ((fsa->fsa_digipeater[ct].ax25_call[6] & AX25_HBIT) && sk->protinfo.ax25->iamdigi) { |
digi->repeated[ct] = 1; |
digi->lastrepeat = ct; |
} else { |
digi->repeated[ct] = 0; |
} |
digi->calls[ct] = fsa->fsa_digipeater[ct]; |
ct++; |
} |
} |
|
/* |
* Must bind first - autobinding in this may or may not work. If |
* the socket is already bound, check to see if the device has |
* been filled in, error if it hasn't. |
*/ |
if (sk->zapped) { |
/* check if we can remove this feature. It is broken. */ |
printk(KERN_WARNING "ax25_connect(): %s uses autobind, please contact jreuter@yaina.de\n", |
current->comm); |
if ((err = ax25_rt_autobind(sk->protinfo.ax25, &fsa->fsa_ax25.sax25_call)) < 0) |
return err; |
ax25_fillin_cb(sk->protinfo.ax25, sk->protinfo.ax25->ax25_dev); |
ax25_insert_socket(sk->protinfo.ax25); |
} else { |
if (sk->protinfo.ax25->ax25_dev == NULL) |
return -EHOSTUNREACH; |
} |
|
if (sk->type == SOCK_SEQPACKET && ax25_find_cb(&sk->protinfo.ax25->source_addr, &fsa->fsa_ax25.sax25_call, digi, sk->protinfo.ax25->ax25_dev->dev) != NULL) { |
if (digi != NULL) kfree(digi); |
return -EADDRINUSE; /* Already such a connection */ |
} |
|
sk->protinfo.ax25->dest_addr = fsa->fsa_ax25.sax25_call; |
sk->protinfo.ax25->digipeat = digi; |
|
/* First the easy one */ |
if (sk->type != SOCK_SEQPACKET) { |
sock->state = SS_CONNECTED; |
sk->state = TCP_ESTABLISHED; |
return 0; |
} |
|
/* Move to connecting socket, ax.25 lapb WAIT_UA.. */ |
sock->state = SS_CONNECTING; |
sk->state = TCP_SYN_SENT; |
|
switch (sk->protinfo.ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) { |
case AX25_PROTO_STD_SIMPLEX: |
case AX25_PROTO_STD_DUPLEX: |
ax25_std_establish_data_link(sk->protinfo.ax25); |
break; |
|
#ifdef CONFIG_AX25_DAMA_SLAVE |
case AX25_PROTO_DAMA_SLAVE: |
sk->protinfo.ax25->modulus = AX25_MODULUS; |
sk->protinfo.ax25->window = sk->protinfo.ax25->ax25_dev->values[AX25_VALUES_WINDOW]; |
if (sk->protinfo.ax25->ax25_dev->dama.slave) |
ax25_ds_establish_data_link(sk->protinfo.ax25); |
else |
ax25_std_establish_data_link(sk->protinfo.ax25); |
break; |
#endif |
} |
|
sk->protinfo.ax25->state = AX25_STATE_1; |
|
ax25_start_heartbeat(sk->protinfo.ax25); |
|
/* Now the loop */ |
if (sk->state != TCP_ESTABLISHED && (flags & O_NONBLOCK)) |
return -EINPROGRESS; |
|
cli(); /* To avoid races on the sleep */ |
|
/* A DM or timeout will go to closed, a UA will go to ABM */ |
while (sk->state == TCP_SYN_SENT) { |
interruptible_sleep_on(sk->sleep); |
if (signal_pending(current)) { |
sti(); |
return -ERESTARTSYS; |
} |
} |
|
if (sk->state != TCP_ESTABLISHED) { |
/* Not in ABM, not in WAIT_UA -> failed */ |
sti(); |
sock->state = SS_UNCONNECTED; |
return sock_error(sk); /* Always set at this point */ |
} |
|
sock->state = SS_CONNECTED; |
|
sti(); |
|
return 0; |
} |
|
|
static int ax25_accept(struct socket *sock, struct socket *newsock, int flags) |
{ |
struct sock *sk; |
struct sock *newsk; |
struct sk_buff *skb; |
|
if (sock->state != SS_UNCONNECTED) |
return -EINVAL; |
|
if ((sk = sock->sk) == NULL) |
return -EINVAL; |
|
if (sk->type != SOCK_SEQPACKET) |
return -EOPNOTSUPP; |
|
if (sk->state != TCP_LISTEN) |
return -EINVAL; |
|
/* |
* The read queue this time is holding sockets ready to use |
* hooked into the SABM we saved |
*/ |
do { |
if ((skb = skb_dequeue(&sk->receive_queue)) == NULL) { |
if (flags & O_NONBLOCK) |
return -EWOULDBLOCK; |
|
interruptible_sleep_on(sk->sleep); |
if (signal_pending(current)) |
return -ERESTARTSYS; |
} |
} while (skb == NULL); |
|
newsk = skb->sk; |
newsk->pair = NULL; |
newsk->socket = newsock; |
newsk->sleep = &newsock->wait; |
|
/* Now attach up the new socket */ |
kfree_skb(skb); |
sk->ack_backlog--; |
newsock->sk = newsk; |
newsock->state = SS_CONNECTED; |
|
return 0; |
} |
|
static int ax25_getname(struct socket *sock, struct sockaddr *uaddr, int *uaddr_len, int peer) |
{ |
struct sock *sk = sock->sk; |
struct full_sockaddr_ax25 *fsa = (struct full_sockaddr_ax25 *)uaddr; |
unsigned char ndigi, i; |
|
if (peer != 0) { |
if (sk->state != TCP_ESTABLISHED) |
return -ENOTCONN; |
|
fsa->fsa_ax25.sax25_family = AF_AX25; |
fsa->fsa_ax25.sax25_call = sk->protinfo.ax25->dest_addr; |
fsa->fsa_ax25.sax25_ndigis = 0; |
|
if (sk->protinfo.ax25->digipeat != NULL) { |
ndigi = sk->protinfo.ax25->digipeat->ndigi; |
fsa->fsa_ax25.sax25_ndigis = ndigi; |
for (i = 0; i < ndigi; i++) |
fsa->fsa_digipeater[i] = sk->protinfo.ax25->digipeat->calls[i]; |
} |
} else { |
fsa->fsa_ax25.sax25_family = AF_AX25; |
fsa->fsa_ax25.sax25_call = sk->protinfo.ax25->source_addr; |
fsa->fsa_ax25.sax25_ndigis = 1; |
if (sk->protinfo.ax25->ax25_dev != NULL) { |
memcpy(&fsa->fsa_digipeater[0], sk->protinfo.ax25->ax25_dev->dev->dev_addr, AX25_ADDR_LEN); |
} else { |
fsa->fsa_digipeater[0] = null_ax25_address; |
} |
} |
*uaddr_len = sizeof (struct full_sockaddr_ax25); |
return 0; |
} |
|
static int ax25_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; |
ax25_digi *dp; |
ax25_digi dtmp; |
int lv; |
int addr_len = msg->msg_namelen; |
|
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.ax25->ax25_dev == NULL) |
return -ENETUNREACH; |
|
if (usax != NULL) { |
if (usax->sax25_family != AF_AX25) |
return -EINVAL; |
|
if (addr_len == sizeof(struct sockaddr_ax25)) { |
printk(KERN_WARNING "ax25_sendmsg(): %s uses obsolete socket structure\n", |
current->comm); |
} |
else if (addr_len != sizeof(struct full_sockaddr_ax25)) { |
/* support for old structure may go away some time */ |
if ((addr_len < sizeof(struct sockaddr_ax25) + sizeof(ax25_address) * 6) || |
(addr_len > sizeof(struct full_sockaddr_ax25))) |
return -EINVAL; |
|
printk(KERN_WARNING "ax25_sendmsg(): %s uses old (6 digipeater) socket structure.\n", |
current->comm); |
} |
|
if (addr_len > sizeof(struct sockaddr_ax25) && usax->sax25_ndigis != 0) { |
int ct = 0; |
struct full_sockaddr_ax25 *fsa = (struct full_sockaddr_ax25 *)usax; |
|
/* Valid number of digipeaters ? */ |
if (usax->sax25_ndigis < 1 || usax->sax25_ndigis > AX25_MAX_DIGIS) |
return -EINVAL; |
|
dtmp.ndigi = usax->sax25_ndigis; |
|
while (ct < usax->sax25_ndigis) { |
dtmp.repeated[ct] = 0; |
dtmp.calls[ct] = fsa->fsa_digipeater[ct]; |
ct++; |
} |
|
dtmp.lastrepeat = 0; |
} |
|
sax = *usax; |
if (sk->type == SOCK_SEQPACKET && ax25cmp(&sk->protinfo.ax25->dest_addr, &sax.sax25_call) != 0) |
return -EISCONN; |
if (usax->sax25_ndigis == 0) |
dp = NULL; |
else |
dp = &dtmp; |
} else { |
/* |
* FIXME: 1003.1g - if the socket is like this because |
* it has become closed (not started closed) and is VC |
* we ought to SIGPIPE, EPIPE |
*/ |
if (sk->state != TCP_ESTABLISHED) |
return -ENOTCONN; |
sax.sax25_family = AF_AX25; |
sax.sax25_call = sk->protinfo.ax25->dest_addr; |
dp = sk->protinfo.ax25->digipeat; |
} |
|
SOCK_DEBUG(sk, "AX.25: sendto: Addresses built.\n"); |
|
/* Build a packet */ |
SOCK_DEBUG(sk, "AX.25: sendto: building packet.\n"); |
|
/* Assume the worst case */ |
size = len + 3 + ax25_addr_size(dp) + AX25_BPQ_HEADER_LEN; |
|
if ((skb = sock_alloc_send_skb(sk, size, msg->msg_flags & MSG_DONTWAIT, &err)) == NULL) |
return err; |
|
skb_reserve(skb, size - len); |
|
SOCK_DEBUG(sk, "AX.25: Appending user data\n"); |
|
/* User data follows immediately after the AX.25 data */ |
memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len); |
skb->nh.raw = skb->data; |
|
/* Add the PID if one is not supplied by the user in the skb */ |
if (!sk->protinfo.ax25->pidincl) { |
asmptr = skb_push(skb, 1); |
*asmptr = sk->protocol; |
} |
|
SOCK_DEBUG(sk, "AX.25: Transmitting buffer\n"); |
|
if (sk->type == SOCK_SEQPACKET) { |
/* Connected mode sockets go via the LAPB machine */ |
if (sk->state != TCP_ESTABLISHED) { |
kfree_skb(skb); |
return -ENOTCONN; |
} |
|
ax25_output(sk->protinfo.ax25, sk->protinfo.ax25->paclen, skb); /* Shove it onto the queue and kick */ |
|
return len; |
} else { |
asmptr = skb_push(skb, 1 + ax25_addr_size(dp)); |
|
SOCK_DEBUG(sk, "Building AX.25 Header (dp=%p).\n", dp); |
|
if (dp != NULL) |
SOCK_DEBUG(sk, "Num digipeaters=%d\n", dp->ndigi); |
|
/* Build an AX.25 header */ |
asmptr += (lv = ax25_addr_build(asmptr, &sk->protinfo.ax25->source_addr, &sax.sax25_call, dp, AX25_COMMAND, AX25_MODULUS)); |
|
SOCK_DEBUG(sk, "Built header (%d bytes)\n",lv); |
|
skb->h.raw = asmptr; |
|
SOCK_DEBUG(sk, "base=%p pos=%p\n", skb->data, asmptr); |
|
*asmptr = AX25_UI; |
|
/* Datagram frames go straight out of the door as UI */ |
skb->dev = sk->protinfo.ax25->ax25_dev->dev; |
|
ax25_queue_xmit(skb); |
|
return len; |
} |
} |
|
static int ax25_recvmsg(struct socket *sock, struct msghdr *msg, int size, int flags, struct scm_cookie *scm) |
{ |
struct sock *sk = sock->sk; |
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->type == SOCK_SEQPACKET && 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; |
|
if (!sk->protinfo.ax25->pidincl) |
skb_pull(skb, 1); /* Remove PID */ |
|
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 (msg->msg_namelen != 0) { |
struct sockaddr_ax25 *sax = (struct sockaddr_ax25 *)msg->msg_name; |
ax25_digi digi; |
ax25_address dest; |
|
ax25_addr_parse(skb->mac.raw+1, skb->data-skb->mac.raw-1, NULL, &dest, &digi, NULL, NULL); |
|
sax->sax25_family = AF_AX25; |
/* We set this correctly, even though we may not let the |
application know the digi calls further down (because it |
did NOT ask to know them). This could get political... **/ |
sax->sax25_ndigis = digi.ndigi; |
sax->sax25_call = dest; |
|
if (sax->sax25_ndigis != 0) { |
int ct; |
struct full_sockaddr_ax25 *fsa = (struct full_sockaddr_ax25 *)sax; |
|
for (ct = 0; ct < digi.ndigi; ct++) |
fsa->fsa_digipeater[ct] = digi.calls[ct]; |
} |
msg->msg_namelen = sizeof(struct full_sockaddr_ax25); |
} |
|
skb_free_datagram(sk, skb); |
|
return copied; |
} |
|
static int ax25_shutdown(struct socket *sk, int how) |
{ |
/* FIXME - generate DM and RNR states */ |
return -EOPNOTSUPP; |
} |
|
static int ax25_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 SIOCAX25ADDUID: /* Add a uid to the uid/call map table */ |
case SIOCAX25DELUID: /* Delete a uid from the uid/call map table */ |
case SIOCAX25GETUID: { |
struct sockaddr_ax25 sax25; |
if (copy_from_user(&sax25, (void *)arg, sizeof(sax25))) |
return -EFAULT; |
return ax25_uid_ioctl(cmd, &sax25); |
} |
|
case SIOCAX25NOUID: { /* Set the default policy (default/bar) */ |
long amount; |
if (!capable(CAP_NET_ADMIN)) |
return -EPERM; |
if (get_user(amount, (long *)arg)) |
return -EFAULT; |
if (amount > AX25_NOUID_BLOCK) |
return -EINVAL; |
ax25_uid_policy = amount; |
return 0; |
} |
|
case SIOCADDRT: |
case SIOCDELRT: |
case SIOCAX25OPTRT: |
if (!capable(CAP_NET_ADMIN)) |
return -EPERM; |
return ax25_rt_ioctl(cmd, (void *)arg); |
|
case SIOCAX25CTLCON: |
if (!capable(CAP_NET_ADMIN)) |
return -EPERM; |
return ax25_ctl_ioctl(cmd, (void *)arg); |
|
case SIOCAX25GETINFO: |
case SIOCAX25GETINFOOLD: { |
struct ax25_info_struct ax25_info; |
|
ax25_info.t1 = sk->protinfo.ax25->t1 / HZ; |
ax25_info.t2 = sk->protinfo.ax25->t2 / HZ; |
ax25_info.t3 = sk->protinfo.ax25->t3 / HZ; |
ax25_info.idle = sk->protinfo.ax25->idle / (60 * HZ); |
ax25_info.n2 = sk->protinfo.ax25->n2; |
ax25_info.t1timer = ax25_display_timer(&sk->protinfo.ax25->t1timer) / HZ; |
ax25_info.t2timer = ax25_display_timer(&sk->protinfo.ax25->t2timer) / HZ; |
ax25_info.t3timer = ax25_display_timer(&sk->protinfo.ax25->t3timer) / HZ; |
ax25_info.idletimer = ax25_display_timer(&sk->protinfo.ax25->idletimer) / (60 * HZ); |
ax25_info.n2count = sk->protinfo.ax25->n2count; |
ax25_info.state = sk->protinfo.ax25->state; |
ax25_info.rcv_q = atomic_read(&sk->rmem_alloc); |
ax25_info.snd_q = atomic_read(&sk->wmem_alloc); |
ax25_info.vs = sk->protinfo.ax25->vs; |
ax25_info.vr = sk->protinfo.ax25->vr; |
ax25_info.va = sk->protinfo.ax25->va; |
ax25_info.vs_max = sk->protinfo.ax25->vs; /* reserved */ |
ax25_info.paclen = sk->protinfo.ax25->paclen; |
ax25_info.window = sk->protinfo.ax25->window; |
|
/* old structure? */ |
if (cmd == SIOCAX25GETINFOOLD) { |
static int warned = 0; |
if (!warned) { |
printk(KERN_INFO "%s uses old SIOCAX25GETINFO\n", |
current->comm); |
warned=1; |
} |
|
if (copy_to_user((void *)arg, &ax25_info, sizeof(struct ax25_info_struct_depreciated))) |
return -EFAULT; |
} else { |
if (copy_to_user((void *)arg, &ax25_info, sizeof(struct ax25_info_struct))) |
return -EINVAL; |
} |
return 0; |
} |
|
case SIOCAX25ADDFWD: |
case SIOCAX25DELFWD: { |
struct ax25_fwd_struct ax25_fwd; |
if (!capable(CAP_NET_ADMIN)) |
return -EPERM; |
if (copy_from_user(&ax25_fwd, (void *)arg, sizeof(ax25_fwd))) |
return -EFAULT; |
return ax25_fwd_ioctl(cmd, &ax25_fwd); |
} |
|
case SIOCGIFADDR: |
case SIOCSIFADDR: |
case SIOCGIFDSTADDR: |
case SIOCSIFDSTADDR: |
case SIOCGIFBRDADDR: |
case SIOCSIFBRDADDR: |
case SIOCGIFNETMASK: |
case SIOCSIFNETMASK: |
case SIOCGIFMETRIC: |
case SIOCSIFMETRIC: |
return -EINVAL; |
|
default: |
return dev_ioctl(cmd, (void *)arg); |
} |
|
/*NOTREACHED*/ |
return 0; |
} |
|
static int ax25_get_info(char *buffer, char **start, off_t offset, int length) |
{ |
ax25_cb *ax25; |
int k; |
int len = 0; |
off_t pos = 0; |
off_t begin = 0; |
|
cli(); |
|
/* |
* New format: |
* magic dev src_addr dest_addr,digi1,digi2,.. st vs vr va t1 t1 t2 t2 t3 t3 idle idle n2 n2 rtt window paclen Snd-Q Rcv-Q inode |
*/ |
|
for (ax25 = ax25_list; ax25 != NULL; ax25 = ax25->next) { |
len += sprintf(buffer+len, "%8.8lx %s %s%s ", |
(long) ax25, |
ax25->ax25_dev == NULL? "???" : ax25->ax25_dev->dev->name, |
ax2asc(&ax25->source_addr), |
ax25->iamdigi? "*":""); |
|
len += sprintf(buffer+len, "%s", ax2asc(&ax25->dest_addr)); |
|
for (k=0; (ax25->digipeat != NULL) && (k < ax25->digipeat->ndigi); k++) { |
len += sprintf(buffer+len, ",%s%s", |
ax2asc(&ax25->digipeat->calls[k]), |
ax25->digipeat->repeated[k]? "*":""); |
} |
|
len += sprintf(buffer+len, " %d %d %d %d %lu %lu %lu %lu %lu %lu %lu %lu %d %d %lu %d %d", |
ax25->state, |
ax25->vs, ax25->vr, ax25->va, |
ax25_display_timer(&ax25->t1timer) / HZ, ax25->t1 / HZ, |
ax25_display_timer(&ax25->t2timer) / HZ, ax25->t2 / HZ, |
ax25_display_timer(&ax25->t3timer) / HZ, ax25->t3 / HZ, |
ax25_display_timer(&ax25->idletimer) / (60 * HZ), |
ax25->idle / (60 * HZ), |
ax25->n2count, ax25->n2, |
ax25->rtt / HZ, |
ax25->window, |
ax25->paclen); |
|
if (ax25->sk != NULL) { |
len += sprintf(buffer + len, " %d %d %ld\n", |
atomic_read(&ax25->sk->wmem_alloc), |
atomic_read(&ax25->sk->rmem_alloc), |
ax25->sk->socket != NULL ? ax25->sk->socket->inode->i_ino : 0L); |
} else { |
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); |
} |
|
static struct net_proto_family ax25_family_ops = { |
family: PF_AX25, |
create: ax25_create, |
}; |
|
static struct proto_ops SOCKOPS_WRAPPED(ax25_proto_ops) = { |
family: PF_AX25, |
|
release: ax25_release, |
bind: ax25_bind, |
connect: ax25_connect, |
socketpair: sock_no_socketpair, |
accept: ax25_accept, |
getname: ax25_getname, |
poll: datagram_poll, |
ioctl: ax25_ioctl, |
listen: ax25_listen, |
shutdown: ax25_shutdown, |
setsockopt: ax25_setsockopt, |
getsockopt: ax25_getsockopt, |
sendmsg: ax25_sendmsg, |
recvmsg: ax25_recvmsg, |
mmap: sock_no_mmap, |
sendpage: sock_no_sendpage, |
}; |
|
#include <linux/smp_lock.h> |
SOCKOPS_WRAP(ax25_proto, PF_AX25); |
|
/* |
* Called by socket.c on kernel start up |
*/ |
static struct packet_type ax25_packet_type = { |
type: __constant_htons(ETH_P_AX25), |
func: ax25_kiss_rcv, |
}; |
|
static struct notifier_block ax25_dev_notifier = { |
notifier_call: ax25_device_event, |
}; |
|
EXPORT_SYMBOL(ax25_encapsulate); |
EXPORT_SYMBOL(ax25_rebuild_header); |
EXPORT_SYMBOL(ax25_findbyuid); |
EXPORT_SYMBOL(ax25_find_cb); |
EXPORT_SYMBOL(ax25_linkfail_register); |
EXPORT_SYMBOL(ax25_linkfail_release); |
EXPORT_SYMBOL(ax25_listen_register); |
EXPORT_SYMBOL(ax25_listen_release); |
EXPORT_SYMBOL(ax25_protocol_register); |
EXPORT_SYMBOL(ax25_protocol_release); |
EXPORT_SYMBOL(ax25_send_frame); |
EXPORT_SYMBOL(ax25_uid_policy); |
EXPORT_SYMBOL(ax25cmp); |
EXPORT_SYMBOL(ax2asc); |
EXPORT_SYMBOL(asc2ax); |
EXPORT_SYMBOL(null_ax25_address); |
EXPORT_SYMBOL(ax25_display_timer); |
|
static char banner[] __initdata = KERN_INFO "NET4: G4KLX/GW4PTS AX.25 for Linux. Version 0.37 for Linux NET4.0\n"; |
|
static int __init ax25_init(void) |
{ |
sock_register(&ax25_family_ops); |
dev_add_pack(&ax25_packet_type); |
register_netdevice_notifier(&ax25_dev_notifier); |
ax25_register_sysctl(); |
|
proc_net_create("ax25_route", 0, ax25_rt_get_info); |
proc_net_create("ax25", 0, ax25_get_info); |
proc_net_create("ax25_calls", 0, ax25_uid_get_info); |
|
printk(banner); |
return 0; |
} |
module_init(ax25_init); |
|
|
MODULE_AUTHOR("Jonathan Naylor G4KLX <g4klx@g4klx.demon.co.uk>"); |
MODULE_DESCRIPTION("The amateur radio AX.25 link layer protocol"); |
MODULE_LICENSE("GPL"); |
|
static void __exit ax25_exit(void) |
{ |
proc_net_remove("ax25_route"); |
proc_net_remove("ax25"); |
proc_net_remove("ax25_calls"); |
ax25_rt_free(); |
ax25_uid_free(); |
ax25_dev_free(); |
|
ax25_unregister_sysctl(); |
unregister_netdevice_notifier(&ax25_dev_notifier); |
|
dev_remove_pack(&ax25_packet_type); |
|
sock_unregister(PF_AX25); |
} |
module_exit(ax25_exit); |
/sysctl_net_ax25.c
0,0 → 1,162
/* -*- linux-c -*- |
* sysctl_net_ax25.c: sysctl interface to net AX.25 subsystem. |
* |
* Begun April 1, 1996, Mike Shaver. |
* Added /proc/sys/net/ax25 directory entry (empty =) ). [MS] |
*/ |
|
#include <linux/config.h> |
#include <linux/mm.h> |
#include <linux/sysctl.h> |
#include <net/ax25.h> |
|
static int min_ipdefmode[] = {0}, max_ipdefmode[] = {1}; |
static int min_axdefmode[] = {0}, max_axdefmode[] = {1}; |
static int min_backoff[] = {0}, max_backoff[] = {2}; |
static int min_conmode[] = {0}, max_conmode[] = {2}; |
static int min_window[] = {1}, max_window[] = {7}; |
static int min_ewindow[] = {1}, max_ewindow[] = {63}; |
static int min_t1[] = {1}, max_t1[] = {30 * HZ}; |
static int min_t2[] = {1}, max_t2[] = {20 * HZ}; |
static int min_t3[] = {0}, max_t3[] = {3600 * HZ}; |
static int min_idle[] = {0}, max_idle[] = {65535 * HZ}; |
static int min_n2[] = {1}, max_n2[] = {31}; |
static int min_paclen[] = {1}, max_paclen[] = {512}; |
static int min_proto[] = {0}, max_proto[] = {3}; |
static int min_ds_timeout[] = {0}, max_ds_timeout[] = {65535 * HZ}; |
|
static struct ctl_table_header *ax25_table_header; |
|
static ctl_table *ax25_table; |
static int ax25_table_size; |
|
static ctl_table ax25_dir_table[] = { |
{NET_AX25, "ax25", NULL, 0, 0555, NULL}, |
{0} |
}; |
|
static ctl_table ax25_root_table[] = { |
{CTL_NET, "net", NULL, 0, 0555, ax25_dir_table}, |
{0} |
}; |
|
static const ctl_table ax25_param_table[] = { |
{NET_AX25_IP_DEFAULT_MODE, "ip_default_mode", |
NULL, sizeof(int), 0644, NULL, |
&proc_dointvec_minmax, &sysctl_intvec, NULL, |
&min_ipdefmode, &max_ipdefmode}, |
{NET_AX25_DEFAULT_MODE, "ax25_default_mode", |
NULL, sizeof(int), 0644, NULL, |
&proc_dointvec_minmax, &sysctl_intvec, NULL, |
&min_axdefmode, &max_axdefmode}, |
{NET_AX25_BACKOFF_TYPE, "backoff_type", |
NULL, sizeof(int), 0644, NULL, |
&proc_dointvec_minmax, &sysctl_intvec, NULL, |
&min_backoff, &max_backoff}, |
{NET_AX25_CONNECT_MODE, "connect_mode", |
NULL, sizeof(int), 0644, NULL, |
&proc_dointvec_minmax, &sysctl_intvec, NULL, |
&min_conmode, &max_conmode}, |
{NET_AX25_STANDARD_WINDOW, "standard_window_size", |
NULL, sizeof(int), 0644, NULL, |
&proc_dointvec_minmax, &sysctl_intvec, NULL, |
&min_window, &max_window}, |
{NET_AX25_EXTENDED_WINDOW, "extended_window_size", |
NULL, sizeof(int), 0644, NULL, |
&proc_dointvec_minmax, &sysctl_intvec, NULL, |
&min_ewindow, &max_ewindow}, |
{NET_AX25_T1_TIMEOUT, "t1_timeout", |
NULL, sizeof(int), 0644, NULL, |
&proc_dointvec_minmax, &sysctl_intvec, NULL, |
&min_t1, &max_t1}, |
{NET_AX25_T2_TIMEOUT, "t2_timeout", |
NULL, sizeof(int), 0644, NULL, |
&proc_dointvec_minmax, &sysctl_intvec, NULL, |
&min_t2, &max_t2}, |
{NET_AX25_T3_TIMEOUT, "t3_timeout", |
NULL, sizeof(int), 0644, NULL, |
&proc_dointvec_minmax, &sysctl_intvec, NULL, |
&min_t3, &max_t3}, |
{NET_AX25_IDLE_TIMEOUT, "idle_timeout", |
NULL, sizeof(int), 0644, NULL, |
&proc_dointvec_minmax, &sysctl_intvec, NULL, |
&min_idle, &max_idle}, |
{NET_AX25_N2, "maximum_retry_count", |
NULL, sizeof(int), 0644, NULL, |
&proc_dointvec_minmax, &sysctl_intvec, NULL, |
&min_n2, &max_n2}, |
{NET_AX25_PACLEN, "maximum_packet_length", |
NULL, sizeof(int), 0644, NULL, |
&proc_dointvec_minmax, &sysctl_intvec, NULL, |
&min_paclen, &max_paclen}, |
{NET_AX25_PROTOCOL, "protocol", |
NULL, sizeof(int), 0644, NULL, |
&proc_dointvec_minmax, &sysctl_intvec, NULL, |
&min_proto, &max_proto}, |
{NET_AX25_DAMA_SLAVE_TIMEOUT, "dama_slave_timeout", |
NULL, sizeof(int), 0644, NULL, |
&proc_dointvec_minmax, &sysctl_intvec, NULL, |
&min_ds_timeout, &max_ds_timeout}, |
{0} /* that's all, folks! */ |
}; |
|
void ax25_register_sysctl(void) |
{ |
ax25_dev *ax25_dev; |
int n, k; |
|
for (ax25_table_size = sizeof(ctl_table), ax25_dev = ax25_dev_list; ax25_dev != NULL; ax25_dev = ax25_dev->next) |
ax25_table_size += sizeof(ctl_table); |
|
if ((ax25_table = kmalloc(ax25_table_size, GFP_ATOMIC)) == NULL) |
return; |
|
memset(ax25_table, 0x00, ax25_table_size); |
|
for (n = 0, ax25_dev = ax25_dev_list; ax25_dev != NULL; ax25_dev = ax25_dev->next) { |
ctl_table *child = kmalloc(sizeof(ax25_param_table), GFP_ATOMIC); |
if (!child) { |
while (n--) |
kfree(ax25_table[n].child); |
kfree(ax25_table); |
return; |
} |
memcpy(child, ax25_param_table, sizeof(ax25_param_table)); |
ax25_table[n].child = ax25_dev->systable = child; |
ax25_table[n].ctl_name = n + 1; |
ax25_table[n].procname = ax25_dev->dev->name; |
ax25_table[n].mode = 0555; |
|
#ifndef CONFIG_AX25_DAMA_SLAVE |
/* |
* We do not wish to have a representation of this parameter |
* in /proc/sys/ when configured *not* to include the |
* AX.25 DAMA slave code, do we? |
*/ |
|
child[AX25_VALUES_DS_TIMEOUT].procname = NULL; |
#endif |
|
child[AX25_MAX_VALUES].ctl_name = 0; /* just in case... */ |
|
for (k = 0; k < AX25_MAX_VALUES; k++) |
child[k].data = &ax25_dev->values[k]; |
|
n++; |
} |
|
ax25_dir_table[0].child = ax25_table; |
|
ax25_table_header = register_sysctl_table(ax25_root_table, 1); |
} |
|
void ax25_unregister_sysctl(void) |
{ |
ctl_table *p; |
unregister_sysctl_table(ax25_table_header); |
|
ax25_dir_table[0].child = NULL; |
for (p = ax25_table; p->ctl_name; p++) |
kfree(p->child); |
kfree(ax25_table); |
} |
/Config.in
0,0 → 1,36
# |
# Amateur Radio protocols and AX.25 device configuration |
# |
# 19971130 Now in an own category to make correct compilation of the |
# AX.25 stuff easier... |
# Joerg Reuter DL1BKE <jreuter@yaina.de> |
# 19980129 Moved to net/ax25/Config.in, sourcing device drivers. |
|
mainmenu_option next_comment |
comment 'Amateur Radio support' |
bool 'Amateur Radio support' CONFIG_HAMRADIO |
|
if [ "$CONFIG_HAMRADIO" != "n" ]; then |
if [ "$CONFIG_NET" != "n" ]; then |
comment 'Packet Radio protocols' |
tristate ' Amateur Radio AX.25 Level 2 protocol' CONFIG_AX25 |
if [ "$CONFIG_AX25" != "n" ]; then |
bool ' AX.25 DAMA Slave support' CONFIG_AX25_DAMA_SLAVE |
# bool ' AX.25 DAMA Master support' CONFIG_AX25_DAMA_MASTER |
dep_tristate ' Amateur Radio NET/ROM protocol' CONFIG_NETROM $CONFIG_AX25 |
dep_tristate ' Amateur Radio X.25 PLP (Rose)' CONFIG_ROSE $CONFIG_AX25 |
fi |
|
if [ "$CONFIG_AX25" != "n" ]; then |
mainmenu_option next_comment |
comment 'AX.25 network device drivers' |
|
source drivers/net/hamradio/Config.in |
|
endmenu |
fi |
fi |
|
fi |
|
endmenu |
/ax25_std_timer.c
0,0 → 1,170
/* |
* AX.25 release 037 |
* |
* 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 |
* AX.25 028a Jonathan(G4KLX) New state machine based on SDL diagrams. |
* AX.25 028b Jonathan(G4KLX) Extracted AX25 control block from the |
* sock structure. |
* AX.25 029 Alan(GW4PTS) Switched to KA9Q constant names. |
* AX.25 031 Joerg(DL1BKE) Added DAMA support |
* AX.25 032 Joerg(DL1BKE) Fixed DAMA timeout bug |
* AX.25 033 Jonathan(G4KLX) Modularisation functions. |
* AX.25 035 Frederic(F1OAT) Support for pseudo-digipeating. |
* AX.25 036 Jonathan(G4KLX) Split from ax25_timer.c. |
* AX.25 037 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> |
|
void ax25_std_heartbeat_expiry(ax25_cb *ax25) |
{ |
switch (ax25->state) { |
|
case AX25_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 (ax25->sk == NULL || ax25->sk->destroy || (ax25->sk->state == TCP_LISTEN && ax25->sk->dead)) { |
ax25_destroy_socket(ax25); |
return; |
} |
break; |
|
case AX25_STATE_3: |
case AX25_STATE_4: |
/* |
* Check the state of the receive buffer. |
*/ |
if (ax25->sk != NULL) { |
if (atomic_read(&ax25->sk->rmem_alloc) < (ax25->sk->rcvbuf / 2) && |
(ax25->condition & AX25_COND_OWN_RX_BUSY)) { |
ax25->condition &= ~AX25_COND_OWN_RX_BUSY; |
ax25->condition &= ~AX25_COND_ACK_PENDING; |
ax25_send_control(ax25, AX25_RR, AX25_POLLOFF, AX25_RESPONSE); |
break; |
} |
} |
} |
|
ax25_start_heartbeat(ax25); |
} |
|
void ax25_std_t2timer_expiry(ax25_cb *ax25) |
{ |
if (ax25->condition & AX25_COND_ACK_PENDING) { |
ax25->condition &= ~AX25_COND_ACK_PENDING; |
ax25_std_timeout_response(ax25); |
} |
} |
|
void ax25_std_t3timer_expiry(ax25_cb *ax25) |
{ |
ax25->n2count = 0; |
ax25_std_transmit_enquiry(ax25); |
ax25->state = AX25_STATE_4; |
} |
|
void ax25_std_idletimer_expiry(ax25_cb *ax25) |
{ |
ax25_clear_queues(ax25); |
|
ax25->n2count = 0; |
ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND); |
ax25->state = AX25_STATE_2; |
|
ax25_calculate_t1(ax25); |
ax25_start_t1timer(ax25); |
ax25_stop_t2timer(ax25); |
ax25_stop_t3timer(ax25); |
|
if (ax25->sk != NULL) { |
ax25->sk->state = TCP_CLOSE; |
ax25->sk->err = 0; |
ax25->sk->shutdown |= SEND_SHUTDOWN; |
if (!ax25->sk->dead) |
ax25->sk->state_change(ax25->sk); |
ax25->sk->dead = 1; |
} |
} |
|
void ax25_std_t1timer_expiry(ax25_cb *ax25) |
{ |
switch (ax25->state) { |
case AX25_STATE_1: |
if (ax25->n2count == ax25->n2) { |
if (ax25->modulus == AX25_MODULUS) { |
ax25_disconnect(ax25, ETIMEDOUT); |
return; |
} else { |
ax25->modulus = AX25_MODULUS; |
ax25->window = ax25->ax25_dev->values[AX25_VALUES_WINDOW]; |
ax25->n2count = 0; |
ax25_send_control(ax25, AX25_SABM, AX25_POLLON, AX25_COMMAND); |
} |
} else { |
ax25->n2count++; |
if (ax25->modulus == AX25_MODULUS) |
ax25_send_control(ax25, AX25_SABM, AX25_POLLON, AX25_COMMAND); |
else |
ax25_send_control(ax25, AX25_SABME, AX25_POLLON, AX25_COMMAND); |
} |
break; |
|
case AX25_STATE_2: |
if (ax25->n2count == ax25->n2) { |
ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND); |
ax25_disconnect(ax25, ETIMEDOUT); |
return; |
} else { |
ax25->n2count++; |
ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND); |
} |
break; |
|
case AX25_STATE_3: |
ax25->n2count = 1; |
ax25_std_transmit_enquiry(ax25); |
ax25->state = AX25_STATE_4; |
break; |
|
case AX25_STATE_4: |
if (ax25->n2count == ax25->n2) { |
ax25_send_control(ax25, AX25_DM, AX25_POLLON, AX25_RESPONSE); |
ax25_disconnect(ax25, ETIMEDOUT); |
return; |
} else { |
ax25->n2count++; |
ax25_std_transmit_enquiry(ax25); |
} |
break; |
} |
|
ax25_calculate_t1(ax25); |
ax25_start_t1timer(ax25); |
} |
/ax25_ds_in.c
0,0 → 1,312
/* |
* AX.25 release 037 |
* |
* 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 |
* AX.25 036 Jonathan(G4KLX) Cloned from ax25_in.c |
* Joerg(DL1BKE) Fixed it. |
* AX.25 037 Jonathan(G4KLX) New timer architecture. |
* Joerg(DL1BKE) ax25->n2count never got reset |
*/ |
|
#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> |
|
/* |
* State machine for state 1, Awaiting Connection State. |
* The handling of the timer(s) is in file ax25_ds_timer.c. |
* Handling of state 0 and connection release is in ax25.c. |
*/ |
static int ax25_ds_state1_machine(ax25_cb *ax25, struct sk_buff *skb, int frametype, int pf, int type) |
{ |
switch (frametype) { |
case AX25_SABM: |
ax25->modulus = AX25_MODULUS; |
ax25->window = ax25->ax25_dev->values[AX25_VALUES_WINDOW]; |
ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE); |
break; |
|
case AX25_SABME: |
ax25->modulus = AX25_EMODULUS; |
ax25->window = ax25->ax25_dev->values[AX25_VALUES_EWINDOW]; |
ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE); |
break; |
|
case AX25_DISC: |
ax25_send_control(ax25, AX25_DM, pf, AX25_RESPONSE); |
break; |
|
case AX25_UA: |
ax25_calculate_rtt(ax25); |
ax25_stop_t1timer(ax25); |
ax25_start_t3timer(ax25); |
ax25_start_idletimer(ax25); |
ax25->vs = 0; |
ax25->va = 0; |
ax25->vr = 0; |
ax25->state = AX25_STATE_3; |
ax25->n2count = 0; |
if (ax25->sk != NULL) { |
ax25->sk->state = TCP_ESTABLISHED; |
/* For WAIT_SABM connections we will produce an accept ready socket here */ |
if (!ax25->sk->dead) |
ax25->sk->state_change(ax25->sk); |
} |
ax25_dama_on(ax25); |
|
|
* send a RR RESPONSE FINAL NR=0. |
*/ |
|
ax25_std_enquiry_response(ax25); |
break; |
|
case AX25_DM: |
if (pf) ax25_disconnect(ax25, ECONNREFUSED); |
break; |
|
default: |
if (pf) ax25_send_control(ax25, AX25_SABM, AX25_POLLON, AX25_COMMAND); |
break; |
} |
|
return 0; |
} |
|
/* |
* State machine for state 2, Awaiting Release State. |
* The handling of the timer(s) is in file ax25_ds_timer.c |
* Handling of state 0 and connection release is in ax25.c. |
*/ |
static int ax25_ds_state2_machine(ax25_cb *ax25, struct sk_buff *skb, int frametype, int pf, int type) |
{ |
switch (frametype) { |
case AX25_SABM: |
case AX25_SABME: |
ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND); |
ax25_dama_off(ax25); |
break; |
|
case AX25_DISC: |
ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE); |
ax25_dama_off(ax25); |
ax25_disconnect(ax25, 0); |
break; |
|
case AX25_DM: |
case AX25_UA: |
if (pf) { |
ax25_dama_off(ax25); |
ax25_disconnect(ax25, 0); |
} |
break; |
|
case AX25_I: |
case AX25_REJ: |
case AX25_RNR: |
case AX25_RR: |
if (pf) { |
ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND); |
ax25_dama_off(ax25); |
} |
break; |
|
default: |
break; |
} |
|
return 0; |
} |
|
/* |
* State machine for state 3, Connected State. |
* The handling of the timer(s) is in file ax25_timer.c |
* Handling of state 0 and connection release is in ax25.c. |
*/ |
static int ax25_ds_state3_machine(ax25_cb *ax25, struct sk_buff *skb, int frametype, int ns, int nr, int pf, int type) |
{ |
int queued = 0; |
|
switch (frametype) { |
case AX25_SABM: |
case AX25_SABME: |
if (frametype == AX25_SABM) { |
ax25->modulus = AX25_MODULUS; |
ax25->window = ax25->ax25_dev->values[AX25_VALUES_WINDOW]; |
} else { |
ax25->modulus = AX25_EMODULUS; |
ax25->window = ax25->ax25_dev->values[AX25_VALUES_EWINDOW]; |
} |
ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE); |
ax25_stop_t1timer(ax25); |
ax25_start_t3timer(ax25); |
ax25_start_idletimer(ax25); |
ax25->condition = 0x00; |
ax25->vs = 0; |
ax25->va = 0; |
ax25->vr = 0; |
ax25_requeue_frames(ax25); |
ax25_dama_on(ax25); |
break; |
|
case AX25_DISC: |
ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE); |
ax25_dama_off(ax25); |
ax25_disconnect(ax25, 0); |
break; |
|
case AX25_DM: |
ax25_dama_off(ax25); |
ax25_disconnect(ax25, ECONNRESET); |
break; |
|
case AX25_RR: |
case AX25_RNR: |
if (frametype == AX25_RR) |
ax25->condition &= ~AX25_COND_PEER_RX_BUSY; |
else |
ax25->condition |= AX25_COND_PEER_RX_BUSY; |
|
if (ax25_validate_nr(ax25, nr)) { |
if (ax25_check_iframes_acked(ax25, nr)) |
ax25->n2count=0; |
if (type == AX25_COMMAND && pf) |
ax25_ds_enquiry_response(ax25); |
} else { |
ax25_ds_nr_error_recovery(ax25); |
ax25->state = AX25_STATE_1; |
} |
break; |
|
case AX25_REJ: |
ax25->condition &= ~AX25_COND_PEER_RX_BUSY; |
|
if (ax25_validate_nr(ax25, nr)) { |
if (ax25->va != nr) |
ax25->n2count=0; |
|
ax25_frames_acked(ax25, nr); |
ax25_calculate_rtt(ax25); |
ax25_stop_t1timer(ax25); |
ax25_start_t3timer(ax25); |
ax25_requeue_frames(ax25); |
|
if (type == AX25_COMMAND && pf) |
ax25_ds_enquiry_response(ax25); |
} else { |
ax25_ds_nr_error_recovery(ax25); |
ax25->state = AX25_STATE_1; |
} |
break; |
|
case AX25_I: |
if (!ax25_validate_nr(ax25, nr)) { |
ax25_ds_nr_error_recovery(ax25); |
ax25->state = AX25_STATE_1; |
break; |
} |
if (ax25->condition & AX25_COND_PEER_RX_BUSY) { |
ax25_frames_acked(ax25, nr); |
ax25->n2count = 0; |
} else { |
if (ax25_check_iframes_acked(ax25, nr)) |
ax25->n2count = 0; |
} |
if (ax25->condition & AX25_COND_OWN_RX_BUSY) { |
if (pf) ax25_ds_enquiry_response(ax25); |
break; |
} |
if (ns == ax25->vr) { |
ax25->vr = (ax25->vr + 1) % ax25->modulus; |
queued = ax25_rx_iframe(ax25, skb); |
if (ax25->condition & AX25_COND_OWN_RX_BUSY) |
ax25->vr = ns; /* ax25->vr - 1 */ |
ax25->condition &= ~AX25_COND_REJECT; |
if (pf) { |
ax25_ds_enquiry_response(ax25); |
} else { |
if (!(ax25->condition & AX25_COND_ACK_PENDING)) { |
ax25->condition |= AX25_COND_ACK_PENDING; |
ax25_start_t2timer(ax25); |
} |
} |
} else { |
if (ax25->condition & AX25_COND_REJECT) { |
if (pf) ax25_ds_enquiry_response(ax25); |
} else { |
ax25->condition |= AX25_COND_REJECT; |
ax25_ds_enquiry_response(ax25); |
ax25->condition &= ~AX25_COND_ACK_PENDING; |
} |
} |
break; |
|
case AX25_FRMR: |
case AX25_ILLEGAL: |
ax25_ds_establish_data_link(ax25); |
ax25->state = AX25_STATE_1; |
break; |
|
default: |
break; |
} |
|
return queued; |
} |
|
/* |
* Higher level upcall for a LAPB frame |
*/ |
int ax25_ds_frame_in(ax25_cb *ax25, struct sk_buff *skb, int type) |
{ |
int queued = 0, frametype, ns, nr, pf; |
|
frametype = ax25_decode(ax25, skb, &ns, &nr, &pf); |
|
switch (ax25->state) { |
case AX25_STATE_1: |
queued = ax25_ds_state1_machine(ax25, skb, frametype, pf, type); |
break; |
case AX25_STATE_2: |
queued = ax25_ds_state2_machine(ax25, skb, frametype, pf, type); |
break; |
case AX25_STATE_3: |
queued = ax25_ds_state3_machine(ax25, skb, frametype, ns, nr, pf, type); |
break; |
} |
|
return queued; |
} |
|
/Makefile
0,0 → 1,25
# |
# Makefile for the Linux AX.25 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 := ax25.o |
|
export-objs := af_ax25.o |
|
obj-y := ax25_addr.o ax25_dev.o ax25_iface.o ax25_in.o ax25_ip.o ax25_out.o \ |
ax25_route.o ax25_std_in.o ax25_std_subr.o ax25_std_timer.o \ |
ax25_subr.o ax25_timer.o ax25_uid.o af_ax25.o |
|
obj-m := $(O_TARGET) |
|
obj-$(CONFIG_AX25_DAMA_SLAVE) += ax25_ds_in.o ax25_ds_subr.o ax25_ds_timer.o |
obj-$(CONFIG_SYSCTL) += sysctl_net_ax25.o |
|
include $(TOPDIR)/Rules.make |
|
/ax25_route.c
0,0 → 1,452
/* |
* AX.25 release 037 |
* |
* 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. |
* |
* Other kernels modules in this kit are generally BSD derived. See the copyright headers. |
* |
* |
* History |
* AX.25 020 Jonathan(G4KLX) First go. |
* AX.25 022 Jonathan(G4KLX) Added the actual meat to this - we now have a nice heard list. |
* AX.25 025 Alan(GW4PTS) First cut at autobinding by route scan. |
* AX.25 028b Jonathan(G4KLX) Extracted AX25 control block from the |
* sock structure. Device removal now |
* removes the heard structure. |
* AX.25 029 Steven(GW7RRM) Added /proc information for uid/callsign mapping. |
* Jonathan(G4KLX) Handling of IP mode in the routing list and /proc entry. |
* AX.25 030 Jonathan(G4KLX) Added digi-peaters to routing table, and |
* ioctls to manipulate them. Added port |
* configuration. |
* AX.25 031 Jonathan(G4KLX) Added concept of default route. |
* Joerg(DL1BKE) ax25_rt_build_path() find digipeater list and device by |
* destination call. Needed for IP routing via digipeater |
* Jonathan(G4KLX) Added routing for IP datagram packets. |
* Joerg(DL1BKE) Changed routing for IP datagram and VC to use a default |
* route if available. Does not overwrite default routes |
* on route-table overflow anymore. |
* Joerg(DL1BKE) Fixed AX.25 routing of IP datagram and VC, new ioctl() |
* "SIOCAX25OPTRT" to set IP mode and a 'permanent' flag |
* on routes. |
* AX.25 033 Jonathan(G4KLX) Remove auto-router. |
* Joerg(DL1BKE) Moved BPQ Ethernet driver to separate device. |
* AX.25 035 Frederic(F1OAT) Support for pseudo-digipeating. |
* Jonathan(G4KLX) Support for packet forwarding. |
* Arnaldo C. Melo s/suser/capable/ |
*/ |
|
#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/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/mm.h> |
#include <linux/interrupt.h> |
#include <linux/init.h> |
|
static ax25_route *ax25_route_list; |
|
static ax25_route *ax25_find_route(ax25_address *, struct net_device *); |
|
/* |
* small macro to drop non-digipeated digipeaters and reverse path |
*/ |
static inline void ax25_route_invert(ax25_digi *in, ax25_digi *out) |
{ |
int k; |
|
for (k = 0; k < in->ndigi; k++) |
if (!in->repeated[k]) |
break; |
|
in->ndigi = k; |
|
ax25_digi_invert(in, out); |
} |
|
void ax25_rt_device_down(struct net_device *dev) |
{ |
ax25_route *s, *t, *ax25_rt = ax25_route_list; |
|
while (ax25_rt != NULL) { |
s = ax25_rt; |
ax25_rt = ax25_rt->next; |
|
if (s->dev == dev) { |
if (ax25_route_list == s) { |
ax25_route_list = s->next; |
if (s->digipeat != NULL) |
kfree(s->digipeat); |
kfree(s); |
} else { |
for (t = ax25_route_list; t != NULL; t = t->next) { |
if (t->next == s) { |
t->next = s->next; |
if (s->digipeat != NULL) |
kfree(s->digipeat); |
kfree(s); |
break; |
} |
} |
} |
} |
} |
} |
|
int ax25_rt_ioctl(unsigned int cmd, void *arg) |
{ |
unsigned long flags; |
ax25_route *s, *t, *ax25_rt; |
struct ax25_routes_struct route; |
struct ax25_route_opt_struct rt_option; |
ax25_dev *ax25_dev; |
int i; |
|
switch (cmd) { |
case SIOCADDRT: |
if (copy_from_user(&route, arg, sizeof(route))) |
return -EFAULT; |
if ((ax25_dev = ax25_addr_ax25dev(&route.port_addr)) == NULL) |
return -EINVAL; |
if (route.digi_count > AX25_MAX_DIGIS) |
return -EINVAL; |
for (ax25_rt = ax25_route_list; ax25_rt != NULL; ax25_rt = ax25_rt->next) { |
if (ax25cmp(&ax25_rt->callsign, &route.dest_addr) == 0 && ax25_rt->dev == ax25_dev->dev) { |
if (ax25_rt->digipeat != NULL) { |
kfree(ax25_rt->digipeat); |
ax25_rt->digipeat = NULL; |
} |
if (route.digi_count != 0) { |
if ((ax25_rt->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL) |
return -ENOMEM; |
ax25_rt->digipeat->lastrepeat = -1; |
ax25_rt->digipeat->ndigi = route.digi_count; |
for (i = 0; i < route.digi_count; i++) { |
ax25_rt->digipeat->repeated[i] = 0; |
ax25_rt->digipeat->calls[i] = route.digi_addr[i]; |
} |
} |
return 0; |
} |
} |
if ((ax25_rt = kmalloc(sizeof(ax25_route), GFP_ATOMIC)) == NULL) |
return -ENOMEM; |
ax25_rt->callsign = route.dest_addr; |
ax25_rt->dev = ax25_dev->dev; |
ax25_rt->digipeat = NULL; |
ax25_rt->ip_mode = ' '; |
if (route.digi_count != 0) { |
if ((ax25_rt->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL) { |
kfree(ax25_rt); |
return -ENOMEM; |
} |
ax25_rt->digipeat->lastrepeat = -1; |
ax25_rt->digipeat->ndigi = route.digi_count; |
for (i = 0; i < route.digi_count; i++) { |
ax25_rt->digipeat->repeated[i] = 0; |
ax25_rt->digipeat->calls[i] = route.digi_addr[i]; |
} |
} |
save_flags(flags); cli(); |
ax25_rt->next = ax25_route_list; |
ax25_route_list = ax25_rt; |
restore_flags(flags); |
break; |
|
case SIOCDELRT: |
if (copy_from_user(&route, arg, sizeof(route))) |
return -EFAULT; |
if ((ax25_dev = ax25_addr_ax25dev(&route.port_addr)) == NULL) |
return -EINVAL; |
ax25_rt = ax25_route_list; |
while (ax25_rt != NULL) { |
s = ax25_rt; |
ax25_rt = ax25_rt->next; |
if (s->dev == ax25_dev->dev && ax25cmp(&route.dest_addr, &s->callsign) == 0) { |
if (ax25_route_list == s) { |
ax25_route_list = s->next; |
if (s->digipeat != NULL) |
kfree(s->digipeat); |
kfree(s); |
} else { |
for (t = ax25_route_list; t != NULL; t = t->next) { |
if (t->next == s) { |
t->next = s->next; |
if (s->digipeat != NULL) |
kfree(s->digipeat); |
kfree(s); |
break; |
} |
} |
} |
} |
} |
break; |
|
case SIOCAX25OPTRT: |
if (copy_from_user(&rt_option, arg, sizeof(rt_option))) |
return -EFAULT; |
if ((ax25_dev = ax25_addr_ax25dev(&rt_option.port_addr)) == NULL) |
return -EINVAL; |
for (ax25_rt = ax25_route_list; ax25_rt != NULL; ax25_rt = ax25_rt->next) { |
if (ax25_rt->dev == ax25_dev->dev && ax25cmp(&rt_option.dest_addr, &ax25_rt->callsign) == 0) { |
switch (rt_option.cmd) { |
case AX25_SET_RT_IPMODE: |
switch (rt_option.arg) { |
case ' ': |
case 'D': |
case 'V': |
ax25_rt->ip_mode = rt_option.arg; |
break; |
default: |
return -EINVAL; |
} |
break; |
default: |
return -EINVAL; |
} |
} |
} |
break; |
|
default: |
return -EINVAL; |
} |
|
return 0; |
} |
|
int ax25_rt_get_info(char *buffer, char **start, off_t offset, int length) |
{ |
ax25_route *ax25_rt; |
int len = 0; |
off_t pos = 0; |
off_t begin = 0; |
char *callsign; |
int i; |
|
cli(); |
|
len += sprintf(buffer, "callsign dev mode digipeaters\n"); |
|
for (ax25_rt = ax25_route_list; ax25_rt != NULL; ax25_rt = ax25_rt->next) { |
if (ax25cmp(&ax25_rt->callsign, &null_ax25_address) == 0) |
callsign = "default"; |
else |
callsign = ax2asc(&ax25_rt->callsign); |
len += sprintf(buffer + len, "%-9s %-4s", |
callsign, |
ax25_rt->dev ? ax25_rt->dev->name : "???"); |
|
switch (ax25_rt->ip_mode) { |
case 'V': |
len += sprintf(buffer + len, " vc"); |
break; |
case 'D': |
len += sprintf(buffer + len, " dg"); |
break; |
default: |
len += sprintf(buffer + len, " *"); |
break; |
} |
|
if (ax25_rt->digipeat != NULL) |
for (i = 0; i < ax25_rt->digipeat->ndigi; i++) |
len += sprintf(buffer + len, " %s", ax2asc(&ax25_rt->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; |
} |
|
/* |
* Find AX.25 route |
*/ |
static ax25_route *ax25_find_route(ax25_address *addr, struct net_device *dev) |
{ |
ax25_route *ax25_spe_rt = NULL; |
ax25_route *ax25_def_rt = NULL; |
ax25_route *ax25_rt; |
|
/* |
* Bind to the physical interface we heard them on, or the default |
* route if none is found; |
*/ |
for (ax25_rt = ax25_route_list; ax25_rt != NULL; ax25_rt = ax25_rt->next) { |
if (dev == NULL) { |
if (ax25cmp(&ax25_rt->callsign, addr) == 0 && ax25_rt->dev != NULL) |
ax25_spe_rt = ax25_rt; |
if (ax25cmp(&ax25_rt->callsign, &null_ax25_address) == 0 && ax25_rt->dev != NULL) |
ax25_def_rt = ax25_rt; |
} else { |
if (ax25cmp(&ax25_rt->callsign, addr) == 0 && ax25_rt->dev == dev) |
ax25_spe_rt = ax25_rt; |
if (ax25cmp(&ax25_rt->callsign, &null_ax25_address) == 0 && ax25_rt->dev == dev) |
ax25_def_rt = ax25_rt; |
} |
} |
|
if (ax25_spe_rt != NULL) |
return ax25_spe_rt; |
|
return ax25_def_rt; |
} |
|
/* |
* Adjust path: If you specify a default route and want to connect |
* a target on the digipeater path but w/o having a special route |
* set before, the path has to be truncated from your target on. |
*/ |
static inline void ax25_adjust_path(ax25_address *addr, ax25_digi *digipeat) |
{ |
int k; |
|
for (k = 0; k < digipeat->ndigi; k++) { |
if (ax25cmp(addr, &digipeat->calls[k]) == 0) |
break; |
} |
|
digipeat->ndigi = k; |
} |
|
|
/* |
* Find which interface to use. |
*/ |
int ax25_rt_autobind(ax25_cb *ax25, ax25_address *addr) |
{ |
ax25_route *ax25_rt; |
ax25_address *call; |
|
if ((ax25_rt = ax25_find_route(addr, NULL)) == NULL) |
return -EHOSTUNREACH; |
|
if ((ax25->ax25_dev = ax25_dev_ax25dev(ax25_rt->dev)) == NULL) |
return -EHOSTUNREACH; |
|
if ((call = ax25_findbyuid(current->euid)) == NULL) { |
if (ax25_uid_policy && !capable(CAP_NET_BIND_SERVICE)) |
return -EPERM; |
call = (ax25_address *)ax25->ax25_dev->dev->dev_addr; |
} |
|
ax25->source_addr = *call; |
|
if (ax25_rt->digipeat != NULL) { |
if ((ax25->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL) |
return -ENOMEM; |
memcpy(ax25->digipeat, ax25_rt->digipeat, sizeof(ax25_digi)); |
ax25_adjust_path(addr, ax25->digipeat); |
} |
|
if (ax25->sk != NULL) |
ax25->sk->zapped = 0; |
|
return 0; |
} |
|
/* |
* dl1bke 960117: build digipeater path |
* dl1bke 960301: use the default route if it exists |
*/ |
ax25_route *ax25_rt_find_route(ax25_address *addr, struct net_device *dev) |
{ |
static ax25_route route; |
ax25_route *ax25_rt; |
|
if ((ax25_rt = ax25_find_route(addr, dev)) == NULL) { |
route.next = NULL; |
route.callsign = *addr; |
route.dev = dev; |
route.digipeat = NULL; |
route.ip_mode = ' '; |
return &route; |
} |
|
return ax25_rt; |
} |
|
struct sk_buff *ax25_rt_build_path(struct sk_buff *skb, ax25_address *src, ax25_address *dest, ax25_digi *digi) |
{ |
struct sk_buff *skbn; |
unsigned char *bp; |
int len; |
|
len = digi->ndigi * AX25_ADDR_LEN; |
|
if (skb_headroom(skb) < len) { |
if ((skbn = skb_realloc_headroom(skb, len)) == NULL) { |
printk(KERN_CRIT "AX.25: ax25_dg_build_path - out of memory\n"); |
return NULL; |
} |
|
if (skb->sk != NULL) |
skb_set_owner_w(skbn, skb->sk); |
|
kfree_skb(skb); |
|
skb = skbn; |
} |
|
bp = skb_push(skb, len); |
|
ax25_addr_build(bp, src, dest, digi, AX25_COMMAND, AX25_MODULUS); |
|
return skb; |
} |
|
/* |
* Free all memory associated with routing structures. |
*/ |
void __exit ax25_rt_free(void) |
{ |
ax25_route *s, *ax25_rt = ax25_route_list; |
|
while (ax25_rt != NULL) { |
s = ax25_rt; |
ax25_rt = ax25_rt->next; |
|
if (s->digipeat != NULL) |
kfree(s->digipeat); |
|
kfree(s); |
} |
} |