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

Subversion Repositories or1k

[/] [or1k/] [trunk/] [linux/] [linux-2.4/] [net/] [ipv4/] [fib_semantics.c] - Blame information for rev 1765

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 1275 phoenix
/*
2
 * INET         An implementation of the TCP/IP protocol suite for the LINUX
3
 *              operating system.  INET is implemented using the  BSD Socket
4
 *              interface as the means of communication with the user level.
5
 *
6
 *              IPv4 Forwarding Information Base: semantics.
7
 *
8
 * Version:     $Id: fib_semantics.c,v 1.1.1.1 2004-04-15 01:13:45 phoenix Exp $
9
 *
10
 * Authors:     Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
11
 *
12
 *              This program is free software; you can redistribute it and/or
13
 *              modify it under the terms of the GNU General Public License
14
 *              as published by the Free Software Foundation; either version
15
 *              2 of the License, or (at your option) any later version.
16
 */
17
 
18
#include <linux/config.h>
19
#include <asm/uaccess.h>
20
#include <asm/system.h>
21
#include <asm/bitops.h>
22
#include <linux/types.h>
23
#include <linux/kernel.h>
24
#include <linux/sched.h>
25
#include <linux/mm.h>
26
#include <linux/string.h>
27
#include <linux/socket.h>
28
#include <linux/sockios.h>
29
#include <linux/errno.h>
30
#include <linux/in.h>
31
#include <linux/inet.h>
32
#include <linux/netdevice.h>
33
#include <linux/if_arp.h>
34
#include <linux/proc_fs.h>
35
#include <linux/skbuff.h>
36
#include <linux/netlink.h>
37
#include <linux/init.h>
38
 
39
#include <net/ip.h>
40
#include <net/protocol.h>
41
#include <net/route.h>
42
#include <net/tcp.h>
43
#include <net/sock.h>
44
#include <net/ip_fib.h>
45
 
46
#define FSprintk(a...)
47
 
48
static struct fib_info  *fib_info_list;
49
static rwlock_t fib_info_lock = RW_LOCK_UNLOCKED;
50
int fib_info_cnt;
51
 
52
#define for_fib_info() { struct fib_info *fi; \
53
        for (fi = fib_info_list; fi; fi = fi->fib_next)
54
 
55
#define endfor_fib_info() }
56
 
57
#ifdef CONFIG_IP_ROUTE_MULTIPATH
58
 
59
static spinlock_t fib_multipath_lock = SPIN_LOCK_UNLOCKED;
60
 
61
#define for_nexthops(fi) { int nhsel; const struct fib_nh * nh; \
62
for (nhsel=0, nh = (fi)->fib_nh; nhsel < (fi)->fib_nhs; nh++, nhsel++)
63
 
64
#define change_nexthops(fi) { int nhsel; struct fib_nh * nh; \
65
for (nhsel=0, nh = (struct fib_nh*)((fi)->fib_nh); nhsel < (fi)->fib_nhs; nh++, nhsel++)
66
 
67
#else /* CONFIG_IP_ROUTE_MULTIPATH */
68
 
69
/* Hope, that gcc will optimize it to get rid of dummy loop */
70
 
71
#define for_nexthops(fi) { int nhsel=0; const struct fib_nh * nh = (fi)->fib_nh; \
72
for (nhsel=0; nhsel < 1; nhsel++)
73
 
74
#define change_nexthops(fi) { int nhsel=0; struct fib_nh * nh = (struct fib_nh*)((fi)->fib_nh); \
75
for (nhsel=0; nhsel < 1; nhsel++)
76
 
77
#endif /* CONFIG_IP_ROUTE_MULTIPATH */
78
 
79
#define endfor_nexthops(fi) }
80
 
81
 
82
static struct
83
{
84
        int     error;
85
        u8      scope;
86
} fib_props[RTA_MAX+1] = {
87
        { 0, RT_SCOPE_NOWHERE},          /* RTN_UNSPEC */
88
        { 0, RT_SCOPE_UNIVERSE}, /* RTN_UNICAST */
89
        { 0, RT_SCOPE_HOST},             /* RTN_LOCAL */
90
        { 0, RT_SCOPE_LINK},             /* RTN_BROADCAST */
91
        { 0, RT_SCOPE_LINK},             /* RTN_ANYCAST */
92
        { 0, RT_SCOPE_UNIVERSE}, /* RTN_MULTICAST */
93
        { -EINVAL, RT_SCOPE_UNIVERSE},  /* RTN_BLACKHOLE */
94
        { -EHOSTUNREACH, RT_SCOPE_UNIVERSE},/* RTN_UNREACHABLE */
95
        { -EACCES, RT_SCOPE_UNIVERSE},  /* RTN_PROHIBIT */
96
        { -EAGAIN, RT_SCOPE_UNIVERSE},  /* RTN_THROW */
97
#ifdef CONFIG_IP_ROUTE_NAT
98
        { 0, RT_SCOPE_HOST},             /* RTN_NAT */
99
#else
100
        { -EINVAL, RT_SCOPE_NOWHERE},   /* RTN_NAT */
101
#endif
102
        { -EINVAL, RT_SCOPE_NOWHERE}    /* RTN_XRESOLVE */
103
};
104
 
105
 
106
/* Release a nexthop info record */
107
 
108
void free_fib_info(struct fib_info *fi)
109
{
110
        if (fi->fib_dead == 0) {
111
                printk("Freeing alive fib_info %p\n", fi);
112
                return;
113
        }
114
        change_nexthops(fi) {
115
                if (nh->nh_dev)
116
                        dev_put(nh->nh_dev);
117
                nh->nh_dev = NULL;
118
        } endfor_nexthops(fi);
119
        fib_info_cnt--;
120
        kfree(fi);
121
}
122
 
