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

Subversion Repositories or1k

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

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 1275 phoenix
/*
2
 *      RAW sockets for IPv6
3
 *      Linux INET6 implementation
4
 *
5
 *      Authors:
6
 *      Pedro Roque             <roque@di.fc.ul.pt>
7
 *
8
 *      Adapted from linux/net/ipv4/raw.c
9
 *
10
 *      $Id: raw.c,v 1.1.1.1 2004-04-15 01:14:33 phoenix Exp $
11
 *
12
 *      Fixes:
13
 *      Hideaki YOSHIFUJI       :       sin6_scope_id support
14
 *      YOSHIFUJI,H.@USAGI      :       raw checksum (RFC2292(bis) compliance)
15
 *
16
 *      This program is free software; you can redistribute it and/or
17
 *      modify it under the terms of the GNU General Public License
18
 *      as published by the Free Software Foundation; either version
19
 *      2 of the License, or (at your option) any later version.
20
 */
21
 
22
#include <linux/errno.h>
23
#include <linux/types.h>
24
#include <linux/socket.h>
25
#include <linux/sockios.h>
26
#include <linux/sched.h>
27
#include <linux/net.h>
28
#include <linux/in6.h>
29
#include <linux/netdevice.h>
30
#include <linux/if_arp.h>
31
#include <linux/icmpv6.h>
32
#include <asm/uaccess.h>
33
#include <asm/ioctls.h>
34
 
35
#include <net/sock.h>
36
#include <net/snmp.h>
37
 
38
#include <net/ipv6.h>
39
#include <net/ndisc.h>
40
#include <net/protocol.h>
41
#include <net/ip6_route.h>
42
#include <net/addrconf.h>
43
#include <net/transp_v6.h>
44
#include <net/udp.h>
45
#include <net/inet_common.h>
46
 
47
#include <net/rawv6.h>
48
 
49
struct sock *raw_v6_htable[RAWV6_HTABLE_SIZE];
50
rwlock_t raw_v6_lock = RW_LOCK_UNLOCKED;
51
 
52
static void raw_v6_hash(struct sock *sk)
53
{
54
        struct sock **skp = &raw_v6_htable[sk->num & (RAWV6_HTABLE_SIZE - 1)];
55
 
56
        write_lock_bh(&raw_v6_lock);
57
        if ((sk->next = *skp) != NULL)
58
                (*skp)->pprev = &sk->next;
59
        *skp = sk;
60
        sk->pprev = skp;
61
        sock_prot_inc_use(sk->prot);
62
        sock_hold(sk);
63
        write_unlock_bh(&raw_v6_lock);
64
}
65
 
66
static void raw_v6_unhash(struct sock *sk)
67
{
68
        write_lock_bh(&raw_v6_lock);
69
        if (sk->pprev) {
70
                if (sk->next)
71
                        sk->next->pprev = sk->pprev;
72
                *sk->pprev = sk->next;
73
                sk->pprev = NULL;
74
                sock_prot_dec_use(sk->prot);
75
                __sock_put(sk);
76
        }
77
        write_unlock_bh(&raw_v6_lock);
78
}
79
 
80
 
81
/* Grumble... icmp and ip_input want to get at this... */
82
struct sock *__raw_v6_lookup(struct sock *sk, unsigned short num,
83
                             struct in6_addr *loc_addr, struct in6_addr *rmt_addr)
84
{
85
        struct sock *s = sk;
86
        int addr_type = ipv6_addr_type(loc_addr);
87
 
88
        for(s = sk; s; s = s->next) {
89
                if(s->num == num) {
90
                        struct ipv6_pinfo *np = &s->net_pinfo.af_inet6;
91
 
92
                        if (!ipv6_addr_any(&np->daddr) &&
93
                            ipv6_addr_cmp(&np->daddr, rmt_addr))
94
                                continue;
95
 
96
                        if (!ipv6_addr_any(&np->rcv_saddr)) {
97
                                if (ipv6_addr_cmp(&np->rcv_saddr, loc_addr) == 0)
98
                                        break;
99
                                if ((addr_type & IPV6_ADDR_MULTICAST) &&
100
                                    inet6_mc_check(s, loc_addr, rmt_addr))
101
                                        break;
102
                                continue;
103
                        }
104
                        break;
105
                }
106
        }
107
        return s;
108
}
109
 
110
/*
111
 *      0 - deliver
112
 *      1 - block
113
 */
