linux/net/netrom/nr_in.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 *
   4 * Copyright Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk)
   5 * Copyright Darryl Miles G7LED (dlm@g7led.demon.co.uk)
   6 */
   7#include <linux/errno.h>
   8#include <linux/types.h>
   9#include <linux/socket.h>
  10#include <linux/in.h>
  11#include <linux/kernel.h>
  12#include <linux/timer.h>
  13#include <linux/string.h>
  14#include <linux/sockios.h>
  15#include <linux/net.h>
  16#include <linux/slab.h>
  17#include <net/ax25.h>
  18#include <linux/inet.h>
  19#include <linux/netdevice.h>
  20#include <linux/skbuff.h>
  21#include <net/sock.h>
  22#include <net/tcp_states.h>
  23#include <linux/uaccess.h>
  24#include <linux/fcntl.h>
  25#include <linux/mm.h>
  26#include <linux/interrupt.h>
  27#include <net/netrom.h>
  28
  29static int nr_queue_rx_frame(struct sock *sk, struct sk_buff *skb, int more)
  30{
  31        struct sk_buff *skbo, *skbn = skb;
  32        struct nr_sock *nr = nr_sk(sk);
  33
  34        skb_pull(skb, NR_NETWORK_LEN + NR_TRANSPORT_LEN);
  35
  36        nr_start_idletimer(sk);
  37
  38        if (more) {
  39                nr->fraglen += skb->len;
  40                skb_queue_tail(&nr->frag_queue, skb);
  41                return 0;
  42        }
  43
  44        if (!more && nr->fraglen > 0) { /* End of fragment */
  45                nr->fraglen += skb->len;
  46                skb_queue_tail(&nr->frag_queue, skb);
  47
  48                if ((skbn = alloc_skb(nr->fraglen, GFP_ATOMIC)) == NULL)
  49                        return 1;
  50
  51                skb_reset_transport_header(skbn);
  52
  53                while ((skbo = skb_dequeue(&nr->frag_queue)) != NULL) {
  54                        skb_copy_from_linear_data(skbo,
  55                                                  skb_put(skbn, skbo->len),
  56                                                  skbo->len);
  57                        kfree_skb(skbo);
  58                }
  59
  60                nr->fraglen = 0;
  61        }
  62
  63        return sock_queue_rcv_skb(sk, skbn);
  64}
  65
  66/*
  67 * State machine for state 1, Awaiting Connection State.
  68 * The handling of the timer(s) is in file nr_timer.c.
  69 * Handling of state 0 and connection release is in netrom.c.
  70 */
  71static int nr_state1_machine(struct sock *sk, struct sk_buff *skb,
  72        int frametype)
  73{
  74        switch (frametype) {
  75        case NR_CONNACK: {
  76                struct nr_sock *nr = nr_sk(sk);
  77
  78                nr_stop_t1timer(sk);
  79                nr_start_idletimer(sk);
  80                nr->your_index = skb->data[17];
  81                nr->your_id    = skb->data[18];
  82                nr->vs         = 0;
  83                nr->va         = 0;
  84                nr->vr         = 0;
  85                nr->vl         = 0;
  86                nr->state      = NR_STATE_3;
  87                nr->n2count    = 0;
  88                nr->window     = skb->data[20];
  89                sk->sk_state   = TCP_ESTABLISHED;
  90                if (!sock_flag(sk, SOCK_DEAD))
  91                        sk->sk_state_change(sk);
  92                break;
  93        }
  94
  95        case NR_CONNACK | NR_CHOKE_FLAG:
  96                nr_disconnect(sk, ECONNREFUSED);
  97                break;
  98
  99        case NR_RESET:
 100                if (sysctl_netrom_reset_circuit)
 101                        nr_disconnect(sk, ECONNRESET);
 102                break;
 103
 104        default:
 105                break;
 106        }
 107        return 0;
 108}
 109
 110/*
 111 * State machine for state 2, Awaiting Release State.
 112 * The handling of the timer(s) is in file nr_timer.c
 113 * Handling of state 0 and connection release is in netrom.c.
 114 */
 115static int nr_state2_machine(struct sock *sk, struct sk_buff *skb,
 116        int frametype)
 117{
 118        switch (frametype) {
 119        case NR_CONNACK | NR_CHOKE_FLAG:
 120                nr_disconnect(sk, ECONNRESET);
 121                break;
 122
 123        case NR_DISCREQ:
 124                nr_write_internal(sk, NR_DISCACK);
 125                fallthrough;
 126        case NR_DISCACK:
 127                nr_disconnect(sk, 0);
 128                break;
 129
 130        case NR_RESET:
 131                if (sysctl_netrom_reset_circuit)
 132                        nr_disconnect(sk, ECONNRESET);
 133                break;
 134
 135        default:
 136                break;
 137        }
 138        return 0;
 139}
 140
 141/*
 142 * State machine for state 3, Connected State.
 143 * The handling of the timer(s) is in file nr_timer.c
 144 * Handling of state 0 and connection release is in netrom.c.
 145 */
 146static int nr_state3_machine(struct sock *sk, struct sk_buff *skb, int frametype)
 147{
 148        struct nr_sock *nrom = nr_sk(sk);
 149        struct sk_buff_head temp_queue;
 150        struct sk_buff *skbn;
 151        unsigned short save_vr;
 152        unsigned short nr, ns;
 153        int queued = 0;
 154
 155        nr = skb->data[18];
 156
 157        switch (frametype) {
 158        case NR_CONNREQ:
 159                nr_write_internal(sk, NR_CONNACK);
 160                break;
 161
 162        case NR_DISCREQ:
 163                nr_write_internal(sk, NR_DISCACK);
 164                nr_disconnect(sk, 0);
 165                break;
 166
 167        case NR_CONNACK | NR_CHOKE_FLAG:
 168        case NR_DISCACK:
 169                nr_disconnect(sk, ECONNRESET);
 170                break;
 171
 172        case NR_INFOACK:
 173        case NR_INFOACK | NR_CHOKE_FLAG:
 174        case NR_INFOACK | NR_NAK_FLAG:
 175        case NR_INFOACK | NR_NAK_FLAG | NR_CHOKE_FLAG:
 176                if (frametype & NR_CHOKE_FLAG) {
 177                        nrom->condition |= NR_COND_PEER_RX_BUSY;
 178                        nr_start_t4timer(sk);
 179                } else {
 180                        nrom->condition &= ~NR_COND_PEER_RX_BUSY;
 181                        nr_stop_t4timer(sk);
 182                }
 183                if (!nr_validate_nr(sk, nr)) {
 184                        break;
 185                }
 186                if (frametype & NR_NAK_FLAG) {
 187                        nr_frames_acked(sk, nr);
 188                        nr_send_nak_frame(sk);
 189                } else {
 190                        if (nrom->condition & NR_COND_PEER_RX_BUSY) {
 191                                nr_frames_acked(sk, nr);
 192                        } else {
 193                                nr_check_iframes_acked(sk, nr);
 194                        }
 195                }
 196                break;
 197
 198        case NR_INFO:
 199        case NR_INFO | NR_NAK_FLAG:
 200        case NR_INFO | NR_CHOKE_FLAG:
 201        case NR_INFO | NR_MORE_FLAG:
 202        case NR_INFO | NR_NAK_FLAG | NR_CHOKE_FLAG:
 203        case NR_INFO | NR_CHOKE_FLAG | NR_MORE_FLAG:
 204        case NR_INFO | NR_NAK_FLAG | NR_MORE_FLAG:
 205        case NR_INFO | NR_NAK_FLAG | NR_CHOKE_FLAG | NR_MORE_FLAG:
 206                if (frametype & NR_CHOKE_FLAG) {
 207                        nrom->condition |= NR_COND_PEER_RX_BUSY;
 208                        nr_start_t4timer(sk);
 209                } else {
 210                        nrom->condition &= ~NR_COND_PEER_RX_BUSY;
 211                        nr_stop_t4timer(sk);
 212                }
 213                if (nr_validate_nr(sk, nr)) {
 214                        if (frametype & NR_NAK_FLAG) {
 215                                nr_frames_acked(sk, nr);
 216                                nr_send_nak_frame(sk);
 217                        } else {
 218                                if (nrom->condition & NR_COND_PEER_RX_BUSY) {
 219                                        nr_frames_acked(sk, nr);
 220                                } else {
 221                                        nr_check_iframes_acked(sk, nr);
 222                                }
 223                        }
 224                }
 225                queued = 1;
 226                skb_queue_head(&nrom->reseq_queue, skb);
 227                if (nrom->condition & NR_COND_OWN_RX_BUSY)
 228                        break;
 229                skb_queue_head_init(&temp_queue);
 230                do {
 231                        save_vr = nrom->vr;
 232                        while ((skbn = skb_dequeue(&nrom->reseq_queue)) != NULL) {
 233                                ns = skbn->data[17];
 234                                if (ns == nrom->vr) {
 235                                        if (nr_queue_rx_frame(sk, skbn, frametype & NR_MORE_FLAG) == 0) {
 236                                                nrom->vr = (nrom->vr + 1) % NR_MODULUS;
 237                                        } else {
 238                                                nrom->condition |= NR_COND_OWN_RX_BUSY;
 239                                                skb_queue_tail(&temp_queue, skbn);
 240                                        }
 241                                } else if (nr_in_rx_window(sk, ns)) {
 242                                        skb_queue_tail(&temp_queue, skbn);
 243                                } else {
 244                                        kfree_skb(skbn);
 245                                }
 246                        }
 247                        while ((skbn = skb_dequeue(&temp_queue)) != NULL) {
 248                                skb_queue_tail(&nrom->reseq_queue, skbn);
 249                        }
 250                } while (save_vr != nrom->vr);
 251                /*
 252                 * Window is full, ack it immediately.
 253                 */
 254                if (((nrom->vl + nrom->window) % NR_MODULUS) == nrom->vr) {
 255                        nr_enquiry_response(sk);
 256                } else {
 257                        if (!(nrom->condition & NR_COND_ACK_PENDING)) {
 258                                nrom->condition |= NR_COND_ACK_PENDING;
 259                                nr_start_t2timer(sk);
 260                        }
 261                }
 262                break;
 263
 264        case NR_RESET:
 265                if (sysctl_netrom_reset_circuit)
 266                        nr_disconnect(sk, ECONNRESET);
 267                break;
 268
 269        default:
 270                break;
 271        }
 272        return queued;
 273}
 274
 275/* Higher level upcall for a LAPB frame - called with sk locked */
 276int nr_process_rx_frame(struct sock *sk, struct sk_buff *skb)
 277{
 278        struct nr_sock *nr = nr_sk(sk);
 279        int queued = 0, frametype;
 280
 281        if (nr->state == NR_STATE_0)
 282                return 0;
 283
 284        frametype = skb->data[19];
 285
 286        switch (nr->state) {
 287        case NR_STATE_1:
 288                queued = nr_state1_machine(sk, skb, frametype);
 289                break;
 290        case NR_STATE_2:
 291                queued = nr_state2_machine(sk, skb, frametype);
 292                break;
 293        case NR_STATE_3:
 294                queued = nr_state3_machine(sk, skb, frametype);
 295                break;
 296        }
 297
 298        nr_kick(sk);
 299
 300        return queued;
 301}
 302