123
void fib_release_info(struct fib_info *fi)
124
{
125
        write_lock(&fib_info_lock);
126
        if (fi && --fi->fib_treeref == 0) {
127
                if (fi->fib_next)
128
                        fi->fib_next->fib_prev = fi->fib_prev;
129
                if (fi->fib_prev)
130
                        fi->fib_prev->fib_next = fi->fib_next;
131
                if (fi == fib_info_list)
132
                        fib_info_list = fi->fib_next;
133
                fi->fib_dead = 1;
134
                fib_info_put(fi);
135
        }
136
        write_unlock(&fib_info_lock);
137
}
138
 
139
static __inline__ int nh_comp(const struct fib_info *fi, const struct fib_info *ofi)
140
{
141
        const struct fib_nh *onh = ofi->fib_nh;
142
 
143
        for_nexthops(fi) {
144
                if (nh->nh_oif != onh->nh_oif ||
145
                    nh->nh_gw  != onh->nh_gw ||
146
                    nh->nh_scope != onh->nh_scope ||
147
#ifdef CONFIG_IP_ROUTE_MULTIPATH
148
                    nh->nh_weight != onh->nh_weight ||
149
#endif
150
#ifdef CONFIG_NET_CLS_ROUTE
151
                    nh->nh_tclassid != onh->nh_tclassid ||
152
#endif
153
                    ((nh->nh_flags^onh->nh_flags)&~RTNH_F_DEAD))
154
                        return -1;
155
                onh++;
156
        } endfor_nexthops(fi);
157
        return 0;
158
}
159
 
160
static __inline__ struct fib_info * fib_find_info(const struct fib_info *nfi)
161
{
162
        for_fib_info() {
163
                if (fi->fib_nhs != nfi->fib_nhs)
164
                        continue;
165
                if (nfi->fib_protocol == fi->fib_protocol &&
166
                    nfi->fib_prefsrc == fi->fib_prefsrc &&
167
                    nfi->fib_priority == fi->fib_priority &&
168
                    memcmp(nfi->fib_metrics, fi->fib_metrics, sizeof(fi->fib_metrics)) == 0 &&
169
                    ((nfi->fib_flags^fi->fib_flags)&~RTNH_F_DEAD) == 0 &&
170
                    (nfi->fib_nhs == 0 || nh_comp(fi, nfi) == 0))
171
                        return fi;
172
        } endfor_fib_info();
173
        return NULL;
174
}
175
 
176
/* Check, that the gateway is already configured.
177
   Used only by redirect accept routine.
178
 */
179
 
180
int ip_fib_check_default(u32 gw, struct net_device *dev)
181
{
182
        read_lock(&fib_info_lock);
183
        for_fib_info() {
184
                if (fi->fib_flags & RTNH_F_DEAD)
185
                        continue;
186
                for_nexthops(fi) {
187
                        if (nh->nh_dev == dev && nh->nh_gw == gw &&
188
                            nh->nh_scope == RT_SCOPE_LINK &&
189
                            !(nh->nh_flags&RTNH_F_DEAD)) {
190
                                read_unlock(&fib_info_lock);
191
                                return 0;
192
                        }
193
                } endfor_nexthops(fi);
194
        } endfor_fib_info();
195
        read_unlock(&fib_info_lock);
196
        return -1;
197
}
198
 
199
#ifdef CONFIG_IP_ROUTE_MULTIPATH
200
 
201
static u32 fib_get_attr32(struct rtattr *attr, int attrlen, int type)
202
{
203
        while (RTA_OK(attr,attrlen)) {
204
                if (attr->rta_type == type)
205
                        return *(u32*)RTA_DATA(attr);
206
                attr = RTA_NEXT(attr, attrlen);
207
        }
208
        return 0;
209
}
210
 
211
static int
212
fib_count_nexthops(struct rtattr *rta)
213
{
214
        int nhs = 0;
215
        struct rtnexthop *nhp = RTA_DATA(rta);
216
        int nhlen = RTA_PAYLOAD(rta);
217
 
218
        while (nhlen >= (int)sizeof(struct rtnexthop)) {
219
                if ((nhlen -= nhp->rtnh_len) < 0)
220
                        return 0;
221
                nhs++;
222
                nhp = RTNH_NEXT(nhp);
223
        };
224
        return nhs;
225
}
226
 
227
static int
228
fib_get_nhs(struct fib_info *fi, const struct rtattr *rta, const struct rtmsg *r)
229
{
230
        struct rtnexthop *nhp = RTA_DATA(rta);
231
        int nhlen = RTA_PAYLOAD(rta);
232
 
233
        change_nexthops(fi) {
234
                int attrlen = nhlen - sizeof(struct rtnexthop);
235
                if (attrlen < 0 || (nhlen -= nhp->rtnh_len) < 0)
236
                        return -EINVAL;
237
                nh->nh_flags = (r->rtm_flags&~0xFF) | nhp->rtnh_flags;
238
                nh->nh_oif = nhp->rtnh_ifindex;
239
                nh->nh_weight = nhp->rtnh_hops + 1;
240
                if (attrlen) {
241
                        nh->nh_gw = fib_get_attr32(RTNH_DATA(nhp), attrlen, RTA_GATEWAY);
242
#ifdef CONFIG_NET_CLS_ROUTE
243
                        nh->nh_tclassid = fib_get_attr32(RTNH_DATA(nhp), attrlen, RTA_FLOW);
244
#endif
245
                }
246
                nhp = RTNH_NEXT(nhp);
247
        } endfor_nexthops(fi);
248
        return 0;
249
}
250
 
