Blame view

net/decnet/dn_nsp_in.c 21.1 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
  /*
   * DECnet       An implementation of the DECnet protocol suite for the LINUX
   *              operating system.  DECnet is implemented using the  BSD Socket
   *              interface as the means of communication with the user level.
   *
   *              DECnet Network Services Protocol (Input)
   *
   * Author:      Eduardo Marcelo Serrat <emserrat@geocities.com>
   *
   * Changes:
   *
   *    Steve Whitehouse:  Split into dn_nsp_in.c and dn_nsp_out.c from
   *                       original dn_nsp.c.
   *    Steve Whitehouse:  Updated to work with my new routing architecture.
   *    Steve Whitehouse:  Add changes from Eduardo Serrat's patches.
   *    Steve Whitehouse:  Put all ack handling code in a common routine.
   *    Steve Whitehouse:  Put other common bits into dn_nsp_rx()
   *    Steve Whitehouse:  More checks on skb->len to catch bogus packets
   *                       Fixed various race conditions and possible nasties.
   *    Steve Whitehouse:  Now handles returned conninit frames.
   *     David S. Miller:  New socket locking
   *    Steve Whitehouse:  Fixed lockup when socket filtering was enabled.
   *         Paul Koning:  Fix to push CC sockets into RUN when acks are
   *                       received.
   *    Steve Whitehouse:
   *   Patrick Caulfield:  Checking conninits for correctness & sending of error
   *                       responses.
   *    Steve Whitehouse:  Added backlog congestion level return codes.
   *   Patrick Caulfield:
   *    Steve Whitehouse:  Added flow control support (outbound)
   *    Steve Whitehouse:  Prepare for nonlinear skbs
   */
  
  /******************************************************************************
      (c) 1995-1998 E.M. Serrat		emserrat@geocities.com
429eb0fae   YOSHIFUJI Hideaki   [NET] DECNET: Fix...
36

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
37
38
39
40
41
42
43
44
45
46
      This program 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
      any later version.
  
      This program is distributed in the hope that it will be useful,
      but WITHOUT ANY WARRANTY; without even the implied warranty of
      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      GNU General Public License for more details.
  *******************************************************************************/
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
47
48
49
50
51
  #include <linux/errno.h>
  #include <linux/types.h>
  #include <linux/socket.h>
  #include <linux/in.h>
  #include <linux/kernel.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
52
53
54
55
56
57
58
  #include <linux/timer.h>
  #include <linux/string.h>
  #include <linux/sockios.h>
  #include <linux/net.h>
  #include <linux/netdevice.h>
  #include <linux/inet.h>
  #include <linux/route.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
59
  #include <linux/slab.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
60
  #include <net/sock.h>
c752f0739   Arnaldo Carvalho de Melo   [TCP]: Move the t...
61
  #include <net/tcp_states.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
62
63
64
  #include <asm/system.h>
  #include <linux/fcntl.h>
  #include <linux/mm.h>
