summaryrefslogtreecommitdiffstats
path: root/net/ax25/ax25_lapb.c
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>2015-06-24 04:23:46 +0200
committerRalf Baechle <ralf@linux-mips.org>2015-06-24 10:03:18 +0200
commite5067d7cd967cb17067de24a162306b79f432b20 (patch)
tree541f101762df32a5742bec354009986a96d8e564 /net/ax25/ax25_lapb.c
parent86a981e836404006efc35881ebf3d5ae36925e82 (diff)
Import newax25-2.4.3.patch.1.bz2HEADnewax25-2.4.3-1
And cleanup the *.orig and *.rej files and whitespace errors that are part of the original patch. Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
Diffstat (limited to 'net/ax25/ax25_lapb.c')
-rw-r--r--net/ax25/ax25_lapb.c789
1 files changed, 789 insertions, 0 deletions
diff --git a/net/ax25/ax25_lapb.c b/net/ax25/ax25_lapb.c
new file mode 100644
index 000000000..849eeb44d
--- /dev/null
+++ b/net/ax25/ax25_lapb.c
@@ -0,0 +1,789 @@
+/*
+ * ax25_lapb.c: NEW-AX.25 state machine
+ *
+ * Authors: Jens David (DG1KJD), Matthias Welwarsky (DG2FEF)
+ *
+ * Comment: Most of this code is based on the SDL diagrams published in the
+ * ARRL Computer Networking Conference papers. The diagrams have mi
+ * in them, but are mostly correct. Before you modify the code coul
+ * read the SDL diagrams as the code is not obvious and probably ve
+ * easy to break;
+ * Rewritten from scratch by Matthias in 1998.
+ *
+ * Changelog:
+ *
+ * License: 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.
+ */
+
+#include <linux/socket.h>
+#include <linux/time.h>
+#include <net/ax25.h>
+#include <net/ax25dev.h>
+#include <net/tcp.h>
+
+#include "ax25_ddi.h"
+#include "ax25_vj.h"
+#include "ax25_route.h"
+#include "ax25_in.h"
+#include "ax25_lapb.h"
+#include "ax25_core.h"
+#include "ax25_subr.h"
+#include "ax25_timer.h"
+
+/*
+ * Calculate the Round Trip Time
+ */
+static void ax25_calculate_rtt(ax25_cb *ax25, unsigned long rtt_raw)
+{
+ switch (ax25->backoff) {
+ case 0:
+ ax25->rtt = rtt_raw;
+ break;
+
+ case 1:
+ case 2:
+ ax25->rtt = (9 * ax25->rtt + rtt_raw) / 10;
+ break;
+ }
+}
+
+/*
+ * 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.
+ */
+static void ax25_frames_acked(ax25_cb *ax25, unsigned short nr)
+{
+ struct sk_buff *skb;
+ /*
+ * Remove all the ack-ed frames from ack_queue.
+ */
+ while (ax25->va != nr && (skb = skb_dequeue(&ax25->ack_queue)) != NULL) {
+ ax25->va = (ax25->va + 1) & ax25->seqmask;
+ kfree_skb(skb);
+
+ if (ax25->vs_rtt == ax25->va) {
+ ax25_calculate_rtt(ax25, jiffies - ax25->rtt_timestamp + 1);
+ ax25->vs_rtt = -1;
+ }
+ }
+ if (ax25->condition & AX25_COND_SETUP)
+ ax25_clr_cond(ax25, AX25_COND_SETUP);
+}
+
+/*
+ * This routine decides whether to restart T1 or not on an incoming
+ * frame. If the frame acks all outstanding frames, T1 is
+ * stopped, T3 is started. Else if it acks at least one
+ * outstanding frame T1 is restarted.
+ */
+static void ax25_check_iframes_acked(ax25_cb *ax25, unsigned short nr)
+{
+ write_lock(&ax25->timer_lock);
+ if (ax25->va != nr) {
+ /* at least one frame acked */
+ ax25_frames_acked(ax25, nr);
+ if ((ax25->vs != nr) && !(DAMA_STATE(ax25) & DAMA_SLAVE))
+ ax25->wrt_timer = ax25->t1;
+ }
+ if (ax25->vs == ax25->va) ax25->wrt_timer = ax25->t3;
+ write_unlock(&ax25->timer_lock);
+}
+
+/*
+ * Due to the changed retransmission handling frames are not
+ * requeued from the ack_queue into the write queue. All
+ * retransmission stuff is done in ax25_dev_timer() now. This
+ * function is for clarity only, it will either be removed
+ * completely or turn into an inline function.
+ */
+static void ax25_requeue_frames(ax25_cb *ax25)
+{
+ /*
+ * reset the send sequence counter.
+ */
+ ax25->vs = ax25->va;
+}
+
+/*
+ * Validate that the value of nr is between va and vs. Return true or
+ * false for testing.
+ */
+static int ax25_validate_nr(ax25_cb *ax25, unsigned short nr)
+{
+ unsigned short ds;
+ unsigned short dr;
+
+ ds = ax25->vs_max - ax25->va;
+ ds &= ax25->seqmask;
+
+ dr = nr - ax25->va;
+ dr &= ax25->seqmask;
+
+ return (dr <= ds);
+}
+
+/*
+ * link reset handling. This is a rather complex routine, as it
+ * has to cope with a tricky situation in the protocol flow.
+ */
+static int ax25_reset_link(ax25_cb* ax25, struct sk_buff* skb)
+{
+ ax25_cb* peer = ax25->peer;
+
+ write_lock(&ax25->timer_lock);
+ ax25->wrt_timer = 0;
+ ax25->ack_timer = 0;
+ ax25->idletimer = ax25->idle;
+ ax25->n2count = 0;
+ write_unlock(&ax25->timer_lock);
+
+ /*
+ * peer just didn't get our UA, handle gracefully.
+ */
+ if (ax25->condition & AX25_COND_SETUP) {
+ ax25_requeue_frames(ax25);
+ ax25->state = AX25_STATE_3;
+ ax25->vs_rtt = -1;
+ ax25_tx_response(ax25, AX25_UA, AX25_POLLON);
+ return 0;
+ }
+
+ /*
+ * Ok, this is a *real* reset. If we have an attached socket,
+ * disconnect it and produce a new, accept-ready one.
+ */
+ if (ax25->sk != NULL) {
+ struct sock* make;
+ struct sock* sk;
+ ax25_cb* tmp = ax25_find_listener(&ax25->addr.src, 0, ax25->device);
+
+ printk(KERN_DEBUG "ax25_lapb.c: resetting socket\n");
+
+ if (!tmp || !(sk = tmp->sk)
+ || sk->ack_backlog == sk->max_ack_backlog
+ || (make = ax25_make_new(sk, ax25->device)) == NULL)
+ {
+ ax25_set_cond(ax25, AX25_COND_STATE_CHANGE);
+ ax25->state = AX25_STATE_0;
+ ax25_tx_response(ax25, AX25_DM, AX25_POLLON);
+ return 0;
+ }
+
+ /* set the destination address */
+ make->protinfo.ax25->addr = ax25->addr;
+
+ /*
+ * ax25_make_new() has produced a new ax25_cb, attached to
+ * a struct sock, we just throw away the old one the regular
+ * way and insert the new one into the appropriate device queue
+ *
+ * It should be enough to disconnect() the old control block,
+ * as this will wake up the application which then in turn
+ * does a close() on the respective socket descriptor.
+ */
+ ax25_remove_cb(ax25);
+ ax25_disconnect(ax25, ECONNRESET);
+ ax25_close_socket(ax25->sk, ECONNRESET);
+
+ /*
+ * done with the old one, now for the new one
+ */
+ write_lock(&ax25->timer_lock);
+ ax25 = make->protinfo.ax25;
+ ax25->state = AX25_STATE_3;
+ ax25->wrt_timer = ax25->t3;
+ ax25->idletimer = ax25->idle;
+ ax25->vs_rtt = -1;
+ write_unlock(&ax25->timer_lock);
+
+ make->pair = sk;
+ skb->sk = make;
+ skb_queue_head(&sk->receive_queue, skb);
+ sk->ack_backlog++;
+ ax25_dev_insert_cb(ax25);
+ ax25_tx_response(ax25, AX25_UA, AX25_POLLON);
+ sk->data_ready(sk, skb->len);
+ return 1;
+ }
+
+ ax25->vs = 0;
+ ax25->va = 0;
+ ax25->vr = 0;
+ ax25->vl = 0;
+
+ /*
+ * check if we have a peer connection to notify
+ */
+ if (peer) {
+
+ printk(KERN_DEBUG "ax25_lapb.c: resetting digipeated connection, state:%d peer state:%d\n",
+ ax25->state, peer->state);
+
+ ax25_clear_queues(ax25);
+
+ write_lock(&ax25->timer_lock);
+
+ ax25->killtimer = 30 * AX25_SLOWHZ;
+ ax25->wrt_timer = 0;
+ ax25->ack_timer = 0;
+ ax25->state = AX25_STATE_0;
+ ax25->condition = AX25_COND_SETUP;
+
+ write_unlock(&ax25->timer_lock);
+
+ ax25_clear_queues(peer);
+
+ write_lock(&peer->timer_lock);
+
+ peer->wrt_timer = 0;
+ peer->ack_timer = 0;
+ peer->vs = 0;
+ peer->va = 0;
+ peer->vr = 0;
+ peer->vl = 0;
+ peer->condition = AX25_COND_SETUP;
+ peer->state = AX25_STATE_1;
+
+ write_unlock(&peer->timer_lock);
+
+ if (peer->seqmask == AX25_SEQMASK)
+ ax25_tx_command(peer, AX25_SABM, AX25_POLLON);
+ else
+ ax25_tx_command(peer, AX25_SABME, AX25_POLLON);
+ return 0;
+ }
+
+ printk(KERN_DEBUG "ax25_lapb.c: resetting protocol uplink\n");
+
+ if (ax25->slcomp != NULL) {
+ axhc_free(ax25->slcomp);
+ ax25->slcomp = NULL;
+ }
+ ax25->state = AX25_STATE_3;
+ ax25->vs_rtt = -1;
+ ax25->wrt_timer = ax25->t3;
+ ax25->condition = AX25_COND_SETUP;
+ ax25_tx_response(ax25, AX25_UA, AX25_POLLON);
+
+ return 0;
+}
+
+/*
+ * State machine for state 0, Disconnected State.
+ * we need this now for the hop-2-hop acknowledgement
+ */
+static int ax25_state0_machine(ax25_cb *ax25, struct sk_buff *skb, ax25_pktinfo *pkt)
+{
+ ax25_cb *peer = ax25->peer;
+
+ switch (pkt->frametype) {
+ case AX25_SABM:
+ case AX25_SABME:
+ if (peer) {
+ if (peer->condition & AX25_COND_SETUP) {
+ if (peer->seqmask == AX25_SEQMASK)
+ ax25_tx_command(peer, AX25_SABM, AX25_POLLON);
+ else
+ ax25_tx_command(peer, AX25_SABME, AX25_POLLON);
+ ax25->killtimer = 30 * AX25_SLOWHZ;
+ } else {
+ struct net_device *dev;
+ if ((dev = ax25_rt_set_addr(&peer->addr, &pkt->addr, ax25->device, peer->device)) == NULL)
+ ax25_tx_command(ax25, AX25_DM, AX25_POLLON);
+ else {
+ ax25_fillin_cb(peer, dev);
+ ax25_reset_link(ax25, skb);
+ }
+ }
+ } else if (!ax25->sk)
+ ax25_reset_link(ax25, skb);
+ break;
+
+ case AX25_DISC:
+ ax25_tx_response(ax25, AX25_DM, pkt->pf);
+ break;
+
+ default:
+ if (pkt->pf && pkt->cmdrsp == AX25_COMMAND)
+ ax25_tx_response(ax25, AX25_DM, AX25_POLLON);
+ break;
+ }
+ return 0; /* we never queue */
+}
+
+
+/*
+ * State machine for state 1, Awaiting Connection 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_state1_machine(ax25_cb *ax25, struct sk_buff *skb, ax25_pktinfo *pkt)
+{
+ ax25_cb *peer = ax25->peer;
+
+ switch (pkt->frametype) {
+ case AX25_SABM:
+ if (peer || ax25->sk == NULL) {
+ if (ax25->seqmask != AX25_SEQMASK) {
+ ax25->seqmask = AX25_SEQMASK;
+ ax25->window = ax25_dev_get_value(ax25->device, AX25_VALUES_WINDOW);
+ }
+ ax25->state = AX25_STATE_3;
+ ax25->vs_rtt = -1;
+ ax25_tx_response(ax25, AX25_UA, pkt->pf);
+ if (peer) {
+ peer->state = AX25_STATE_3;
+ peer->vs_rtt = -1;
+ ax25_tx_response(peer, AX25_UA, pkt->pf);
+ }
+ }
+ break;
+
+ case AX25_SABME:
+ if (ax25->seqmask == AX25_ESEQMASK && (peer || ax25->sk == NULL)) {
+ ax25->state = AX25_STATE_3;
+ ax25->vs_rtt = -1;
+ ax25_tx_response(ax25, AX25_UA, pkt->pf);
+ if (peer) {
+ peer->state = AX25_STATE_3;
+ peer->vs_rtt = -1;
+ ax25_tx_response(peer, AX25_UA, pkt->pf);
+ }
+ }
+ break;
+
+ case AX25_DISC:
+ ax25_tx_response(ax25, AX25_DM, pkt->pf);
+ break;
+
+ case AX25_UA:
+ if (pkt->pf) {
+ write_lock(&ax25->timer_lock);
+ ax25->state = AX25_STATE_3;
+ ax25->wrt_timer = ax25->t3;
+ ax25->idletimer = ax25->idle;
+ ax25->killtimer = 0;
+ ax25->vs = 0;
+ ax25->va = 0;
+ ax25->vr = 0;
+ ax25->vl = 0;
+ ax25->vs_rtt = -1;
+ ax25->n2count = 0;
+ ax25->tx_cmd = 0;
+ ax25->tx_rsp = 0;
+ write_unlock(&ax25->timer_lock);
+
+ if (peer != NULL) {
+ write_lock(&peer->timer_lock);
+ peer->wrt_timer = peer->t3;
+ peer->idletimer = 0;
+ peer->killtimer = 0;
+ peer->vs = 0;
+ peer->va = 0;
+ peer->vr = 0;
+ peer->vl = 0;
+ peer->vs_rtt = -1;
+ peer->n2count = 0;
+ peer->state = AX25_STATE_3;
+ ax25_clr_cond(peer, AX25_COND_STATE_CHANGE);
+ ax25_tx_response(peer, AX25_UA, pkt->pf);
+ write_unlock(&peer->timer_lock);
+ } else if (ax25->sk != NULL) {
+ ax25->sk->state = TCP_ESTABLISHED;
+ /* For WAIT_SABM connections we will produce an accept ready socket here */
+ ax25->sk->state_change(ax25->sk);
+ }
+
+ ax25_kick(ax25);
+ }
+ break;
+
+ case AX25_DM:
+ if (peer != NULL) {
+ ax25_set_cond(peer, AX25_COND_STATE_CHANGE);
+ ax25_disconnect(peer, 0);
+ ax25_tx_response(peer, AX25_DM, pkt->pf);
+ } else if (ax25->sk)
+ ax25_close_socket(ax25->sk, ECONNREFUSED);
+ ax25_disconnect(ax25, ECONNREFUSED);
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+/*
+ * State machine for state 2, Awaiting Release 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_state2_machine(ax25_cb *ax25, struct sk_buff *skb, ax25_pktinfo *pkt)
+{
+ int queued = 0;
+
+ switch (pkt->frametype) {
+ case AX25_SABM:
+ case AX25_SABME:
+ if (pkt->frametype == AX25_SABM) {
+ ax25->seqmask = AX25_SEQMASK;
+ ax25->window = ax25_dev_get_value(ax25->device, AX25_VALUES_WINDOW);
+ } else {
+ ax25->seqmask = AX25_ESEQMASK;
+ ax25->window = ax25_dev_get_value(ax25->device, AX25_VALUES_EWINDOW);
+ }
+ queued = ax25_reset_link(ax25, skb);
+ break;
+
+ case AX25_DISC:
+ if (ax25->sk)
+ ax25_close_socket(ax25->sk, 0);
+ ax25_set_cond(ax25, AX25_COND_STATE_CHANGE);
+ ax25_disconnect(ax25, 0);
+ ax25_tx_response(ax25, AX25_DM, pkt->pf);
+ break;
+
+ case AX25_DM:
+ case AX25_UA:
+ if (pkt->pf) {
+ if (ax25->sk)
+ ax25_close_socket(ax25->sk, 0);
+ ax25_disconnect(ax25, 0);
+ ax25->killtimer=0;
+ ax25->tx_cmd=0;
+ }
+ break;
+
+ case AX25_I:
+ case AX25_REJ:
+ case AX25_RNR:
+ case AX25_RR:
+ if (pkt->pf && pkt->cmdrsp == AX25_COMMAND)
+ ax25_tx_response(ax25, AX25_DM, AX25_POLLON);
+ break;
+
+ default:
+ break;
+ }
+ return queued;
+}
+
+/*
+ * 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_state3_machine(ax25_cb *ax25, struct sk_buff *skb, ax25_pktinfo *pkt)
+{
+ int queued = 0;
+ ax25_cb *peer = ax25->peer;
+
+ switch (pkt->frametype) {
+ case AX25_SABM:
+ case AX25_SABME:
+ if (pkt->frametype == AX25_SABM) {
+ ax25->seqmask = AX25_SEQMASK;
+ ax25->window = ax25_dev_get_value(ax25->device, AX25_VALUES_WINDOW);
+ } else {
+ ax25->seqmask = AX25_ESEQMASK;
+ ax25->window = ax25_dev_get_value(ax25->device, AX25_VALUES_EWINDOW);
+ }
+ queued = ax25_reset_link(ax25, skb);
+ break;
+
+ case AX25_DISC:
+ if (peer)
+ ax25_set_cond(peer, AX25_COND_RELEASE);
+ else if (ax25->sk)
+ ax25_close_socket(ax25->sk, 0);
+ ax25_set_cond(ax25, AX25_COND_STATE_CHANGE);
+ ax25_disconnect(ax25, 0);
+ ax25_tx_response(ax25, AX25_UA, pkt->pf);
+ break;
+
+ case AX25_DM:
+ if (peer)
+ ax25_set_cond(peer, AX25_COND_RELEASE);
+ else if (ax25->sk)
+ ax25_close_socket(ax25->sk, ECONNRESET);
+ ax25_disconnect(ax25, ECONNRESET);
+ break;
+
+ case AX25_RR:
+ case AX25_RNR:
+ if (!ax25_validate_nr(ax25, pkt->nr)) {
+ ax25_nr_error_recovery(ax25);
+ break;
+ }
+
+ ax25_check_iframes_acked(ax25, pkt->nr);
+
+ if (pkt->frametype == AX25_RR)
+ ax25_clr_cond(ax25, AX25_COND_PEER_RX_BUSY);
+ else
+ ax25_set_cond(ax25, AX25_COND_PEER_RX_BUSY);
+
+ if (pkt->pf) {
+ if (pkt->cmdrsp == AX25_COMMAND) {
+ ax25_enquiry_response(ax25);
+ }
+ if (ax25->vs == ax25->va) {
+ ax25->n2count=0;
+ } else {
+ ax25_requeue_frames(ax25);
+ if (DAMA_STATE(ax25) & DAMA_SLAVE) {
+ /*
+ * in DAMA mode we use n2count in state 3
+ * to track the number of DAMA polls
+ * ack'ing only part of our I frames. This
+ * is neccessary to prevent hangs with buggy
+ * masters.
+ */
+ if (++ax25->n2count == ax25->n2) {
+ ax25->vs_rtt = -1;
+ ax25->n2count = 1;
+ ax25_start_t1(ax25);
+ ax25->state = AX25_STATE_4;
+ ax25_transmit_enquiry(ax25);
+ }
+ }
+ }
+ }
+
+ ax25_kick(ax25);
+ break;
+
+ case AX25_REJ:
+ if (!ax25_validate_nr(ax25, pkt->nr)) {
+ ax25_nr_error_recovery(ax25);
+ break;
+ }
+ ax25_check_iframes_acked(ax25, pkt->nr);
+ ax25_clr_cond(ax25, AX25_COND_PEER_RX_BUSY);
+ ax25_requeue_frames(ax25);
+ ax25_kick(ax25);
+ AX25_PTR(ax25->device)->rx_rejects++;
+ break;
+
+ case AX25_I:
+ if (!ax25_validate_nr(ax25, pkt->nr)) {
+ ax25_nr_error_recovery(ax25);
+ break;
+ }
+
+ ax25_check_iframes_acked(ax25, pkt->nr);
+
+ if (pkt->ns == ax25->vr) {
+ if ((queued = ax25_rx_iframe(ax25, skb)) != 0) {
+ ax25_reseq_update(ax25, skb, pkt->ns);
+ ax25->vr = (ax25->vr+1) & ax25->seqmask;
+ ax25_reseq_out(ax25);
+ ax25_clr_cond(ax25, AX25_COND_REJECT);
+ }
+
+ if (pkt->pf) {
+ ax25_enquiry_response(ax25);
+#ifdef notdef
+ } else if (((pkt->ns+2) & ax25->seqmask) == ax25->vl) {
+ /*
+ * optimization (fast ack):
+ * If our peer's TX window is sent out
+ * completely we ack immediately without
+ * waiting for T2.
+ */
+ ax25_timeout_response(ax25);
+ } else {
+ ax25->ack_timer = ax25->t2;
+ ax25_set_cond(ax25, AX25_COND_ACK_PENDING);
+ }
+#else
+ } else ax25_timeout_response(ax25);
+#endif
+ } else {
+ /* frame is not in sequence */
+ queued = ax25_reseq_in(ax25, skb, pkt->ns, pkt->pf);
+ if (ax25->condition & AX25_COND_REJECT) {
+ if (pkt->pf) {
+ ax25_enquiry_response(ax25);
+ } else if (ax25->ack_timer == 0) {
+ ax25->ack_timer = ax25->t2;
+ ax25_set_cond(ax25, AX25_COND_ACK_PENDING);
+ }
+ } else {
+ ax25->ack_timer = 0;
+ ax25_clr_cond(ax25, AX25_COND_ACK_PENDING);
+ ax25_set_cond(ax25, AX25_COND_REJECT);
+ ax25_tx_response(ax25, AX25_REJ, pkt->pf);
+ }
+ }
+ ax25_kick(ax25);
+ break;
+
+ case AX25_FRMR:
+ case AX25_ILLEGAL:
+ ax25_nr_error_recovery(ax25);
+ break;
+
+ default:
+ break;
+ }
+
+ return queued;
+}
+
+/*
+ * State machine for state 4, Timer Recovery 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_state4_machine(ax25_cb *ax25, struct sk_buff *skb, ax25_pktinfo *pkt)
+{
+ int queued = 0;
+ ax25_cb *peer = ax25->peer;
+
+ switch (pkt->frametype) {
+ case AX25_SABM:
+ case AX25_SABME:
+ if (pkt->frametype == AX25_SABM) {
+ ax25->seqmask = AX25_SEQMASK;
+ ax25->window = ax25_dev_get_value(ax25->device, AX25_VALUES_WINDOW);
+ } else {
+ ax25->seqmask = AX25_ESEQMASK;
+ ax25->window = ax25_dev_get_value(ax25->device, AX25_VALUES_EWINDOW);
+ }
+ queued = ax25_reset_link(ax25, skb);
+ break;
+
+ case AX25_DISC:
+ if (peer)
+ ax25_set_cond(peer, AX25_COND_RELEASE);
+ else if (ax25->sk)
+ ax25_close_socket(ax25->sk, 0);
+ ax25_set_cond(ax25, AX25_COND_STATE_CHANGE);
+ ax25_disconnect(ax25, 0);
+ ax25_tx_response(ax25, AX25_UA, pkt->pf);
+ break;
+
+ case AX25_DM:
+ if (peer)
+ ax25_set_cond(peer, AX25_COND_RELEASE);
+ else if (ax25->sk)
+ ax25_close_socket(ax25->sk, ECONNRESET);
+ ax25_disconnect(ax25, ECONNRESET);
+ break;
+
+ case AX25_RR:
+ case AX25_RNR:
+ case AX25_REJ:
+ if (!ax25_validate_nr(ax25, pkt->nr)) {
+ ax25_nr_error_recovery(ax25);
+ break;
+ }
+
+ ax25_frames_acked(ax25, pkt->nr);
+
+ if (pkt->frametype == AX25_RR)
+ ax25_clr_cond(ax25, AX25_COND_PEER_RX_BUSY);
+ else if (pkt->frametype == AX25_REJ) {
+ ax25_clr_cond(ax25, AX25_COND_PEER_RX_BUSY);
+ AX25_PTR(ax25->device)->rx_rejects++;
+ } else
+ ax25_set_cond(ax25, AX25_COND_PEER_RX_BUSY);
+
+ if (pkt->pf) {
+ if (pkt->cmdrsp == AX25_RESPONSE) {
+ write_lock(&ax25->timer_lock);
+ if (ax25->vs == ax25->va) {
+ ax25->n2count = 0;
+ ax25->wrt_timer = 0;
+ ax25->state = AX25_STATE_3;
+ ax25->tx_cmd = 0;
+ ax25->wrt_timer = ax25->t3;
+ }
+ write_unlock(&ax25->timer_lock);
+ ax25_requeue_frames(ax25);
+ } else {
+ if (DAMA_STATE(ax25) & DAMA_SLAVE)
+ ax25_requeue_frames(ax25);
+ ax25_enquiry_response(ax25);
+ }
+ }
+ ax25_kick(ax25);
+ break;
+
+ case AX25_I:
+ if (!ax25_validate_nr(ax25, pkt->nr)) {
+ ax25_nr_error_recovery(ax25);
+ break;
+ }
+ ax25_frames_acked(ax25, pkt->nr);
+ write_lock(&ax25->timer_lock);
+
+ if (pkt->ns == ax25->vr) {
+ /* frame is in sequence */
+ if ((queued = ax25_rx_iframe(ax25, skb)) != 0) {
+ ax25_reseq_update(ax25, skb, pkt->ns);
+ ax25->vr = (ax25->vr+1) & ax25->seqmask;
+ ax25_reseq_out(ax25);
+ ax25_clr_cond(ax25, AX25_COND_REJECT);
+ }
+ if (pkt->pf) {
+ ax25_enquiry_response(ax25);
+ } else if (ax25->ack_timer == 0) {
+ ax25->ack_timer = ax25->t2;
+ ax25_set_cond(ax25, AX25_COND_ACK_PENDING);
+ }
+ } else {
+ /* frame is not in sequence */
+ queued = ax25_reseq_in(ax25, skb, pkt->ns, pkt->pf);
+ if (ax25->condition & AX25_COND_REJECT) {
+ if (pkt->pf)
+ ax25_enquiry_response(ax25);
+ else if (ax25->ack_timer == 0) {
+ ax25->ack_timer = ax25->t2;
+ ax25_set_cond(ax25, AX25_COND_ACK_PENDING);
+ }
+ } else {
+ ax25->ack_timer = 0;
+ ax25_clr_cond(ax25, AX25_COND_ACK_PENDING);
+ ax25_set_cond(ax25, AX25_COND_REJECT);
+ ax25_tx_response(ax25, AX25_REJ, pkt->pf);
+ }
+ }
+
+ write_unlock(&ax25->timer_lock);
+ break;
+
+ case AX25_FRMR:
+ case AX25_ILLEGAL:
+ ax25_nr_error_recovery(ax25);
+ break;
+
+ default:
+ break;
+ }
+
+ return queued;
+}
+
+static int ax25_state_nop(ax25_cb *ax25, struct sk_buff *skb, ax25_pktinfo *pkt)
+{
+ printk(KERN_DEBUG "ax25_state_nop()\n");
+ return 0;
+}
+
+ax25_statefunc_t ax25_lapb_table[] =
+{
+ ax25_state0_machine,
+ ax25_state1_machine,
+ ax25_state2_machine,
+ ax25_state3_machine,
+ ax25_state4_machine,
+ ax25_state_nop
+};