251
#endif
252
 
253
int fib_nh_match(struct rtmsg *r, struct nlmsghdr *nlh, struct kern_rta *rta,
254
                 struct fib_info *fi)
255
{
256
#ifdef CONFIG_IP_ROUTE_MULTIPATH
257
        struct rtnexthop *nhp;
258
        int nhlen;
259
#endif
260
 
261
        if (rta->rta_priority &&
262
            *rta->rta_priority != fi->fib_priority)
263
                return 1;
264
 
265
        if (rta->rta_oif || rta->rta_gw) {
266
                if ((!rta->rta_oif || *rta->rta_oif == fi->fib_nh->nh_oif) &&
267
                    (!rta->rta_gw  || memcmp(rta->rta_gw, &fi->fib_nh->nh_gw, 4) == 0))
268
                        return 0;
269
                return 1;
270
        }
271
 
272
#ifdef CONFIG_IP_ROUTE_MULTIPATH
273
        if (rta->rta_mp == NULL)
274
                return 0;
275
        nhp = RTA_DATA(rta->rta_mp);
276
        nhlen = RTA_PAYLOAD(rta->rta_mp);
277
 
278
        for_nexthops(fi) {
279
                int attrlen = nhlen - sizeof(struct rtnexthop);
280
                u32 gw;
281
 
282
                if (attrlen < 0 || (nhlen -= nhp->rtnh_len) < 0)
283
                        return -EINVAL;
284
                if (nhp->rtnh_ifindex && nhp->rtnh_ifindex != nh->nh_oif)
285
                        return 1;
286
                if (attrlen) {
287
                        gw = fib_get_attr32(RTNH_DATA(nhp), attrlen, RTA_GATEWAY);
288
                        if (gw && gw != nh->nh_gw)
289
                                return 1;
290
#ifdef CONFIG_NET_CLS_ROUTE
291
                        gw = fib_get_attr32(RTNH_DATA(nhp), attrlen, RTA_FLOW);
292
                        if (gw && gw != nh->nh_tclassid)
293
                                return 1;
294
#endif
295
                }
296
                nhp = RTNH_NEXT(nhp);
297
        } endfor_nexthops(fi);
298
#endif
299
        return 0;
300
}
301
 
302
 
303
/*
304
   Picture
305
   -------
306
 
307
   Semantics of nexthop is very messy by historical reasons.
308
   We have to take into account, that:
309
   a) gateway can be actually local interface address,
310
      so that gatewayed route is direct.
311
   b) gateway must be on-link address, possibly
312
      described not by an ifaddr, but also by a direct route.
313
   c) If both gateway and interface are specified, they should not
314
      contradict.
315
   d) If we use tunnel routes, gateway could be not on-link.
316
 
317
   Attempt to reconcile all of these (alas, self-contradictory) conditions
318
   results in pretty ugly and hairy code with obscure logic.
319
 
320
   I choosed to generalized it instead, so that the size
321
   of code does not increase practically, but it becomes
322
   much more general.
323
   Every prefix is assigned a "scope" value: "host" is local address,
324
   "link" is direct route,
325
   [ ... "site" ... "interior" ... ]
326
   and "universe" is true gateway route with global meaning.
327
 
328
   Every prefix refers to a set of "nexthop"s (gw, oif),
329
   where gw must have narrower scope. This recursion stops
330
   when gw has LOCAL scope or if "nexthop" is declared ONLINK,
331
   which means that gw is forced to be on link.
332
 
333
   Code is still hairy, but now it is apparently logically
334
   consistent and very flexible. F.e. as by-product it allows
335
   to co-exists in peace independent exterior and interior
336
   routing processes.
337
 
338
   Normally it looks as following.
339
 
340
   {universe prefix}  -> (gw, oif) [scope link]
341
                          |
342
                          |-> {link prefix} -> (gw, oif) [scope local]
343
                                                |
344
                                                |-> {local prefix} (terminal node)
345
 */
346
 
