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

Subversion Repositories or1k

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

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 1275 phoenix
/*
2
 *      ip6_flowlabel.c         IPv6 flowlabel manager.
3
 *
4
 *      This program is free software; you can redistribute it and/or
5
 *      modify it under the terms of the GNU General Public License
6
 *      as published by the Free Software Foundation; either version
7
 *      2 of the License, or (at your option) any later version.
8
 *
9
 *      Authors:        Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
10
 */
11
 
12
#include <linux/config.h>
13
#include <linux/errno.h>
14
#include <linux/types.h>
15
#include <linux/socket.h>
16
#include <linux/net.h>
17
#include <linux/netdevice.h>
18
#include <linux/if_arp.h>
19
#include <linux/in6.h>
20
#include <linux/route.h>
21
#include <linux/proc_fs.h>
22
 
23
#include <net/sock.h>
24
 
25
#include <net/ipv6.h>
26
#include <net/ndisc.h>
27
#include <net/protocol.h>
28
#include <net/ip6_route.h>
29
#include <net/addrconf.h>
30
#include <net/rawv6.h>
31
#include <net/icmp.h>
32
#include <net/transp_v6.h>
33
 
34
#include <asm/uaccess.h>
35
 
36
#define FL_MIN_LINGER   6       /* Minimal linger. It is set to 6sec specified
37
                                   in old IPv6 RFC. Well, it was reasonable value.
38
                                 */
39
#define FL_MAX_LINGER   60      /* Maximal linger timeout */
40
 
41
/* FL hash table */
42
 
43
#define FL_MAX_PER_SOCK 32
44
#define FL_MAX_SIZE     4096
45
#define FL_HASH_MASK    255
46
#define FL_HASH(l)      (ntohl(l)&FL_HASH_MASK)
47
 
48
static atomic_t fl_size = ATOMIC_INIT(0);
49
static struct ip6_flowlabel *fl_ht[FL_HASH_MASK+1];
50
 
51
static struct timer_list ip6_fl_gc_timer;
52
 
53
/* FL hash table lock: it protects only of GC */
54
 
55
static rwlock_t ip6_fl_lock = RW_LOCK_UNLOCKED;
56
 
57
/* Big socket sock */
58
 
59
static rwlock_t ip6_sk_fl_lock = RW_LOCK_UNLOCKED;
60
 
61
 
62
static __inline__ struct ip6_flowlabel * __fl_lookup(u32 label)
63
{
64
        struct ip6_flowlabel *fl;
65
 
66
        for (fl=fl_ht[FL_HASH(label)]; fl; fl = fl->next) {
67
                if (fl->label == label)
68
                        return fl;
69
        }
70
        return NULL;
71
}
72
 
73
static struct ip6_flowlabel * fl_lookup(u32 label)
74
{
75
        struct ip6_flowlabel *fl;
76
 
77
        read_lock_bh(&ip6_fl_lock);
78
        fl = __fl_lookup(label);
79
        if (fl)
80
                atomic_inc(&fl->users);
81
        read_unlock_bh(&ip6_fl_lock);
82
        return fl;
83
}
84
 
85
 
86
static void fl_free(struct ip6_flowlabel *fl)
87
{
88
        if (fl->opt)
89
                kfree(fl->opt);
90
        kfree(fl);
91
}
92
 
93
static void fl_release(struct ip6_flowlabel *fl)
94
{
95
        fl->lastuse = jiffies;
96
        if (atomic_dec_and_test(&fl->users)) {
97
                unsigned long ttd = fl->lastuse + fl->linger;
98
                if ((long)(ttd - fl->expires) > 0)
99
                        fl->expires = ttd;
100
                ttd = fl->expires;
101
                if (fl->opt && fl->share == IPV6_FL_S_EXCL) {
102
                        struct ipv6_txoptions *opt = fl->opt;
103
                        fl->opt = NULL;
104
                        kfree(opt);
105
                }
106
                if (!del_timer(&ip6_fl_gc_timer) ||
107
                    (long)(ip6_fl_gc_timer.expires - ttd) > 0)
108
                        ip6_fl_gc_timer.expires = ttd;
109
                add_timer(&ip6_fl_gc_timer);
110
        }
111
}
112
 