114
static __inline__ int icmpv6_filter(struct sock *sk, struct sk_buff *skb)
115
{
116
        struct icmp6hdr *icmph;
117
        struct raw6_opt *opt;
118
 
119
        opt = &sk->tp_pinfo.tp_raw;
120
        if (pskb_may_pull(skb, sizeof(struct icmp6hdr))) {
121
                __u32 *data = &opt->filter.data[0];
122
                int bit_nr;
123
 
124
                icmph = (struct icmp6hdr *) skb->data;
125
                bit_nr = icmph->icmp6_type;
126
 
127
                return (data[bit_nr >> 5] & (1 << (bit_nr & 31))) != 0;
128
        }
129
        return 0;
130
}
131
 
132
/*
133
 *      demultiplex raw sockets.
134
 *      (should consider queueing the skb in the sock receive_queue
135
 *      without calling rawv6.c)
136
 */
137
struct sock * ipv6_raw_deliver(struct sk_buff *skb, int nexthdr)
138
{
139
        struct in6_addr *saddr;
140
        struct in6_addr *daddr;
141
        struct sock *sk, *sk2;
142
        __u8 hash;
143
 
144
        saddr = &skb->nh.ipv6h->saddr;
145
        daddr = saddr + 1;
146
 
147
        hash = nexthdr & (MAX_INET_PROTOS - 1);
148
 
149
        read_lock(&raw_v6_lock);
150
        sk = raw_v6_htable[hash];
151
 
152
        /*
153
         *      The first socket found will be delivered after
154
         *      delivery to transport protocols.
155
         */
156
 
157
        if (sk == NULL)
158
                goto out;
159
 
160
        sk = __raw_v6_lookup(sk, nexthdr, daddr, saddr);
161
 
162
        if (sk) {
163
                sk2 = sk;
164
 
165
                while ((sk2 = __raw_v6_lookup(sk2->next, nexthdr, daddr, saddr))) {
166
                        struct sk_buff *buff;
167
 
168
                        if (nexthdr == IPPROTO_ICMPV6 &&
169
                            icmpv6_filter(sk2, skb))
170
                                continue;
171
 
172
                        buff = skb_clone(skb, GFP_ATOMIC);
173
                        if (buff)
174
                                rawv6_rcv(sk2, buff);
175
                }
176
        }
177
 
178
        if (sk && nexthdr == IPPROTO_ICMPV6 && icmpv6_filter(sk, skb))
179
                sk = NULL;
180
 
181
out:
182
        if (sk)
183
                sock_hold(sk);
184
        read_unlock(&raw_v6_lock);
185
        return sk;
186
}
187
 
188
/* This cleans up af_inet6 a bit. -DaveM */
189
static int rawv6_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len)
190
{
191
        struct sockaddr_in6 *addr = (struct sockaddr_in6 *) uaddr;
192
        __u32 v4addr = 0;
193
        int addr_type;
194
        int err;
195
 
196
        if (addr_len < SIN6_LEN_RFC2133)
197
                return -EINVAL;
198
        addr_type = ipv6_addr_type(&addr->sin6_addr);
199
 
200
        /* Raw sockets are IPv6 only */
201
        if (addr_type == IPV6_ADDR_MAPPED)
202
                return(-EADDRNOTAVAIL);
203
 
204
        lock_sock(sk);
205
 
206
        err = -EINVAL;
207
        if (sk->state != TCP_CLOSE)
208
                goto out;
209
 
210
        if (addr_type & IPV6_ADDR_LINKLOCAL) {
211
                if (addr_len >= sizeof(struct sockaddr_in6) &&
212
                    addr->sin6_scope_id) {
213
                        /* Override any existing binding, if another one
214
                         * is supplied by user.
215
                         */
216
                        sk->bound_dev_if = addr->sin6_scope_id;
217
                }
218
 
219
                /* Binding to link-local address requires an interface */
220
                if (sk->bound_dev_if == 0)
221
                        goto out;
222
        }
223
 
224
        /* Check if the address belongs to the host. */
225
        if (addr_type != IPV6_ADDR_ANY) {
226
                /* ipv4 addr of the socket is invalid.  Only the
227
                 * unpecified and mapped address have a v4 equivalent.
228
                 */
229
                v4addr = LOOPBACK4_IPV6;
230
                if (!(addr_type & IPV6_ADDR_MULTICAST)) {
231
                        err = -EADDRNOTAVAIL;
232
                        if (!ipv6_chk_addr(&addr->sin6_addr, NULL))
233
                                goto out;
234
                }
235
        }
236
 
237
        sk->rcv_saddr = v4addr;
238
        sk->saddr = v4addr;
239
        ipv6_addr_copy(&sk->net_pinfo.af_inet6.rcv_saddr, &addr->sin6_addr);
240
        if (!(addr_type & IPV6_ADDR_MULTICAST))
241
                ipv6_addr_copy(&sk->net_pinfo.af_inet6.saddr, &addr->sin6_addr);
242
        err = 0;
243
out:
244
        release_sock(sk);
245
        return err;
246
}
247
 