347
static int fib_check_nh(const struct rtmsg *r, struct fib_info *fi, struct fib_nh *nh)
348
{
349
        int err;
350
 
351
        if (nh->nh_gw) {
352
                struct rt_key key;
353
                struct fib_result res;
354
 
355
#ifdef CONFIG_IP_ROUTE_PERVASIVE
356
                if (nh->nh_flags&RTNH_F_PERVASIVE)
357
                        return 0;
358
#endif
359
                if (nh->nh_flags&RTNH_F_ONLINK) {
360
                        struct net_device *dev;
361
 
362
                        if (r->rtm_scope >= RT_SCOPE_LINK)
363
                                return -EINVAL;
364
                        if (inet_addr_type(nh->nh_gw) != RTN_UNICAST)
365
                                return -EINVAL;
366
                        if ((dev = __dev_get_by_index(nh->nh_oif)) == NULL)
367
                                return -ENODEV;
368
                        if (!(dev->flags&IFF_UP))
369
                                return -ENETDOWN;
370
                        nh->nh_dev = dev;
371
                        dev_hold(dev);
372
                        nh->nh_scope = RT_SCOPE_LINK;
373
                        return 0;
374
                }
375
                memset(&key, 0, sizeof(key));
376
                key.dst = nh->nh_gw;
377
                key.oif = nh->nh_oif;
378
                key.scope = r->rtm_scope + 1;
379
 
380
                /* It is not necessary, but requires a bit of thinking */
381
                if (key.scope < RT_SCOPE_LINK)
382
                        key.scope = RT_SCOPE_LINK;
383
                if ((err = fib_lookup(&key, &res)) != 0)
384
                        return err;
385
                err = -EINVAL;
386
                if (res.type != RTN_UNICAST && res.type != RTN_LOCAL)
387
                        goto out;
388
                nh->nh_scope = res.scope;
389
                nh->nh_oif = FIB_RES_OIF(res);
390
                if ((nh->nh_dev = FIB_RES_DEV(res)) == NULL)
391
                        goto out;
392
                dev_hold(nh->nh_dev);
393
                err = -ENETDOWN;
394
                if (!(nh->nh_dev->flags & IFF_UP))
395
                        goto out;
396
                err = 0;
397
out:
398
                fib_res_put(&res);
399
                return err;
400
        } else {
401
                struct in_device *in_dev;
402
 
403
                if (nh->nh_flags&(RTNH_F_PERVASIVE|RTNH_F_ONLINK))
404
                        return -EINVAL;
405
 
406
                in_dev = inetdev_by_index(nh->nh_oif);
407
                if (in_dev == NULL)
408
                        return -ENODEV;
409
                if (!(in_dev->dev->flags&IFF_UP)) {
410
                        in_dev_put(in_dev);
411
                        return -ENETDOWN;
412
                }
413
                nh->nh_dev = in_dev->dev;
414
                dev_hold(nh->nh_dev);
415
                nh->nh_scope = RT_SCOPE_HOST;
416
                in_dev_put(in_dev);
417
        }
418
        return 0;
419
}
420
 
421
struct fib_info *
422
fib_create_info(const struct rtmsg *r, struct kern_rta *rta,
423
                const struct nlmsghdr *nlh, int *errp)
424
{
425
        int err;
426
        struct fib_info *fi = NULL;
427
        struct fib_info *ofi;
428
#ifdef CONFIG_IP_ROUTE_MULTIPATH
429
        int nhs = 1;
430
#else
431
        const int nhs = 1;
432
#endif
433
 
434
        /* Fast check to catch the most weird cases */
435
        if (fib_props[r->rtm_type].scope > r->rtm_scope)
436
                goto err_inval;
437
 
438
#ifdef CONFIG_IP_ROUTE_MULTIPATH
439
        if (rta->rta_mp) {
440
                nhs = fib_count_nexthops(rta->rta_mp);
441
                if (nhs == 0)
442
                        goto err_inval;
443
        }
444
#endif
445
 
446
        fi = kmalloc(sizeof(*fi)+nhs*sizeof(struct fib_nh), GFP_KERNEL);
447
        err = -ENOBUFS;
448
        if (fi == NULL)
449
                goto failure;
450
        fib_info_cnt++;
451
        memset(fi, 0, sizeof(*fi)+nhs*sizeof(struct fib_nh));
452
 
453
        fi->fib_protocol = r->rtm_protocol;
454
        fi->fib_nhs = nhs;
455
        fi->fib_flags = r->rtm_flags;
456
        if (rta->rta_priority)
457
                fi->fib_priority = *rta->rta_priority;
458
        if (rta->rta_mx) {
459
                int attrlen = RTA_PAYLOAD(rta->rta_mx);
460
                struct rtattr *attr = RTA_DATA(rta->rta_mx);
461
 
462
                while (RTA_OK(attr, attrlen)) {
463
                        unsigned flavor = attr->rta_type;
464
                        if (flavor) {
465
                                if (flavor > RTAX_MAX)
466
                                        goto err_inval;
467
                                fi->fib_metrics[flavor-1] = *(unsigned*)RTA_DATA(attr);
468
                        }
469
                        attr = RTA_NEXT(attr, attrlen);
470
                }
471
        }
472
        if (rta->rta_prefsrc)
473
                memcpy(&fi->fib_prefsrc, rta->rta_prefsrc, 4);
474
 
475
        if (rta->rta_mp) {
476
#ifdef CONFIG_IP_ROUTE_MULTIPATH
477
                if ((err = fib_get_nhs(fi, rta->rta_mp, r)) != 0)
478
                        goto failure;
479
                if (rta->rta_oif && fi->fib_nh->nh_oif != *rta->rta_oif)
480
                        goto err_inval;
481
                if (rta->rta_gw && memcmp(&fi->fib_nh->nh_gw, rta->rta_gw, 4))
482
                        goto err_inval;
483
#ifdef CONFIG_NET_CLS_ROUTE
484
                if (rta->rta_flow && memcmp(&fi->fib_nh->nh_tclassid, rta->rta_flow, 4))
485
                        goto err_inval;
486
#endif
487
#else
488
                goto err_inval;
489
#endif
490
        } else {
491
                struct fib_nh *nh = fi->fib_nh;
492
                if (rta->rta_oif)
493
                        nh->nh_oif = *rta->rta_oif;
494
                if (rta->rta_gw)
495
                        memcpy(&nh->nh_gw, rta->rta_gw, 4);
496
#ifdef CONFIG_NET_CLS_ROUTE
497
                if (rta->rta_flow)
498
                        memcpy(&nh->nh_tclassid, rta->rta_flow, 4);
499
#endif
500
                nh->nh_flags = r->rtm_flags;
501
#ifdef CONFIG_IP_ROUTE_MULTIPATH
502
                nh->nh_weight = 1;
503
#endif
504
        }
505
 
506
#ifdef CONFIG_IP_ROUTE_NAT
507
        if (r->rtm_type == RTN_NAT) {
508
                if (rta->rta_gw == NULL || nhs != 1 || rta->rta_oif)
509
                        goto err_inval;
510
                memcpy(&fi->fib_nh->nh_gw, rta->rta_gw, 4);
511
                goto link_it;
512
        }
513
#endif
514
 
515
        if (fib_props[r->rtm_type].error) {
516
                if (rta->rta_gw || rta->rta_oif || rta->rta_mp)
517
                        goto err_inval;
518
                goto link_it;
519
        }
520
 
521
        if (r->rtm_scope > RT_SCOPE_HOST)
522
                goto err_inval;
523
 
524
        if (r->rtm_scope == RT_SCOPE_HOST) {
525
                struct fib_nh *nh = fi->fib_nh;
526
 
527
                /* Local address is added. */
528
                if (nhs != 1 || nh->nh_gw)
529
                        goto err_inval;
530
                nh->nh_scope = RT_SCOPE_NOWHERE;
531
                nh->nh_dev = dev_get_by_index(fi->fib_nh->nh_oif);
532
                err = -ENODEV;
533
                if (nh->nh_dev == NULL)
534
                        goto failure;
535
        } else {
536
                change_nexthops(fi) {
537
                        if ((err = fib_check_nh(r, fi, nh)) != 0)
538
                                goto failure;
539
                } endfor_nexthops(fi)
540
        }
541
 
542
        if (fi->fib_prefsrc) {
543
                if (r->rtm_type != RTN_LOCAL || rta->rta_dst == NULL ||
544
                    memcmp(&fi->fib_prefsrc, rta->rta_dst, 4))
545
                        if (inet_addr_type(fi->fib_prefsrc) != RTN_LOCAL)
546
                                goto err_inval;
547
        }
548
 
549
link_it:
550
        if ((ofi = fib_find_info(fi)) != NULL) {
551
                fi->fib_dead = 1;
552
                free_fib_info(fi);
553
                ofi->fib_treeref++;
554
                return ofi;
555
        }
556
 
557
        fi->fib_treeref++;
558
        atomic_inc(&fi->fib_clntref);
559
        write_lock(&fib_info_lock);
560
        fi->fib_next = fib_info_list;
561
        fi->fib_prev = NULL;
562
        if (fib_info_list)
563
                fib_info_list->fib_prev = fi;
564
        fib_info_list = fi;
565
        write_unlock(&fib_info_lock);
566
        return fi;
567
 
568
err_inval:
569
        err = -EINVAL;
570
 
571
failure:
572
        *errp = err;
573
        if (fi) {
574
                fi->fib_dead = 1;
575
                free_fib_info(fi);
576
        }
577
        return NULL;
578
}
579
 