113
static void ip6_fl_gc(unsigned long dummy)
114
{
115
        int i;
116
        unsigned long now = jiffies;
117
        unsigned long sched = 0;
118
 
119
        write_lock(&ip6_fl_lock);
120
 
121
        for (i=0; i<=FL_HASH_MASK; i++) {
122
                struct ip6_flowlabel *fl, **flp;
123
                flp = &fl_ht[i];
124
                while ((fl=*flp) != NULL) {
125
                        if (atomic_read(&fl->users) == 0) {
126
                                unsigned long ttd = fl->lastuse + fl->linger;
127
                                if ((long)(ttd - fl->expires) > 0)
128
                                        fl->expires = ttd;
129
                                ttd = fl->expires;
130
                                if ((long)(now - ttd) >= 0) {
131
                                        *flp = fl->next;
132
                                        fl_free(fl);
133
                                        atomic_dec(&fl_size);
134
                                        continue;
135
                                }
136
                                if (!sched || (long)(ttd - sched) < 0)
137
                                        sched = ttd;
138
                        }
139
                        flp = &fl->next;
140
                }
141
        }
142
        if (!sched && atomic_read(&fl_size))
143
                sched = now + FL_MAX_LINGER;
144
        if (sched) {
145
                ip6_fl_gc_timer.expires = sched;
146
                add_timer(&ip6_fl_gc_timer);
147
        }
148
        write_unlock(&ip6_fl_lock);
149
}
150
 
151
static int fl_intern(struct ip6_flowlabel *fl, __u32 label)
152
{
153
        fl->label = label & IPV6_FLOWLABEL_MASK;
154
 
155
        write_lock_bh(&ip6_fl_lock);
156
        if (label == 0) {
157
                for (;;) {
158
                        fl->label = htonl(net_random())&IPV6_FLOWLABEL_MASK;
159
                        if (fl->label) {
160
                                struct ip6_flowlabel *lfl;
161
                                lfl = __fl_lookup(fl->label);
162
                                if (lfl == NULL)
163
                                        break;
164
                        }
165
                }
166
        }
167
 
168
        fl->lastuse = jiffies;
169
        fl->next = fl_ht[FL_HASH(fl->label)];
170
        fl_ht[FL_HASH(fl->label)] = fl;
171
        atomic_inc(&fl_size);
172
        write_unlock_bh(&ip6_fl_lock);
173
        return 0;
174
}
175
 
176
 
177
 
178
/* Socket flowlabel lists */
179
 
180
struct ip6_flowlabel * fl6_sock_lookup(struct sock *sk, u32 label)
181
{
182
        struct ipv6_fl_socklist *sfl;
183
        struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
184
 
185
        label &= IPV6_FLOWLABEL_MASK;
186
 
187
        for (sfl=np->ipv6_fl_list; sfl; sfl = sfl->next) {
188
                struct ip6_flowlabel *fl = sfl->fl;
189
                if (fl->label == label) {
190
                        fl->lastuse = jiffies;
191
                        atomic_inc(&fl->users);
192
                        return fl;
193
                }
194
        }
195
        return NULL;
196
}
197
 
198
void fl6_free_socklist(struct sock *sk)
199
{
200
        struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
201
        struct ipv6_fl_socklist *sfl;
202
 
203
        while ((sfl = np->ipv6_fl_list) != NULL) {
204
                np->ipv6_fl_list = sfl->next;
205
                fl_release(sfl->fl);
206
                kfree(sfl);
207
        }
208
}
209
 
210
/* Service routines */
211
 
212
 
213
/*
214
   It is the only difficult place. flowlabel enforces equal headers
215
   before and including routing header, however user may supply options
216
   following rthdr.
217
 */
218
 
219
struct ipv6_txoptions *fl6_merge_options(struct ipv6_txoptions * opt_space,
220
                                         struct ip6_flowlabel * fl,
221
                                         struct ipv6_txoptions * fopt)
