blob: ef6a3d5865912beff81df572a4c8b95664a78c84 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
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 Alan Cox GW4PTS (alan@lxorguk.ukuu.org.uk)
9 * Copyright Tomi Manninen OH2BNS (oh2bns@sral.fi)
10 */
11#include <linux/errno.h>
12#include <linux/types.h>
13#include <linux/socket.h>
14#include <linux/in.h>
15#include <linux/kernel.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070016#include <linux/timer.h>
17#include <linux/string.h>
18#include <linux/sockios.h>
19#include <linux/net.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090020#include <linux/slab.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070021#include <net/ax25.h>
22#include <linux/inet.h>
23#include <linux/netdevice.h>
24#include <net/arp.h>
25#include <linux/if_arp.h>
26#include <linux/skbuff.h>
27#include <net/sock.h>
Fabian Frederickdc8e5412014-10-17 22:00:22 +020028#include <linux/uaccess.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070029#include <linux/fcntl.h>
30#include <linux/termios.h> /* For TIOCINQ/OUTQ */
31#include <linux/mm.h>
32#include <linux/interrupt.h>
33#include <linux/notifier.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070034#include <linux/init.h>
35#include <linux/spinlock.h>
36#include <net/netrom.h>
37#include <linux/seq_file.h>
Paul Gortmakerbc3b2d72011-07-15 11:47:34 -040038#include <linux/export.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070039
40static unsigned int nr_neigh_no = 1;
41
42static HLIST_HEAD(nr_node_list);
43static DEFINE_SPINLOCK(nr_node_list_lock);
44static HLIST_HEAD(nr_neigh_list);
45static DEFINE_SPINLOCK(nr_neigh_list_lock);
46
47static struct nr_node *nr_node_get(ax25_address *callsign)
48{
49 struct nr_node *found = NULL;
50 struct nr_node *nr_node;
Linus Torvalds1da177e2005-04-16 15:20:36 -070051
52 spin_lock_bh(&nr_node_list_lock);
Sasha Levinb67bfe02013-02-27 17:06:00 -080053 nr_node_for_each(nr_node, &nr_node_list)
Linus Torvalds1da177e2005-04-16 15:20:36 -070054 if (ax25cmp(callsign, &nr_node->callsign) == 0) {
55 nr_node_hold(nr_node);
56 found = nr_node;
57 break;
58 }
59 spin_unlock_bh(&nr_node_list_lock);
60 return found;
61}
62
63static struct nr_neigh *nr_neigh_get_dev(ax25_address *callsign,
64 struct net_device *dev)
65{
66 struct nr_neigh *found = NULL;
67 struct nr_neigh *nr_neigh;
Linus Torvalds1da177e2005-04-16 15:20:36 -070068
69 spin_lock_bh(&nr_neigh_list_lock);
Sasha Levinb67bfe02013-02-27 17:06:00 -080070 nr_neigh_for_each(nr_neigh, &nr_neigh_list)
Linus Torvalds1da177e2005-04-16 15:20:36 -070071 if (ax25cmp(callsign, &nr_neigh->callsign) == 0 &&
72 nr_neigh->dev == dev) {
73 nr_neigh_hold(nr_neigh);
74 found = nr_neigh;
75 break;
76 }
77 spin_unlock_bh(&nr_neigh_list_lock);
78 return found;
79}
80
81static void nr_remove_neigh(struct nr_neigh *);
82
83/*
84 * Add a new route to a node, and in the process add the node and the
85 * neighbour if it is new.
86 */
Ralf Baechlec9266b92006-12-14 15:49:28 -080087static int __must_check nr_add_node(ax25_address *nr, const char *mnemonic,
88 ax25_address *ax25, ax25_digi *ax25_digi, struct net_device *dev,
89 int quality, int obs_count)
Linus Torvalds1da177e2005-04-16 15:20:36 -070090{
91 struct nr_node *nr_node;
92 struct nr_neigh *nr_neigh;
93 struct nr_route nr_route;
94 int i, found;
95 struct net_device *odev;
96
97 if ((odev=nr_dev_get(nr)) != NULL) { /* Can't add routes to ourself */
98 dev_put(odev);
99 return -EINVAL;
100 }
101
102 nr_node = nr_node_get(nr);
103
104 nr_neigh = nr_neigh_get_dev(ax25, dev);
105
106 /*
107 * The L2 link to a neighbour has failed in the past
108 * and now a frame comes from this neighbour. We assume
109 * it was a temporary trouble with the link and reset the
110 * routes now (and not wait for a node broadcast).
111 */
112 if (nr_neigh != NULL && nr_neigh->failed != 0 && quality == 0) {
113 struct nr_node *nr_nodet;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700114
115 spin_lock_bh(&nr_node_list_lock);
Sasha Levinb67bfe02013-02-27 17:06:00 -0800116 nr_node_for_each(nr_nodet, &nr_node_list) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700117 nr_node_lock(nr_nodet);
118 for (i = 0; i < nr_nodet->count; i++)
119 if (nr_nodet->routes[i].neighbour == nr_neigh)
120 if (i < nr_nodet->which)
121 nr_nodet->which = i;
122 nr_node_unlock(nr_nodet);
123 }
124 spin_unlock_bh(&nr_node_list_lock);
125 }
126
127 if (nr_neigh != NULL)
128 nr_neigh->failed = 0;
129
130 if (quality == 0 && nr_neigh != NULL && nr_node != NULL) {
131 nr_neigh_put(nr_neigh);
132 nr_node_put(nr_node);
133 return 0;
134 }
135
136 if (nr_neigh == NULL) {
137 if ((nr_neigh = kmalloc(sizeof(*nr_neigh), GFP_ATOMIC)) == NULL) {
138 if (nr_node)
139 nr_node_put(nr_node);
140 return -ENOMEM;
141 }
142
143 nr_neigh->callsign = *ax25;
144 nr_neigh->digipeat = NULL;
145 nr_neigh->ax25 = NULL;
146 nr_neigh->dev = dev;
147 nr_neigh->quality = sysctl_netrom_default_path_quality;
148 nr_neigh->locked = 0;
149 nr_neigh->count = 0;
150 nr_neigh->number = nr_neigh_no++;
151 nr_neigh->failed = 0;
152 atomic_set(&nr_neigh->refcount, 1);
153
154 if (ax25_digi != NULL && ax25_digi->ndigi > 0) {
Arnaldo Carvalho de Meloeafff862006-11-17 13:05:04 -0200155 nr_neigh->digipeat = kmemdup(ax25_digi,
156 sizeof(*ax25_digi),
157 GFP_KERNEL);
158 if (nr_neigh->digipeat == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700159 kfree(nr_neigh);
160 if (nr_node)
161 nr_node_put(nr_node);
162 return -ENOMEM;
163 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700164 }
165
166 spin_lock_bh(&nr_neigh_list_lock);
167 hlist_add_head(&nr_neigh->neigh_node, &nr_neigh_list);
168 nr_neigh_hold(nr_neigh);
169 spin_unlock_bh(&nr_neigh_list_lock);
170 }
171
172 if (quality != 0 && ax25cmp(nr, ax25) == 0 && !nr_neigh->locked)
173 nr_neigh->quality = quality;
174
175 if (nr_node == NULL) {
176 if ((nr_node = kmalloc(sizeof(*nr_node), GFP_ATOMIC)) == NULL) {
177 if (nr_neigh)
178 nr_neigh_put(nr_neigh);
179 return -ENOMEM;
180 }
181
182 nr_node->callsign = *nr;
183 strcpy(nr_node->mnemonic, mnemonic);
184
185 nr_node->which = 0;
186 nr_node->count = 1;
187 atomic_set(&nr_node->refcount, 1);
188 spin_lock_init(&nr_node->node_lock);
189
190 nr_node->routes[0].quality = quality;
191 nr_node->routes[0].obs_count = obs_count;
192 nr_node->routes[0].neighbour = nr_neigh;
193
194 nr_neigh_hold(nr_neigh);
195 nr_neigh->count++;
196
197 spin_lock_bh(&nr_node_list_lock);
198 hlist_add_head(&nr_node->node_node, &nr_node_list);
199 /* refcount initialized at 1 */
200 spin_unlock_bh(&nr_node_list_lock);
201
Xiyu Yang9d983582020-04-15 16:36:19 +0800202 nr_neigh_put(nr_neigh);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700203 return 0;
204 }
205 nr_node_lock(nr_node);
206
207 if (quality != 0)
208 strcpy(nr_node->mnemonic, mnemonic);
209
210 for (found = 0, i = 0; i < nr_node->count; i++) {
211 if (nr_node->routes[i].neighbour == nr_neigh) {
212 nr_node->routes[i].quality = quality;
213 nr_node->routes[i].obs_count = obs_count;
214 found = 1;
215 break;
216 }
217 }
218
219 if (!found) {
220 /* We have space at the bottom, slot it in */
221 if (nr_node->count < 3) {
222 nr_node->routes[2] = nr_node->routes[1];
223 nr_node->routes[1] = nr_node->routes[0];
224
225 nr_node->routes[0].quality = quality;
226 nr_node->routes[0].obs_count = obs_count;
227 nr_node->routes[0].neighbour = nr_neigh;
228
229 nr_node->which++;
230 nr_node->count++;
231 nr_neigh_hold(nr_neigh);
232 nr_neigh->count++;
233 } else {
234 /* It must be better than the worst */
235 if (quality > nr_node->routes[2].quality) {
236 nr_node->routes[2].neighbour->count--;
237 nr_neigh_put(nr_node->routes[2].neighbour);
238
239 if (nr_node->routes[2].neighbour->count == 0 && !nr_node->routes[2].neighbour->locked)
240 nr_remove_neigh(nr_node->routes[2].neighbour);
241
242 nr_node->routes[2].quality = quality;
243 nr_node->routes[2].obs_count = obs_count;
244 nr_node->routes[2].neighbour = nr_neigh;
245
246 nr_neigh_hold(nr_neigh);
247 nr_neigh->count++;
248 }
249 }
250 }
251
252 /* Now re-sort the routes in quality order */
253 switch (nr_node->count) {
254 case 3:
255 if (nr_node->routes[1].quality > nr_node->routes[0].quality) {
256 switch (nr_node->which) {
Joe Perchesc4855382011-07-01 09:43:10 +0000257 case 0:
258 nr_node->which = 1;
259 break;
260 case 1:
261 nr_node->which = 0;
262 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700263 }
264 nr_route = nr_node->routes[0];
265 nr_node->routes[0] = nr_node->routes[1];
266 nr_node->routes[1] = nr_route;
267 }
268 if (nr_node->routes[2].quality > nr_node->routes[1].quality) {
269 switch (nr_node->which) {
270 case 1: nr_node->which = 2;
271 break;
272
273 case 2: nr_node->which = 1;
274 break;
275
276 default:
277 break;
278 }
279 nr_route = nr_node->routes[1];
280 nr_node->routes[1] = nr_node->routes[2];
281 nr_node->routes[2] = nr_route;
282 }
283 case 2:
284 if (nr_node->routes[1].quality > nr_node->routes[0].quality) {
285 switch (nr_node->which) {
286 case 0: nr_node->which = 1;
287 break;
288
289 case 1: nr_node->which = 0;
290 break;
291
292 default: break;
293 }
294 nr_route = nr_node->routes[0];
295 nr_node->routes[0] = nr_node->routes[1];
296 nr_node->routes[1] = nr_route;
297 }
298 case 1:
299 break;
300 }
301
302 for (i = 0; i < nr_node->count; i++) {
303 if (nr_node->routes[i].neighbour == nr_neigh) {
304 if (i < nr_node->which)
305 nr_node->which = i;
306 break;
307 }
308 }
309
310 nr_neigh_put(nr_neigh);
311 nr_node_unlock(nr_node);
312 nr_node_put(nr_node);
313 return 0;
314}
315
316static inline void __nr_remove_node(struct nr_node *nr_node)
317{
318 hlist_del_init(&nr_node->node_node);
319 nr_node_put(nr_node);
320}
321
322#define nr_remove_node_locked(__node) \
323 __nr_remove_node(__node)
324
325static void nr_remove_node(struct nr_node *nr_node)
326{
327 spin_lock_bh(&nr_node_list_lock);
328 __nr_remove_node(nr_node);
329 spin_unlock_bh(&nr_node_list_lock);
330}
331
332static inline void __nr_remove_neigh(struct nr_neigh *nr_neigh)
333{
334 hlist_del_init(&nr_neigh->neigh_node);
335 nr_neigh_put(nr_neigh);
336}
337
338#define nr_remove_neigh_locked(__neigh) \
339 __nr_remove_neigh(__neigh)
340
341static void nr_remove_neigh(struct nr_neigh *nr_neigh)
342{
343 spin_lock_bh(&nr_neigh_list_lock);
344 __nr_remove_neigh(nr_neigh);
345 spin_unlock_bh(&nr_neigh_list_lock);
346}
347
348/*
349 * "Delete" a node. Strictly speaking remove a route to a node. The node
350 * is only deleted if no routes are left to it.
351 */
352static int nr_del_node(ax25_address *callsign, ax25_address *neighbour, struct net_device *dev)
353{
354 struct nr_node *nr_node;
355 struct nr_neigh *nr_neigh;
356 int i;
357
358 nr_node = nr_node_get(callsign);
359
360 if (nr_node == NULL)
361 return -EINVAL;
362
363 nr_neigh = nr_neigh_get_dev(neighbour, dev);
364
365 if (nr_neigh == NULL) {
366 nr_node_put(nr_node);
367 return -EINVAL;
368 }
369
370 nr_node_lock(nr_node);
371 for (i = 0; i < nr_node->count; i++) {
372 if (nr_node->routes[i].neighbour == nr_neigh) {
373 nr_neigh->count--;
374 nr_neigh_put(nr_neigh);
375
376 if (nr_neigh->count == 0 && !nr_neigh->locked)
377 nr_remove_neigh(nr_neigh);
378 nr_neigh_put(nr_neigh);
379
380 nr_node->count--;
381
382 if (nr_node->count == 0) {
383 nr_remove_node(nr_node);
384 } else {
385 switch (i) {
386 case 0:
387 nr_node->routes[0] = nr_node->routes[1];
388 case 1:
389 nr_node->routes[1] = nr_node->routes[2];
390 case 2:
391 break;
392 }
393 nr_node_put(nr_node);
394 }
395 nr_node_unlock(nr_node);
396
397 return 0;
398 }
399 }
400 nr_neigh_put(nr_neigh);
401 nr_node_unlock(nr_node);
402 nr_node_put(nr_node);
403
404 return -EINVAL;
405}
406
407/*
408 * Lock a neighbour with a quality.
409 */
Ralf Baechlec9266b92006-12-14 15:49:28 -0800410static int __must_check nr_add_neigh(ax25_address *callsign,
411 ax25_digi *ax25_digi, struct net_device *dev, unsigned int quality)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700412{
413 struct nr_neigh *nr_neigh;
414
415 nr_neigh = nr_neigh_get_dev(callsign, dev);
416 if (nr_neigh) {
417 nr_neigh->quality = quality;
418 nr_neigh->locked = 1;
419 nr_neigh_put(nr_neigh);
420 return 0;
421 }
422
423 if ((nr_neigh = kmalloc(sizeof(*nr_neigh), GFP_ATOMIC)) == NULL)
424 return -ENOMEM;
425
426 nr_neigh->callsign = *callsign;
427 nr_neigh->digipeat = NULL;
428 nr_neigh->ax25 = NULL;
429 nr_neigh->dev = dev;
430 nr_neigh->quality = quality;
431 nr_neigh->locked = 1;
432 nr_neigh->count = 0;
433 nr_neigh->number = nr_neigh_no++;
434 nr_neigh->failed = 0;
435 atomic_set(&nr_neigh->refcount, 1);
436
437 if (ax25_digi != NULL && ax25_digi->ndigi > 0) {
Arnaldo Carvalho de Meloeafff862006-11-17 13:05:04 -0200438 nr_neigh->digipeat = kmemdup(ax25_digi, sizeof(*ax25_digi),
439 GFP_KERNEL);
440 if (nr_neigh->digipeat == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700441 kfree(nr_neigh);
442 return -ENOMEM;
443 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700444 }
445
446 spin_lock_bh(&nr_neigh_list_lock);
447 hlist_add_head(&nr_neigh->neigh_node, &nr_neigh_list);
448 /* refcount is initialized at 1 */
449 spin_unlock_bh(&nr_neigh_list_lock);
450
451 return 0;
452}
453
454/*
455 * "Delete" a neighbour. The neighbour is only removed if the number
456 * of nodes that may use it is zero.
457 */
458static int nr_del_neigh(ax25_address *callsign, struct net_device *dev, unsigned int quality)
459{
460 struct nr_neigh *nr_neigh;
461
462 nr_neigh = nr_neigh_get_dev(callsign, dev);
463
464 if (nr_neigh == NULL) return -EINVAL;
465
466 nr_neigh->quality = quality;
467 nr_neigh->locked = 0;
468
469 if (nr_neigh->count == 0)
470 nr_remove_neigh(nr_neigh);
471 nr_neigh_put(nr_neigh);
472
473 return 0;
474}
475
476/*
477 * Decrement the obsolescence count by one. If a route is reduced to a
478 * count of zero, remove it. Also remove any unlocked neighbours with
479 * zero nodes routing via it.
480 */
481static int nr_dec_obs(void)
482{
483 struct nr_neigh *nr_neigh;
484 struct nr_node *s;
Sasha Levinb67bfe02013-02-27 17:06:00 -0800485 struct hlist_node *nodet;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700486 int i;
487
488 spin_lock_bh(&nr_node_list_lock);
Sasha Levinb67bfe02013-02-27 17:06:00 -0800489 nr_node_for_each_safe(s, nodet, &nr_node_list) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700490 nr_node_lock(s);
491 for (i = 0; i < s->count; i++) {
492 switch (s->routes[i].obs_count) {
493 case 0: /* A locked entry */
494 break;
495
496 case 1: /* From 1 -> 0 */
497 nr_neigh = s->routes[i].neighbour;
498
499 nr_neigh->count--;
500 nr_neigh_put(nr_neigh);
501
502 if (nr_neigh->count == 0 && !nr_neigh->locked)
503 nr_remove_neigh(nr_neigh);
504
505 s->count--;
506
507 switch (i) {
Joe Perchesc4855382011-07-01 09:43:10 +0000508 case 0:
509 s->routes[0] = s->routes[1];
510 /* Fallthrough */
511 case 1:
512 s->routes[1] = s->routes[2];
513 case 2:
514 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700515 }
516 break;
517
518 default:
519 s->routes[i].obs_count--;
520 break;
521
522 }
523 }
524
525 if (s->count <= 0)
526 nr_remove_node_locked(s);
527 nr_node_unlock(s);
528 }
529 spin_unlock_bh(&nr_node_list_lock);
530
531 return 0;
532}
533
534/*
535 * A device has been removed. Remove its routes and neighbours.
536 */
537void nr_rt_device_down(struct net_device *dev)
538{
539 struct nr_neigh *s;
Sasha Levinb67bfe02013-02-27 17:06:00 -0800540 struct hlist_node *nodet, *node2t;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700541 struct nr_node *t;
542 int i;
543
544 spin_lock_bh(&nr_neigh_list_lock);
Sasha Levinb67bfe02013-02-27 17:06:00 -0800545 nr_neigh_for_each_safe(s, nodet, &nr_neigh_list) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700546 if (s->dev == dev) {
547 spin_lock_bh(&nr_node_list_lock);
Sasha Levinb67bfe02013-02-27 17:06:00 -0800548 nr_node_for_each_safe(t, node2t, &nr_node_list) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700549 nr_node_lock(t);
550 for (i = 0; i < t->count; i++) {
551 if (t->routes[i].neighbour == s) {
552 t->count--;
553
554 switch (i) {
555 case 0:
556 t->routes[0] = t->routes[1];
557 case 1:
558 t->routes[1] = t->routes[2];
559 case 2:
560 break;
561 }
562 }
563 }
564
565 if (t->count <= 0)
566 nr_remove_node_locked(t);
567 nr_node_unlock(t);
568 }
569 spin_unlock_bh(&nr_node_list_lock);
570
571 nr_remove_neigh_locked(s);
572 }
573 }
574 spin_unlock_bh(&nr_neigh_list_lock);
575}
576
577/*
578 * Check that the device given is a valid AX.25 interface that is "up".
579 * Or a valid ethernet interface with an AX.25 callsign binding.
580 */
581static struct net_device *nr_ax25_dev_get(char *devname)
582{
583 struct net_device *dev;
584
Eric W. Biederman881d9662007-09-17 11:56:21 -0700585 if ((dev = dev_get_by_name(&init_net, devname)) == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700586 return NULL;
587
588 if ((dev->flags & IFF_UP) && dev->type == ARPHRD_AX25)
589 return dev;
590
591 dev_put(dev);
592 return NULL;
593}
594
595/*
596 * Find the first active NET/ROM device, usually "nr0".
597 */
598struct net_device *nr_dev_first(void)
599{
600 struct net_device *dev, *first = NULL;
601
Eric Dumazetc6d14c82009-11-04 05:43:23 -0800602 rcu_read_lock();
603 for_each_netdev_rcu(&init_net, dev) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700604 if ((dev->flags & IFF_UP) && dev->type == ARPHRD_NETROM)
605 if (first == NULL || strncmp(dev->name, first->name, 3) < 0)
606 first = dev;
607 }
608 if (first)
609 dev_hold(first);
Eric Dumazetc6d14c82009-11-04 05:43:23 -0800610 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700611
612 return first;
613}
614
615/*
616 * Find the NET/ROM device for the given callsign.
617 */
618struct net_device *nr_dev_get(ax25_address *addr)
619{
620 struct net_device *dev;
621
Eric Dumazetc6d14c82009-11-04 05:43:23 -0800622 rcu_read_lock();
623 for_each_netdev_rcu(&init_net, dev) {
624 if ((dev->flags & IFF_UP) && dev->type == ARPHRD_NETROM &&
625 ax25cmp(addr, (ax25_address *)dev->dev_addr) == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700626 dev_hold(dev);
627 goto out;
628 }
629 }
Pavel Emelianov7562f872007-05-03 15:13:45 -0700630 dev = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700631out:
Eric Dumazetc6d14c82009-11-04 05:43:23 -0800632 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700633 return dev;
634}
635
Ralf Baechlec6ba9732009-08-17 18:05:32 -0700636static ax25_digi *nr_call_to_digi(ax25_digi *digi, int ndigis,
637 ax25_address *digipeaters)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700638{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700639 int i;
640
641 if (ndigis == 0)
642 return NULL;
643
644 for (i = 0; i < ndigis; i++) {
Ralf Baechlec6ba9732009-08-17 18:05:32 -0700645 digi->calls[i] = digipeaters[i];
646 digi->repeated[i] = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700647 }
648
Ralf Baechlec6ba9732009-08-17 18:05:32 -0700649 digi->ndigi = ndigis;
650 digi->lastrepeat = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700651
Ralf Baechlec6ba9732009-08-17 18:05:32 -0700652 return digi;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700653}
654
655/*
656 * Handle the ioctls that control the routing functions.
657 */
658int nr_rt_ioctl(unsigned int cmd, void __user *arg)
659{
660 struct nr_route_struct nr_route;
661 struct net_device *dev;
Ralf Baechlec6ba9732009-08-17 18:05:32 -0700662 ax25_digi digi;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700663 int ret;
664
665 switch (cmd) {
666 case SIOCADDRT:
667 if (copy_from_user(&nr_route, arg, sizeof(struct nr_route_struct)))
668 return -EFAULT;
Ralf Baechle10cae1c2011-11-24 23:09:00 +0000669 if (nr_route.ndigis > AX25_MAX_DIGIS)
670 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700671 if ((dev = nr_ax25_dev_get(nr_route.device)) == NULL)
672 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700673 switch (nr_route.type) {
674 case NETROM_NODE:
Ralf Baechleac1a1de2011-11-24 23:08:49 +0000675 if (strnlen(nr_route.mnemonic, 7) == 7) {
676 ret = -EINVAL;
677 break;
678 }
679
Linus Torvalds1da177e2005-04-16 15:20:36 -0700680 ret = nr_add_node(&nr_route.callsign,
681 nr_route.mnemonic,
682 &nr_route.neighbour,
Ralf Baechlec6ba9732009-08-17 18:05:32 -0700683 nr_call_to_digi(&digi, nr_route.ndigis,
684 nr_route.digipeaters),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700685 dev, nr_route.quality,
686 nr_route.obs_count);
687 break;
688 case NETROM_NEIGH:
689 ret = nr_add_neigh(&nr_route.callsign,
Ralf Baechlec6ba9732009-08-17 18:05:32 -0700690 nr_call_to_digi(&digi, nr_route.ndigis,
691 nr_route.digipeaters),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700692 dev, nr_route.quality);
693 break;
694 default:
695 ret = -EINVAL;
696 }
697 dev_put(dev);
698 return ret;
699
700 case SIOCDELRT:
701 if (copy_from_user(&nr_route, arg, sizeof(struct nr_route_struct)))
702 return -EFAULT;
703 if ((dev = nr_ax25_dev_get(nr_route.device)) == NULL)
704 return -EINVAL;
705 switch (nr_route.type) {
706 case NETROM_NODE:
707 ret = nr_del_node(&nr_route.callsign,
708 &nr_route.neighbour, dev);
709 break;
710 case NETROM_NEIGH:
711 ret = nr_del_neigh(&nr_route.callsign,
712 dev, nr_route.quality);
713 break;
714 default:
715 ret = -EINVAL;
716 }
717 dev_put(dev);
718 return ret;
719
720 case SIOCNRDECOBS:
721 return nr_dec_obs();
722
723 default:
724 return -EINVAL;
725 }
726
727 return 0;
728}
729
730/*
731 * A level 2 link has timed out, therefore it appears to be a poor link,
732 * then don't use that neighbour until it is reset.
733 */
734void nr_link_failed(ax25_cb *ax25, int reason)
735{
736 struct nr_neigh *s, *nr_neigh = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700737 struct nr_node *nr_node = NULL;
738
739 spin_lock_bh(&nr_neigh_list_lock);
Sasha Levinb67bfe02013-02-27 17:06:00 -0800740 nr_neigh_for_each(s, &nr_neigh_list) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700741 if (s->ax25 == ax25) {
742 nr_neigh_hold(s);
743 nr_neigh = s;
744 break;
745 }
Ralf Baechle52383672006-06-26 00:05:23 -0700746 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700747 spin_unlock_bh(&nr_neigh_list_lock);
748
Ralf Baechle52383672006-06-26 00:05:23 -0700749 if (nr_neigh == NULL)
750 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700751
752 nr_neigh->ax25 = NULL;
753 ax25_cb_put(ax25);
754
755 if (++nr_neigh->failed < sysctl_netrom_link_fails_count) {
756 nr_neigh_put(nr_neigh);
757 return;
758 }
759 spin_lock_bh(&nr_node_list_lock);
Sasha Levinb67bfe02013-02-27 17:06:00 -0800760 nr_node_for_each(nr_node, &nr_node_list) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700761 nr_node_lock(nr_node);
Ralf Baechle52383672006-06-26 00:05:23 -0700762 if (nr_node->which < nr_node->count &&
763 nr_node->routes[nr_node->which].neighbour == nr_neigh)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700764 nr_node->which++;
765 nr_node_unlock(nr_node);
Ralf Baechle52383672006-06-26 00:05:23 -0700766 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700767 spin_unlock_bh(&nr_node_list_lock);
768 nr_neigh_put(nr_neigh);
769}
770
771/*
772 * Route a frame to an appropriate AX.25 connection. A NULL ax25_cb
773 * indicates an internally generated frame.
774 */
775int nr_route_frame(struct sk_buff *skb, ax25_cb *ax25)
776{
777 ax25_address *nr_src, *nr_dest;
778 struct nr_neigh *nr_neigh;
779 struct nr_node *nr_node;
780 struct net_device *dev;
781 unsigned char *dptr;
782 ax25_cb *ax25s;
783 int ret;
784 struct sk_buff *skbn;
785
786
787 nr_src = (ax25_address *)(skb->data + 0);
788 nr_dest = (ax25_address *)(skb->data + 7);
789
Ralf Baechle58bc5742006-12-14 15:50:58 -0800790 if (ax25 != NULL) {
791 ret = nr_add_node(nr_src, "", &ax25->dest_addr, ax25->digipeat,
YOSHIFUJI Hideaki5f8f59d2007-02-09 23:25:09 +0900792 ax25->ax25_dev->dev, 0,
793 sysctl_netrom_obsolescence_count_initialiser);
Ralf Baechle58bc5742006-12-14 15:50:58 -0800794 if (ret)
795 return ret;
796 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700797
798 if ((dev = nr_dev_get(nr_dest)) != NULL) { /* Its for me */
799 if (ax25 == NULL) /* Its from me */
800 ret = nr_loopback_queue(skb);
801 else
802 ret = nr_rx_frame(skb, dev);
803 dev_put(dev);
804 return ret;
805 }
806
807 if (!sysctl_netrom_routing_control && ax25 != NULL)
808 return 0;
809
810 /* Its Time-To-Live has expired */
811 if (skb->data[14] == 1) {
812 return 0;
813 }
814
815 nr_node = nr_node_get(nr_dest);
816 if (nr_node == NULL)
817 return 0;
818 nr_node_lock(nr_node);
819
820 if (nr_node->which >= nr_node->count) {
821 nr_node_unlock(nr_node);
822 nr_node_put(nr_node);
823 return 0;
824 }
825
826 nr_neigh = nr_node->routes[nr_node->which].neighbour;
827
828 if ((dev = nr_dev_first()) == NULL) {
829 nr_node_unlock(nr_node);
830 nr_node_put(nr_node);
831 return 0;
832 }
833
834 /* We are going to change the netrom headers so we should get our
835 own skb, we also did not know until now how much header space
836 we had to reserve... - RXQ */
837 if ((skbn=skb_copy_expand(skb, dev->hard_header_len, 0, GFP_ATOMIC)) == NULL) {
838 nr_node_unlock(nr_node);
839 nr_node_put(nr_node);
840 dev_put(dev);
841 return 0;
842 }
843 kfree_skb(skb);
844 skb=skbn;
845 skb->data[14]--;
846
847 dptr = skb_push(skb, 1);
848 *dptr = AX25_P_NETROM;
849
Jarek Poplawskid00c3622010-01-16 01:04:04 -0800850 ax25s = nr_neigh->ax25;
851 nr_neigh->ax25 = ax25_send_frame(skb, 256,
852 (ax25_address *)dev->dev_addr,
853 &nr_neigh->callsign,
854 nr_neigh->digipeat, nr_neigh->dev);
855 if (ax25s)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700856 ax25_cb_put(ax25s);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700857
858 dev_put(dev);
859 ret = (nr_neigh->ax25 != NULL);
860 nr_node_unlock(nr_node);
861 nr_node_put(nr_node);
Ralf Baechle58bc5742006-12-14 15:50:58 -0800862
Linus Torvalds1da177e2005-04-16 15:20:36 -0700863 return ret;
864}
865
866#ifdef CONFIG_PROC_FS
867
868static void *nr_node_start(struct seq_file *seq, loff_t *pos)
869{
YOSHIFUJI Hideaki5f8f59d2007-02-09 23:25:09 +0900870 spin_lock_bh(&nr_node_list_lock);
Li Zefan90dd7f52010-02-08 23:19:42 +0000871 return seq_hlist_start_head(&nr_node_list, *pos);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700872}
873
874static void *nr_node_next(struct seq_file *seq, void *v, loff_t *pos)
875{
Li Zefan90dd7f52010-02-08 23:19:42 +0000876 return seq_hlist_next(v, &nr_node_list, pos);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700877}
878
879static void nr_node_stop(struct seq_file *seq, void *v)
880{
881 spin_unlock_bh(&nr_node_list_lock);
882}
883
884static int nr_node_show(struct seq_file *seq, void *v)
885{
Ralf Baechlef75268c2005-09-06 15:49:39 -0700886 char buf[11];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700887 int i;
888
889 if (v == SEQ_START_TOKEN)
890 seq_puts(seq,
891 "callsign mnemonic w n qual obs neigh qual obs neigh qual obs neigh\n");
892 else {
Li Zefan90dd7f52010-02-08 23:19:42 +0000893 struct nr_node *nr_node = hlist_entry(v, struct nr_node,
894 node_node);
895
Linus Torvalds1da177e2005-04-16 15:20:36 -0700896 nr_node_lock(nr_node);
897 seq_printf(seq, "%-9s %-7s %d %d",
Ralf Baechlef75268c2005-09-06 15:49:39 -0700898 ax2asc(buf, &nr_node->callsign),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700899 (nr_node->mnemonic[0] == '\0') ? "*" : nr_node->mnemonic,
900 nr_node->which + 1,
901 nr_node->count);
902
903 for (i = 0; i < nr_node->count; i++) {
904 seq_printf(seq, " %3d %d %05d",
905 nr_node->routes[i].quality,
906 nr_node->routes[i].obs_count,
907 nr_node->routes[i].neighbour->number);
908 }
909 nr_node_unlock(nr_node);
910
911 seq_puts(seq, "\n");
912 }
913 return 0;
914}
915
Philippe De Muyter56b3d972007-07-10 23:07:31 -0700916static const struct seq_operations nr_node_seqops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700917 .start = nr_node_start,
918 .next = nr_node_next,
919 .stop = nr_node_stop,
920 .show = nr_node_show,
921};
922
923static int nr_node_info_open(struct inode *inode, struct file *file)
924{
925 return seq_open(file, &nr_node_seqops);
926}
927
Arjan van de Venda7071d2007-02-12 00:55:36 -0800928const struct file_operations nr_nodes_fops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700929 .owner = THIS_MODULE,
930 .open = nr_node_info_open,
931 .read = seq_read,
932 .llseek = seq_lseek,
933 .release = seq_release,
934};
935
936static void *nr_neigh_start(struct seq_file *seq, loff_t *pos)
937{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700938 spin_lock_bh(&nr_neigh_list_lock);
Li Zefan90dd7f52010-02-08 23:19:42 +0000939 return seq_hlist_start_head(&nr_neigh_list, *pos);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700940}
941
942static void *nr_neigh_next(struct seq_file *seq, void *v, loff_t *pos)
943{
Li Zefan90dd7f52010-02-08 23:19:42 +0000944 return seq_hlist_next(v, &nr_neigh_list, pos);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700945}
946
947static void nr_neigh_stop(struct seq_file *seq, void *v)
948{
949 spin_unlock_bh(&nr_neigh_list_lock);
950}
951
952static int nr_neigh_show(struct seq_file *seq, void *v)
953{
Ralf Baechlef75268c2005-09-06 15:49:39 -0700954 char buf[11];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700955 int i;
956
957 if (v == SEQ_START_TOKEN)
958 seq_puts(seq, "addr callsign dev qual lock count failed digipeaters\n");
959 else {
Li Zefan90dd7f52010-02-08 23:19:42 +0000960 struct nr_neigh *nr_neigh;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700961
Li Zefan90dd7f52010-02-08 23:19:42 +0000962 nr_neigh = hlist_entry(v, struct nr_neigh, neigh_node);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700963 seq_printf(seq, "%05d %-9s %-4s %3d %d %3d %3d",
964 nr_neigh->number,
Ralf Baechlef75268c2005-09-06 15:49:39 -0700965 ax2asc(buf, &nr_neigh->callsign),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700966 nr_neigh->dev ? nr_neigh->dev->name : "???",
967 nr_neigh->quality,
968 nr_neigh->locked,
969 nr_neigh->count,
970 nr_neigh->failed);
971
972 if (nr_neigh->digipeat != NULL) {
973 for (i = 0; i < nr_neigh->digipeat->ndigi; i++)
YOSHIFUJI Hideaki5f8f59d2007-02-09 23:25:09 +0900974 seq_printf(seq, " %s",
Ralf Baechlef75268c2005-09-06 15:49:39 -0700975 ax2asc(buf, &nr_neigh->digipeat->calls[i]));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700976 }
977
978 seq_puts(seq, "\n");
979 }
980 return 0;
981}
982
Philippe De Muyter56b3d972007-07-10 23:07:31 -0700983static const struct seq_operations nr_neigh_seqops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700984 .start = nr_neigh_start,
985 .next = nr_neigh_next,
986 .stop = nr_neigh_stop,
987 .show = nr_neigh_show,
988};
989
990static int nr_neigh_info_open(struct inode *inode, struct file *file)
991{
992 return seq_open(file, &nr_neigh_seqops);
993}
994
Arjan van de Venda7071d2007-02-12 00:55:36 -0800995const struct file_operations nr_neigh_fops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700996 .owner = THIS_MODULE,
997 .open = nr_neigh_info_open,
998 .read = seq_read,
999 .llseek = seq_lseek,
1000 .release = seq_release,
1001};
1002
1003#endif
1004
1005/*
1006 * Free all memory associated with the nodes and routes lists.
1007 */
1008void __exit nr_rt_free(void)
1009{
1010 struct nr_neigh *s = NULL;
1011 struct nr_node *t = NULL;
Sasha Levinb67bfe02013-02-27 17:06:00 -08001012 struct hlist_node *nodet;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001013
1014 spin_lock_bh(&nr_neigh_list_lock);
1015 spin_lock_bh(&nr_node_list_lock);
Sasha Levinb67bfe02013-02-27 17:06:00 -08001016 nr_node_for_each_safe(t, nodet, &nr_node_list) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001017 nr_node_lock(t);
1018 nr_remove_node_locked(t);
1019 nr_node_unlock(t);
1020 }
Sasha Levinb67bfe02013-02-27 17:06:00 -08001021 nr_neigh_for_each_safe(s, nodet, &nr_neigh_list) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001022 while(s->count) {
1023 s->count--;
1024 nr_neigh_put(s);
1025 }
1026 nr_remove_neigh_locked(s);
1027 }
1028 spin_unlock_bh(&nr_node_list_lock);
1029 spin_unlock_bh(&nr_neigh_list_lock);
1030}