580
int
581
fib_semantic_match(int type, struct fib_info *fi, const struct rt_key *key, struct fib_result *res)
582
{
583
        int err = fib_props[type].error;
584
 
585
        if (err == 0) {
586
                if (fi->fib_flags&RTNH_F_DEAD)
587
                        return 1;
588
 
589
                res->fi = fi;
590
 
591
                switch (type) {
592
#ifdef CONFIG_IP_ROUTE_NAT
593
                case RTN_NAT:
594
                        FIB_RES_RESET(*res);
595
                        atomic_inc(&fi->fib_clntref);
596
                        return 0;
597
#endif
598
                case RTN_UNICAST:
599
                case RTN_LOCAL:
600
                case RTN_BROADCAST:
601
                case RTN_ANYCAST:
602
                case RTN_MULTICAST:
603
                        for_nexthops(fi) {
604
                                if (nh->nh_flags&RTNH_F_DEAD)
605
                                        continue;
606
                                if (!key->oif || key->oif == nh->nh_oif)
607
                                        break;
608
                        }
609
#ifdef CONFIG_IP_ROUTE_MULTIPATH
610
                        if (nhsel < fi->fib_nhs) {
611
                                res->nh_sel = nhsel;
612
                                atomic_inc(&fi->fib_clntref);
613
                                return 0;
614
                        }
615
#else
616
                        if (nhsel < 1) {
617
                                atomic_inc(&fi->fib_clntref);
618
                                return 0;
619
                        }
620
#endif
621
                        endfor_nexthops(fi);
622
                        res->fi = NULL;
623
                        return 1;
624
                default:
625
                        res->fi = NULL;
626
                        printk(KERN_DEBUG "impossible 102\n");
627
                        return -EINVAL;
628
                }
629
        }
630
        return err;
631
}
632
 
633
/* Find appropriate source address to this destination */
634
 
635
u32 __fib_res_prefsrc(struct fib_result *res)
636
{
637
        return inet_select_addr(FIB_RES_DEV(*res), FIB_RES_GW(*res), res->scope);
638
}
639
 
640
int
641
fib_dump_info(struct sk_buff *skb, u32 pid, u32 seq, int event,
642
              u8 tb_id, u8 type, u8 scope, void *dst, int dst_len, u8 tos,
643
              struct fib_info *fi)