222
{
223
        struct ipv6_txoptions * fl_opt = fl->opt;
224
 
225
        if (fopt == NULL || fopt->opt_flen == 0)
226
                return fl_opt;
227
 
228
        if (fl_opt != NULL) {
229
                opt_space->hopopt = fl_opt->hopopt;
230
                opt_space->dst0opt = fl_opt->dst0opt;
231
                opt_space->srcrt = fl_opt->srcrt;
232
                opt_space->opt_nflen = fl_opt->opt_nflen;
233
        } else {
234
                if (fopt->opt_nflen == 0)
235
                        return fopt;
236
                opt_space->hopopt = NULL;
237
                opt_space->dst0opt = NULL;
238
                opt_space->srcrt = NULL;
239
                opt_space->opt_nflen = 0;
240
        }
241
        opt_space->dst1opt = fopt->dst1opt;
242
        opt_space->auth = fopt->auth;
243
        opt_space->opt_flen = fopt->opt_flen;
244
        return opt_space;
245
}
246
 
247
static __u32 check_linger(__u16 ttl)
248
{
249
        if (ttl < FL_MIN_LINGER)
250
                return FL_MIN_LINGER*HZ;
251
        if (ttl > FL_MAX_LINGER && !capable(CAP_NET_ADMIN))
252
                return 0;
253
        return ttl*HZ;
254
}
255
 
256
static int fl6_renew(struct ip6_flowlabel *fl, unsigned linger, unsigned expires)
257
{
258
        linger = check_linger(linger);
259
        if (!linger)
260
                return -EPERM;
261
        expires = check_linger(expires);
262
        if (!expires)
263
                return -EPERM;
264
        fl->lastuse = jiffies;
265
        if (fl->linger < linger)
266
                fl->linger = linger;
267
        if (expires < fl->linger)
268
                expires = fl->linger;
269
        if ((long)(fl->expires - (fl->lastuse+expires)) < 0)
270
                fl->expires = fl->lastuse + expires;
271
        return 0;
272
}
273
 
274
static struct ip6_flowlabel *
275
fl_create(struct in6_flowlabel_req *freq, char *optval, int optlen, int *err_p)
276
{
277
        struct ip6_flowlabel *fl;
278
        int olen;
279
        int addr_type;
280
        int err;
281
 
282
        err = -ENOMEM;
283
        fl = kmalloc(sizeof(*fl), GFP_KERNEL);
284
        if (fl == NULL)
285
                goto done;
286
        memset(fl, 0, sizeof(*fl));
287
 
288
        olen = optlen - CMSG_ALIGN(sizeof(*freq));
289
        if (olen > 0) {
290
                struct msghdr msg;
291
                struct flowi flowi;
292
                int junk;
293
 
294
                err = -ENOMEM;
295
                fl->opt = kmalloc(sizeof(*fl->opt) + olen, GFP_KERNEL);
296
                if (fl->opt == NULL)
297
                        goto done;
298
 
299
                memset(fl->opt, 0, sizeof(*fl->opt));
300
                fl->opt->tot_len = sizeof(*fl->opt) + olen;
301
                err = -EFAULT;
302
                if (copy_from_user(fl->opt+1, optval+CMSG_ALIGN(sizeof(*freq)), olen))
303
                        goto done;
304
 
305
                msg.msg_controllen = olen;
306
                msg.msg_control = (void*)(fl->opt+1);
307
                flowi.oif = 0;
308
 
309
                err = datagram_send_ctl(&msg, &flowi, fl->opt, &junk);
310
                if (err)
311
                        goto done;
312
                err = -EINVAL;
313
                if (fl->opt->opt_flen)
314
                        goto done;
315
                if (fl->opt->opt_nflen == 0) {
316
                        kfree(fl->opt);
317
                        fl->opt = NULL;
318
                }
319
        }
320
 
321
        fl->expires = jiffies;
322
        err = fl6_renew(fl, freq->flr_linger, freq->flr_expires);
323
        if (err)
324
                goto done;
325
        fl->share = freq->flr_share;
326
        addr_type = ipv6_addr_type(&freq->flr_dst);
327
        if ((addr_type&IPV6_ADDR_MAPPED)
328
            || addr_type == IPV6_ADDR_ANY)
329
                goto done;
330
        ipv6_addr_copy(&fl->dst, &freq->flr_dst);
331
        atomic_set(&fl->users, 1);
332
        switch (fl->share) {
333
        case IPV6_FL_S_EXCL:
334
        case IPV6_FL_S_ANY:
335
                break;
336
        case IPV6_FL_S_PROCESS:
337
                fl->owner = current->pid;
338
                break;
339
        case IPV6_FL_S_USER:
340
                fl->owner = current->euid;
341
                break;
342
        default:
343
                err = -EINVAL;
344
                goto done;
345
        }
346
        return fl;
347
 
348
done:
349
        if (fl)
350
                fl_free(fl);
351
        *err_p = err;
352
        return NULL;
353
}
354
 