248
void rawv6_err(struct sock *sk, struct sk_buff *skb,
249
               struct inet6_skb_parm *opt,
250
               int type, int code, int offset, u32 info)
251
{
252
        int err;
253
        int harderr;
254
 
255
        /* Report error on raw socket, if:
256
           1. User requested recverr.
257
           2. Socket is connected (otherwise the error indication
258
              is useless without recverr and error is hard.
259
         */
260
        if (!sk->net_pinfo.af_inet6.recverr && sk->state != TCP_ESTABLISHED)
261
                return;
262
 
263
        harderr = icmpv6_err_convert(type, code, &err);
264
        if (type == ICMPV6_PKT_TOOBIG)
265
                harderr = (sk->net_pinfo.af_inet6.pmtudisc == IPV6_PMTUDISC_DO);
266
 
267
        if (sk->net_pinfo.af_inet6.recverr) {
268
                u8 *payload = skb->data;
269
                if (!sk->protinfo.af_inet.hdrincl)
270
                        payload += offset;
271
                ipv6_icmp_error(sk, skb, err, 0, ntohl(info), payload);
272
        }
273
 
274
        if (sk->net_pinfo.af_inet6.recverr || harderr) {
275
                sk->err = err;
276
                sk->error_report(sk);
277
        }
278
}
279
 
280
static inline int rawv6_rcv_skb(struct sock * sk, struct sk_buff * skb)
281
{
282
#if defined(CONFIG_FILTER)
283
        if (sk->filter && skb->ip_summed != CHECKSUM_UNNECESSARY) {
284
                if ((unsigned short)csum_fold(skb_checksum(skb, 0, skb->len, skb->csum))) {
285
                        IP6_INC_STATS_BH(Ip6InDiscards);
286
                        kfree_skb(skb);
287
                        return 0;
288
                }
289
                skb->ip_summed = CHECKSUM_UNNECESSARY;
290
        }
291
#endif
292
        /* Charge it to the socket. */
293
        if (sock_queue_rcv_skb(sk,skb)<0) {
294
                IP6_INC_STATS_BH(Ip6InDiscards);
295
                kfree_skb(skb);
296
                return 0;
297
        }
298
 
299
        IP6_INC_STATS_BH(Ip6InDelivers);
300
        return 0;
301
}
302
 
303
/*
304
 *      This is next to useless...
305
 *      if we demultiplex in network layer we don't need the extra call
306
 *      just to queue the skb...
307
 *      maybe we could have the network decide uppon a hint if it
308
 *      should call raw_rcv for demultiplexing
309
 */
310
int rawv6_rcv(struct sock *sk, struct sk_buff *skb)
311
{
312
        if (!sk->tp_pinfo.tp_raw.checksum)
313
                skb->ip_summed = CHECKSUM_UNNECESSARY;
314
 
315
        if (skb->ip_summed != CHECKSUM_UNNECESSARY) {
316
                if (skb->ip_summed == CHECKSUM_HW) {
317
                        skb->ip_summed = CHECKSUM_UNNECESSARY;
318
                        if (csum_ipv6_magic(&skb->nh.ipv6h->saddr,
319
                                            &skb->nh.ipv6h->daddr,
320
                                            skb->len, sk->num, skb->csum)) {
321
                                NETDEBUG(if (net_ratelimit()) printk(KERN_DEBUG "raw v6 hw csum failure.\n"));
322
                                skb->ip_summed = CHECKSUM_NONE;
323
                        }
324
                }
325
                if (skb->ip_summed == CHECKSUM_NONE)
326
                        skb->csum = ~csum_ipv6_magic(&skb->nh.ipv6h->saddr,
327
                                                     &skb->nh.ipv6h->daddr,
328
                                                     skb->len, sk->num, 0);
329
        }
330
 
331
        if (sk->protinfo.af_inet.hdrincl) {
332
                if (skb->ip_summed != CHECKSUM_UNNECESSARY &&
333
                    (unsigned short)csum_fold(skb_checksum(skb, 0, skb->len, skb->csum))) {
334
                        IP6_INC_STATS_BH(Ip6InDiscards);
335
                        kfree_skb(skb);
336
                        return 0;
337
                }
338
                skb->ip_summed = CHECKSUM_UNNECESSARY;
339
        }
340
 
341
        rawv6_rcv_skb(sk, skb);
342
        return 0;
343
}
344
 
