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