Blame view

net/netrom/nr_route.c 21.8 KB
2874c5fd2   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-or-later
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2
  /*
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
3
4
5
6
7
8
9
10
11
12
   *
   * Copyright Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk)
   * Copyright Alan Cox GW4PTS (alan@lxorguk.ukuu.org.uk)
   * Copyright Tomi Manninen OH2BNS (oh2bns@sral.fi)
   */
  #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
13
14
15
16
  #include <linux/timer.h>
  #include <linux/string.h>
  #include <linux/sockios.h>
  #include <linux/net.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
17
  #include <linux/slab.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
18
19
20
21
22
23
24
  #include <net/ax25.h>
  #include <linux/inet.h>
  #include <linux/netdevice.h>
  #include <net/arp.h>
  #include <linux/if_arp.h>
  #include <linux/skbuff.h>
  #include <net/sock.h>
dc8e54165   Fabian Frederick   netrom: use linux...
25
  #include <linux/uaccess.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
26
27
28
29
30
  #include <linux/fcntl.h>
  #include <linux/termios.h>	/* For TIOCINQ/OUTQ */
  #include <linux/mm.h>
  #include <linux/interrupt.h>
  #include <linux/notifier.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
31
32
33
34
  #include <linux/init.h>
  #include <linux/spinlock.h>
  #include <net/netrom.h>
  #include <linux/seq_file.h>
bc3b2d7fb   Paul Gortmaker   net: Add export.h...
35
  #include <linux/export.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
36
37
38
39
40
41
42
43
44
45
46
47
  
  static unsigned int nr_neigh_no = 1;
  
  static HLIST_HEAD(nr_node_list);
  static DEFINE_SPINLOCK(nr_node_list_lock);
  static HLIST_HEAD(nr_neigh_list);
  static DEFINE_SPINLOCK(nr_neigh_list_lock);
  
  static struct nr_node *nr_node_get(ax25_address *callsign)
  {
  	struct nr_node *found = NULL;
  	struct nr_node *nr_node;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
48
49
  
  	spin_lock_bh(&nr_node_list_lock);
b67bfe0d4   Sasha Levin   hlist: drop the n...
50
  	nr_node_for_each(nr_node, &nr_node_list)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
51
52
53
54
55
56
57
58
59
60
61
62
63
64
  		if (ax25cmp(callsign, &nr_node->callsign) == 0) {
  			nr_node_hold(nr_node);
  			found = nr_node;
  			break;
  		}
  	spin_unlock_bh(&nr_node_list_lock);
  	return found;
  }
  
  static struct nr_neigh *nr_neigh_get_dev(ax25_address *callsign,
  					 struct net_device *dev)
  {
  	struct nr_neigh *found = NULL;
  	struct nr_neigh *nr_neigh;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
65
66
  
  	spin_lock_bh(&nr_neigh_list_lock);
b67bfe0d4   Sasha Levin   hlist: drop the n...
67
  	nr_neigh_for_each(nr_neigh, &nr_neigh_list)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
68
69
70
71
72
73
74
75
76
77
78
  		if (ax25cmp(callsign, &nr_neigh->callsign) == 0 &&
  		    nr_neigh->dev == dev) {
  			nr_neigh_hold(nr_neigh);
  			found = nr_neigh;
  			break;
  		}
  	spin_unlock_bh(&nr_neigh_list_lock);
  	return found;
  }
  
  static void nr_remove_neigh(struct nr_neigh *);