355
static int mem_check(struct sock *sk)
356
{
357
        struct ipv6_fl_socklist *sfl;
358
        int room = FL_MAX_SIZE - atomic_read(&fl_size);
359
        int count = 0;
360
 
361
        if (room > FL_MAX_SIZE - FL_MAX_PER_SOCK)
362
                return 0;
363
 
364
        for (sfl = sk->net_pinfo.af_inet6.ipv6_fl_list; sfl; sfl = sfl->next)
365
                count++;
366
 
367
        if (room <= 0 ||
368
            ((count >= FL_MAX_PER_SOCK ||
369
             (count > 0 && room < FL_MAX_SIZE/2) || room < FL_MAX_SIZE/4)
370
             && !capable(CAP_NET_ADMIN)))
371
                return -ENOBUFS;
372
 
373
        return 0;
374
}
375
 
376
static int ipv6_hdr_cmp(struct ipv6_opt_hdr *h1, struct ipv6_opt_hdr *h2)
377
{
378
        if (h1 == h2)
379
                return 0;
380
        if (h1 == NULL || h2 == NULL)
381
                return 1;
382
        if (h1->hdrlen != h2->hdrlen)
383
                return 1;
384
        return memcmp(h1+1, h2+1, ((h1->hdrlen+1)<<3) - sizeof(*h1));
385
}
386
 
387
static int ipv6_opt_cmp(struct ipv6_txoptions *o1, struct ipv6_txoptions *o2)
388
{
389
        if (o1 == o2)
390
                return 0;
391
        if (o1 == NULL || o2 == NULL)
392
                return 1;
393
        if (o1->opt_nflen != o2->opt_nflen)
394
                return 1;
395
        if (ipv6_hdr_cmp(o1->hopopt, o2->hopopt))
396
                return 1;
397
        if (ipv6_hdr_cmp(o1->dst0opt, o2->dst0opt))
398
                return 1;
399
        if (ipv6_hdr_cmp((struct ipv6_opt_hdr *)o1->srcrt, (struct ipv6_opt_hdr *)o2->srcrt))
400
                return 1;
401
        return 0;
402
}
403
 