345
 
346
/*
347
 *      This should be easy, if there is something there
348
 *      we return it, otherwise we block.
349
 */
350
 
351
int rawv6_recvmsg(struct sock *sk, struct msghdr *msg, int len,
352
                  int noblock, int flags, int *addr_len)
353
{
354
        struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)msg->msg_name;
355
        struct sk_buff *skb;
356
        int copied, err;
357
 
358
        if (flags & MSG_OOB)
359
                return -EOPNOTSUPP;
360
 
361
        if (addr_len)
362
                *addr_len=sizeof(*sin6);
363
 
364
        if (flags & MSG_ERRQUEUE)
365
                return ipv6_recv_error(sk, msg, len);
366
 
367
        skb = skb_recv_datagram(sk, flags, noblock, &err);
368
        if (!skb)
369
                goto out;
370
 
371
        copied = skb->len;
372
        if (copied > len) {
373
                copied = len;
374
                msg->msg_flags |= MSG_TRUNC;
375
        }
376
 
377
        if (skb->ip_summed==CHECKSUM_UNNECESSARY) {
378
                err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
379
        } else if (msg->msg_flags&MSG_TRUNC) {
380
                if ((unsigned short)csum_fold(skb_checksum(skb, 0, skb->len, skb->csum)))
381
                        goto csum_copy_err;
382
                err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
383
        } else {
384
                err = skb_copy_and_csum_datagram_iovec(skb, 0, msg->msg_iov);
385
                if (err == -EINVAL)
386
                        goto csum_copy_err;
387
        }
388
        if (err)
389
                goto out_free;
390
 
391
        /* Copy the address. */
392
        if (sin6) {
393
                sin6->sin6_family = AF_INET6;
394
                memcpy(&sin6->sin6_addr, &skb->nh.ipv6h->saddr,
395
                       sizeof(struct in6_addr));
396
                sin6->sin6_flowinfo = 0;
397
                sin6->sin6_scope_id = 0;
398
                if (ipv6_addr_type(&sin6->sin6_addr) & IPV6_ADDR_LINKLOCAL) {
399
                        struct inet6_skb_parm *opt = (struct inet6_skb_parm *) skb->cb;
400
                        sin6->sin6_scope_id = opt->iif;
401
                }
402
        }
403
 
404
        sock_recv_timestamp(msg, sk, skb);
405
 
406
        if (sk->net_pinfo.af_inet6.rxopt.all)
407
                datagram_recv_ctl(sk, msg, skb);
408
        err = copied;
409
 
410
out_free:
411
        skb_free_datagram(sk, skb);
412
out:
413
        return err;
414
 
415
csum_copy_err:
416
        /* Clear queue. */
417
        if (flags&MSG_PEEK) {
418
                int clear = 0;
419
                spin_lock_irq(&sk->receive_queue.lock);
420
                if (skb == skb_peek(&sk->receive_queue)) {
421
                        __skb_unlink(skb, &sk->receive_queue);
422
                        clear = 1;
423
                }
424
                spin_unlock_irq(&sk->receive_queue.lock);
425
                if (clear)
426
                        kfree_skb(skb);
427
        }
428
 
429
        /* Error for blocking case is chosen to masquerade
430
           as some normal condition.
431
         */
432
        err = (flags&MSG_DONTWAIT) ? -EAGAIN : -EHOSTUNREACH;
433
        IP6_INC_STATS_USER(Ip6InDiscards);
434
        goto out_free;
435
}
436
 
437
/*
438
 *      Sending...
439
 */
440
 
441
struct rawv6_fakehdr {
442
        struct iovec    *iov;
443
        struct sock     *sk;
444
        __u32           len;
445
        __u32           cksum;
446
        __u32           proto;
447
        struct in6_addr *daddr;
448
};
449
 
