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