429eb0fae   YOSHIFUJI Hideaki   [NET] DECNET: Fix...
65
  #include <linux/termios.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
  #include <linux/interrupt.h>
  #include <linux/proc_fs.h>
  #include <linux/stat.h>
  #include <linux/init.h>
  #include <linux/poll.h>
  #include <linux/netfilter_decnet.h>
  #include <net/neighbour.h>
  #include <net/dst.h>
  #include <net/dn.h>
  #include <net/dn_nsp.h>
  #include <net/dn_dev.h>
  #include <net/dn_route.h>
  
  extern int decnet_log_martians;
  
  static void dn_log_martian(struct sk_buff *skb, const char *msg)
  {
  	if (decnet_log_martians && net_ratelimit()) {
  		char *devname = skb->dev ? skb->dev->name : "???";
  		struct dn_skb_cb *cb = DN_SKB_CB(skb);
c4106aa88   Harvey Harrison   decnet: remove pr...
86
87
88
89
  		printk(KERN_INFO "DECnet: Martian packet (%s) dev=%s src=0x%04hx dst=0x%04hx srcport=0x%04hx dstport=0x%04hx
  ",
  		       msg, devname, le16_to_cpu(cb->src), le16_to_cpu(cb->dst),
  		       le16_to_cpu(cb->src_port), le16_to_cpu(cb->dst_port));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
90
91
92
93
94
95
96
97
98
99
100
101
102
  	}
  }
  
  /*
   * For this function we've flipped the cross-subchannel bit
   * if the message is an otherdata or linkservice message. Thus
   * we can use it to work out what to update.
   */
  static void dn_ack(struct sock *sk, struct sk_buff *skb, unsigned short ack)
  {
  	struct dn_scp *scp = DN_SK(sk);
  	unsigned short type = ((ack >> 12) & 0x0003);
  	int wakeup = 0;
06f8fe11b   Joe Perches   decnet: Reduce sw...
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
  	switch (type) {
  	case 0: /* ACK - Data */
  		if (dn_after(ack, scp->ackrcv_dat)) {
  			scp->ackrcv_dat = ack & 0x0fff;
  			wakeup |= dn_nsp_check_xmit_queue(sk, skb,
  							  &scp->data_xmit_queue,
  							  ack);
  		}
  		break;
  	case 1: /* NAK - Data */
  		break;
  	case 2: /* ACK - OtherData */
  		if (dn_after(ack, scp->ackrcv_oth)) {
  			scp->ackrcv_oth = ack & 0x0fff;
  			wakeup |= dn_nsp_check_xmit_queue(sk, skb,
  							  &scp->other_xmit_queue,
  							  ack);
  		}
  		break;
  	case 3: /* NAK - OtherData */
  		break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
124
125
126
127
128
129
130
131
132
133
134
  	}
  
  	if (wakeup && !sock_flag(sk, SOCK_DEAD))
  		sk->sk_state_change(sk);
  }
  
  /*
   * This function is a universal ack processor.
   */
  static int dn_process_ack(struct sock *sk, struct sk_buff *skb, int oth)
  {
c4ea94ab3   Steven Whitehouse   [DECnet]: Endian ...
135
  	__le16 *ptr = (__le16 *)skb->data;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
136
137
138
139
140
  	int len = 0;
  	unsigned short ack;
  
  	if (skb->len < 2)
  		return len;
c4106aa88   Harvey Harrison   decnet: remove pr...
141
  	if ((ack = le16_to_cpu(*ptr)) & 0x8000) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
142
143
144
145
  		skb_pull(skb, 2);
  		ptr++;
  		len += 2;
  		if ((ack & 0x4000) == 0) {
429eb0fae   YOSHIFUJI Hideaki   [NET] DECNET: Fix...
146
  			if (oth)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
147
148
149
150
151
152
153
  				ack ^= 0x2000;
  			dn_ack(sk, skb, ack);
  		}
  	}
  
  	if (skb->len < 2)
  		return len;
c4106aa88   Harvey Harrison   decnet: remove pr...
154
  	if ((ack = le16_to_cpu(*ptr)) & 0x8000) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