450
static int rawv6_getfrag(const void *data, struct in6_addr *saddr,
451
                          char *buff, unsigned int offset, unsigned int len)
452
{
453
        struct iovec *iov = (struct iovec *) data;
454
 
455
        return memcpy_fromiovecend(buff, iov, offset, len);
456
}
457
 
458
static int rawv6_frag_cksum(const void *data, struct in6_addr *addr,
459
                             char *buff, unsigned int offset,
460
                             unsigned int len)
461
{
462
        struct rawv6_fakehdr *hdr = (struct rawv6_fakehdr *) data;
463
 
464
        if (csum_partial_copy_fromiovecend(buff, hdr->iov, offset,
465
                                                    len, &hdr->cksum))
466
                return -EFAULT;
467
 
468
        if (offset == 0) {
469
                struct sock *sk;
470
                struct raw6_opt *opt;
471
                struct in6_addr *daddr;
472
 
473
                sk = hdr->sk;
474
                opt = &sk->tp_pinfo.tp_raw;
475
 
476
                if (hdr->daddr)
477
                        daddr = hdr->daddr;
478
                else
479
                        daddr = addr + 1;
480
 
481
                hdr->cksum = csum_ipv6_magic(addr, daddr, hdr->len,
482
                                             hdr->proto, hdr->cksum);
483
 
484
                if (opt->offset + 1 < len) {
485
                        __u16 *csum;
486
 
487
                        csum = (__u16 *) (buff + opt->offset);
488
                        if (*csum) {
489
                                /* in case cksum was not initialized */
490
                                __u32 sum = hdr->cksum;
491
                                sum += *csum;
492
                                *csum = hdr->cksum = (sum + (sum>>16));
493
                        } else {
494
                                *csum = hdr->cksum;
495
                        }
496
                } else {
497
                        if (net_ratelimit())
498
                                printk(KERN_DEBUG "icmp: cksum offset too big\n");
499
                        return -EINVAL;
500
                }
501
        }
502
        return 0;
503
}
504
 
505
 
