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