644
{
645
        struct rtmsg *rtm;
646
        struct nlmsghdr  *nlh;
647
        unsigned char    *b = skb->tail;
648
 
649
        nlh = NLMSG_PUT(skb, pid, seq, event, sizeof(*rtm));
650
        rtm = NLMSG_DATA(nlh);
651
        rtm->rtm_family = AF_INET;
652
        rtm->rtm_dst_len = dst_len;
653
        rtm->rtm_src_len = 0;
654
        rtm->rtm_tos = tos;
655
        rtm->rtm_table = tb_id;
656
        rtm->rtm_type = type;
657
        rtm->rtm_flags = fi->fib_flags;
658
        rtm->rtm_scope = scope;
659
        if (rtm->rtm_dst_len)
660
                RTA_PUT(skb, RTA_DST, 4, dst);
661
        rtm->rtm_protocol = fi->fib_protocol;
662
        if (fi->fib_priority)
663
                RTA_PUT(skb, RTA_PRIORITY, 4, &fi->fib_priority);
664
#ifdef CONFIG_NET_CLS_ROUTE
665
        if (fi->fib_nh[0].nh_tclassid)
666
                RTA_PUT(skb, RTA_FLOW, 4, &fi->fib_nh[0].nh_tclassid);
667
#endif
668
        if (rtnetlink_put_metrics(skb, fi->fib_metrics) < 0)
669
                goto rtattr_failure;
670
        if (fi->fib_prefsrc)
671
                RTA_PUT(skb, RTA_PREFSRC, 4, &fi->fib_prefsrc);
672
        if (fi->fib_nhs == 1) {
673
                if (fi->fib_nh->nh_gw)
674
                        RTA_PUT(skb, RTA_GATEWAY, 4, &fi->fib_nh->nh_gw);
675
                if (fi->fib_nh->nh_oif)
676
                        RTA_PUT(skb, RTA_OIF, sizeof(int), &fi->fib_nh->nh_oif);
677
        }
678
#ifdef CONFIG_IP_ROUTE_MULTIPATH
679
        if (fi->fib_nhs > 1) {
680
                struct rtnexthop *nhp;
681
                struct rtattr *mp_head;
682
                if (skb_tailroom(skb) <= RTA_SPACE(0))
683
                        goto rtattr_failure;
684
                mp_head = (struct rtattr*)skb_put(skb, RTA_SPACE(0));
685
 
686
                for_nexthops(fi) {
687
                        if (skb_tailroom(skb) < RTA_ALIGN(RTA_ALIGN(sizeof(*nhp)) + 4))
688
                                goto rtattr_failure;
689
                        nhp = (struct rtnexthop*)skb_put(skb, RTA_ALIGN(sizeof(*nhp)));
690
                        nhp->rtnh_flags = nh->nh_flags & 0xFF;
691
                        nhp->rtnh_hops = nh->nh_weight-1;
692
                        nhp->rtnh_ifindex = nh->nh_oif;
693
                        if (nh->nh_gw)
694
                                RTA_PUT(skb, RTA_GATEWAY, 4, &nh->nh_gw);
695
                        nhp->rtnh_len = skb->tail - (unsigned char*)nhp;
696
                } endfor_nexthops(fi);
697
                mp_head->rta_type = RTA_MULTIPATH;
698
                mp_head->rta_len = skb->tail - (u8*)mp_head;
699
        }
700
#endif
701
        nlh->nlmsg_len = skb->tail - b;
702
        return skb->len;
703
 
704
nlmsg_failure:
705
rtattr_failure:
706
        skb_trim(skb, b - skb->data);
707
        return -1;
708
}
709
 
710
#ifndef CONFIG_IP_NOSIOCRT
711
 
712
int
713
fib_convert_rtentry(int cmd, struct nlmsghdr *nl, struct rtmsg *rtm,
714
                    struct kern_rta *rta, struct rtentry *r)
715
{
716
        int    plen;
717
        u32    *ptr;
718
 
719
        memset(rtm, 0, sizeof(*rtm));
720
        memset(rta, 0, sizeof(*rta));
721
 
722
        if (r->rt_dst.sa_family != AF_INET)
723
                return -EAFNOSUPPORT;
724
 
725
        /* Check mask for validity:
726
           a) it must be contiguous.
727
           b) destination must have all host bits clear.
728
           c) if application forgot to set correct family (AF_INET),
729
              reject request unless it is absolutely clear i.e.
730
              both family and mask are zero.
731
         */
732
        plen = 32;
733
        ptr = &((struct sockaddr_in*)&r->rt_dst)->sin_addr.s_addr;
734
        if (!(r->rt_flags&RTF_HOST)) {
735
                u32 mask = ((struct sockaddr_in*)&r->rt_genmask)->sin_addr.s_addr;
736
                if (r->rt_genmask.sa_family != AF_INET) {
737
                        if (mask || r->rt_genmask.sa_family)
738
                                return -EAFNOSUPPORT;
739
                }
740
                if (bad_mask(mask, *ptr))
741
                        return -EINVAL;
742
                plen = inet_mask_len(mask);
743
        }
744
 
745
        nl->nlmsg_flags = NLM_F_REQUEST;
746
        nl->nlmsg_pid = 0;
747
        nl->nlmsg_seq = 0;
748
        nl->nlmsg_len = NLMSG_LENGTH(sizeof(*rtm));
749
        if (cmd == SIOCDELRT) {
750
                nl->nlmsg_type = RTM_DELROUTE;
751
                nl->nlmsg_flags = 0;
752
        } else {
753
                nl->nlmsg_type = RTM_NEWROUTE;
754
                nl->nlmsg_flags = NLM_F_REQUEST|NLM_F_CREATE;
755
                rtm->rtm_protocol = RTPROT_BOOT;
756
        }
757
 
758
        rtm->rtm_dst_len = plen;
759
        rta->rta_dst = ptr;
760
 
761
        if (r->rt_metric) {
762
                *(u32*)&r->rt_pad3 = r->rt_metric - 1;
763
                rta->rta_priority = (u32*)&r->rt_pad3;
764
        }
765
        if (r->rt_flags&RTF_REJECT) {
766
                rtm->rtm_scope = RT_SCOPE_HOST;
767
                rtm->rtm_type = RTN_UNREACHABLE;
768
                return 0;
769
        }
770
        rtm->rtm_scope = RT_SCOPE_NOWHERE;
771
        rtm->rtm_type = RTN_UNICAST;
772
 
773
        if (r->rt_dev) {
774
                char *colon;
775
                struct net_device *dev;
776
                char   devname[IFNAMSIZ];
777
 
778
                if (copy_from_user(devname, r->rt_dev, IFNAMSIZ-1))
779
                        return -EFAULT;
780
                devname[IFNAMSIZ-1] = 0;
781
                colon = strchr(devname, ':');
782
                if (colon)
783
                        *colon = 0;
784
                dev = __dev_get_by_name(devname);
785
                if (!dev)
786
                        return -ENODEV;
787
                rta->rta_oif = &dev->ifindex;
788
                if (colon) {
789
                        struct in_ifaddr *ifa;
790
                        struct in_device *in_dev = __in_dev_get(dev);
791
                        if (!in_dev)
792
                                return -ENODEV;
793
                        *colon = ':';
794
                        for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next)
795
                                if (strcmp(ifa->ifa_label, devname) == 0)
796
                                        break;
797
                        if (ifa == NULL)
798
                                return -ENODEV;
799
                        rta->rta_prefsrc = &ifa->ifa_local;
800
                }
801
        }
