xref: /linux/net/netrom/nr_in.c (revision c537b994505099b7197e7d3125b942ecbcc51eb6)
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 }
304