506
static int rawv6_sendmsg(struct sock *sk, struct msghdr *msg, int len)
507
{
508
        struct ipv6_txoptions opt_space;
509
        struct sockaddr_in6 * sin6 = (struct sockaddr_in6 *) msg->msg_name;
510
        struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
511
        struct ipv6_txoptions *opt = NULL;
512
        struct ip6_flowlabel *flowlabel = NULL;
513
        struct flowi fl;
514
        int addr_len = msg->msg_namelen;
515
        struct in6_addr *daddr;
516
        struct raw6_opt *raw_opt;
517
        int hlimit = -1;
518
        u16 proto;
519
        int err;
520
 
521
        /* Rough check on arithmetic overflow,
522
           better check is made in ip6_build_xmit
523
         */
524
        if (len < 0)
525
                return -EMSGSIZE;
526
 
527
        /* Mirror BSD error message compatibility */
528
        if (msg->msg_flags & MSG_OOB)
529
                return -EOPNOTSUPP;
530
 
531
        /*
532
         *      Get and verify the address.
533
         */
534
 
535
        fl.fl6_flowlabel = 0;
536
        fl.oif = 0;
537
 
538
        if (sin6) {
539
                if (addr_len < SIN6_LEN_RFC2133)
540
                        return -EINVAL;
541
 
542
                if (sin6->sin6_family && sin6->sin6_family != AF_INET6)
543
                        return(-EINVAL);
544
 
545
                /* port is the proto value [0..255] carried in nexthdr */
546
                proto = ntohs(sin6->sin6_port);
547
 
548
                if (!proto)
549
                        proto = sk->num;
550
 
551
                if (proto > 255)
552
                        return(-EINVAL);
553
 
554
                daddr = &sin6->sin6_addr;
555
                if (np->sndflow) {
556
                        fl.fl6_flowlabel = sin6->sin6_flowinfo&IPV6_FLOWINFO_MASK;
557
                        if (fl.fl6_flowlabel&IPV6_FLOWLABEL_MASK) {
558
                                flowlabel = fl6_sock_lookup(sk, fl.fl6_flowlabel);
559
                                if (flowlabel == NULL)
560
                                        return -EINVAL;
561
                                daddr = &flowlabel->dst;
562
                        }
563
                }
564
 
565
                /* Otherwise it will be difficult to maintain sk->dst_cache. */
566
                if (sk->state == TCP_ESTABLISHED &&
567
                    !ipv6_addr_cmp(daddr, &sk->net_pinfo.af_inet6.daddr))
568
                        daddr = &sk->net_pinfo.af_inet6.daddr;
569
 
570
                if (addr_len >= sizeof(struct sockaddr_in6) &&
571
                    sin6->sin6_scope_id &&
572
                    ipv6_addr_type(daddr)&IPV6_ADDR_LINKLOCAL)
573
                        fl.oif = sin6->sin6_scope_id;
574
        } else {
575
                if (sk->state != TCP_ESTABLISHED)
576
                        return -EDESTADDRREQ;
577
 
578
                proto = sk->num;
579
                daddr = &(sk->net_pinfo.af_inet6.daddr);
580
                fl.fl6_flowlabel = np->flow_label;
581
        }
582
 
583
        if (ipv6_addr_any(daddr)) {
584
                /*
585
                 * unspecfied destination address
586
                 * treated as error... is this correct ?
587
                 */
588
                return(-EINVAL);
589
        }
590
 
591
        if (fl.oif == 0)
592
                fl.oif = sk->bound_dev_if;
593
        fl.fl6_src = NULL;
594
 
595
        if (msg->msg_controllen) {
596
                opt = &opt_space;
597
                memset(opt, 0, sizeof(struct ipv6_txoptions));
598
 
599
                err = datagram_send_ctl(msg, &fl, opt, &hlimit);
600
                if (err < 0) {
601
                        fl6_sock_release(flowlabel);
602
                        return err;
603
                }
604
                if ((fl.fl6_flowlabel&IPV6_FLOWLABEL_MASK) && !flowlabel) {
605
                        flowlabel = fl6_sock_lookup(sk, fl.fl6_flowlabel);
606
                        if (flowlabel == NULL)
607
                                return -EINVAL;
608
                }
609
                if (!(opt->opt_nflen|opt->opt_flen))
610
                        opt = NULL;
611
        }
612
        if (opt == NULL)
613
                opt = np->opt;
614
        if (flowlabel)
615
                opt = fl6_merge_options(&opt_space, flowlabel, opt);
616
 
617
        raw_opt = &sk->tp_pinfo.tp_raw;
618
 
619
        fl.proto = proto;
620
        fl.fl6_dst = daddr;
621
        if (fl.fl6_src == NULL && !ipv6_addr_any(&np->saddr))
622
                fl.fl6_src = &np->saddr;
623
        fl.uli_u.icmpt.type = 0;
624
        fl.uli_u.icmpt.code = 0;
625
 
626
        if (raw_opt->checksum) {
627
                struct rawv6_fakehdr hdr;
628
 
629
                hdr.iov = msg->msg_iov;
630
                hdr.sk  = sk;
631
                hdr.len = len;
632
                hdr.cksum = 0;
633
                hdr.proto = proto;
634
 
635
                if (opt && opt->srcrt)
636
                        hdr.daddr = daddr;
637
                else
638
                        hdr.daddr = NULL;
639
 
640
                err = ip6_build_xmit(sk, rawv6_frag_cksum, &hdr, &fl, len,
641
                                     opt, hlimit, msg->msg_flags);
642
        } else {
643
                err = ip6_build_xmit(sk, rawv6_getfrag, msg->msg_iov, &fl, len,
644
                                     opt, hlimit, msg->msg_flags);
645
        }
646
 
647
        fl6_sock_release(flowlabel);
648
 
649
        return err<0?err:len;
650
}
651
 
652
static int rawv6_seticmpfilter(struct sock *sk, int level, int optname,
653
                               char *optval, int optlen)
654
{
655
        switch (optname) {
656
        case ICMPV6_FILTER:
657
                if (optlen > sizeof(struct icmp6_filter))
658
                        optlen = sizeof(struct icmp6_filter);
659
                if (copy_from_user(&sk->tp_pinfo.tp_raw.filter, optval, optlen))
660
                        return -EFAULT;
661
                return 0;
662
        default:
663
                return -ENOPROTOOPT;
664
        };
665
 
666
        return 0;
667
}
668
 
669
static int rawv6_geticmpfilter(struct sock *sk, int level, int optname,
670
                               char *optval, int *optlen)
