Import 2.1.42pre1
[davej-history.git] / net / netrom / nr_route.c
blob41399a53caa35abfc42d0cfb8e60f5b926ab61cd
1 /*
2 * NET/ROM release 006
4 * This code REQUIRES 2.1.15 or higher/ NET3.038
6 * This module:
7 * This module is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version
10 * 2 of the License, or (at your option) any later version.
12 * History
13 * NET/ROM 001 Jonathan(G4KLX) First attempt.
14 * NET/ROM 003 Jonathan(G4KLX) Use SIOCADDRT/SIOCDELRT ioctl values
15 * for NET/ROM routes.
16 * Use '*' for a blank mnemonic in /proc/net/nr_nodes.
17 * Change default quality for new neighbour when same
18 * as node callsign.
19 * Alan Cox(GW4PTS) Added the firewall hooks.
20 * NET/ROM 006 Jonathan(G4KLX) Added the setting of digipeated neighbours.
21 * Tomi(OH2BNS) Routing quality and link failure changes.
24 #include <linux/config.h>
25 #if defined(CONFIG_NETROM) || defined(CONFIG_NETROM_MODULE)
26 #include <linux/errno.h>
27 #include <linux/types.h>
28 #include <linux/socket.h>
29 #include <linux/in.h>
30 #include <linux/kernel.h>
31 #include <linux/sched.h>
32 #include <linux/timer.h>
33 #include <linux/string.h>
34 #include <linux/sockios.h>
35 #include <linux/net.h>
36 #include <net/ax25.h>
37 #include <linux/inet.h>
38 #include <linux/netdevice.h>
39 #include <net/arp.h>
40 #include <linux/if_arp.h>
41 #include <linux/skbuff.h>
42 #include <net/sock.h>
43 #include <asm/uaccess.h>
44 #include <asm/system.h>
45 #include <linux/fcntl.h>
46 #include <linux/termios.h> /* For TIOCINQ/OUTQ */
47 #include <linux/mm.h>
48 #include <linux/interrupt.h>
49 #include <linux/notifier.h>
50 #include <linux/firewall.h>
51 #include <net/netrom.h>
53 static unsigned int nr_neigh_no = 1;
55 static struct nr_node *nr_node_list = NULL;
56 static struct nr_neigh *nr_neigh_list = NULL;
58 static void nr_remove_neigh(struct nr_neigh *);
61 * Add a new route to a node, and in the process add the node and the
62 * neighbour if it is new.
64 static int nr_add_node(ax25_address *nr, const char *mnemonic, ax25_address *ax25,
65 ax25_digi *ax25_digi, struct device *dev, int quality, int obs_count)
67 struct nr_node *nr_node;
68 struct nr_neigh *nr_neigh;
69 struct nr_route nr_route;
70 unsigned long flags;
71 int i, found;
73 if (nr_dev_get(nr) != NULL) /* Can't add routes to ourself */
74 return -EINVAL;
76 for (nr_node = nr_node_list; nr_node != NULL; nr_node = nr_node->next)
77 if (ax25cmp(nr, &nr_node->callsign) == 0)
78 break;
80 for (nr_neigh = nr_neigh_list; nr_neigh != NULL; nr_neigh = nr_neigh->next)
81 if (ax25cmp(ax25, &nr_neigh->callsign) == 0 && nr_neigh->dev == dev)
82 break;
84 if (nr_neigh != NULL)
85 nr_neigh->failed = 0;
87 if (quality == 0 && nr_neigh != NULL && nr_node != NULL)
88 return 0;
90 if (nr_neigh == NULL) {
91 if ((nr_neigh = kmalloc(sizeof(*nr_neigh), GFP_ATOMIC)) == NULL)
92 return -ENOMEM;
94 nr_neigh->callsign = *ax25;
95 nr_neigh->digipeat = NULL;
96 nr_neigh->dev = dev;
97 nr_neigh->quality = sysctl_netrom_default_path_quality;
98 nr_neigh->locked = 0;
99 nr_neigh->count = 0;
100 nr_neigh->number = nr_neigh_no++;
101 nr_neigh->failed = 0;
103 if (ax25_digi != NULL && ax25_digi->ndigi > 0) {
104 if ((nr_neigh->digipeat = kmalloc(sizeof(*ax25_digi), GFP_KERNEL)) == NULL) {
105 kfree(nr_neigh);
106 return -ENOMEM;
108 *nr_neigh->digipeat = *ax25_digi;
111 save_flags(flags);
112 cli();
114 nr_neigh->next = nr_neigh_list;
115 nr_neigh_list = nr_neigh;
117 restore_flags(flags);
120 if (quality != 0 && ax25cmp(nr, ax25) == 0 && !nr_neigh->locked)
121 nr_neigh->quality = quality;
123 if (nr_node == NULL) {
124 if ((nr_node = kmalloc(sizeof(*nr_node), GFP_ATOMIC)) == NULL)
125 return -ENOMEM;
127 nr_node->callsign = *nr;
128 strcpy(nr_node->mnemonic, mnemonic);
130 nr_node->which = 0;
131 nr_node->count = 1;
133 nr_node->routes[0].quality = quality;
134 nr_node->routes[0].obs_count = obs_count;
135 nr_node->routes[0].neighbour = nr_neigh;
137 save_flags(flags);
138 cli();
140 nr_node->next = nr_node_list;
141 nr_node_list = nr_node;
143 restore_flags(flags);
145 nr_neigh->count++;
147 return 0;
150 if (quality != 0)
151 strcpy(nr_node->mnemonic, mnemonic);
153 for (found = 0, i = 0; i < nr_node->count; i++) {
154 if (nr_node->routes[i].neighbour == nr_neigh) {
155 nr_node->routes[i].quality = quality;
156 nr_node->routes[i].obs_count = obs_count;
157 found = 1;
158 break;
162 if (!found) {
163 /* We have space at the bottom, slot it in */
164 if (nr_node->count < 3) {
165 nr_node->routes[2] = nr_node->routes[1];
166 nr_node->routes[1] = nr_node->routes[0];
168 nr_node->routes[0].quality = quality;
169 nr_node->routes[0].obs_count = obs_count;
170 nr_node->routes[0].neighbour = nr_neigh;
172 nr_node->which++;
173 nr_node->count++;
174 nr_neigh->count++;
175 } else {
176 /* It must be better than the worst */
177 if (quality > nr_node->routes[2].quality) {
178 nr_node->routes[2].neighbour->count--;
180 if (nr_node->routes[2].neighbour->count == 0 && !nr_node->routes[2].neighbour->locked)
181 nr_remove_neigh(nr_node->routes[2].neighbour);
183 nr_node->routes[2].quality = quality;
184 nr_node->routes[2].obs_count = obs_count;
185 nr_node->routes[2].neighbour = nr_neigh;
187 nr_neigh->count++;
192 /* Now re-sort the routes in quality order */
193 switch (nr_node->count) {
194 case 3:
195 if (nr_node->routes[1].quality > nr_node->routes[0].quality) {
196 switch (nr_node->which) {
197 case 0: nr_node->which = 1; break;
198 case 1: nr_node->which = 0; break;
199 default: break;
201 nr_route = nr_node->routes[0];
202 nr_node->routes[0] = nr_node->routes[1];
203 nr_node->routes[1] = nr_route;
205 if (nr_node->routes[2].quality > nr_node->routes[1].quality) {
206 switch (nr_node->which) {
207 case 1: nr_node->which = 2; break;
208 case 2: nr_node->which = 1; break;
209 default: break;
211 nr_route = nr_node->routes[1];
212 nr_node->routes[1] = nr_node->routes[2];
213 nr_node->routes[2] = nr_route;
215 case 2:
216 if (nr_node->routes[1].quality > nr_node->routes[0].quality) {
217 switch (nr_node->which) {
218 case 0: nr_node->which = 1; break;
219 case 1: nr_node->which = 0; break;
220 default: break;
222 nr_route = nr_node->routes[0];
223 nr_node->routes[0] = nr_node->routes[1];
224 nr_node->routes[1] = nr_route;
226 case 1:
227 break;
230 for (i = 0; i < nr_node->count; i++) {
231 if (nr_node->routes[i].neighbour == nr_neigh) {
232 if (i < nr_node->which)
233 nr_node->which = i;
234 break;
238 return 0;
241 static void nr_remove_node(struct nr_node *nr_node)
243 struct nr_node *s;
244 unsigned long flags;
246 save_flags(flags);
247 cli();
249 if ((s = nr_node_list) == nr_node) {
250 nr_node_list = nr_node->next;
251 restore_flags(flags);
252 kfree(nr_node);
253 return;
256 while (s != NULL && s->next != NULL) {
257 if (s->next == nr_node) {
258 s->next = nr_node->next;
259 restore_flags(flags);
260 kfree(nr_node);
261 return;
264 s = s->next;
267 restore_flags(flags);
270 static void nr_remove_neigh(struct nr_neigh *nr_neigh)
272 struct nr_neigh *s;
273 unsigned long flags;
275 save_flags(flags);
276 cli();
278 if ((s = nr_neigh_list) == nr_neigh) {
279 nr_neigh_list = nr_neigh->next;
280 restore_flags(flags);
281 if (nr_neigh->digipeat != NULL)
282 kfree(nr_neigh->digipeat);
283 kfree(nr_neigh);
284 return;
287 while (s != NULL && s->next != NULL) {
288 if (s->next == nr_neigh) {
289 s->next = nr_neigh->next;
290 restore_flags(flags);
291 if (nr_neigh->digipeat != NULL)
292 kfree(nr_neigh->digipeat);
293 kfree(nr_neigh);
294 return;
297 s = s->next;
300 restore_flags(flags);
304 * "Delete" a node. Strictly speaking remove a route to a node. The node
305 * is only deleted if no routes are left to it.
307 static int nr_del_node(ax25_address *callsign, ax25_address *neighbour, struct device *dev)
309 struct nr_node *nr_node;
310 struct nr_neigh *nr_neigh;
311 int i;
313 for (nr_node = nr_node_list; nr_node != NULL; nr_node = nr_node->next)
314 if (ax25cmp(callsign, &nr_node->callsign) == 0)
315 break;
317 if (nr_node == NULL) return -EINVAL;
319 for (nr_neigh = nr_neigh_list; nr_neigh != NULL; nr_neigh = nr_neigh->next)
320 if (ax25cmp(neighbour, &nr_neigh->callsign) == 0 && nr_neigh->dev == dev)
321 break;
323 if (nr_neigh == NULL) return -EINVAL;
325 for (i = 0; i < nr_node->count; i++) {
326 if (nr_node->routes[i].neighbour == nr_neigh) {
327 nr_neigh->count--;
329 if (nr_neigh->count == 0 && !nr_neigh->locked)
330 nr_remove_neigh(nr_neigh);
332 nr_node->count--;
334 if (nr_node->count == 0) {
335 nr_remove_node(nr_node);
336 } else {
337 switch (i) {
338 case 0:
339 nr_node->routes[0] = nr_node->routes[1];
340 case 1:
341 nr_node->routes[1] = nr_node->routes[2];
342 case 2:
343 break;
347 return 0;
351 return -EINVAL;
355 * Lock a neighbour with a quality.
357 static int nr_add_neigh(ax25_address *callsign, ax25_digi *ax25_digi, struct device *dev, unsigned int quality)
359 struct nr_neigh *nr_neigh;
360 unsigned long flags;
362 for (nr_neigh = nr_neigh_list; nr_neigh != NULL; nr_neigh = nr_neigh->next) {
363 if (ax25cmp(callsign, &nr_neigh->callsign) == 0 && nr_neigh->dev == dev) {
364 nr_neigh->quality = quality;
365 nr_neigh->locked = 1;
366 return 0;
370 if ((nr_neigh = kmalloc(sizeof(*nr_neigh), GFP_ATOMIC)) == NULL)
371 return -ENOMEM;
373 nr_neigh->callsign = *callsign;
374 nr_neigh->digipeat = NULL;
375 nr_neigh->dev = dev;
376 nr_neigh->quality = quality;
377 nr_neigh->locked = 1;
378 nr_neigh->count = 0;
379 nr_neigh->number = nr_neigh_no++;
380 nr_neigh->failed = 0;
382 if (ax25_digi != NULL && ax25_digi->ndigi > 0) {
383 if ((nr_neigh->digipeat = kmalloc(sizeof(*ax25_digi), GFP_KERNEL)) == NULL) {
384 kfree(nr_neigh);
385 return -ENOMEM;
387 *nr_neigh->digipeat = *ax25_digi;
390 save_flags(flags);
391 cli();
393 nr_neigh->next = nr_neigh_list;
394 nr_neigh_list = nr_neigh;
396 restore_flags(flags);
398 return 0;
402 * "Delete" a neighbour. The neighbour is only removed if the number
403 * of nodes that may use it is zero.
405 static int nr_del_neigh(ax25_address *callsign, struct device *dev, unsigned int quality)
407 struct nr_neigh *nr_neigh;
409 for (nr_neigh = nr_neigh_list; nr_neigh != NULL; nr_neigh = nr_neigh->next)
410 if (ax25cmp(callsign, &nr_neigh->callsign) == 0 && nr_neigh->dev == dev)
411 break;
413 if (nr_neigh == NULL) return -EINVAL;
415 nr_neigh->quality = quality;
416 nr_neigh->locked = 0;
418 if (nr_neigh->count == 0)
419 nr_remove_neigh(nr_neigh);
421 return 0;
425 * Decrement the obsolescence count by one. If a route is reduced to a
426 * count of zero, remove it. Also remove any unlocked neighbours with
427 * zero nodes routing via it.
429 static int nr_dec_obs(void)
431 struct nr_neigh *nr_neigh;
432 struct nr_node *s, *nr_node;
433 int i;
435 nr_node = nr_node_list;
437 while (nr_node != NULL) {
438 s = nr_node;
439 nr_node = nr_node->next;
441 for (i = 0; i < s->count; i++) {
442 switch (s->routes[i].obs_count) {
444 case 0: /* A locked entry */
445 break;
447 case 1: /* From 1 -> 0 */
448 nr_neigh = s->routes[i].neighbour;
450 nr_neigh->count--;
452 if (nr_neigh->count == 0 && !nr_neigh->locked)
453 nr_remove_neigh(nr_neigh);
455 s->count--;
457 switch (i) {
458 case 0:
459 s->routes[0] = s->routes[1];
460 case 1:
461 s->routes[1] = s->routes[2];
462 case 2:
463 break;
465 break;
467 default:
468 s->routes[i].obs_count--;
469 break;
474 if (s->count <= 0)
475 nr_remove_node(s);
478 return 0;
482 * A device has been removed. Remove its routes and neighbours.
484 void nr_rt_device_down(struct device *dev)
486 struct nr_neigh *s, *nr_neigh = nr_neigh_list;
487 struct nr_node *t, *nr_node;
488 int i;
490 while (nr_neigh != NULL) {
491 s = nr_neigh;
492 nr_neigh = nr_neigh->next;
494 if (s->dev == dev) {
495 nr_node = nr_node_list;
497 while (nr_node != NULL) {
498 t = nr_node;
499 nr_node = nr_node->next;
501 for (i = 0; i < t->count; i++) {
502 if (t->routes[i].neighbour == s) {
503 t->count--;
505 switch (i) {
506 case 0:
507 t->routes[0] = t->routes[1];
508 case 1:
509 t->routes[1] = t->routes[2];
510 case 2:
511 break;
516 if (t->count <= 0)
517 nr_remove_node(t);
520 nr_remove_neigh(s);
526 * Check that the device given is a valid AX.25 interface that is "up".
527 * Or a valid ethernet interface with an AX.25 callsign binding.
529 static struct device *nr_ax25_dev_get(char *devname)
531 struct device *dev;
533 if ((dev = dev_get(devname)) == NULL)
534 return NULL;
536 if ((dev->flags & IFF_UP) && dev->type == ARPHRD_AX25)
537 return dev;
539 return NULL;
543 * Find the first active NET/ROM device, usually "nr0".
545 struct device *nr_dev_first(void)
547 struct device *dev, *first = NULL;
549 for (dev = dev_base; dev != NULL; dev = dev->next)
550 if ((dev->flags & IFF_UP) && dev->type == ARPHRD_NETROM)
551 if (first == NULL || strncmp(dev->name, first->name, 3) < 0)
552 first = dev;
554 return first;
558 * Find the NET/ROM device for the given callsign.
560 struct device *nr_dev_get(ax25_address *addr)
562 struct device *dev;
564 for (dev = dev_base; dev != NULL; dev = dev->next)
565 if ((dev->flags & IFF_UP) && dev->type == ARPHRD_NETROM && ax25cmp(addr, (ax25_address *)dev->dev_addr) == 0)
566 return dev;
568 return NULL;
571 static ax25_digi *nr_call_to_digi(int ndigis, ax25_address *digipeaters)
573 static ax25_digi ax25_digi;
574 int i;
576 if (ndigis == 0)
577 return NULL;
579 for (i = 0; i < ndigis; i++) {
580 ax25_digi.calls[i] = digipeaters[i];
581 ax25_digi.repeated[i] = 0;
584 ax25_digi.ndigi = ndigis;
585 ax25_digi.lastrepeat = 0;
587 return &ax25_digi;
591 * Handle the ioctls that control the routing functions.
593 int nr_rt_ioctl(unsigned int cmd, void *arg)
595 struct nr_route_struct nr_route;
596 struct device *dev;
597 int err;
599 switch (cmd) {
601 case SIOCADDRT:
602 if ((err = verify_area(VERIFY_READ, arg, sizeof(struct nr_route_struct))) != 0)
603 return err;
604 copy_from_user(&nr_route, arg, sizeof(struct nr_route_struct));
605 if ((dev = nr_ax25_dev_get(nr_route.device)) == NULL)
606 return -EINVAL;
607 if (nr_route.ndigis < 0 || nr_route.ndigis > AX25_MAX_DIGIS)
608 return -EINVAL;
609 switch (nr_route.type) {
610 case NETROM_NODE:
611 return nr_add_node(&nr_route.callsign,
612 nr_route.mnemonic,
613 &nr_route.neighbour,
614 nr_call_to_digi(nr_route.ndigis, nr_route.digipeaters),
615 dev, nr_route.quality,
616 nr_route.obs_count);
617 case NETROM_NEIGH:
618 return nr_add_neigh(&nr_route.callsign,
619 nr_call_to_digi(nr_route.ndigis, nr_route.digipeaters),
620 dev, nr_route.quality);
621 default:
622 return -EINVAL;
625 case SIOCDELRT:
626 if ((err = verify_area(VERIFY_READ, arg, sizeof(struct nr_route_struct))) != 0)
627 return err;
628 copy_from_user(&nr_route, arg, sizeof(struct nr_route_struct));
629 if ((dev = nr_ax25_dev_get(nr_route.device)) == NULL)
630 return -EINVAL;
631 switch (nr_route.type) {
632 case NETROM_NODE:
633 return nr_del_node(&nr_route.callsign,
634 &nr_route.neighbour, dev);
635 case NETROM_NEIGH:
636 return nr_del_neigh(&nr_route.callsign,
637 dev, nr_route.quality);
638 default:
639 return -EINVAL;
642 case SIOCNRDECOBS:
643 return nr_dec_obs();
645 default:
646 return -EINVAL;
649 return 0;
653 * A level 2 link has timed out, therefore it appears to be a poor link,
654 * then don't use that neighbour until it is reset.
656 void nr_link_failed(ax25_address *callsign, struct device *dev)
658 struct nr_neigh *nr_neigh;
659 struct nr_node *nr_node;
661 for (nr_neigh = nr_neigh_list; nr_neigh != NULL; nr_neigh = nr_neigh->next)
662 if (ax25cmp(&nr_neigh->callsign, callsign) == 0 && nr_neigh->dev == dev)
663 break;
665 if (nr_neigh == NULL) return;
667 if (++nr_neigh->failed < sysctl_netrom_link_fails_count) return;
669 for (nr_node = nr_node_list; nr_node != NULL; nr_node = nr_node->next)
670 if (nr_node->which < nr_node->count && nr_node->routes[nr_node->which].neighbour == nr_neigh)
671 nr_node->which++;
675 * Route a frame to an appropriate AX.25 connection. A NULL ax25_cb
676 * indicates an internally generated frame.
678 int nr_route_frame(struct sk_buff *skb, ax25_cb *ax25)
680 ax25_address *nr_src, *nr_dest;
681 struct nr_neigh *nr_neigh;
682 struct nr_node *nr_node;
683 struct device *dev;
684 unsigned char *dptr;
686 if (ax25 != NULL && call_in_firewall(PF_NETROM, skb->dev, skb->data, NULL, &skb) != FW_ACCEPT)
687 return 0;
689 if (ax25 == NULL && call_out_firewall(PF_NETROM, skb->dev, skb->data, NULL, &skb) != FW_ACCEPT)
690 return 0;
692 nr_src = (ax25_address *)(skb->data + 0);
693 nr_dest = (ax25_address *)(skb->data + 7);
695 if (ax25 != NULL)
696 nr_add_node(nr_src, "", &ax25->dest_addr, ax25->digipeat,
697 ax25->ax25_dev->dev, 0, sysctl_netrom_obsolescence_count_initialiser);
699 if ((dev = nr_dev_get(nr_dest)) != NULL) /* Its for me */
700 return nr_rx_frame(skb, dev);
702 if (!sysctl_netrom_routing_control && ax25 != NULL)
703 return 0;
705 /* Its Time-To-Live has expired */
706 if (--skb->data[14] == 0)
707 return 0;
709 for (nr_node = nr_node_list; nr_node != NULL; nr_node = nr_node->next)
710 if (ax25cmp(nr_dest, &nr_node->callsign) == 0)
711 break;
713 if (nr_node == NULL || nr_node->which >= nr_node->count)
714 return 0;
716 nr_neigh = nr_node->routes[nr_node->which].neighbour;
718 if ((dev = nr_dev_first()) == NULL)
719 return 0;
721 if (ax25 != NULL && call_fw_firewall(PF_NETROM, skb->dev, skb->data, NULL, &skb) != FW_ACCEPT)
722 return 0;
724 dptr = skb_push(skb, 1);
725 *dptr = AX25_P_NETROM;
727 return ax25_send_frame(skb, 256, (ax25_address *)dev->dev_addr, &nr_neigh->callsign, nr_neigh->digipeat, nr_neigh->dev);
730 int nr_nodes_get_info(char *buffer, char **start, off_t offset,
731 int length, int dummy)
733 struct nr_node *nr_node;
734 int len = 0;
735 off_t pos = 0;
736 off_t begin = 0;
737 int i;
739 cli();
741 len += sprintf(buffer, "callsign mnemonic w n qual obs neigh qual obs neigh qual obs neigh\n");
743 for (nr_node = nr_node_list; nr_node != NULL; nr_node = nr_node->next) {
744 len += sprintf(buffer + len, "%-9s %-7s %d %d",
745 ax2asc(&nr_node->callsign),
746 (nr_node->mnemonic[0] == '\0') ? "*" : nr_node->mnemonic,
747 nr_node->which + 1,
748 nr_node->count);
750 for (i = 0; i < nr_node->count; i++) {
751 len += sprintf(buffer + len, " %3d %d %05d",
752 nr_node->routes[i].quality,
753 nr_node->routes[i].obs_count,
754 nr_node->routes[i].neighbour->number);
757 len += sprintf(buffer + len, "\n");
759 pos = begin + len;
761 if (pos < offset) {
762 len = 0;
763 begin = pos;
766 if (pos > offset + length)
767 break;
770 sti();
772 *start = buffer + (offset - begin);
773 len -= (offset - begin);
775 if (len > length) len = length;
777 return len;
780 int nr_neigh_get_info(char *buffer, char **start, off_t offset,
781 int length, int dummy)
783 struct nr_neigh *nr_neigh;
784 int len = 0;
785 off_t pos = 0;
786 off_t begin = 0;
787 int i;
789 cli();
791 len += sprintf(buffer, "addr callsign dev qual lock count failed digipeaters\n");
793 for (nr_neigh = nr_neigh_list; nr_neigh != NULL; nr_neigh = nr_neigh->next) {
794 len += sprintf(buffer + len, "%05d %-9s %-4s %3d %d %3d %3d",
795 nr_neigh->number,
796 ax2asc(&nr_neigh->callsign),
797 nr_neigh->dev ? nr_neigh->dev->name : "???",
798 nr_neigh->quality,
799 nr_neigh->locked,
800 nr_neigh->count,
801 nr_neigh->failed);
803 if (nr_neigh->digipeat != NULL) {
804 for (i = 0; i < nr_neigh->digipeat->ndigi; i++)
805 len += sprintf(buffer + len, " %s", ax2asc(&nr_neigh->digipeat->calls[i]));
808 len += sprintf(buffer + len, "\n");
810 pos = begin + len;
812 if (pos < offset) {
813 len = 0;
814 begin = pos;
817 if (pos > offset + length)
818 break;
821 sti();
823 *start = buffer + (offset - begin);
824 len -= (offset - begin);
826 if (len > length) len = length;
828 return len;
831 #ifdef MODULE
834 * Free all memory associated with the nodes and routes lists.
836 void nr_rt_free(void)
838 struct nr_neigh *s, *nr_neigh = nr_neigh_list;
839 struct nr_node *t, *nr_node = nr_node_list;
841 while (nr_node != NULL) {
842 t = nr_node;
843 nr_node = nr_node->next;
845 nr_remove_node(t);
848 while (nr_neigh != NULL) {
849 s = nr_neigh;
850 nr_neigh = nr_neigh->next;
852 nr_remove_neigh(s);
856 #endif
858 #endif