404
int ipv6_flowlabel_opt(struct sock *sk, char *optval, int optlen)
405
{
406
        int err;
407
        struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
408
        struct in6_flowlabel_req freq;
409
        struct ipv6_fl_socklist *sfl1=NULL;
410
        struct ipv6_fl_socklist *sfl, **sflp;
411
        struct ip6_flowlabel *fl;
412
 
413
        if (optlen < sizeof(freq))
414
                return -EINVAL;
415
 
416
        if (copy_from_user(&freq, optval, sizeof(freq)))
417
                return -EFAULT;
418
 
419
        switch (freq.flr_action) {
420
        case IPV6_FL_A_PUT:
421
                write_lock_bh(&ip6_sk_fl_lock);
422
                for (sflp = &np->ipv6_fl_list; (sfl=*sflp)!=NULL; sflp = &sfl->next) {
423
                        if (sfl->fl->label == freq.flr_label) {
424
                                if (freq.flr_label == (np->flow_label&IPV6_FLOWLABEL_MASK))
425
                                        np->flow_label &= ~IPV6_FLOWLABEL_MASK;
426
                                *sflp = sfl->next;
427
                                write_unlock_bh(&ip6_sk_fl_lock);
428
                                fl_release(sfl->fl);
429
                                kfree(sfl);
430
                                return 0;
431
                        }
432
                }
433
                write_unlock_bh(&ip6_sk_fl_lock);
434
                return -ESRCH;
435
 
436
        case IPV6_FL_A_RENEW:
437
                read_lock_bh(&ip6_sk_fl_lock);
438
                for (sfl = np->ipv6_fl_list; sfl; sfl = sfl->next) {
439
                        if (sfl->fl->label == freq.flr_label) {
440
                                err = fl6_renew(sfl->fl, freq.flr_linger, freq.flr_expires);
441
                                read_unlock_bh(&ip6_sk_fl_lock);
442
                                return err;
443
                        }
444
                }
445
                read_unlock_bh(&ip6_sk_fl_lock);
446
 
447
                if (freq.flr_share == IPV6_FL_S_NONE && capable(CAP_NET_ADMIN)) {
448
                        fl = fl_lookup(freq.flr_label);
449
                        if (fl) {
450
                                err = fl6_renew(fl, freq.flr_linger, freq.flr_expires);
451
                                fl_release(fl);
452
                                return err;
453
                        }
454
                }
455
                return -ESRCH;
456
 
457
        case IPV6_FL_A_GET:
458
                if (freq.flr_label & ~IPV6_FLOWLABEL_MASK)
459
                        return -EINVAL;
460
 
461
                fl = fl_create(&freq, optval, optlen, &err);
462
                if (fl == NULL)
463
                        return err;
464
                sfl1 = kmalloc(sizeof(*sfl1), GFP_KERNEL);
465
 
466
                if (freq.flr_label) {
467
                        struct ip6_flowlabel *fl1 = NULL;
468
 
469
                        err = -EEXIST;
470
                        read_lock_bh(&ip6_sk_fl_lock);
471
                        for (sfl = np->ipv6_fl_list; sfl; sfl = sfl->next) {
472
                                if (sfl->fl->label == freq.flr_label) {
473
                                        if (freq.flr_flags&IPV6_FL_F_EXCL) {
474
                                                read_unlock_bh(&ip6_sk_fl_lock);
475
                                                goto done;
476
                                        }
477
                                        fl1 = sfl->fl;
478
                                        atomic_inc(&fl->users);
479
                                        break;
480
                                }
481
                        }
482
                        read_unlock_bh(&ip6_sk_fl_lock);
483
 
484
                        if (fl1 == NULL)
485
                                fl1 = fl_lookup(freq.flr_label);
486
                        if (fl1) {
487
                                err = -EEXIST;
488
                                if (freq.flr_flags&IPV6_FL_F_EXCL)
489
                                        goto release;
490
                                err = -EPERM;
491
                                if (fl1->share == IPV6_FL_S_EXCL ||
492
                                    fl1->share != fl->share ||
493
                                    fl1->owner != fl->owner)
494
                                        goto release;
495
 
496
                                err = -EINVAL;
497
                                if (ipv6_addr_cmp(&fl1->dst, &fl->dst) ||
498
                                    ipv6_opt_cmp(fl1->opt, fl->opt))
499
                                        goto release;
500
 
501
                                err = -ENOMEM;
502
                                if (sfl1 == NULL)
503
                                        goto release;
504
                                if (fl->linger > fl1->linger)
505
                                        fl1->linger = fl->linger;
506
                                if ((long)(fl->expires - fl1->expires) > 0)
507
                                        fl1->expires = fl->expires;
508
                                write_lock_bh(&ip6_sk_fl_lock);
509
                                sfl1->fl = fl1;
510
                                sfl1->next = np->ipv6_fl_list;
511
                                np->ipv6_fl_list = sfl1;
512
                                write_unlock_bh(&ip6_sk_fl_lock);
513
                                fl_free(fl);
514
                                return 0;
515
 
516
release:
517
                                fl_release(fl1);
518
                                goto done;
519
                        }
520
                }
521
                err = -ENOENT;
522
                if (!(freq.flr_flags&IPV6_FL_F_CREATE))
523
                        goto done;
524
 
525
                err = -ENOMEM;
526
                if (sfl1 == NULL || (err = mem_check(sk)) != 0)
527
                        goto done;
528
 
529
                err = fl_intern(fl, freq.flr_label);
530
                if (err)
531
                        goto done;
532
 
533
                /* Do not check for fault */
534
                if (!freq.flr_label)
535
                        copy_to_user(optval + ((u8*)&freq.flr_label - (u8*)&freq), &fl->label, sizeof(fl->label));
536
 
537
                sfl1->fl = fl;
538
                sfl1->next = np->ipv6_fl_list;
539
                np->ipv6_fl_list = sfl1;
540
                return 0;
541
 
542
        default:
543
                return -EINVAL;
544
        }
545
 
546
done:
547
        if (fl)
548
                fl_free(fl);
549
        if (sfl1)
550
                kfree(sfl1);
551
        return err;
552
}
553
 