802
 
803
        ptr = &((struct sockaddr_in*)&r->rt_gateway)->sin_addr.s_addr;
804
        if (r->rt_gateway.sa_family == AF_INET && *ptr) {
805
                rta->rta_gw = ptr;
806
                if (r->rt_flags&RTF_GATEWAY && inet_addr_type(*ptr) == RTN_UNICAST)
807
                        rtm->rtm_scope = RT_SCOPE_UNIVERSE;
808
        }
809
 
810
        if (cmd == SIOCDELRT)
811
                return 0;
812
 
813
        if (r->rt_flags&RTF_GATEWAY && rta->rta_gw == NULL)
814
                return -EINVAL;
815
 
816
        if (rtm->rtm_scope == RT_SCOPE_NOWHERE)
817
                rtm->rtm_scope = RT_SCOPE_LINK;
818
 
819
        if (r->rt_flags&(RTF_MTU|RTF_WINDOW|RTF_IRTT)) {
820
                struct rtattr *rec;
821
                struct rtattr *mx = kmalloc(RTA_LENGTH(3*RTA_LENGTH(4)), GFP_KERNEL);
822
                if (mx == NULL)
823
                        return -ENOMEM;
824
                rta->rta_mx = mx;
825
                mx->rta_type = RTA_METRICS;
826
                mx->rta_len  = RTA_LENGTH(0);
827
                if (r->rt_flags&RTF_MTU) {
828
                        rec = (void*)((char*)mx + RTA_ALIGN(mx->rta_len));
829
                        rec->rta_type = RTAX_ADVMSS;
830
                        rec->rta_len = RTA_LENGTH(4);
831
                        mx->rta_len += RTA_LENGTH(4);
832
                        *(u32*)RTA_DATA(rec) = r->rt_mtu - 40;
833
                }
834
                if (r->rt_flags&RTF_WINDOW) {
835
                        rec = (void*)((char*)mx + RTA_ALIGN(mx->rta_len));
836
                        rec->rta_type = RTAX_WINDOW;
837
                        rec->rta_len = RTA_LENGTH(4);
838
                        mx->rta_len += RTA_LENGTH(4);
839
                        *(u32*)RTA_DATA(rec) = r->rt_window;
840
                }
841
                if (r->rt_flags&RTF_IRTT) {
842
                        rec = (void*)((char*)mx + RTA_ALIGN(mx->rta_len));
843
                        rec->rta_type = RTAX_RTT;
844
                        rec->rta_len = RTA_LENGTH(4);
845
                        mx->rta_len += RTA_LENGTH(4);
846
                        *(u32*)RTA_DATA(rec) = r->rt_irtt<<3;
847
                }
848
        }
849
        return 0;
850
}
851
 
852
#endif
853
 
854
/*
855
   Update FIB if:
856
   - local address disappeared -> we must delete all the entries
857
     referring to it.
858
   - device went down -> we must shutdown all nexthops going via it.
859
 */
860
 
861
int fib_sync_down(u32 local, struct net_device *dev, int force)
862
{
863
        int ret = 0;
864
        int scope = RT_SCOPE_NOWHERE;
865
 
866
        if (force)
867
                scope = -1;
868
 
869
        for_fib_info() {
870
                if (local && fi->fib_prefsrc == local) {
871
                        fi->fib_flags |= RTNH_F_DEAD;
872
                        ret++;
873
                } else if (dev && fi->fib_nhs) {
874
                        int dead = 0;
875
 
876
                        change_nexthops(fi) {
877
                                if (nh->nh_flags&RTNH_F_DEAD)
878
                                        dead++;
879
                                else if (nh->nh_dev == dev &&
880
                                         nh->nh_scope != scope) {
881
                                        nh->nh_flags |= RTNH_F_DEAD;
882
#ifdef CONFIG_IP_ROUTE_MULTIPATH
883
                                        spin_lock_bh(&fib_multipath_lock);
884
                                        fi->fib_power -= nh->nh_power;
885
                                        nh->nh_power = 0;
886
                                        spin_unlock_bh(&fib_multipath_lock);
887
#endif
888
                                        dead++;
889
                                }
890
#ifdef CONFIG_IP_ROUTE_MULTIPATH
891
                                if (force > 1 && nh->nh_dev == dev) {
892
                                        dead = fi->fib_nhs;
893
                                        break;
894
                                }
895
#endif
896
                        } endfor_nexthops(fi)
897
                        if (dead == fi->fib_nhs) {
898
                                fi->fib_flags |= RTNH_F_DEAD;
899
                                ret++;
900
                        }
901
                }
902
        } endfor_fib_info();
903
        return ret;
904
}
905
 