4c3160692   Gustavo A. R. Silva   net: netrom: nr_r...
79
80
81
82
83
84
85
86
87
88
89
90
  /*      re-sort the routes in quality order.    */
  static void re_sort_routes(struct nr_node *nr_node, int x, int y)
  {
  	if (nr_node->routes[y].quality > nr_node->routes[x].quality) {
  		if (nr_node->which == x)
  			nr_node->which = y;
  		else if (nr_node->which == y)
  			nr_node->which = x;
  
  		swap(nr_node->routes[x], nr_node->routes[y]);
  	}
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
91
92
93
94
  /*
   *	Add a new route to a node, and in the process add the node and the
   *	neighbour if it is new.
   */
c9266b99e   Ralf Baechle   [AX.25]: Mark all...
95
96
97
  static int __must_check nr_add_node(ax25_address *nr, const char *mnemonic,
  	ax25_address *ax25, ax25_digi *ax25_digi, struct net_device *dev,
  	int quality, int obs_count)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
98
99
100
  {
  	struct nr_node  *nr_node;
  	struct nr_neigh *nr_neigh;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
  	int i, found;
  	struct net_device *odev;
  
  	if ((odev=nr_dev_get(nr)) != NULL) {	/* Can't add routes to ourself */
  		dev_put(odev);
  		return -EINVAL;
  	}
  
  	nr_node = nr_node_get(nr);
  
  	nr_neigh = nr_neigh_get_dev(ax25, dev);
  
  	/*
  	 * The L2 link to a neighbour has failed in the past
  	 * and now a frame comes from this neighbour. We assume
  	 * it was a temporary trouble with the link and reset the
  	 * routes now (and not wait for a node broadcast).
  	 */
  	if (nr_neigh != NULL && nr_neigh->failed != 0 && quality == 0) {
  		struct nr_node *nr_nodet;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
121
122
  
  		spin_lock_bh(&nr_node_list_lock);
b67bfe0d4   Sasha Levin   hlist: drop the n...
123
  		nr_node_for_each(nr_nodet, &nr_node_list) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
  			nr_node_lock(nr_nodet);
  			for (i = 0; i < nr_nodet->count; i++)
  				if (nr_nodet->routes[i].neighbour == nr_neigh)
  					if (i < nr_nodet->which)
  						nr_nodet->which = i;
  			nr_node_unlock(nr_nodet);
  		}
  		spin_unlock_bh(&nr_node_list_lock);
  	}
  
  	if (nr_neigh != NULL)
  		nr_neigh->failed = 0;
  
  	if (quality == 0 && nr_neigh != NULL && nr_node != NULL) {
  		nr_neigh_put(nr_neigh);
  		nr_node_put(nr_node);
  		return 0;
  	}
  
  	if (nr_neigh == NULL) {
  		if ((nr_neigh = kmalloc(sizeof(*nr_neigh), GFP_ATOMIC)) == NULL) {
  			if (nr_node)
  				nr_node_put(nr_node);
  			return -ENOMEM;
  		}
  
  		nr_neigh->callsign = *ax25;
  		nr_neigh->digipeat = NULL;
  		nr_neigh->ax25     = NULL;
  		nr_neigh->dev      = dev;
  		nr_neigh->quality  = sysctl_netrom_default_path_quality;
  		nr_neigh->locked   = 0;
  		nr_neigh->count    = 0;
  		nr_neigh->number   = nr_neigh_no++;
  		nr_neigh->failed   = 0;
af4207494   Reshetova, Elena   net, netrom: conv...
159
  		refcount_set(&nr_neigh->refcount, 1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
160
161
  
  		if (ax25_digi != NULL && ax25_digi->ndigi > 0) {
eafff86d3   Arnaldo Carvalho de Melo   [NETROM]: Use kme...
162
163
164
165
  			nr_neigh->digipeat = kmemdup(ax25_digi,
  						     sizeof(*ax25_digi),
  						     GFP_KERNEL);
  			if (nr_neigh->digipeat == NULL) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
166
167
168
169
170
  				kfree(nr_neigh);
  				if (nr_node)
  					nr_node_put(nr_node);
  				return -ENOMEM;
  			}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
  		}
  
  		spin_lock_bh(&nr_neigh_list_lock);
  		hlist_add_head(&nr_neigh->neigh_node, &nr_neigh_list);
  		nr_neigh_hold(nr_neigh);
  		spin_unlock_bh(&nr_neigh_list_lock);
  	}
  
  	if (quality != 0 && ax25cmp(nr, ax25) == 0 && !nr_neigh->locked)
  		nr_neigh->quality = quality;
  
  	if (nr_node == NULL) {
  		if ((nr_node = kmalloc(sizeof(*nr_node), GFP_ATOMIC)) == NULL) {
  			if (nr_neigh)
  				nr_neigh_put(nr_neigh);
  			return -ENOMEM;
  		}
  
  		nr_node->callsign = *nr;
  		strcpy(nr_node->mnemonic, mnemonic);
  
  		nr_node->which = 0;
  		nr_node->count = 1;
156be7edc   Reshetova, Elena   net, netrom: conv...
194
  		refcount_set(&nr_node->refcount, 1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
195
196
197
198
199
200
201
202
203
204
205
206
207
  		spin_lock_init(&nr_node->node_lock);
  
  		nr_node->routes[0].quality   = quality;
  		nr_node->routes[0].obs_count = obs_count;
  		nr_node->routes[0].neighbour = nr_neigh;
  
  		nr_neigh_hold(nr_neigh);
  		nr_neigh->count++;
  
  		spin_lock_bh(&nr_node_list_lock);
  		hlist_add_head(&nr_node->node_node, &nr_node_list);
  		/* refcount initialized at 1 */
  		spin_unlock_bh(&nr_node_list_lock);
d03f22847   Xiyu Yang   net: netrom: Fix ...
208
  		nr_neigh_put(nr_neigh);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
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
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
  		return 0;
  	}
  	nr_node_lock(nr_node);
  
  	if (quality != 0)
  		strcpy(nr_node->mnemonic, mnemonic);
  
  	for (found = 0, i = 0; i < nr_node->count; i++) {
  		if (nr_node->routes[i].neighbour == nr_neigh) {
  			nr_node->routes[i].quality   = quality;
  			nr_node->routes[i].obs_count = obs_count;
  			found = 1;
  			break;
  		}
  	}
  
  	if (!found) {
  		/* We have space at the bottom, slot it in */
  		if (nr_node->count < 3) {
  			nr_node->routes[2] = nr_node->routes[1];
  			nr_node->routes[1] = nr_node->routes[0];
  
  			nr_node->routes[0].quality   = quality;
  			nr_node->routes[0].obs_count = obs_count;
  			nr_node->routes[0].neighbour = nr_neigh;
  
  			nr_node->which++;
  			nr_node->count++;
  			nr_neigh_hold(nr_neigh);
  			nr_neigh->count++;
  		} else {
  			/* It must be better than the worst */
  			if (quality > nr_node->routes[2].quality) {
  				nr_node->routes[2].neighbour->count--;
  				nr_neigh_put(nr_node->routes[2].neighbour);
  
  				if (nr_node->routes[2].neighbour->count == 0 && !nr_node->routes[2].neighbour->locked)
  					nr_remove_neigh(nr_node->routes[2].neighbour);
  
  				nr_node->routes[2].quality   = quality;
  				nr_node->routes[2].obs_count = obs_count;
  				nr_node->routes[2].neighbour = nr_neigh;
  
  				nr_neigh_hold(nr_neigh);
  				nr_neigh->count++;
  			}
  		}
  	}
  
  	/* Now re-sort the routes in quality order */
  	switch (nr_node->count) {
  	case 3:
4c3160692   Gustavo A. R. Silva   net: netrom: nr_r...
261
262
  		re_sort_routes(nr_node, 0, 1);
  		re_sort_routes(nr_node, 1, 2);
df561f668   Gustavo A. R. Silva   treewide: Use fal...
263
  		fallthrough;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
264
  	case 2:
4c3160692   Gustavo A. R. Silva   net: netrom: nr_r...
265
  		re_sort_routes(nr_node, 0, 1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
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
351
352
353
354
355
  	case 1:
  		break;
  	}
  
  	for (i = 0; i < nr_node->count; i++) {
  		if (nr_node->routes[i].neighbour == nr_neigh) {
  			if (i < nr_node->which)
  				nr_node->which = i;
  			break;
  		}
  	}
  
  	nr_neigh_put(nr_neigh);
  	nr_node_unlock(nr_node);
  	nr_node_put(nr_node);
  	return 0;
  }
  
  static inline void __nr_remove_node(struct nr_node *nr_node)
  {
  	hlist_del_init(&nr_node->node_node);
  	nr_node_put(nr_node);
  }
  
  #define nr_remove_node_locked(__node) \
  	__nr_remove_node(__node)
  
  static void nr_remove_node(struct nr_node *nr_node)
  {
  	spin_lock_bh(&nr_node_list_lock);
  	__nr_remove_node(nr_node);
  	spin_unlock_bh(&nr_node_list_lock);
  }
  
  static inline void __nr_remove_neigh(struct nr_neigh *nr_neigh)
  {
  	hlist_del_init(&nr_neigh->neigh_node);
  	nr_neigh_put(nr_neigh);
  }
  
  #define nr_remove_neigh_locked(__neigh) \
  	__nr_remove_neigh(__neigh)
  
  static void nr_remove_neigh(struct nr_neigh *nr_neigh)
  {
  	spin_lock_bh(&nr_neigh_list_lock);
  	__nr_remove_neigh(nr_neigh);
  	spin_unlock_bh(&nr_neigh_list_lock);
  }
  
  /*
   *	"Delete" a node. Strictly speaking remove a route to a node. The node
   *	is only deleted if no routes are left to it.
   */
  static int nr_del_node(ax25_address *callsign, ax25_address *neighbour, struct net_device *dev)
  {
  	struct nr_node  *nr_node;
  	struct nr_neigh *nr_neigh;
  	int i;
  
  	nr_node = nr_node_get(callsign);
  
  	if (nr_node == NULL)
  		return -EINVAL;
  
  	nr_neigh = nr_neigh_get_dev(neighbour, dev);
  
  	if (nr_neigh == NULL) {
  		nr_node_put(nr_node);
  		return -EINVAL;
  	}
  
  	nr_node_lock(nr_node);
  	for (i = 0; i < nr_node->count; i++) {
  		if (nr_node->routes[i].neighbour == nr_neigh) {
  			nr_neigh->count--;
  			nr_neigh_put(nr_neigh);
  
  			if (nr_neigh->count == 0 && !nr_neigh->locked)
  				nr_remove_neigh(nr_neigh);
  			nr_neigh_put(nr_neigh);
  
  			nr_node->count--;
  
  			if (nr_node->count == 0) {
  				nr_remove_node(nr_node);
  			} else {
  				switch (i) {
  				case 0:
  					nr_node->routes[0] = nr_node->routes[1];
df561f668   Gustavo A. R. Silva   treewide: Use fal...
356
  					fallthrough;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
  				case 1:
  					nr_node->routes[1] = nr_node->routes[2];
  				case 2:
  					break;
  				}
  				nr_node_put(nr_node);
  			}
  			nr_node_unlock(nr_node);
  
  			return 0;
  		}
  	}
  	nr_neigh_put(nr_neigh);
  	nr_node_unlock(nr_node);
  	nr_node_put(nr_node);
  
  	return -EINVAL;
  }
  
  /*
   *	Lock a neighbour with a quality.
   */
c9266b99e   Ralf Baechle   [AX.25]: Mark all...
379
380
  static int __must_check nr_add_neigh(ax25_address *callsign,
  	ax25_digi *ax25_digi, struct net_device *dev, unsigned int quality)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
  {
  	struct nr_neigh *nr_neigh;
  
  	nr_neigh = nr_neigh_get_dev(callsign, dev);
  	if (nr_neigh) {
  		nr_neigh->quality = quality;
  		nr_neigh->locked  = 1;
  		nr_neigh_put(nr_neigh);
  		return 0;
  	}
  
  	if ((nr_neigh = kmalloc(sizeof(*nr_neigh), GFP_ATOMIC)) == NULL)
  		return -ENOMEM;
  
  	nr_neigh->callsign = *callsign;
  	nr_neigh->digipeat = NULL;
  	nr_neigh->ax25     = NULL;
  	nr_neigh->dev      = dev;
  	nr_neigh->quality  = quality;
  	nr_neigh->locked   = 1;
  	nr_neigh->count    = 0;
  	nr_neigh->number   = nr_neigh_no++;
  	nr_neigh->failed   = 0;
af4207494   Reshetova, Elena   net, netrom: conv...
404
  	refcount_set(&nr_neigh->refcount, 1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
405
406
  
  	if (ax25_digi != NULL && ax25_digi->ndigi > 0) {
eafff86d3   Arnaldo Carvalho de Melo   [NETROM]: Use kme...
407
408
409
  		nr_neigh->digipeat = kmemdup(ax25_digi, sizeof(*ax25_digi),
  					     GFP_KERNEL);
  		if (nr_neigh->digipeat == NULL) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
410
411
412
  			kfree(nr_neigh);
  			return -ENOMEM;
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
  	}
  
  	spin_lock_bh(&nr_neigh_list_lock);
  	hlist_add_head(&nr_neigh->neigh_node, &nr_neigh_list);
  	/* refcount is initialized at 1 */
  	spin_unlock_bh(&nr_neigh_list_lock);
  
  	return 0;
  }
  
  /*
   *	"Delete" a neighbour. The neighbour is only removed if the number
   *	of nodes that may use it is zero.
   */
  static int nr_del_neigh(ax25_address *callsign, struct net_device *dev, unsigned int quality)
  {
  	struct nr_neigh *nr_neigh;
  
  	nr_neigh = nr_neigh_get_dev(callsign, dev);
  
  	if (nr_neigh == NULL) return -EINVAL;
  
  	nr_neigh->quality = quality;
  	nr_neigh->locked  = 0;
  
  	if (nr_neigh->count == 0)
  		nr_remove_neigh(nr_neigh);
  	nr_neigh_put(nr_neigh);
  
  	return 0;
  }
  
  /*
   *	Decrement the obsolescence count by one. If a route is reduced to a
   *	count of zero, remove it. Also remove any unlocked neighbours with
   *	zero nodes routing via it.
   */
  static int nr_dec_obs(void)
  {
  	struct nr_neigh *nr_neigh;
  	struct nr_node  *s;
b67bfe0d4   Sasha Levin   hlist: drop the n...
454
  	struct hlist_node *nodet;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
455
456
457
  	int i;
  
  	spin_lock_bh(&nr_node_list_lock);
b67bfe0d4   Sasha Levin   hlist: drop the n...
458
  	nr_node_for_each_safe(s, nodet, &nr_node_list) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
  		nr_node_lock(s);
  		for (i = 0; i < s->count; i++) {
  			switch (s->routes[i].obs_count) {
  			case 0:		/* A locked entry */
  				break;
  
  			case 1:		/* From 1 -> 0 */
  				nr_neigh = s->routes[i].neighbour;
  
  				nr_neigh->count--;
  				nr_neigh_put(nr_neigh);
  
  				if (nr_neigh->count == 0 && !nr_neigh->locked)
  					nr_remove_neigh(nr_neigh);
  
  				s->count--;
  
  				switch (i) {
c48553890   Joe Perches   netrom: Reduce sw...
477
478
  				case 0:
  					s->routes[0] = s->routes[1];
df561f668   Gustavo A. R. Silva   treewide: Use fal...
479
  					fallthrough;
c48553890   Joe Perches   netrom: Reduce sw...
480
481
482
483
  				case 1:
  					s->routes[1] = s->routes[2];
  				case 2:
  					break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
  				}
  				break;
  
  			default:
  				s->routes[i].obs_count--;
  				break;
  
  			}
  		}
  
  		if (s->count <= 0)
  			nr_remove_node_locked(s);
  		nr_node_unlock(s);
  	}
  	spin_unlock_bh(&nr_node_list_lock);
  
  	return 0;
  }
  
  /*
   *	A device has been removed. Remove its routes and neighbours.
   */
  void nr_rt_device_down(struct net_device *dev)
  {
  	struct nr_neigh *s;
b67bfe0d4   Sasha Levin   hlist: drop the n...
509
  	struct hlist_node *nodet, *node2t;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
510
511
512
513
  	struct nr_node  *t;
  	int i;
  
  	spin_lock_bh(&nr_neigh_list_lock);
b67bfe0d4   Sasha Levin   hlist: drop the n...
514
  	nr_neigh_for_each_safe(s, nodet, &nr_neigh_list) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
515
516
  		if (s->dev == dev) {
  			spin_lock_bh(&nr_node_list_lock);
b67bfe0d4   Sasha Levin   hlist: drop the n...
517
  			nr_node_for_each_safe(t, node2t, &nr_node_list) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
518
519
520
521
522
523
524
525
  				nr_node_lock(t);
  				for (i = 0; i < t->count; i++) {
  					if (t->routes[i].neighbour == s) {
  						t->count--;
  
  						switch (i) {
  						case 0:
  							t->routes[0] = t->routes[1];
df561f668   Gustavo A. R. Silva   treewide: Use fal...
526
  							fallthrough;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
  						case 1:
  							t->routes[1] = t->routes[2];
  						case 2:
  							break;
  						}
  					}
  				}
  
  				if (t->count <= 0)
  					nr_remove_node_locked(t);
  				nr_node_unlock(t);
  			}
  			spin_unlock_bh(&nr_node_list_lock);
  
  			nr_remove_neigh_locked(s);
  		}
  	}
  	spin_unlock_bh(&nr_neigh_list_lock);
  }
  
  /*
   *	Check that the device given is a valid AX.25 interface that is "up".
   *	Or a valid ethernet interface with an AX.25 callsign binding.
   */
  static struct net_device *nr_ax25_dev_get(char *devname)
  {
  	struct net_device *dev;
881d966b4   Eric W. Biederman   [NET]: Make the d...
554
  	if ((dev = dev_get_by_name(&init_net, devname)) == NULL)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
  		return NULL;
  
  	if ((dev->flags & IFF_UP) && dev->type == ARPHRD_AX25)
  		return dev;
  
  	dev_put(dev);
  	return NULL;
  }
  
  /*
   *	Find the first active NET/ROM device, usually "nr0".
   */
  struct net_device *nr_dev_first(void)
  {
  	struct net_device *dev, *first = NULL;
c6d14c845   Eric Dumazet   net: Introduce fo...
570
571
  	rcu_read_lock();
  	for_each_netdev_rcu(&init_net, dev) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
572
573
574
575
576
577
  		if ((dev->flags & IFF_UP) && dev->type == ARPHRD_NETROM)
  			if (first == NULL || strncmp(dev->name, first->name, 3) < 0)
  				first = dev;
  	}
  	if (first)
  		dev_hold(first);
