OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

[/] [or1k/] [trunk/] [rc203soc/] [sw/] [uClinux/] [net/] [ax25/] [ax25_route.c] - Blame information for rev 1772

Go to most recent revision | Details | Compare with Previous | View Log

Line No. Rev Author Line
1 1629 jcastillo
/*
2
 *      AX.25 release 035
3
 *
4
 *      This code REQUIRES 1.2.1 or higher/ NET3.029
5
 *
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.
11
 *
12
 *      Other kernels modules in this kit are generally BSD derived. See the copyright headers.
13
 *
14
 *
15
 *      History
16
 *      AX.25 020       Jonathan(G4KLX) First go.
17
 *      AX.25 022       Jonathan(G4KLX) Added the actual meat to this - we now have a nice heard list.
18
 *      AX.25 025       Alan(GW4PTS)    First cut at autobinding by route scan.
19
 *      AX.25 028b      Jonathan(G4KLX) Extracted AX25 control block from the
20
 *                                      sock structure. Device removal now
21
 *                                      removes the heard structure.
22
 *      AX.25 029       Steven(GW7RRM)  Added /proc information for uid/callsign mapping.
23
 *                      Jonathan(G4KLX) Handling of IP mode in the routing list and /proc entry.
24
 *      AX.25 030       Jonathan(G4KLX) Added digi-peaters to routing table, and
25
 *                                      ioctls to manipulate them. Added port
26
 *                                      configuration.
27
 *      AX.25 031       Jonathan(G4KLX) Added concept of default route.
28
 *                      Joerg(DL1BKE)   ax25_rt_build_path() find digipeater list and device by
29
 *                                      destination call. Needed for IP routing via digipeater
30
 *                      Jonathan(G4KLX) Added routing for IP datagram packets.
31
 *                      Joerg(DL1BKE)   Changed routing for IP datagram and VC to use a default
32
 *                                      route if available. Does not overwrite default routes
33
 *                                      on route-table overflow anymore.
34
 *                      Joerg(DL1BKE)   Fixed AX.25 routing of IP datagram and VC, new ioctl()
35
 *                                      "SIOCAX25OPTRT" to set IP mode and a 'permanent' flag
36
 *                                      on routes.
37
 *      AX.25 033       Jonathan(G4KLX) Remove auto-router.
38
 *                      Joerg(DL1BKE)   Moved BPQ Ethernet driver to seperate device.
39
 *      AX.25 035       Frederic(F1OAT) Support for pseudo-digipeating.
40
 *                      Jonathan(G4KLX) Support for packet forwarding.
41
 */
42
 
43
#include <linux/config.h>
44
#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
45
#include <linux/errno.h>
46
#include <linux/types.h>
47
#include <linux/socket.h>
48
#include <linux/in.h>
49
#include <linux/kernel.h>
50
#include <linux/sched.h>
51
#include <linux/timer.h>
52
#include <linux/string.h>
53
#include <linux/sockios.h>
54
#include <linux/net.h>
55
#include <net/ax25.h>
56
#include <linux/inet.h>
57
#include <linux/netdevice.h>
58
#include <linux/if_arp.h>
59
#include <linux/skbuff.h>
60
#include <net/sock.h>
61
#include <asm/segment.h>
62
#include <asm/system.h>
63
#include <linux/fcntl.h>
64
#include <linux/mm.h>
65
#include <linux/interrupt.h>
66
 
67
static struct ax25_route {
68
        struct ax25_route *next;
69
        ax25_address callsign;
70
        struct device *dev;
71
        ax25_digi *digipeat;
72
        char ip_mode;
73
} *ax25_route = NULL;
74
 
75
struct ax25_dev ax25_device[AX25_MAX_DEVICES] = {
76
        {"", NULL}, {"", NULL}, {"", NULL}, {"", NULL}, {"", NULL},
77
        {"", NULL}, {"", NULL}, {"", NULL}, {"", NULL}, {"", NULL},
78
        {"", NULL}, {"", NULL}, {"", NULL}, {"", NULL}, {"", NULL},
79
        {"", NULL}, {"", NULL}, {"", NULL}, {"", NULL}, {"", NULL}
80
};
81
 
82
static struct ax25_route *ax25_find_route(ax25_address *, struct device *);
83
 
