cleanup
[linux-2.4.21-pre4.git] / net / ipv6 / raw.c
1 /*
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 2005/04/11 02:51:13 jack 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))
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(-EINVAL);
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
775         default:
776                 return -ENOPROTOOPT;
777         }
778
779         len = min_t(unsigned int, sizeof(int), len);
780
781         if (put_user(len, optlen))
782                 return -EFAULT;
783         if (copy_to_user(optval,&val,len))
784                 return -EFAULT;
785         return 0;
786 }
787
788 static int rawv6_ioctl(struct sock *sk, int cmd, unsigned long arg)
789 {
790         switch(cmd) {
791                 case SIOCOUTQ:
792                 {
793                         int amount = atomic_read(&sk->wmem_alloc);
794                         return put_user(amount, (int *)arg);
795                 }
796                 case SIOCINQ:
797                 {
798                         struct sk_buff *skb;
799                         int amount = 0;
800
801                         spin_lock_irq(&sk->receive_queue.lock);
802                         skb = skb_peek(&sk->receive_queue);
803                         if (skb != NULL)
804                                 amount = skb->tail - skb->h.raw;
805                         spin_unlock_irq(&sk->receive_queue.lock);
806                         return put_user(amount, (int *)arg);
807                 }
808
809                 default:
810                         return -ENOIOCTLCMD;
811         }
812 }
813
814 static void rawv6_close(struct sock *sk, long timeout)
815 {
816         if (sk->num == IPPROTO_RAW)
817                 ip6_ra_control(sk, -1, NULL);
818
819         inet_sock_release(sk);
820 }
821
822 static int rawv6_init_sk(struct sock *sk)
823 {
824         if (sk->num == IPPROTO_ICMPV6){
825                 struct raw6_opt *opt = &sk->tp_pinfo.tp_raw;
826                 opt->checksum = 1;
827                 opt->offset = 2;
828         }
829         return(0);
830 }
831
832 #define LINE_LEN 190
833 #define LINE_FMT "%-190s\n"
834
835 static void get_raw6_sock(struct sock *sp, char *tmpbuf, int i)
836 {
837         struct in6_addr *dest, *src;
838         __u16 destp, srcp;
839
840         dest  = &sp->net_pinfo.af_inet6.daddr;
841         src   = &sp->net_pinfo.af_inet6.rcv_saddr;
842         destp = 0;
843         srcp  = sp->num;
844         sprintf(tmpbuf,
845                 "%4d: %08X%08X%08X%08X:%04X %08X%08X%08X%08X:%04X "
846                 "%02X %08X:%08X %02X:%08lX %08X %5d %8d %lu %d %p",
847                 i,
848                 src->s6_addr32[0], src->s6_addr32[1],
849                 src->s6_addr32[2], src->s6_addr32[3], srcp,
850                 dest->s6_addr32[0], dest->s6_addr32[1],
851                 dest->s6_addr32[2], dest->s6_addr32[3], destp,
852                 sp->state, 
853                 atomic_read(&sp->wmem_alloc), atomic_read(&sp->rmem_alloc),
854                 0, 0L, 0,
855                 sock_i_uid(sp), 0,
856                 sock_i_ino(sp),
857                 atomic_read(&sp->refcnt), sp);
858 }
859
860 int raw6_get_info(char *buffer, char **start, off_t offset, int length)
861 {
862         int len = 0, num = 0, i;
863         off_t pos = 0;
864         off_t begin;
865         char tmpbuf[LINE_LEN+2];
866
867         if (offset < LINE_LEN+1)
868                 len += sprintf(buffer, LINE_FMT,
869                                "  sl  "                                         /* 6 */
870                                "local_address                         "         /* 38 */
871                                "remote_address                        "         /* 38 */
872                                "st tx_queue rx_queue tr tm->when retrnsmt"      /* 41 */
873                                "   uid  timeout inode");                        /* 21 */
874                                                                                 /*----*/
875                                                                                 /*144 */
876         pos = LINE_LEN+1;
877         read_lock(&raw_v6_lock);
878         for (i = 0; i < RAWV6_HTABLE_SIZE; i++) {
879                 struct sock *sk;
880
881                 for (sk = raw_v6_htable[i]; sk; sk = sk->next, num++) {
882                         if (sk->family != PF_INET6)
883                                 continue;
884                         pos += LINE_LEN+1;
885                         if (pos <= offset)
886                                 continue;
887                         get_raw6_sock(sk, tmpbuf, i);
888                         len += sprintf(buffer+len, LINE_FMT, tmpbuf);
889                         if(len >= length)
890                                 goto out;
891                 }
892         }
893 out:
894         read_unlock(&raw_v6_lock);
895         begin = len - (pos - offset);
896         *start = buffer + begin;
897         len -= begin;
898         if(len > length)
899                 len = length;
900         if (len < 0)
901                 len = 0; 
902         return len;
903 }
904
905 struct proto rawv6_prot = {
906         name:           "RAW",
907         close:          rawv6_close,
908         connect:        udpv6_connect,
909         disconnect:     udp_disconnect,
910         ioctl:          rawv6_ioctl,
911         init:           rawv6_init_sk,
912         destroy:        inet6_destroy_sock,
913         setsockopt:     rawv6_setsockopt,
914         getsockopt:     rawv6_getsockopt,
915         sendmsg:        rawv6_sendmsg,
916         recvmsg:        rawv6_recvmsg,
917         bind:           rawv6_bind,
918         backlog_rcv:    rawv6_rcv_skb,
919         hash:           raw_v6_hash,
920         unhash:         raw_v6_unhash,
921 };