c6d14c845   Eric Dumazet   net: Introduce fo...
578
  	rcu_read_unlock();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
579
580
581
582
583
584
585
586
587
588
  
  	return first;
  }
  
  /*
   *	Find the NET/ROM device for the given callsign.
   */
  struct net_device *nr_dev_get(ax25_address *addr)
  {
  	struct net_device *dev;
c6d14c845   Eric Dumazet   net: Introduce fo...
589
590
591
592
  	rcu_read_lock();
  	for_each_netdev_rcu(&init_net, dev) {
  		if ((dev->flags & IFF_UP) && dev->type == ARPHRD_NETROM &&
  		    ax25cmp(addr, (ax25_address *)dev->dev_addr) == 0) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
593
594
595
596
  			dev_hold(dev);
  			goto out;
  		}
  	}
7562f876c   Pavel Emelianov   [NET]: Rework dev...
597
  	dev = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
598
  out:
c6d14c845   Eric Dumazet   net: Introduce fo...
599
  	rcu_read_unlock();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
600
601
  	return dev;
  }
c6ba973b8   Ralf Baechle   NETROM: Fix use o...
602
603
  static ax25_digi *nr_call_to_digi(ax25_digi *digi, int ndigis,
  	ax25_address *digipeaters)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
604
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
605
606
607
608
609
610
  	int i;
  
  	if (ndigis == 0)
  		return NULL;
  
  	for (i = 0; i < ndigis; i++) {
c6ba973b8   Ralf Baechle   NETROM: Fix use o...
611
612
  		digi->calls[i]    = digipeaters[i];
  		digi->repeated[i] = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
613
  	}