84
/*
85
 * small macro to drop non-digipeated digipeaters and reverse path
86
 */
87
static inline void ax25_route_invert(ax25_digi *in, ax25_digi *out)
88
{
89
        int k;
90
 
91
        for (k = 0; k < in->ndigi; k++)
92
                if (!in->repeated[k])
93
                        break;
94
 
95
        in->ndigi = k;
96
 
97
        ax25_digi_invert(in, out);
98
}
99
 
100
void ax25_rt_device_down(struct device *dev)
101
{
102
        struct ax25_route *s, *t, *ax25_rt = ax25_route;
103
 
104
        while (ax25_rt != NULL) {
105
                s       = ax25_rt;
106
                ax25_rt = ax25_rt->next;
107
 
108
                if (s->dev == dev) {
109
                        if (ax25_route == s) {
110
                                ax25_route = s->next;
111
                                if (s->digipeat != NULL)
112
                                        kfree_s((void *)s->digipeat, sizeof(ax25_digi));
113
                                kfree_s((void *)s, (sizeof *s));
114
                        } else {
115
                                for (t = ax25_route; t != NULL; t = t->next) {
116
                                        if (t->next == s) {
117
                                                t->next = s->next;
118
                                                if (s->digipeat != NULL)
119
                                                        kfree_s((void *)s->digipeat, sizeof(ax25_digi));
120
                                                kfree_s((void *)s, sizeof(*s));
121
                                                break;
122
                                        }
123
                                }
124
                        }
125
                }
126
        }
127
}
128
 