155
156
157
  		skb_pull(skb, 2);
  		len += 2;
  		if ((ack & 0x4000) == 0) {
429eb0fae   YOSHIFUJI Hideaki   [NET] DECNET: Fix...
158
  			if (oth)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
  				ack ^= 0x2000;
  			dn_ack(sk, skb, ack);
  		}
  	}
  
  	return len;
  }
  
  
  /**
   * dn_check_idf - Check an image data field format is correct.
   * @pptr: Pointer to pointer to image data
   * @len: Pointer to length of image data
   * @max: The maximum allowed length of the data in the image data field
   * @follow_on: Check that this many bytes exist beyond the end of the image data
   *
   * Returns: 0 if ok, -1 on error
   */
  static inline int dn_check_idf(unsigned char **pptr, int *len, unsigned char max, unsigned char follow_on)
  {
  	unsigned char *ptr = *pptr;
  	unsigned char flen = *ptr++;
  
  	(*len)--;
  	if (flen > max)
  		return -1;
  	if ((flen + follow_on) > *len)
  		return -1;
  
  	*len -= flen;
  	*pptr = ptr + flen;
  	return 0;
  }
  
  /*
   * Table of reason codes to pass back to node which sent us a badly
   * formed message, plus text messages for the log. A zero entry in
   * the reason field means "don't reply" otherwise a disc init is sent with
   * the specified reason code.
   */
  static struct {
  	unsigned short reason;
  	const char *text;
  } ci_err_table[] = {
   { 0,             "CI: Truncated message" },
   { NSP_REASON_ID, "CI: Destination username error" },
   { NSP_REASON_ID, "CI: Destination username type" },
   { NSP_REASON_US, "CI: Source username error" },
   { 0,             "CI: Truncated at menuver" },
   { 0,             "CI: Truncated before access or user data" },
   { NSP_REASON_IO, "CI: Access data format error" },
   { NSP_REASON_IO, "CI: User data format error" }
  };
  
  /*
   * This function uses a slightly different lookup method
   * to find its sockets, since it searches on object name/number
   * rather than port numbers. Various tests are done to ensure that
   * the incoming data is in the correct format before it is queued to
   * a socket.
   */
  static struct sock *dn_find_listener(struct sk_buff *skb, unsigned short *reason)
  {
  	struct dn_skb_cb *cb = DN_SKB_CB(skb);
  	struct nsp_conn_init_msg *msg = (struct nsp_conn_init_msg *)skb->data;
  	struct sockaddr_dn dstaddr;
  	struct sockaddr_dn srcaddr;
  	unsigned char type = 0;
  	int dstlen;
  	int srclen;
  	unsigned char *ptr;
  	int len;
  	int err = 0;
  	unsigned char menuver;
  
  	memset(&dstaddr, 0, sizeof(struct sockaddr_dn));
  	memset(&srcaddr, 0, sizeof(struct sockaddr_dn));
  
  	/*
  	 * 1. Decode & remove message header
  	 */
  	cb->src_port = msg->srcaddr;
  	cb->dst_port = msg->dstaddr;
  	cb->services = msg->services;
  	cb->info     = msg->info;
c4106aa88   Harvey Harrison   decnet: remove pr...
244
  	cb->segsize  = le16_to_cpu(msg->segsize);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
  
  	if (!pskb_may_pull(skb, sizeof(*msg)))
  		goto err_out;
  
  	skb_pull(skb, sizeof(*msg));
  
  	len = skb->len;
  	ptr = skb->data;
  
  	/*
  	 * 2. Check destination end username format
  	 */
  	dstlen = dn_username2sockaddr(ptr, len, &dstaddr, &type);
  	err++;
  	if (dstlen < 0)
  		goto err_out;
  
  	err++;
  	if (type > 1)
  		goto err_out;
  
  	len -= dstlen;
  	ptr += dstlen;
  
  	/*
  	 * 3. Check source end username format
  	 */
  	srclen = dn_username2sockaddr(ptr, len, &srcaddr, &type);
  	err++;
  	if (srclen < 0)
  		goto err_out;
  
  	len -= srclen;
  	ptr += srclen;
  	err++;
  	if (len < 1)
  		goto err_out;
  
  	menuver = *ptr;
  	ptr++;
  	len--;
  
  	/*
  	 * 4. Check that optional data actually exists if menuver says it does
  	 */
  	err++;
  	if ((menuver & (DN_MENUVER_ACC | DN_MENUVER_USR)) && (len < 1))
  		goto err_out;
  
  	/*
  	 * 5. Check optional access data format
  	 */
  	err++;
  	if (menuver & DN_MENUVER_ACC) {
  		if (dn_check_idf(&ptr, &len, 39, 1))
  			goto err_out;
  		if (dn_check_idf(&ptr, &len, 39, 1))
  			goto err_out;
  		if (dn_check_idf(&ptr, &len, 39, (menuver & DN_MENUVER_USR) ? 1 : 0))
  			goto err_out;
  	}
  
  	/*
  	 * 6. Check optional user data format
  	 */
  	err++;
  	if (menuver & DN_MENUVER_USR) {
  		if (dn_check_idf(&ptr, &len, 16, 0))
  			goto err_out;
  	}
  
  	/*
  	 * 7. Look up socket based on destination end username
  	 */
  	return dn_sklist_find_listener(&dstaddr);
  err_out:
  	dn_log_martian(skb, ci_err_table[err].text);
  	*reason = ci_err_table[err].reason;
  	return NULL;
  }
  
  
  static void dn_nsp_conn_init(struct sock *sk, struct sk_buff *skb)
  {
  	if (sk_acceptq_is_full(sk)) {
  		kfree_skb(skb);
  		return;
  	}
  
  	sk->sk_ack_backlog++;
  	skb_queue_tail(&sk->sk_receive_queue, skb);
  	sk->sk_state_change(sk);
  }
  
  static void dn_nsp_conn_conf(struct sock *sk, struct sk_buff *skb)
  {
  	struct dn_skb_cb *cb = DN_SKB_CB(skb);
  	struct dn_scp *scp = DN_SK(sk);
  	unsigned char *ptr;
  
  	if (skb->len < 4)
  		goto out;
  
  	ptr = skb->data;
  	cb->services = *ptr++;
  	cb->info = *ptr++;
c4106aa88   Harvey Harrison   decnet: remove pr...
351
  	cb->segsize = le16_to_cpu(*(__le16 *)ptr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
352
353
354
  
  	if ((scp->state == DN_CI) || (scp->state == DN_CD)) {
  		scp->persist = 0;
429eb0fae   YOSHIFUJI Hideaki   [NET] DECNET: Fix...
355
356
357
  		scp->addrrem = cb->src_port;
  		sk->sk_state = TCP_ESTABLISHED;
  		scp->state = DN_RUN;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
358
359
360
361
362
363
364
365
  		scp->services_rem = cb->services;
  		scp->info_rem = cb->info;
  		scp->segsize_rem = cb->segsize;
  
  		if ((scp->services_rem & NSP_FC_MASK) == NSP_FC_NONE)
  			scp->max_window = decnet_no_fc_max_cwnd;
  
  		if (skb->len > 0) {
375d9d718   Steven Whitehouse   [DECNET]: Endiane...
366
  			u16 dlen = *skb->data;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
367
  			if ((dlen <= 16) && (dlen <= skb->len)) {
c4106aa88   Harvey Harrison   decnet: remove pr...
368
  				scp->conndata_in.opt_optl = cpu_to_le16(dlen);
d626f62b1   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
369
370
  				skb_copy_from_linear_data_offset(skb, 1,
  					      scp->conndata_in.opt_data, dlen);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
371
372
  			}
  		}
429eb0fae   YOSHIFUJI Hideaki   [NET] DECNET: Fix...
373
374
375
376
  		dn_nsp_send_link(sk, DN_NOCHANGE, 0);
  		if (!sock_flag(sk, SOCK_DEAD))
  			sk->sk_state_change(sk);
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
377
378
  
  out:
429eb0fae   YOSHIFUJI Hideaki   [NET] DECNET: Fix...
379
  	kfree_skb(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
  }
  
  static void dn_nsp_conn_ack(struct sock *sk, struct sk_buff *skb)
  {
  	struct dn_scp *scp = DN_SK(sk);
  
  	if (scp->state == DN_CI) {
  		scp->state = DN_CD;
  		scp->persist = 0;
  	}
  
  	kfree_skb(skb);
  }
  
  static void dn_nsp_disc_init(struct sock *sk, struct sk_buff *skb)
  {
  	struct dn_scp *scp = DN_SK(sk);
  	struct dn_skb_cb *cb = DN_SKB_CB(skb);
  	unsigned short reason;
  
  	if (skb->len < 2)
  		goto out;
c4106aa88   Harvey Harrison   decnet: remove pr...
402
  	reason = le16_to_cpu(*(__le16 *)skb->data);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
403
  	skb_pull(skb, 2);
c4106aa88   Harvey Harrison   decnet: remove pr...
404
  	scp->discdata_in.opt_status = cpu_to_le16(reason);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
405
406
407
408
  	scp->discdata_in.opt_optl   = 0;
  	memset(scp->discdata_in.opt_data, 0, 16);
  
  	if (skb->len > 0) {
375d9d718   Steven Whitehouse   [DECNET]: Endiane...
409
  		u16 dlen = *skb->data;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
410
  		if ((dlen <= 16) && (dlen <= skb->len)) {
c4106aa88   Harvey Harrison   decnet: remove pr...
411
  			scp->discdata_in.opt_optl = cpu_to_le16(dlen);
d626f62b1   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
412
  			skb_copy_from_linear_data_offset(skb, 1, scp->discdata_in.opt_data, dlen);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
413
414
415
416
417
  		}
  	}
  
  	scp->addrrem = cb->src_port;
  	sk->sk_state = TCP_CLOSE;
06f8fe11b   Joe Perches   decnet: Reduce sw...
418
419
420
421
422
423
424
425
426
427
428
429
430
  	switch (scp->state) {
  	case DN_CI:
  	case DN_CD:
  		scp->state = DN_RJ;
  		sk->sk_err = ECONNREFUSED;
  		break;
  	case DN_RUN:
  		sk->sk_shutdown |= SHUTDOWN_MASK;
  		scp->state = DN_DN;
  		break;
  	case DN_DI:
  		scp->state = DN_DIC;
  		break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
431
432
433
434
435
436
437
  	}
  
  	if (!sock_flag(sk, SOCK_DEAD)) {
  		if (sk->sk_socket->state != SS_UNCONNECTED)
  			sk->sk_socket->state = SS_DISCONNECTING;
  		sk->sk_state_change(sk);
  	}
429eb0fae   YOSHIFUJI Hideaki   [NET] DECNET: Fix...
438
  	/*
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
  	 * It appears that its possible for remote machines to send disc
  	 * init messages with no port identifier if we are in the CI and
  	 * possibly also the CD state. Obviously we shouldn't reply with
  	 * a message if we don't know what the end point is.
  	 */
  	if (scp->addrrem) {
  		dn_nsp_send_disc(sk, NSP_DISCCONF, NSP_REASON_DC, GFP_ATOMIC);
  	}
  	scp->persist_fxn = dn_destroy_timer;
  	scp->persist = dn_nsp_persist(sk);
  
  out:
  	kfree_skb(skb);
  }
  
  /*
   * disc_conf messages are also called no_resources or no_link
   * messages depending upon the "reason" field.
   */
  static void dn_nsp_disc_conf(struct sock *sk, struct sk_buff *skb)
  {
  	struct dn_scp *scp = DN_SK(sk);
  	unsigned short reason;
  
  	if (skb->len != 2)
  		goto out;
c4106aa88   Harvey Harrison   decnet: remove pr...
465
  	reason = le16_to_cpu(*(__le16 *)skb->data);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
466
467
  
  	sk->sk_state = TCP_CLOSE;
06f8fe11b   Joe Perches   decnet: Reduce sw...
468
469
470
471
472
473
474
475
  	switch (scp->state) {
  	case DN_CI:
  		scp->state = DN_NR;
  		break;
  	case DN_DR:
  		if (reason == NSP_REASON_DC)
  			scp->state = DN_DRC;
  		if (reason == NSP_REASON_NL)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
476
  			scp->state = DN_CN;
06f8fe11b   Joe Perches   decnet: Reduce sw...
477
478
479
480
481
482
483
484
  		break;
  	case DN_DI:
  		scp->state = DN_DIC;
  		break;
  	case DN_RUN:
  		sk->sk_shutdown |= SHUTDOWN_MASK;
  	case DN_CC:
  		scp->state = DN_CN;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
  	}
  
  	if (!sock_flag(sk, SOCK_DEAD)) {
  		if (sk->sk_socket->state != SS_UNCONNECTED)
  			sk->sk_socket->state = SS_DISCONNECTING;
  		sk->sk_state_change(sk);
  	}
  
  	scp->persist_fxn = dn_destroy_timer;
  	scp->persist = dn_nsp_persist(sk);
  
  out:
  	kfree_skb(skb);
  }
  
  static void dn_nsp_linkservice(struct sock *sk, struct sk_buff *skb)
  {
  	struct dn_scp *scp = DN_SK(sk);
  	unsigned short segnum;
  	unsigned char lsflags;
  	signed char fcval;
  	int wake_up = 0;
  	char *ptr = skb->data;
  	unsigned char fctype = scp->services_rem & NSP_FC_MASK;
  
  	if (skb->len != 4)
  		goto out;
c4106aa88   Harvey Harrison   decnet: remove pr...
512
  	segnum = le16_to_cpu(*(__le16 *)ptr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
513
514
515
516
517
518
  	ptr += 2;
  	lsflags = *(unsigned char *)ptr++;
  	fcval = *ptr;
  
  	/*
  	 * Here we ignore erronous packets which should really
429eb0fae   YOSHIFUJI Hideaki   [NET] DECNET: Fix...
519
  	 * should cause a connection abort. It is not critical
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
520
521
522
523
524
525
526
527
528
529
  	 * for now though.
  	 */
  	if (lsflags & 0xf8)
  		goto out;
  
  	if (seq_next(scp->numoth_rcv, segnum)) {
  		seq_add(&scp->numoth_rcv, 1);
  		switch(lsflags & 0x04) { /* FCVAL INT */
  		case 0x00: /* Normal Request */
  			switch(lsflags & 0x03) { /* FCVAL MOD */
429eb0fae   YOSHIFUJI Hideaki   [NET] DECNET: Fix...
530
  			case 0x00: /* Request count */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
531
532
533
534
535
536
537
538
539
540
  				if (fcval < 0) {
  					unsigned char p_fcval = -fcval;
  					if ((scp->flowrem_dat > p_fcval) &&
  					    (fctype == NSP_FC_SCMC)) {
  						scp->flowrem_dat -= p_fcval;
  					}
  				} else if (fcval > 0) {
  					scp->flowrem_dat += fcval;
  					wake_up = 1;
  				}
429eb0fae   YOSHIFUJI Hideaki   [NET] DECNET: Fix...
541
  				break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
  			case 0x01: /* Stop outgoing data */
  				scp->flowrem_sw = DN_DONTSEND;
  				break;
  			case 0x02: /* Ok to start again */
  				scp->flowrem_sw = DN_SEND;
  				dn_nsp_output(sk);
  				wake_up = 1;
  			}
  			break;
  		case 0x04: /* Interrupt Request */
  			if (fcval > 0) {
  				scp->flowrem_oth += fcval;
  				wake_up = 1;
  			}
  			break;
429eb0fae   YOSHIFUJI Hideaki   [NET] DECNET: Fix...
557
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
558
559
  		if (wake_up && !sock_flag(sk, SOCK_DEAD))
  			sk->sk_state_change(sk);
429eb0fae   YOSHIFUJI Hideaki   [NET] DECNET: Fix...
560
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
  
  	dn_nsp_send_oth_ack(sk);
  
  out:
  	kfree_skb(skb);
  }
  
  /*
   * Copy of sock_queue_rcv_skb (from sock.h) without
   * bh_lock_sock() (its already held when this is called) which
   * also allows data and other data to be queued to a socket.
   */
  static __inline__ int dn_queue_skb(struct sock *sk, struct sk_buff *skb, int sig, struct sk_buff_head *queue)
  {
  	int err;
9948bb6a6   Steven Whitehouse   decnet: Use data ...
576
  	int skb_len;
429eb0fae   YOSHIFUJI Hideaki   [NET] DECNET: Fix...
577
578
579
580
581
  
  	/* Cast skb->rcvbuf to unsigned... It's pointless, but reduces
  	   number of warnings when compiling with -W --ANK
  	 */
  	if (atomic_read(&sk->sk_rmem_alloc) + skb->truesize >=
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
582
  	    (unsigned)sk->sk_rcvbuf) {
429eb0fae   YOSHIFUJI Hideaki   [NET] DECNET: Fix...
583
584
585
  		err = -ENOMEM;
  		goto out;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
586

fda9ef5d6   Dmitry Mishin   [NET]: Fix sk->sk...
587
  	err = sk_filter(sk, skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
588
589
  	if (err)
  		goto out;
9948bb6a6   Steven Whitehouse   decnet: Use data ...
590
  	skb_len = skb->len;
429eb0fae   YOSHIFUJI Hideaki   [NET] DECNET: Fix...
591
592
  	skb_set_owner_r(skb, sk);
  	skb_queue_tail(queue, skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
593

9948bb6a6   Steven Whitehouse   decnet: Use data ...
594
595
  	if (!sock_flag(sk, SOCK_DEAD))
  		sk->sk_data_ready(sk, skb_len);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
596
  out:
429eb0fae   YOSHIFUJI Hideaki   [NET] DECNET: Fix...
597
  	return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
598
599
600
601
602
603
604
605
606
607
608
  }
  
  static void dn_nsp_otherdata(struct sock *sk, struct sk_buff *skb)
  {
  	struct dn_scp *scp = DN_SK(sk);
  	unsigned short segnum;
  	struct dn_skb_cb *cb = DN_SKB_CB(skb);
  	int queued = 0;
  
  	if (skb->len < 2)
  		goto out;
c4106aa88   Harvey Harrison   decnet: remove pr...
609
  	cb->segnum = segnum = le16_to_cpu(*(__le16 *)skb->data);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
  	skb_pull(skb, 2);
  
  	if (seq_next(scp->numoth_rcv, segnum)) {
  
  		if (dn_queue_skb(sk, skb, SIGURG, &scp->other_receive_queue) == 0) {
  			seq_add(&scp->numoth_rcv, 1);
  			scp->other_report = 0;
  			queued = 1;
  		}
  	}
  
  	dn_nsp_send_oth_ack(sk);
  out:
  	if (!queued)
  		kfree_skb(skb);
  }
  
  static void dn_nsp_data(struct sock *sk, struct sk_buff *skb)
  {
  	int queued = 0;
  	unsigned short segnum;
  	struct dn_skb_cb *cb = DN_SKB_CB(skb);
  	struct dn_scp *scp = DN_SK(sk);
  
  	if (skb->len < 2)
  		goto out;
c4106aa88   Harvey Harrison   decnet: remove pr...
636
  	cb->segnum = segnum = le16_to_cpu(*(__le16 *)skb->data);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
637
638
639
  	skb_pull(skb, 2);
  
  	if (seq_next(scp->numdat_rcv, segnum)) {
429eb0fae   YOSHIFUJI Hideaki   [NET] DECNET: Fix...
640
  		if (dn_queue_skb(sk, skb, SIGIO, &sk->sk_receive_queue) == 0) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
641
  			seq_add(&scp->numdat_rcv, 1);
429eb0fae   YOSHIFUJI Hideaki   [NET] DECNET: Fix...
642
643
  			queued = 1;
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
644
645
646
647
648
  
  		if ((scp->flowloc_sw == DN_SEND) && dn_congested(sk)) {
  			scp->flowloc_sw = DN_DONTSEND;
  			dn_nsp_send_link(sk, DN_DONTSEND, 0);
  		}
429eb0fae   YOSHIFUJI Hideaki   [NET] DECNET: Fix...
649
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
  
  	dn_nsp_send_data_ack(sk);
  out:
  	if (!queued)
  		kfree_skb(skb);
  }
  
  /*
   * If one of our conninit messages is returned, this function
   * deals with it. It puts the socket into the NO_COMMUNICATION
   * state.
   */
  static void dn_returned_conn_init(struct sock *sk, struct sk_buff *skb)
  {
  	struct dn_scp *scp = DN_SK(sk);
  
  	if (scp->state == DN_CI) {
  		scp->state = DN_NC;
  		sk->sk_state = TCP_CLOSE;
  		if (!sock_flag(sk, SOCK_DEAD))
  			sk->sk_state_change(sk);
  	}
  
  	kfree_skb(skb);
  }
  
  static int dn_nsp_no_socket(struct sk_buff *skb, unsigned short reason)
  {
  	struct dn_skb_cb *cb = DN_SKB_CB(skb);
  	int ret = NET_RX_DROP;
  
  	/* Must not reply to returned packets */
  	if (cb->rt_flags & DN_RT_F_RTS)
  		goto out;
  
  	if ((reason != NSP_REASON_OK) && ((cb->nsp_flags & 0x0c) == 0x08)) {
06f8fe11b   Joe Perches   decnet: Reduce sw...
686
687
688
689
690
691
692
693
694
695
  		switch (cb->nsp_flags & 0x70) {
  		case 0x10:
  		case 0x60: /* (Retransmitted) Connect Init */
  			dn_nsp_return_disc(skb, NSP_DISCINIT, reason);
  			ret = NET_RX_SUCCESS;
  			break;
  		case 0x20: /* Connect Confirm */
  			dn_nsp_return_disc(skb, NSP_DISCCONF, reason);
  			ret = NET_RX_SUCCESS;
  			break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
  		}
  	}
  
  out:
  	kfree_skb(skb);
  	return ret;
  }
  
  static int dn_nsp_rx_packet(struct sk_buff *skb)
  {
  	struct dn_skb_cb *cb = DN_SKB_CB(skb);
  	struct sock *sk = NULL;
  	unsigned char *ptr = (unsigned char *)skb->data;
  	unsigned short reason = NSP_REASON_NL;
  
  	if (!pskb_may_pull(skb, 2))
  		goto free_out;
badff6d01   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
713
  	skb_reset_transport_header(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
714
715
716
717
718
  	cb->nsp_flags = *ptr++;
  
  	if (decnet_debug_level & 2)
  		printk(KERN_DEBUG "dn_nsp_rx: Message type 0x%02x
  ", (int)cb->nsp_flags);
429eb0fae   YOSHIFUJI Hideaki   [NET] DECNET: Fix...
719
  	if (cb->nsp_flags & 0x83)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
720
721
722
723
724
725
  		goto free_out;
  
  	/*
  	 * Filter out conninits and useless packet types
  	 */
  	if ((cb->nsp_flags & 0x0c) == 0x08) {
06f8fe11b   Joe Perches   decnet: Reduce sw...
726
727
728
729
730
731
732
733
  		switch (cb->nsp_flags & 0x70) {
  		case 0x00: /* NOP */
  		case 0x70: /* Reserved */
  		case 0x50: /* Reserved, Phase II node init */
  			goto free_out;
  		case 0x10:
  		case 0x60:
  			if (unlikely(cb->rt_flags & DN_RT_F_RTS))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
734
  				goto free_out;
06f8fe11b   Joe Perches   decnet: Reduce sw...
735
736
  			sk = dn_find_listener(skb, &reason);
  			goto got_it;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
737
738
739
740
741
742
743
744
745
  		}
  	}
  
  	if (!pskb_may_pull(skb, 3))
  		goto free_out;
  
  	/*
  	 * Grab the destination address.
  	 */
c4ea94ab3   Steven Whitehouse   [DECnet]: Endian ...
746
  	cb->dst_port = *(__le16 *)ptr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
747
748
749
750
751
752
753
  	cb->src_port = 0;
  	ptr += 2;
  
  	/*
  	 * If not a connack, grab the source address too.
  	 */
  	if (pskb_may_pull(skb, 5)) {
c4ea94ab3   Steven Whitehouse   [DECnet]: Endian ...
754
  		cb->src_port = *(__le16 *)ptr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
755
756
757
758
759
760
761
762
763
  		ptr += 2;
  		skb_pull(skb, 5);
  	}
  
  	/*
  	 * Returned packets...
  	 * Swap src & dst and look up in the normal way.
  	 */
  	if (unlikely(cb->rt_flags & DN_RT_F_RTS)) {
c4ea94ab3   Steven Whitehouse   [DECnet]: Endian ...
764
  		__le16 tmp = cb->dst_port;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
765
766
767
768
769
770
771
772
773
774
775
776
777
778
  		cb->dst_port = cb->src_port;
  		cb->src_port = tmp;
  		tmp = cb->dst;
  		cb->dst = cb->src;
  		cb->src = tmp;
  	}
  
  	/*
  	 * Find the socket to which this skb is destined.
  	 */
  	sk = dn_find_by_skb(skb);
  got_it:
  	if (sk != NULL) {
  		struct dn_scp *scp = DN_SK(sk);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
779
780
781
782
783
784
785
786
  
  		/* Reset backoff */
  		scp->nsp_rxtshift = 0;
  
  		/*
  		 * We linearize everything except data segments here.
  		 */
  		if (cb->nsp_flags & ~0x60) {
364c6badd   Herbert Xu   [NET]: Clean up s...
787
  			if (unlikely(skb_linearize(skb)))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
788
789
  				goto free_out;
  		}
58a5a7b95   Arnaldo Carvalho de Melo   [NET]: Conditiona...
790
  		return sk_receive_skb(sk, skb, 0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
791
792
793
794
795
796
797
798
799
800
801
  	}
  
  	return dn_nsp_no_socket(skb, reason);
  
  free_out:
  	kfree_skb(skb);
  	return NET_RX_DROP;
  }
  
  int dn_nsp_rx(struct sk_buff *skb)
  {
5d877d876   Jan Engelhardt   netfilter: decnet...
802
803
  	return NF_HOOK(NFPROTO_DECNET, NF_DN_LOCAL_IN, skb, skb->dev, NULL,
  		       dn_nsp_rx_packet);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
  }
  
  /*
   * This is the main receive routine for sockets. It is called
   * from the above when the socket is not busy, and also from
   * sock_release() when there is a backlog queued up.
   */
  int dn_nsp_backlog_rcv(struct sock *sk, struct sk_buff *skb)
  {
  	struct dn_scp *scp = DN_SK(sk);
  	struct dn_skb_cb *cb = DN_SKB_CB(skb);
  
  	if (cb->rt_flags & DN_RT_F_RTS) {
  		if (cb->nsp_flags == 0x18 || cb->nsp_flags == 0x68)
  			dn_returned_conn_init(sk, skb);
  		else
  			kfree_skb(skb);
  		return NET_RX_SUCCESS;
  	}
  
  	/*
  	 * Control packet.
  	 */
  	if ((cb->nsp_flags & 0x0c) == 0x08) {
06f8fe11b   Joe Perches   decnet: Reduce sw...
828
829
830
831
832
833
834
835
836
837
838
839
840
841
  		switch (cb->nsp_flags & 0x70) {
  		case 0x10:
  		case 0x60:
  			dn_nsp_conn_init(sk, skb);
  			break;
  		case 0x20:
  			dn_nsp_conn_conf(sk, skb);
  			break;
  		case 0x30:
  			dn_nsp_disc_init(sk, skb);
  			break;
  		case 0x40:
  			dn_nsp_disc_conf(sk, skb);
  			break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
  		}
  
  	} else if (cb->nsp_flags == 0x24) {
  		/*
  		 * Special for connacks, 'cos they don't have
  		 * ack data or ack otherdata info.
  		 */
  		dn_nsp_conn_ack(sk, skb);
  	} else {
  		int other = 1;
  
  		/* both data and ack frames can kick a CC socket into RUN */
  		if ((scp->state == DN_CC) && !sock_flag(sk, SOCK_DEAD)) {
  			scp->state = DN_RUN;
  			sk->sk_state = TCP_ESTABLISHED;
  			sk->sk_state_change(sk);
  		}
  
  		if ((cb->nsp_flags & 0x1c) == 0)
  			other = 0;
  		if (cb->nsp_flags == 0x04)
  			other = 0;
  
  		/*
  		 * Read out ack data here, this applies equally
  		 * to data, other data, link serivce and both
  		 * ack data and ack otherdata.
  		 */
  		dn_process_ack(sk, skb, other);
  
  		/*
  		 * If we've some sort of data here then call a
  		 * suitable routine for dealing with it, otherwise
  		 * the packet is an ack and can be discarded.
  		 */
  		if ((cb->nsp_flags & 0x0c) == 0) {
  
  			if (scp->state != DN_RUN)
  				goto free_out;
06f8fe11b   Joe Perches   decnet: Reduce sw...
881
882
883
884
885
886
887
888
889
  			switch (cb->nsp_flags) {
  			case 0x10: /* LS */
  				dn_nsp_linkservice(sk, skb);
  				break;
  			case 0x30: /* OD */
  				dn_nsp_otherdata(sk, skb);
  				break;
  			default:
  				dn_nsp_data(sk, skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
890
891
892
893
894
895
896
897
898
899
  			}
  
  		} else { /* Ack, chuck it out here */
  free_out:
  			kfree_skb(skb);
  		}
  	}
  
  	return NET_RX_SUCCESS;
  }