671
{
672
        int len;
673
 
674
        switch (optname) {
675
        case ICMPV6_FILTER:
676
                if (get_user(len, optlen))
677
                        return -EFAULT;
678
                if (len < 0)
679
                        return -EINVAL;
680
                if (len > sizeof(struct icmp6_filter))
681
                        len = sizeof(struct icmp6_filter);
682
                if (put_user(len, optlen))
683
                        return -EFAULT;
684
                if (copy_to_user(optval, &sk->tp_pinfo.tp_raw.filter, len))
685
                        return -EFAULT;
686
                return 0;
687
        default:
688
                return -ENOPROTOOPT;
689
        };
690
 
691
        return 0;
692
}
693
 
694
 
695
static int rawv6_setsockopt(struct sock *sk, int level, int optname,
696
                            char *optval, int optlen)
697
{
698
        struct raw6_opt *opt = &sk->tp_pinfo.tp_raw;
699
        int val;
700
 
701
        switch(level) {
702
                case SOL_RAW:
703
                        break;
704
 
705
                case SOL_ICMPV6:
706
                        if (sk->num != IPPROTO_ICMPV6)
707
                                return -EOPNOTSUPP;
708
                        return rawv6_seticmpfilter(sk, level, optname, optval,
709
                                                   optlen);
710
                case SOL_IPV6:
711
                        if (optname == IPV6_CHECKSUM)
712
                                break;
713
                default:
714
                        return ipv6_setsockopt(sk, level, optname, optval,
715
                                               optlen);
716
        };
717
 
718
        if (get_user(val, (int *)optval))
719
                return -EFAULT;
720
 
721
        switch (optname) {
722
                case IPV6_CHECKSUM:
723
                        /* You may get strange result with a positive odd offset;
724
                           RFC2292bis agrees with me. */
725
                        if (val > 0 && (val&1))
726
                                return(-EINVAL);
727
                        if (val < 0) {
728
                                opt->checksum = 0;
729
                        } else {
730
                                opt->checksum = 1;
731
                                opt->offset = val;
732
                        }
733
 
734
                        return 0;
735
                        break;
736
 
737
                default:
738
                        return(-ENOPROTOOPT);
739
        }
740
}
741
 
742
static int rawv6_getsockopt(struct sock *sk, int level, int optname,
743
                            char *optval, int *optlen)
744
{
745
        struct raw6_opt *opt = &sk->tp_pinfo.tp_raw;
746
        int val, len;
747
 
748
        switch(level) {
749
                case SOL_RAW:
750
                        break;
751
 
752
                case SOL_ICMPV6:
753
                        if (sk->num != IPPROTO_ICMPV6)
754
                                return -EOPNOTSUPP;
755
                        return rawv6_geticmpfilter(sk, level, optname, optval,
756
                                                   optlen);
757
                case SOL_IPV6:
758
                        if (optname == IPV6_CHECKSUM)
759
                                break;
760
                default:
761
                        return ipv6_getsockopt(sk, level, optname, optval,
762
                                               optlen);
763
        };
764
 
765
        if (get_user(len,optlen))
766
                return -EFAULT;
767
 
768
        switch (optname) {
769
        case IPV6_CHECKSUM:
770
                if (opt->checksum == 0)
771
                        val = -1;
772
                else
773
                        val = opt->offset;
774
                break;
775
 
776
        default:
777
                return -ENOPROTOOPT;
778
        }
779
 
780
        len = min_t(unsigned int, sizeof(int), len);
781
 
782
        if (put_user(len, optlen))
783
                return -EFAULT;
784
        if (copy_to_user(optval,&val,len))
785
                return -EFAULT;
786
        return 0;
787
}
788
 
789
static int rawv6_ioctl(struct sock *sk, int cmd, unsigned long arg)
790
{
791
        switch(cmd) {
792
                case SIOCOUTQ:
793
                {
794
                        int amount = atomic_read(&sk->wmem_alloc);
795
                        return put_user(amount, (int *)arg);
796
                }
797
                case SIOCINQ:
798
                {
799
                        struct sk_buff *skb;
800
                        int amount = 0;
801
 
802
                        spin_lock_irq(&sk->receive_queue.lock);
803
                        skb = skb_peek(&sk->receive_queue);
804
                        if (skb != NULL)
805
                                amount = skb->tail - skb->h.raw;
806
                        spin_unlock_irq(&sk->receive_queue.lock);
807
                        return put_user(amount, (int *)arg);
808
                }
809
 
810
                default:
811
                        return -ENOIOCTLCMD;
812
        }
813
}
814
 