554
#ifdef CONFIG_PROC_FS
555
 
556
 
557
static int ip6_fl_read_proc(char *buffer, char **start, off_t offset,
558
                            int length, int *eof, void *data)
559
{
560
        off_t pos=0;
561
        off_t begin=0;
562
        int len=0;
563
        int i, k;
564
        struct ip6_flowlabel *fl;
565
 
566
        len+= sprintf(buffer,"Label S Owner  Users  Linger Expires  "
567
                      "Dst                              Opt\n");
568
 
569
        read_lock_bh(&ip6_fl_lock);
570
        for (i=0; i<=FL_HASH_MASK; i++) {
571
                for (fl = fl_ht[i]; fl; fl = fl->next) {
572
                        len+=sprintf(buffer+len,"%05X %-1d %-6d %-6d %-6d %-8ld ",
573
                                     (unsigned)ntohl(fl->label),
574
                                     fl->share,
575
                                     (unsigned)fl->owner,
576
                                     atomic_read(&fl->users),
577
                                     fl->linger/HZ,
578
                                     (long)(fl->expires - jiffies)/HZ);
579
 
580
                        for (k=0; k<16; k++)
581
                                len+=sprintf(buffer+len, "%02x", fl->dst.s6_addr[k]);
582
                        buffer[len++]=' ';
583
                        len+=sprintf(buffer+len, "%-4d", fl->opt ? fl->opt->opt_nflen : 0);
584
                        buffer[len++]='\n';
585
 
586
                        pos=begin+len;
587
                        if(pos<offset) {
588
                                len=0;
589
                                begin=pos;
590
                        }
591
                        if(pos>offset+length)
592
                                goto done;
593
                }
594
        }
595
        *eof = 1;
596
 
597
done:
598
        read_unlock_bh(&ip6_fl_lock);
599
        *start=buffer+(offset-begin);
600
        len-=(offset-begin);
601
        if(len>length)
602
                len=length;
603
        if(len<0)
604
                len=0;
605
        return len;
606
}
607
#endif
608
 
609
 
610
void ip6_flowlabel_init()
611
{
612
        init_timer(&ip6_fl_gc_timer);
613
        ip6_fl_gc_timer.function = ip6_fl_gc;
614
#ifdef CONFIG_PROC_FS
615
        create_proc_read_entry("net/ip6_flowlabel", 0, 0, ip6_fl_read_proc, NULL);
616
#endif
617
}
618
 
619
void ip6_flowlabel_cleanup()
620
{
621
        del_timer(&ip6_fl_gc_timer);
622
#ifdef CONFIG_PROC_FS
623
        remove_proc_entry("net/ip6_flowlabel", 0);
624
#endif
625
}

powered by: WebSVN 2.1.0

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