/* * 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 #include #include #include #include #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 };