906
#ifdef CONFIG_IP_ROUTE_MULTIPATH
907
 
908
/*
909
   Dead device goes up. We wake up dead nexthops.
910
   It takes sense only on multipath routes.
911
 */
912
 
913
int fib_sync_up(struct net_device *dev)
914
{
915
        int ret = 0;
916
 
917
        if (!(dev->flags&IFF_UP))
918
                return 0;
919
 
920
        for_fib_info() {
921
                int alive = 0;
922
 
923
                change_nexthops(fi) {
924
                        if (!(nh->nh_flags&RTNH_F_DEAD)) {
925
                                alive++;
926
                                continue;
927
                        }
928
                        if (nh->nh_dev == NULL || !(nh->nh_dev->flags&IFF_UP))
929
                                continue;
930
                        if (nh->nh_dev != dev || __in_dev_get(dev) == NULL)
931
                                continue;
932
                        alive++;
933
                        spin_lock_bh(&fib_multipath_lock);
934
                        nh->nh_power = 0;
935
                        nh->nh_flags &= ~RTNH_F_DEAD;
936
                        spin_unlock_bh(&fib_multipath_lock);
937
                } endfor_nexthops(fi)
938
 
939
                if (alive > 0) {
940
                        fi->fib_flags &= ~RTNH_F_DEAD;
941
                        ret++;
942
                }
943
        } endfor_fib_info();
944
        return ret;
945
}
946
 
947
/*
948
   The algorithm is suboptimal, but it provides really
949
   fair weighted route distribution.
950
 */
951
 
952
void fib_select_multipath(const struct rt_key *key, struct fib_result *res)
953
{
954
        struct fib_info *fi = res->fi;
955
        int w;
956
 
957
        spin_lock_bh(&fib_multipath_lock);
958
        if (fi->fib_power <= 0) {
959
                int power = 0;
960
                change_nexthops(fi) {
961
                        if (!(nh->nh_flags&RTNH_F_DEAD)) {
962
                                power += nh->nh_weight;
963
                                nh->nh_power = nh->nh_weight;
964
                        }
965
                } endfor_nexthops(fi);
966
                fi->fib_power = power;
967
                if (power <= 0) {
968
                        spin_unlock_bh(&fib_multipath_lock);
969
                        /* Race condition: route has just become dead. */
970
                        res->nh_sel = 0;
971
                        return;
972
                }
973
        }
974
 
975
 
976
        /* w should be random number [0..fi->fib_power-1],
977
           it is pretty bad approximation.
978
         */
979
 
980
        w = jiffies % fi->fib_power;
981
 
982
        change_nexthops(fi) {
983
                if (!(nh->nh_flags&RTNH_F_DEAD) && nh->nh_power) {
984
                        if ((w -= nh->nh_power) <= 0) {
985
                                nh->nh_power--;
986
                                fi->fib_power--;
987
                                res->nh_sel = nhsel;
988
                                spin_unlock_bh(&fib_multipath_lock);
989
                                return;
990
                        }
991
                }
992
        } endfor_nexthops(fi);
993
 
994
        /* Race condition: route has just become dead. */
995
        res->nh_sel = 0;
996
        spin_unlock_bh(&fib_multipath_lock);
997
}
998
#endif
999
 
1000
 
1001
#ifdef CONFIG_PROC_FS
1002
 
1003
static unsigned fib_flag_trans(int type, int dead, u32 mask, struct fib_info *fi)
1004
{
1005
        static unsigned type2flags[RTN_MAX+1] = {
1006
                0, 0, 0, 0, 0, 0, 0, RTF_REJECT, RTF_REJECT, 0, 0, 0
1007
        };
1008
        unsigned flags = type2flags[type];
1009
 
1010
        if (fi && fi->fib_nh->nh_gw)
1011
                flags |= RTF_GATEWAY;
1012
        if (mask == 0xFFFFFFFF)
1013
                flags |= RTF_HOST;
1014
        if (!dead)
1015
                flags |= RTF_UP;
1016
        return flags;
1017
}
1018
 
1019
void fib_node_get_info(int type, int dead, struct fib_info *fi, u32 prefix, u32 mask, char *buffer)
1020
{
1021
        int len;
1022
        unsigned flags = fib_flag_trans(type, dead, mask, fi);
1023
 
1024
        if (fi) {
1025
                len = sprintf(buffer, "%s\t%08X\t%08X\t%04X\t%d\t%u\t%d\t%08X\t%d\t%u\t%u",
1026
                              fi->fib_dev ? fi->fib_dev->name : "*", prefix,
1027
                              fi->fib_nh->nh_gw, flags, 0, 0, fi->fib_priority,
1028
                              mask, (fi->fib_advmss ? fi->fib_advmss+40 : 0),
1029
                              fi->fib_window, fi->fib_rtt>>3);
1030
        } else {
1031
                len = sprintf(buffer, "*\t%08X\t%08X\t%04X\t%d\t%u\t%d\t%08X\t%d\t%u\t%u",
1032
                              prefix, 0,
1033
                              flags, 0, 0, 0,
1034
                              mask, 0, 0, 0);
1035
        }
1036
        memset(buffer+len, ' ', 127-len);
1037
        buffer[127] = '\n';
1038
}
1039
 
1040
#endif

powered by: WebSVN 2.1.0

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