815
static void rawv6_close(struct sock *sk, long timeout)
816
{
817
        if (sk->num == IPPROTO_RAW)
818
                ip6_ra_control(sk, -1, NULL);
819
 
820
        inet_sock_release(sk);
821
}
822
 
823
static int rawv6_init_sk(struct sock *sk)
824
{
825
        if (sk->num == IPPROTO_ICMPV6){
826
                struct raw6_opt *opt = &sk->tp_pinfo.tp_raw;
827
                opt->checksum = 1;
828
                opt->offset = 2;
829
        }
830
        return(0);
831
}
832
 
833
#define LINE_LEN 190
834
#define LINE_FMT "%-190s\n"
835
 
836
static void get_raw6_sock(struct sock *sp, char *tmpbuf, int i)
837
{
838
        struct in6_addr *dest, *src;
839
        __u16 destp, srcp;
840
 
841
        dest  = &sp->net_pinfo.af_inet6.daddr;
842
        src   = &sp->net_pinfo.af_inet6.rcv_saddr;
843
        destp = 0;
844
        srcp  = sp->num;
845
        sprintf(tmpbuf,
846
                "%4d: %08X%08X%08X%08X:%04X %08X%08X%08X%08X:%04X "
847
                "%02X %08X:%08X %02X:%08lX %08X %5d %8d %lu %d %p",
848
                i,
849
                src->s6_addr32[0], src->s6_addr32[1],
850
                src->s6_addr32[2], src->s6_addr32[3], srcp,
851
                dest->s6_addr32[0], dest->s6_addr32[1],
852
                dest->s6_addr32[2], dest->s6_addr32[3], destp,
853
                sp->state,
854
                atomic_read(&sp->wmem_alloc), atomic_read(&sp->rmem_alloc),
855
                0, 0L, 0,
856
                sock_i_uid(sp), 0,
857
                sock_i_ino(sp),
858
                atomic_read(&sp->refcnt), sp);
859
}
860
 
861
int raw6_get_info(char *buffer, char **start, off_t offset, int length)
862
{
863
        int len = 0, num = 0, i;
864
        off_t pos = 0;
865
        off_t begin;
866
        char tmpbuf[LINE_LEN+2];
867
 
868
        if (offset < LINE_LEN+1)
869
                len += sprintf(buffer, LINE_FMT,
870
                               "  sl  "                                         /* 6 */
871
                               "local_address                         "         /* 38 */
872
                               "remote_address                        "         /* 38 */
873
                               "st tx_queue rx_queue tr tm->when retrnsmt"      /* 41 */
874
                               "   uid  timeout inode");                        /* 21 */
875
                                                                                /*----*/
876
                                                                                /*144 */
877
        pos = LINE_LEN+1;
878
        read_lock(&raw_v6_lock);
879
        for (i = 0; i < RAWV6_HTABLE_SIZE; i++) {
880
                struct sock *sk;
881
 
882
                for (sk = raw_v6_htable[i]; sk; sk = sk->next, num++) {
883
                        if (sk->family != PF_INET6)
884
                                continue;
885
                        pos += LINE_LEN+1;
886
                        if (pos <= offset)
887
                                continue;
888
                        get_raw6_sock(sk, tmpbuf, i);
889
                        len += sprintf(buffer+len, LINE_FMT, tmpbuf);
890
                        if(len >= length)
891
                                goto out;
892
                }
893
        }
894
out:
895
        read_unlock(&raw_v6_lock);
896
        begin = len - (pos - offset);
897
        *start = buffer + begin;
898
        len -= begin;
899
        if(len > length)
900
                len = length;
901
        if (len < 0)
902
                len = 0;
903
        return len;
904
}
905
 
906
struct proto rawv6_prot = {
907
        name:           "RAW",
908
        close:          rawv6_close,
909
        connect:        udpv6_connect,
910
        disconnect:     udp_disconnect,
911
        ioctl:          rawv6_ioctl,
912
        init:           rawv6_init_sk,
913
        destroy:        inet6_destroy_sock,
914
        setsockopt:     rawv6_setsockopt,
915
        getsockopt:     rawv6_getsockopt,
916
        sendmsg:        rawv6_sendmsg,
917
        recvmsg:        rawv6_recvmsg,
918
        bind:           rawv6_bind,
919
        backlog_rcv:    rawv6_rcv_skb,
920
        hash:           raw_v6_hash,
921
        unhash:         raw_v6_unhash,
922
};

powered by: WebSVN 2.1.0

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