c6ba973b8   Ralf Baechle   NETROM: Fix use o...
614
615
  	digi->ndigi      = ndigis;
  	digi->lastrepeat = -1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
616

c6ba973b8   Ralf Baechle   NETROM: Fix use o...
617
  	return digi;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
618
619
620
621
622
623
624
625
626
  }
  
  /*
   *	Handle the ioctls that control the routing functions.
   */
  int nr_rt_ioctl(unsigned int cmd, void __user *arg)
  {
  	struct nr_route_struct nr_route;
  	struct net_device *dev;
c6ba973b8   Ralf Baechle   NETROM: Fix use o...
627
  	ax25_digi digi;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
628
629
630
631
632
633
  	int ret;
  
  	switch (cmd) {
  	case SIOCADDRT:
  		if (copy_from_user(&nr_route, arg, sizeof(struct nr_route_struct)))
  			return -EFAULT;
10cae1c8d   Ralf Baechle   NET: NETROM: Clea...
634
  		if (nr_route.ndigis > AX25_MAX_DIGIS)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
635
  			return -EINVAL;
10cae1c8d   Ralf Baechle   NET: NETROM: Clea...
636
  		if ((dev = nr_ax25_dev_get(nr_route.device)) == NULL)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
637
  			return -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
638
639
  		switch (nr_route.type) {
  		case NETROM_NODE:
ac1a1de31   Ralf Baechle   NET: NETROM: When...
640
641
642
643
  			if (strnlen(nr_route.mnemonic, 7) == 7) {
  				ret = -EINVAL;
  				break;
  			}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
644
645
646
  			ret = nr_add_node(&nr_route.callsign,
  				nr_route.mnemonic,
  				&nr_route.neighbour,
c6ba973b8   Ralf Baechle   NETROM: Fix use o...
647
648
  				nr_call_to_digi(&digi, nr_route.ndigis,
  						nr_route.digipeaters),
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
649
650
651
652
653
  				dev, nr_route.quality,
  				nr_route.obs_count);
  			break;
  		case NETROM_NEIGH:
  			ret = nr_add_neigh(&nr_route.callsign,
c6ba973b8   Ralf Baechle   NETROM: Fix use o...
654
655
  				nr_call_to_digi(&digi, nr_route.ndigis,
  						nr_route.digipeaters),
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
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
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
  				dev, nr_route.quality);
  			break;
  		default:
  			ret = -EINVAL;
  		}
  		dev_put(dev);
  		return ret;
  
  	case SIOCDELRT:
  		if (copy_from_user(&nr_route, arg, sizeof(struct nr_route_struct)))
  			return -EFAULT;
  		if ((dev = nr_ax25_dev_get(nr_route.device)) == NULL)
  			return -EINVAL;
  		switch (nr_route.type) {
  		case NETROM_NODE:
  			ret = nr_del_node(&nr_route.callsign,
  				&nr_route.neighbour, dev);
  			break;
  		case NETROM_NEIGH:
  			ret = nr_del_neigh(&nr_route.callsign,
  				dev, nr_route.quality);
  			break;
  		default:
  			ret = -EINVAL;
  		}
  		dev_put(dev);
  		return ret;
  
  	case SIOCNRDECOBS:
  		return nr_dec_obs();
  
  	default:
  		return -EINVAL;
  	}
  
  	return 0;
  }
  
  /*
   * 	A level 2 link has timed out, therefore it appears to be a poor link,
   *	then don't use that neighbour until it is reset.
   */
  void nr_link_failed(ax25_cb *ax25, int reason)
  {
  	struct nr_neigh *s, *nr_neigh = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
701
702
703
  	struct nr_node  *nr_node = NULL;
  
  	spin_lock_bh(&nr_neigh_list_lock);
b67bfe0d4   Sasha Levin   hlist: drop the n...
704
  	nr_neigh_for_each(s, &nr_neigh_list) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
705
706
707
708
709
  		if (s->ax25 == ax25) {
  			nr_neigh_hold(s);
  			nr_neigh = s;
  			break;
  		}
52383678a   Ralf Baechle   [NETROM]: Fix pos...
710
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
711
  	spin_unlock_bh(&nr_neigh_list_lock);
52383678a   Ralf Baechle   [NETROM]: Fix pos...
712
713
  	if (nr_neigh == NULL)
  		return;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
714
715
716
717
718
719
720
721
722
  
  	nr_neigh->ax25 = NULL;
  	ax25_cb_put(ax25);
  
  	if (++nr_neigh->failed < sysctl_netrom_link_fails_count) {
  		nr_neigh_put(nr_neigh);
  		return;
  	}
  	spin_lock_bh(&nr_node_list_lock);
b67bfe0d4   Sasha Levin   hlist: drop the n...
723
  	nr_node_for_each(nr_node, &nr_node_list) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
724
  		nr_node_lock(nr_node);
52383678a   Ralf Baechle   [NETROM]: Fix pos...
725
726
  		if (nr_node->which < nr_node->count &&
  		    nr_node->routes[nr_node->which].neighbour == nr_neigh)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
727
728
  			nr_node->which++;
  		nr_node_unlock(nr_node);
52383678a   Ralf Baechle   [NETROM]: Fix pos...
729
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
  	spin_unlock_bh(&nr_node_list_lock);
  	nr_neigh_put(nr_neigh);
  }
  
  /*
   *	Route a frame to an appropriate AX.25 connection. A NULL ax25_cb
   *	indicates an internally generated frame.
   */
  int nr_route_frame(struct sk_buff *skb, ax25_cb *ax25)
  {
  	ax25_address *nr_src, *nr_dest;
  	struct nr_neigh *nr_neigh;
  	struct nr_node  *nr_node;
  	struct net_device *dev;
  	unsigned char *dptr;
  	ax25_cb *ax25s;
  	int ret;
  	struct sk_buff *skbn;
  
  
  	nr_src  = (ax25_address *)(skb->data + 0);
  	nr_dest = (ax25_address *)(skb->data + 7);
58bc57471   Ralf Baechle   [AX.25]: Fix unch...
752
753
  	if (ax25 != NULL) {
  		ret = nr_add_node(nr_src, "", &ax25->dest_addr, ax25->digipeat,
5f8f59d66   YOSHIFUJI Hideaki   [NET] NETROM: Fix...
754
755
  				  ax25->ax25_dev->dev, 0,
  				  sysctl_netrom_obsolescence_count_initialiser);
58bc57471   Ralf Baechle   [AX.25]: Fix unch...
756
757
758
  		if (ret)
  			return ret;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
  
  	if ((dev = nr_dev_get(nr_dest)) != NULL) {	/* Its for me */
  		if (ax25 == NULL)			/* Its from me */
  			ret = nr_loopback_queue(skb);
  		else
  			ret = nr_rx_frame(skb, dev);
  		dev_put(dev);
  		return ret;
  	}
  
  	if (!sysctl_netrom_routing_control && ax25 != NULL)
  		return 0;
  
  	/* Its Time-To-Live has expired */
  	if (skb->data[14] == 1) {
  		return 0;
  	}
  
  	nr_node = nr_node_get(nr_dest);
  	if (nr_node == NULL)
  		return 0;
  	nr_node_lock(nr_node);
  
  	if (nr_node->which >= nr_node->count) {
  		nr_node_unlock(nr_node);
  		nr_node_put(nr_node);
  		return 0;
  	}
  
  	nr_neigh = nr_node->routes[nr_node->which].neighbour;
  
  	if ((dev = nr_dev_first()) == NULL) {
  		nr_node_unlock(nr_node);
  		nr_node_put(nr_node);
  		return 0;
  	}
  
  	/* We are going to change the netrom headers so we should get our
  	   own skb, we also did not know until now how much header space
  	   we had to reserve... - RXQ */
  	if ((skbn=skb_copy_expand(skb, dev->hard_header_len, 0, GFP_ATOMIC)) == NULL) {
  		nr_node_unlock(nr_node);
  		nr_node_put(nr_node);
  		dev_put(dev);
  		return 0;
  	}
  	kfree_skb(skb);
  	skb=skbn;
  	skb->data[14]--;
  
  	dptr  = skb_push(skb, 1);
  	*dptr = AX25_P_NETROM;
d00c362f1   Jarek Poplawski   ax25: netrom: ros...
811
812
813
814
815
816
  	ax25s = nr_neigh->ax25;
  	nr_neigh->ax25 = ax25_send_frame(skb, 256,
  					 (ax25_address *)dev->dev_addr,
  					 &nr_neigh->callsign,
  					 nr_neigh->digipeat, nr_neigh->dev);
  	if (ax25s)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
817
  		ax25_cb_put(ax25s);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
818
819
820
821
822
  
  	dev_put(dev);
  	ret = (nr_neigh->ax25 != NULL);
  	nr_node_unlock(nr_node);
  	nr_node_put(nr_node);
58bc57471   Ralf Baechle   [AX.25]: Fix unch...
823

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
824
825
826
827
828
829
  	return ret;
  }
  
  #ifdef CONFIG_PROC_FS
  
  static void *nr_node_start(struct seq_file *seq, loff_t *pos)
5018adfd7   Jules Irenge   net: netrom: Add ...
830
  	__acquires(&nr_node_list_lock)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
831
  {
5f8f59d66   YOSHIFUJI Hideaki   [NET] NETROM: Fix...
832
  	spin_lock_bh(&nr_node_list_lock);
90dd7f5ac   Li Zefan   net: netrom: use ...
833
  	return seq_hlist_start_head(&nr_node_list, *pos);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
834
835
836
837
  }
  
  static void *nr_node_next(struct seq_file *seq, void *v, loff_t *pos)
  {
90dd7f5ac   Li Zefan   net: netrom: use ...
838
  	return seq_hlist_next(v, &nr_node_list, pos);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
839
840
841
  }
  
  static void nr_node_stop(struct seq_file *seq, void *v)
0eb713fb6   Jules Irenge   net: netrom: Add ...
842
  	__releases(&nr_node_list_lock)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
843
844
845
846
847
848
  {
  	spin_unlock_bh(&nr_node_list_lock);
  }
  
  static int nr_node_show(struct seq_file *seq, void *v)
  {
f75268cd6   Ralf Baechle   [AX25]: Make ax2a...
849
  	char buf[11];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
850
851
852
853
854
855
856
  	int i;
  
  	if (v == SEQ_START_TOKEN)
  		seq_puts(seq,
  			 "callsign  mnemonic w n qual obs neigh qual obs neigh qual obs neigh
  ");
  	else {
90dd7f5ac   Li Zefan   net: netrom: use ...
857
858
  		struct nr_node *nr_node = hlist_entry(v, struct nr_node,
  						      node_node);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
859
860
  		nr_node_lock(nr_node);
  		seq_printf(seq, "%-9s %-7s  %d %d",
f75268cd6   Ralf Baechle   [AX25]: Make ax2a...
861
  			ax2asc(buf, &nr_node->callsign),
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
  			(nr_node->mnemonic[0] == '\0') ? "*" : nr_node->mnemonic,
  			nr_node->which + 1,
  			nr_node->count);
  
  		for (i = 0; i < nr_node->count; i++) {
  			seq_printf(seq, "  %3d   %d %05d",
  				nr_node->routes[i].quality,
  				nr_node->routes[i].obs_count,
  				nr_node->routes[i].neighbour->number);
  		}
  		nr_node_unlock(nr_node);
  
  		seq_puts(seq, "
  ");
  	}
  	return 0;
  }
fddda2b7b   Christoph Hellwig   proc: introduce p...
879
  const struct seq_operations nr_node_seqops = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
880
881
882
883
884
  	.start = nr_node_start,
  	.next = nr_node_next,
  	.stop = nr_node_stop,
  	.show = nr_node_show,
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
885
  static void *nr_neigh_start(struct seq_file *seq, loff_t *pos)
2d6b6acfc   Jules Irenge   net: netrom: Add ...
886
  	__acquires(&nr_neigh_list_lock)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
887
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
888
  	spin_lock_bh(&nr_neigh_list_lock);
90dd7f5ac   Li Zefan   net: netrom: use ...
889
  	return seq_hlist_start_head(&nr_neigh_list, *pos);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
890
891
892
893
  }
  
  static void *nr_neigh_next(struct seq_file *seq, void *v, loff_t *pos)
  {
90dd7f5ac   Li Zefan   net: netrom: use ...
894
  	return seq_hlist_next(v, &nr_neigh_list, pos);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
895
896
897
  }
  
  static void nr_neigh_stop(struct seq_file *seq, void *v)
be21139f3   Jules Irenge   net: netrom: Add ...
898
  	__releases(&nr_neigh_list_lock)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
899
900
901
902
903
904
  {
  	spin_unlock_bh(&nr_neigh_list_lock);
  }
  
  static int nr_neigh_show(struct seq_file *seq, void *v)
  {
f75268cd6   Ralf Baechle   [AX25]: Make ax2a...
905
  	char buf[11];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
906
907
908
909
910
911
  	int i;
  
  	if (v == SEQ_START_TOKEN)
  		seq_puts(seq, "addr  callsign  dev  qual lock count failed digipeaters
  ");
  	else {
90dd7f5ac   Li Zefan   net: netrom: use ...
912
  		struct nr_neigh *nr_neigh;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
913

90dd7f5ac   Li Zefan   net: netrom: use ...
914
  		nr_neigh = hlist_entry(v, struct nr_neigh, neigh_node);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
915
916
  		seq_printf(seq, "%05d %-9s %-4s  %3d    %d   %3d    %3d",
  			nr_neigh->number,
f75268cd6   Ralf Baechle   [AX25]: Make ax2a...
917
  			ax2asc(buf, &nr_neigh->callsign),
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
918
919
920
921
922
923
924
925
  			nr_neigh->dev ? nr_neigh->dev->name : "???",
  			nr_neigh->quality,
  			nr_neigh->locked,
  			nr_neigh->count,
  			nr_neigh->failed);
  
  		if (nr_neigh->digipeat != NULL) {
  			for (i = 0; i < nr_neigh->digipeat->ndigi; i++)
5f8f59d66   YOSHIFUJI Hideaki   [NET] NETROM: Fix...
926
  				seq_printf(seq, " %s",
f75268cd6   Ralf Baechle   [AX25]: Make ax2a...
927
  					   ax2asc(buf, &nr_neigh->digipeat->calls[i]));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
928
929
930
931
932
933
934
  		}
  
  		seq_puts(seq, "
  ");
  	}
  	return 0;
  }
fddda2b7b   Christoph Hellwig   proc: introduce p...
935
  const struct seq_operations nr_neigh_seqops = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
936
937
938
939
940
  	.start = nr_neigh_start,
  	.next = nr_neigh_next,
  	.stop = nr_neigh_stop,
  	.show = nr_neigh_show,
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
941
942
943
944
945
  #endif
  
  /*
   *	Free all memory associated with the nodes and routes lists.
   */
d3706566a   YueHaibing   net: netrom: Fix ...
946
  void nr_rt_free(void)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
947
948
949
  {
  	struct nr_neigh *s = NULL;
  	struct nr_node  *t = NULL;
b67bfe0d4   Sasha Levin   hlist: drop the n...
950
  	struct hlist_node *nodet;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
951
952
953
  
  	spin_lock_bh(&nr_neigh_list_lock);
  	spin_lock_bh(&nr_node_list_lock);
b67bfe0d4   Sasha Levin   hlist: drop the n...
954
  	nr_node_for_each_safe(t, nodet, &nr_node_list) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
955
956
957
958
  		nr_node_lock(t);
  		nr_remove_node_locked(t);
  		nr_node_unlock(t);
  	}
b67bfe0d4   Sasha Levin   hlist: drop the n...
959
  	nr_neigh_for_each_safe(s, nodet, &nr_neigh_list) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
960
961
962
963
964
965
966
967
968
  		while(s->count) {
  			s->count--;
  			nr_neigh_put(s);
  		}
  		nr_remove_neigh_locked(s);
  	}
  	spin_unlock_bh(&nr_node_list_lock);
  	spin_unlock_bh(&nr_neigh_list_lock);
  }