129
int ax25_rt_ioctl(unsigned int cmd, void *arg)
130
{
131
        unsigned long flags;
132
        struct ax25_route *s, *t, *ax25_rt;
133
        struct ax25_routes_struct route;
134
        struct ax25_route_opt_struct rt_option;
135
        struct device *dev;
136
        int i, err;
137
 
138
        switch (cmd) {
139
                case SIOCADDRT:
140
                        if ((err = verify_area(VERIFY_READ, arg, sizeof(route))) != 0)
141
                                return err;
142
                        memcpy_fromfs(&route, arg, sizeof(route));
143
                        if ((dev = ax25rtr_get_dev(&route.port_addr)) == NULL)
144
                                return -EINVAL;
145
                        if (route.digi_count > AX25_MAX_DIGIS)
146
                                return -EINVAL;
147
                        for (ax25_rt = ax25_route; ax25_rt != NULL; ax25_rt = ax25_rt->next) {
148
                                if (ax25cmp(&ax25_rt->callsign, &route.dest_addr) == 0 && ax25_rt->dev == dev) {
149
                                        if (ax25_rt->digipeat != NULL) {
150
                                                kfree_s(ax25_rt->digipeat, sizeof(ax25_digi));
151
                                                ax25_rt->digipeat = NULL;
152
                                        }
153
                                        if (route.digi_count != 0) {
154
                                                if ((ax25_rt->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL)
155
                                                        return -ENOMEM;
156
                                                ax25_rt->digipeat->lastrepeat = -1;
157
                                                ax25_rt->digipeat->ndigi      = route.digi_count;
158
                                                for (i = 0; i < route.digi_count; i++) {
159
                                                        ax25_rt->digipeat->repeated[i] = 0;
160
                                                        ax25_rt->digipeat->calls[i]    = route.digi_addr[i];
161
                                                }
162
                                        }
163
                                        return 0;
164
                                }
165
                        }
166
                        if ((ax25_rt = (struct ax25_route *)kmalloc(sizeof(struct ax25_route), GFP_ATOMIC)) == NULL)
167
                                return -ENOMEM;
168
                        ax25_rt->callsign     = route.dest_addr;
169
                        ax25_rt->dev          = dev;
170
                        ax25_rt->digipeat     = NULL;
171
                        ax25_rt->ip_mode      = ' ';
172
                        if (route.digi_count != 0) {
173
                                if ((ax25_rt->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL) {
174
                                        kfree_s(ax25_rt, sizeof(struct ax25_route));
175
                                        return -ENOMEM;
176
                                }
177
                                ax25_rt->digipeat->lastrepeat = -1;
178
                                ax25_rt->digipeat->ndigi      = route.digi_count;
179
                                for (i = 0; i < route.digi_count; i++) {
180
                                        ax25_rt->digipeat->repeated[i] = 0;
181
                                        ax25_rt->digipeat->calls[i]    = route.digi_addr[i];
182
                                }
183
                        }
184
                        save_flags(flags);
185
                        cli();
186
                        ax25_rt->next = ax25_route;
187
                        ax25_route    = ax25_rt;
188
                        restore_flags(flags);
189
                        break;
190
 
191
                case SIOCDELRT:
192
                        if ((err = verify_area(VERIFY_READ, arg, sizeof(route))) != 0)
193
                                return err;
194
                        memcpy_fromfs(&route, arg, sizeof(route));
195
                        if ((dev = ax25rtr_get_dev(&route.port_addr)) == NULL)
196
                                return -EINVAL;
197
                        ax25_rt = ax25_route;
198
                        while (ax25_rt != NULL) {
199
                                s       = ax25_rt;
200
                                ax25_rt = ax25_rt->next;
201
                                if (s->dev == dev && ax25cmp(&route.dest_addr, &s->callsign) == 0) {
202
                                        if (ax25_route == s) {
203
                                                ax25_route = s->next;
204
                                                if (s->digipeat != NULL)
205
                                                        kfree_s((void *)s->digipeat, sizeof(ax25_digi));
206
                                                kfree_s((void *)s, (sizeof *s));
207
                                        } else {
208
                                                for (t = ax25_route; t != NULL; t = t->next) {
209
                                                        if (t->next == s) {
210
                                                                t->next = s->next;
211
                                                                if (s->digipeat != NULL)
212
                                                                        kfree_s((void *)s->digipeat, sizeof(ax25_digi));
213
                                                                kfree_s((void *)s, sizeof(*s));
214
                                                                break;
215
                                                        }
216
                                                }
217
                                        }
218
                                }
219
                        }
220
                        break;
221
 
222
                case SIOCAX25OPTRT:
223
                        if ((err = verify_area(VERIFY_READ, arg, sizeof(rt_option))) != 0)
224
                                return err;
225
                        memcpy_fromfs(&rt_option, arg, sizeof(rt_option));
226
                        if ((dev = ax25rtr_get_dev(&rt_option.port_addr)) == NULL)
227
                                return -EINVAL;
228
                        for (ax25_rt = ax25_route; ax25_rt != NULL; ax25_rt = ax25_rt->next) {
229
                                if (ax25_rt->dev == dev && ax25cmp(&rt_option.dest_addr, &ax25_rt->callsign) == 0) {
230
                                        switch (rt_option.cmd) {
231
                                                case AX25_SET_RT_IPMODE:
232
                                                        switch (rt_option.arg) {
233
                                                                case ' ':
234
                                                                case 'D':
235
                                                                case 'V':
236
                                                                        ax25_rt->ip_mode = rt_option.arg;
237
                                                                        break;
238
                                                                default:
239
                                                                        return -EINVAL;
240
                                                        }
241
                                                        break;
242
                                                default:
243
                                                        return -EINVAL;
244
                                        }
245
                                }
246
                        }
247
                        break;
248
 
249
                default:
250
                        return -EINVAL;
251
        }
252
 
253
        return 0;
254
}
255
 
256
int ax25_rt_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
257
{
258
        struct ax25_route *ax25_rt;
259
        int len     = 0;
260
        off_t pos   = 0;
261
        off_t begin = 0;
262
        char *callsign;
263
        int i;
264
 
265
        cli();
266
 
267
        len += sprintf(buffer, "callsign  dev  mode digipeaters\n");
268
 
269
        for (ax25_rt = ax25_route; ax25_rt != NULL; ax25_rt = ax25_rt->next) {
270
                if (ax25cmp(&ax25_rt->callsign, &null_ax25_address) == 0)
271
                        callsign = "default";
272
                else
273
                        callsign = ax2asc(&ax25_rt->callsign);
274
                len += sprintf(buffer + len, "%-9s %-4s",
275
                        callsign,
276
                        ax25_rt->dev ? ax25_rt->dev->name : "???");
277
 
278
                switch (ax25_rt->ip_mode) {
279
                        case 'V':
280
                                len += sprintf(buffer + len, "   vc");
281
                                break;
282
                        case 'D':
283
                                len += sprintf(buffer + len, "   dg");
284
                                break;
285
                        default:
286
                                len += sprintf(buffer + len, "    *");
287
                                break;
288
                }
289
 
290
                if (ax25_rt->digipeat != NULL)
291
                        for (i = 0; i < ax25_rt->digipeat->ndigi; i++)
292
                                len += sprintf(buffer + len, " %s", ax2asc(&ax25_rt->digipeat->calls[i]));
293
 
294
                len += sprintf(buffer + len, "\n");
295
 
296
                pos = begin + len;
297
 
298
                if (pos < offset) {
299
                        len   = 0;
300
                        begin = pos;
301
                }
302
 
303
                if (pos > offset + length)
304
                        break;
305
        }
306
 
307
        sti();
308
 
309
        *start = buffer + (offset - begin);
310
        len   -= (offset - begin);
311
 
312
        if (len > length) len = length;
313
 
314
        return len;
315
}
316
 
317
int ax25_cs_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
318
{
319
        ax25_uid_assoc *pt;
320
        int len     = 0;
321
        off_t pos   = 0;
322
        off_t begin = 0;
323
 
324
        cli();
325
 
326
        len += sprintf(buffer, "Policy: %d\n", ax25_uid_policy);
327
 
328
        for (pt = ax25_uid_list; pt != NULL; pt = pt->next) {
329
                len += sprintf(buffer + len, "%6d %s\n", pt->uid, ax2asc(&pt->call));
330
 
331
                pos = begin + len;
332
 
333
                if (pos < offset) {
334
                        len = 0;
335
                        begin = pos;
336
                }
337
 
338
                if (pos > offset + length)
339
                        break;
340
        }
341
 
342
        sti();
343
 
344
        *start = buffer + (offset - begin);
345
        len   -= offset - begin;
346
 
347
        if (len > length) len = length;
348
 
349
        return len;
350
}
351
 
352
/*
353
 *      Find AX.25 route
354
 */
355
static struct ax25_route *ax25_find_route(ax25_address *addr, struct device *dev)
356
{
357
        struct ax25_route *ax25_spe_rt = NULL;
358
        struct ax25_route *ax25_def_rt = NULL;
359
        struct ax25_route *ax25_rt;
360
 
361
        /*
362
         *      Bind to the physical interface we heard them on, or the default
363
         *      route if none is found;
364
         */
365
        for (ax25_rt = ax25_route; ax25_rt != NULL; ax25_rt = ax25_rt->next) {
366
                if (dev == NULL) {
367
                        if (ax25cmp(&ax25_rt->callsign, addr) == 0 && ax25_rt->dev != NULL)
368
                                ax25_spe_rt = ax25_rt;
369
                        if (ax25cmp(&ax25_rt->callsign, &null_ax25_address) == 0 && ax25_rt->dev != NULL)
370
                                ax25_def_rt = ax25_rt;
371
                } else {
372
                        if (ax25cmp(&ax25_rt->callsign, addr) == 0 && ax25_rt->dev == dev)
373
                                ax25_spe_rt = ax25_rt;
374
                        if (ax25cmp(&ax25_rt->callsign, &null_ax25_address) == 0 && ax25_rt->dev == dev)
375
                                ax25_def_rt = ax25_rt;
376
                }
377
        }
378
 
379
        if (ax25_spe_rt != NULL)
380
                return ax25_spe_rt;
381
 
382
        return ax25_def_rt;
383
}
384
 
385
/*
386
 *      Adjust path: If you specify a default route and want to connect
387
 *      a target on the digipeater path but w/o having a special route
388
 *      set before, the path has to be truncated from your target on.
389
 */
390
static inline void ax25_adjust_path(ax25_address *addr, ax25_digi *digipeat)
391
{
392
        int k;
393
 
394
        for (k = 0; k < digipeat->ndigi; k++) {
395
                if (ax25cmp(addr, &digipeat->calls[k]) == 0)
396
                        break;
397
        }
398
 
399
        digipeat->ndigi = k;
400
}
401
 
402
 
403
/*
404
 *      Find which interface to use.
405
 */
406
int ax25_rt_autobind(ax25_cb *ax25, ax25_address *addr)
407
{
408
        struct ax25_route *ax25_rt;
409
        ax25_address *call;
410
 
411
        if ((ax25_rt = ax25_find_route(addr, NULL)) == NULL)
412
                return -EHOSTUNREACH;
413
 
414
        ax25->device = ax25_rt->dev;
415
 
416
        if ((call = ax25_findbyuid(current->euid)) == NULL) {
417
                if (ax25_uid_policy && !suser())
418
                        return -EPERM;
419
                call = (ax25_address *)ax25->device->dev_addr;
420
        }
421
 
422
        ax25->source_addr = *call;
423
 
424
        if (ax25_rt->digipeat != NULL) {
425
                if ((ax25->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL)
426
                        return -ENOMEM;
427
                *ax25->digipeat = *ax25_rt->digipeat;
428
                ax25_adjust_path(addr, ax25->digipeat);
429
        }
430
 
431
        if (ax25->sk != NULL)
432
                ax25->sk->zapped = 0;
433
 
434
        return 0;
435
}
436
 
437
/*
438
 *      dl1bke 960117: build digipeater path
439
 *      dl1bke 960301: use the default route if it exists
440
 */
441
ax25_digi *ax25_rt_find_path(ax25_address *addr, struct device *dev)
442
{
443
        struct ax25_route *ax25_rt;
444
 
445
        if ((ax25_rt = ax25_find_route(addr, dev)) == NULL)
446
                return NULL;
447
 
448
        return ax25_rt->digipeat;
449
}
450
 
451
void ax25_rt_build_path(struct sk_buff *skb, ax25_address *src, ax25_address *dest, ax25_digi *digi)
452
{
453
        unsigned char *bp;
454
        int len;
455
 
456
        len = digi->ndigi * AX25_ADDR_LEN;
457
 
458
        if (skb_headroom(skb) < len) {
459
                printk(KERN_CRIT "ax25_rt_build_path: not enough headroom for digis in skb\n");
460
                return;
461
        }
462
 
463
        bp = skb_push(skb, len);
464
 
465
        build_ax25_addr(bp, src, dest, digi, AX25_COMMAND, AX25_MODULUS);
466
}
467
 
468
/*
469
 *      Return the IP mode of a given callsign/device pair.
470
 */
471
char ax25_rt_mode_get(ax25_address *callsign, struct device *dev)
472
{
473
        struct ax25_route *ax25_rt;
474
 
475
        for (ax25_rt = ax25_route; ax25_rt != NULL; ax25_rt = ax25_rt->next)
476
                if (ax25cmp(&ax25_rt->callsign, callsign) == 0 && ax25_rt->dev == dev)
477
                        return ax25_rt->ip_mode;
478
 
479
        return ' ';
480
}
481
 
482
static struct ax25_dev *ax25_dev_get_dev(struct device *dev)
483
{
484
        int i;
485
 
486
        for (i = 0; i < AX25_MAX_DEVICES; i++)
487
                if (ax25_device[i].dev != NULL && ax25_device[i].dev == dev)
488
                        return ax25_device + i;
489
 
490
        return NULL;
491
}
492
 
493
/*
494
 *      Wow, a bit of data hiding. Is this C++ or what ?
495
 */
496
int ax25_dev_get_value(struct device *dev, int valueno)
497
{
498
        struct ax25_dev *ax25_dev;
499
 
500
        if ((ax25_dev = ax25_dev_get_dev(dev)) == NULL) {
501
                printk(KERN_WARNING "ax25_dev_get_value called with invalid device\n");
502
                return 1;
503
        }
504
 
505
        return ax25_dev->values[valueno];
506
}
507
 
508
/*
509
 *      This is called when an interface is brought up. These are
510
 *      reasonable defaults.
511
 */
512
void ax25_dev_device_up(struct device *dev)
513
{
514
        struct ax25_dev *ax25_dev = NULL;
515
        int i;
516
 
517
        for (i = 0; i < AX25_MAX_DEVICES; i++) {
518
                if (ax25_device[i].dev == NULL) {
519
                        ax25_dev = ax25_device + i;
520
                        break;
521
                }
522
        }
523
 
524
        if (ax25_dev == NULL) {
525
                printk(KERN_ERR "ax25_dev_device_up cannot find free AX.25 device\n");
526
                return;
527
        }
528
 
529
        ax25_unregister_sysctl();
530
 
531
        strcpy(ax25_dev->name, dev->name);
532
 
533
        ax25_dev->dev     = dev;
534
        ax25_dev->forward = NULL;
535
 
536
        ax25_dev->values[AX25_VALUES_IPDEFMODE] = AX25_DEF_IPDEFMODE;
537
        ax25_dev->values[AX25_VALUES_AXDEFMODE] = AX25_DEF_AXDEFMODE;
538
        ax25_dev->values[AX25_VALUES_BACKOFF]   = AX25_DEF_BACKOFF;
539
        ax25_dev->values[AX25_VALUES_CONMODE]   = AX25_DEF_CONMODE;
540
        ax25_dev->values[AX25_VALUES_WINDOW]    = AX25_DEF_WINDOW;
541
        ax25_dev->values[AX25_VALUES_EWINDOW]   = AX25_DEF_EWINDOW;
542
        ax25_dev->values[AX25_VALUES_T1]        = AX25_DEF_T1;
543
        ax25_dev->values[AX25_VALUES_T2]        = AX25_DEF_T2;
544
        ax25_dev->values[AX25_VALUES_T3]        = AX25_DEF_T3;
545
        ax25_dev->values[AX25_VALUES_IDLE]      = AX25_DEF_IDLE;
546
        ax25_dev->values[AX25_VALUES_N2]        = AX25_DEF_N2;
547
        ax25_dev->values[AX25_VALUES_PACLEN]    = AX25_DEF_PACLEN;
548
 
549
        ax25_register_sysctl();
550
}
551
 
552
void ax25_dev_device_down(struct device *dev)
553
{
554
        struct ax25_dev *ax25_dev;
555
 
556
        ax25_unregister_sysctl();
557
 
558
        if ((ax25_dev = ax25_dev_get_dev(dev)) != NULL)
559
                ax25_dev->dev = NULL;
560
 
561
        ax25_register_sysctl();
562
}
563
 
564
int ax25_fwd_ioctl(unsigned int cmd, struct ax25_fwd_struct *fwd)
565
{
566
        struct device *dev;
567
        struct ax25_dev *ax25_dev;
568
 
569
        if ((dev = ax25rtr_get_dev(&fwd->port_from)) == NULL)
570
                return -EINVAL;
571
 
572
        if ((ax25_dev = ax25_dev_get_dev(dev)) == NULL)
573
                return -EINVAL;
574
 
575
        switch (cmd) {
576
                case SIOCAX25ADDFWD:
577
                        if ((dev = ax25rtr_get_dev(&fwd->port_to)) == NULL)
578
                                return -EINVAL;
579
                        if (ax25_dev->forward != NULL)
580
                                return -EINVAL;
581
                        ax25_dev->forward = dev;
582
                        break;
583
 
584
                case SIOCAX25DELFWD:
585
                        if (ax25_dev->forward == NULL)
586
                                return -EINVAL;
587
                        ax25_dev->forward = NULL;
588
                        break;
589
 
590
                default:
591
                        return -EINVAL;
592
        }
593
 
594
        return 0;
595
}
596
 
597
struct device *ax25_fwd_dev(struct device *dev)
598
{
599
        struct ax25_dev *ax25_dev;
600
 
601
        if ((ax25_dev = ax25_dev_get_dev(dev)) == NULL)
602
                return dev;
603
 
604
        if (ax25_dev->forward == NULL)
605
                return dev;
606
 
607
        return ax25_dev->forward;
608
}
609
 
610
#ifdef MODULE
611
 
612
/*
613
 *      Free all memory associated with routing and device structures.
614
 */
615
void ax25_rt_free(void)
616
{
617
        struct ax25_route *s, *ax25_rt = ax25_route;
618
 
619
        while (ax25_rt != NULL) {
620
                s       = ax25_rt;
621
                ax25_rt = ax25_rt->next;
622
 
623
                if (s->digipeat != NULL)
624
                        kfree_s(s->digipeat, sizeof(ax25_digi));
625
 
626
                kfree_s(s, sizeof(struct ax25_route));
627
        }
628
}
629
 
630
#endif
631
 
632
#endif

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.