pre-2.3.4..
[davej-history.git] / net / ipv6 / raw.c
blobc472d8f1f69dad4c7f03e638b6086a3762e462eb
1 /*
2 * RAW sockets for IPv6
3 * Linux INET6 implementation
5 * Authors:
6 * Pedro Roque <roque@di.fc.ul.pt>
8 * Adapted from linux/net/ipv4/raw.c
10 * $Id: raw.c,v 1.24 1999/04/22 10:07:45 davem Exp $
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version
15 * 2 of the License, or (at your option) any later version.
18 #include <linux/errno.h>
19 #include <linux/types.h>
20 #include <linux/socket.h>
21 #include <linux/sockios.h>
22 #include <linux/sched.h>
23 #include <linux/net.h>
24 #include <linux/in6.h>
25 #include <linux/netdevice.h>
26 #include <linux/if_arp.h>
27 #include <linux/icmpv6.h>
28 #include <asm/uaccess.h>
30 #include <net/sock.h>
31 #include <net/snmp.h>
33 #include <net/ipv6.h>
34 #include <net/ndisc.h>
35 #include <net/protocol.h>
36 #include <net/ip6_route.h>
37 #include <net/addrconf.h>
38 #include <net/transp_v6.h>
40 #include <net/rawv6.h>
42 #include <asm/uaccess.h>
44 struct sock *raw_v6_htable[RAWV6_HTABLE_SIZE];
46 static void raw_v6_hash(struct sock *sk)
48 struct sock **skp;
49 int num = sk->num;
51 num &= (RAWV6_HTABLE_SIZE - 1);
52 skp = &raw_v6_htable[num];
53 SOCKHASH_LOCK_WRITE();
54 sk->next = *skp;
55 *skp = sk;
56 sk->hashent = num;
57 SOCKHASH_UNLOCK_WRITE();
60 static void raw_v6_unhash(struct sock *sk)
62 struct sock **skp;
63 int num = sk->num;
65 num &= (RAWV6_HTABLE_SIZE - 1);
66 skp = &raw_v6_htable[num];
68 SOCKHASH_LOCK_WRITE();
69 while(*skp != NULL) {
70 if(*skp == sk) {
71 *skp = sk->next;
72 break;
74 skp = &((*skp)->next);
76 SOCKHASH_UNLOCK_WRITE();
79 static void raw_v6_rehash(struct sock *sk)
81 struct sock **skp;
82 int num = sk->num;
83 int oldnum = sk->hashent;
85 num &= (RAWV6_HTABLE_SIZE - 1);
86 skp = &raw_v6_htable[oldnum];
88 SOCKHASH_LOCK_WRITE();
89 while(*skp != NULL) {
90 if(*skp == sk) {
91 *skp = sk->next;
92 break;
94 skp = &((*skp)->next);
96 sk->next = raw_v6_htable[num];
97 raw_v6_htable[num] = sk;
98 sk->hashent = num;
99 SOCKHASH_UNLOCK_WRITE();
102 static __inline__ int inet6_mc_check(struct sock *sk, struct in6_addr *addr)
104 struct ipv6_mc_socklist *mc;
106 for (mc = sk->net_pinfo.af_inet6.ipv6_mc_list; mc; mc=mc->next) {
107 if (ipv6_addr_cmp(&mc->addr, addr) == 0)
108 return 1;
111 return 0;
114 /* Grumble... icmp and ip_input want to get at this... */
115 struct sock *raw_v6_lookup(struct sock *sk, unsigned short num,
116 struct in6_addr *loc_addr, struct in6_addr *rmt_addr)
118 struct sock *s = sk;
119 int addr_type = ipv6_addr_type(loc_addr);
121 for(s = sk; s; s = s->next) {
122 if((s->num == num) &&
123 !(s->dead && (s->state == TCP_CLOSE))) {
124 struct ipv6_pinfo *np = &s->net_pinfo.af_inet6;
126 if (!ipv6_addr_any(&np->daddr) &&
127 ipv6_addr_cmp(&np->daddr, rmt_addr))
128 continue;
130 if (!ipv6_addr_any(&np->rcv_saddr)) {
131 if (ipv6_addr_cmp(&np->rcv_saddr, loc_addr) == 0)
132 return(s);
133 if ((addr_type & IPV6_ADDR_MULTICAST) &&
134 inet6_mc_check(s, loc_addr))
135 return (s);
136 continue;
138 return(s);
141 return NULL;
144 /* This cleans up af_inet6 a bit. -DaveM */
145 static int rawv6_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len)
147 struct sockaddr_in6 *addr = (struct sockaddr_in6 *) uaddr;
148 __u32 v4addr = 0;
149 int addr_type;
151 /* Check these errors. */
152 if (sk->state != TCP_CLOSE || (addr_len < sizeof(struct sockaddr_in6)))
153 return -EINVAL;
155 addr_type = ipv6_addr_type(&addr->sin6_addr);
157 /* Check if the address belongs to the host. */
158 if (addr_type == IPV6_ADDR_MAPPED) {
159 /* Raw sockets are IPv6 only */
160 return(-EADDRNOTAVAIL);
161 } else {
162 if (addr_type != IPV6_ADDR_ANY) {
163 /* ipv4 addr of the socket is invalid. Only the
164 * unpecified and mapped address have a v4 equivalent.
166 v4addr = LOOPBACK4_IPV6;
167 if (!(addr_type & IPV6_ADDR_MULTICAST)) {
168 if (ipv6_chk_addr(&addr->sin6_addr, NULL, 0) == NULL)
169 return(-EADDRNOTAVAIL);
174 sk->rcv_saddr = v4addr;
175 sk->saddr = v4addr;
176 memcpy(&sk->net_pinfo.af_inet6.rcv_saddr, &addr->sin6_addr,
177 sizeof(struct in6_addr));
178 if (!(addr_type & IPV6_ADDR_MULTICAST))
179 memcpy(&sk->net_pinfo.af_inet6.saddr, &addr->sin6_addr,
180 sizeof(struct in6_addr));
181 return 0;
184 void rawv6_err(struct sock *sk, struct sk_buff *skb, struct ipv6hdr *hdr,
185 struct inet6_skb_parm *opt,
186 int type, int code, unsigned char *buff, u32 info)
188 int err;
189 int harderr;
191 if (buff > skb->tail)
192 return;
194 /* Report error on raw socket, if:
195 1. User requested recverr.
196 2. Socket is connected (otherwise the error indication
197 is useless without recverr and error is hard.
199 if (!sk->net_pinfo.af_inet6.recverr && sk->state != TCP_ESTABLISHED)
200 return;
202 harderr = icmpv6_err_convert(type, code, &err);
203 if (type == ICMPV6_PKT_TOOBIG)
204 harderr = (sk->net_pinfo.af_inet6.pmtudisc == IPV6_PMTUDISC_DO);
206 if (sk->net_pinfo.af_inet6.recverr)
207 ipv6_icmp_error(sk, skb, err, 0, ntohl(info), buff);
209 if (sk->net_pinfo.af_inet6.recverr || harderr) {
210 sk->err = err;
211 sk->error_report(sk);
215 static inline int rawv6_rcv_skb(struct sock * sk, struct sk_buff * skb)
217 /* Charge it to the socket. */
218 if (sock_queue_rcv_skb(sk,skb)<0) {
219 ipv6_statistics.Ip6InDiscards++;
220 kfree_skb(skb);
221 return 0;
224 ipv6_statistics.Ip6InDelivers++;
225 return 0;
229 * This is next to useless...
230 * if we demultiplex in network layer we don't need the extra call
231 * just to queue the skb...
232 * maybe we could have the network decide uppon a hint if it
233 * should call raw_rcv for demultiplexing
235 int rawv6_rcv(struct sock *sk, struct sk_buff *skb, unsigned long len)
237 if (sk->ip_hdrincl)
238 skb->h.raw = skb->nh.raw;
240 rawv6_rcv_skb(sk, skb);
241 return 0;
246 * This should be easy, if there is something there
247 * we return it, otherwise we block.
250 int rawv6_recvmsg(struct sock *sk, struct msghdr *msg, int len,
251 int noblock, int flags, int *addr_len)
253 struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)msg->msg_name;
254 struct sk_buff *skb;
255 int copied, err;
257 if (flags & MSG_OOB)
258 return -EOPNOTSUPP;
260 if (addr_len)
261 *addr_len=sizeof(*sin6);
263 if (flags & MSG_ERRQUEUE)
264 return ipv6_recv_error(sk, msg, len);
266 skb = skb_recv_datagram(sk, flags, noblock, &err);
267 if (!skb)
268 goto out;
270 copied = skb->tail - skb->h.raw;
271 if (copied > len) {
272 copied = len;
273 msg->msg_flags |= MSG_TRUNC;
276 err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
277 sk->stamp=skb->stamp;
278 if (err)
279 goto out_free;
281 /* Copy the address. */
282 if (sin6) {
283 sin6->sin6_family = AF_INET6;
284 memcpy(&sin6->sin6_addr, &skb->nh.ipv6h->saddr,
285 sizeof(struct in6_addr));
286 sin6->sin6_flowinfo = 0;
289 if (sk->net_pinfo.af_inet6.rxopt.all)
290 datagram_recv_ctl(sk, msg, skb);
291 err = copied;
293 out_free:
294 skb_free_datagram(sk, skb);
295 out:
296 return err;
300 * Sending...
303 struct rawv6_fakehdr {
304 struct iovec *iov;
305 struct sock *sk;
306 __u32 len;
307 __u32 cksum;
308 __u32 proto;
309 struct in6_addr *daddr;
312 static int rawv6_getfrag(const void *data, struct in6_addr *saddr,
313 char *buff, unsigned int offset, unsigned int len)
315 struct iovec *iov = (struct iovec *) data;
317 return memcpy_fromiovecend(buff, iov, offset, len);
320 static int rawv6_frag_cksum(const void *data, struct in6_addr *addr,
321 char *buff, unsigned int offset,
322 unsigned int len)
324 struct rawv6_fakehdr *hdr = (struct rawv6_fakehdr *) data;
326 if (csum_partial_copy_fromiovecend(buff, hdr->iov, offset,
327 len, &hdr->cksum))
328 return -EFAULT;
330 if (offset == 0) {
331 struct sock *sk;
332 struct raw6_opt *opt;
333 struct in6_addr *daddr;
335 sk = hdr->sk;
336 opt = &sk->tp_pinfo.tp_raw;
338 if (hdr->daddr)
339 daddr = hdr->daddr;
340 else
341 daddr = addr + 1;
343 hdr->cksum = csum_ipv6_magic(addr, daddr, hdr->len,
344 hdr->proto, hdr->cksum);
346 if (opt->offset < len) {
347 __u16 *csum;
349 csum = (__u16 *) (buff + opt->offset);
350 *csum = hdr->cksum;
351 } else {
352 if (net_ratelimit())
353 printk(KERN_DEBUG "icmp: cksum offset too big\n");
354 return -EINVAL;
357 return 0;
361 static int rawv6_sendmsg(struct sock *sk, struct msghdr *msg, int len)
363 struct ipv6_txoptions opt_space;
364 struct sockaddr_in6 * sin6 = (struct sockaddr_in6 *) msg->msg_name;
365 struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
366 struct ipv6_txoptions *opt = NULL;
367 struct ip6_flowlabel *flowlabel = NULL;
368 struct flowi fl;
369 int addr_len = msg->msg_namelen;
370 struct in6_addr *daddr;
371 struct raw6_opt *raw_opt;
372 int hlimit = -1;
373 u16 proto;
374 int err;
376 /* Rough check on arithmetic overflow,
377 better check is made in ip6_build_xmit
379 if (len < 0)
380 return -EMSGSIZE;
382 /* Mirror BSD error message compatibility */
383 if (msg->msg_flags & MSG_OOB)
384 return -EOPNOTSUPP;
386 if (msg->msg_flags & ~(MSG_DONTROUTE|MSG_DONTWAIT))
387 return(-EINVAL);
389 * Get and verify the address.
392 fl.fl6_flowlabel = 0;
394 if (sin6) {
395 if (addr_len < sizeof(struct sockaddr_in6))
396 return(-EINVAL);
398 if (sin6->sin6_family && sin6->sin6_family != AF_INET6)
399 return(-EINVAL);
401 /* port is the proto value [0..255] carried in nexthdr */
402 proto = ntohs(sin6->sin6_port);
404 if (!proto)
405 proto = sk->num;
407 if (proto > 255)
408 return(-EINVAL);
410 daddr = &sin6->sin6_addr;
411 if (np->sndflow) {
412 fl.fl6_flowlabel = sin6->sin6_flowinfo&IPV6_FLOWINFO_MASK;
413 if (fl.fl6_flowlabel&IPV6_FLOWLABEL_MASK) {
414 flowlabel = fl6_sock_lookup(sk, fl.fl6_flowlabel);
415 if (flowlabel == NULL)
416 return -EINVAL;
417 daddr = &flowlabel->dst;
422 /* Otherwise it will be difficult to maintain sk->dst_cache. */
423 if (sk->state == TCP_ESTABLISHED &&
424 !ipv6_addr_cmp(daddr, &sk->net_pinfo.af_inet6.daddr))
425 daddr = &sk->net_pinfo.af_inet6.daddr;
426 } else {
427 if (sk->state != TCP_ESTABLISHED)
428 return(-EINVAL);
430 proto = sk->num;
431 daddr = &(sk->net_pinfo.af_inet6.daddr);
432 fl.fl6_flowlabel = np->flow_label;
435 if (ipv6_addr_any(daddr)) {
437 * unspecfied destination address
438 * treated as error... is this correct ?
440 return(-EINVAL);
443 fl.oif = sk->bound_dev_if;
444 fl.fl6_src = NULL;
446 if (msg->msg_controllen) {
447 opt = &opt_space;
448 memset(opt, 0, sizeof(struct ipv6_txoptions));
450 err = datagram_send_ctl(msg, &fl, opt, &hlimit);
451 if (err < 0) {
452 fl6_sock_release(flowlabel);
453 return err;
455 if ((fl.fl6_flowlabel&IPV6_FLOWLABEL_MASK) && !flowlabel) {
456 flowlabel = fl6_sock_lookup(sk, fl.fl6_flowlabel);
457 if (flowlabel == NULL)
458 return -EINVAL;
460 if (!(opt->opt_nflen|opt->opt_flen))
461 opt = NULL;
463 if (opt == NULL)
464 opt = np->opt;
465 if (flowlabel)
466 opt = fl6_merge_options(&opt_space, flowlabel, opt);
468 raw_opt = &sk->tp_pinfo.tp_raw;
470 fl.proto = proto;
471 fl.fl6_dst = daddr;
472 fl.uli_u.icmpt.type = 0;
473 fl.uli_u.icmpt.code = 0;
475 if (raw_opt->checksum) {
476 struct rawv6_fakehdr hdr;
478 hdr.iov = msg->msg_iov;
479 hdr.sk = sk;
480 hdr.len = len;
481 hdr.cksum = 0;
482 hdr.proto = proto;
484 if (opt && opt->srcrt)
485 hdr.daddr = daddr;
486 else
487 hdr.daddr = NULL;
489 err = ip6_build_xmit(sk, rawv6_frag_cksum, &hdr, &fl, len,
490 opt, hlimit, msg->msg_flags);
491 } else {
492 err = ip6_build_xmit(sk, rawv6_getfrag, msg->msg_iov, &fl, len,
493 opt, hlimit, msg->msg_flags);
496 fl6_sock_release(flowlabel);
498 return err<0?err:len;
501 static int rawv6_seticmpfilter(struct sock *sk, int level, int optname,
502 char *optval, int optlen)
504 switch (optname) {
505 case ICMPV6_FILTER:
506 if (optlen > sizeof(struct icmp6_filter))
507 optlen = sizeof(struct icmp6_filter);
508 if (copy_from_user(&sk->tp_pinfo.tp_raw.filter, optval, optlen))
509 return -EFAULT;
510 return 0;
511 default:
512 return -ENOPROTOOPT;
515 return 0;
518 static int rawv6_geticmpfilter(struct sock *sk, int level, int optname,
519 char *optval, int *optlen)
521 int len;
523 switch (optname) {
524 case ICMPV6_FILTER:
525 if (get_user(len, optlen))
526 return -EFAULT;
527 if (len > sizeof(struct icmp6_filter))
528 len = sizeof(struct icmp6_filter);
529 if (put_user(len, optlen))
530 return -EFAULT;
531 if (copy_to_user(optval, &sk->tp_pinfo.tp_raw.filter, len))
532 return -EFAULT;
533 return 0;
534 default:
535 return -ENOPROTOOPT;
538 return 0;
542 static int rawv6_setsockopt(struct sock *sk, int level, int optname,
543 char *optval, int optlen)
545 struct raw6_opt *opt = &sk->tp_pinfo.tp_raw;
546 int val;
548 switch(level) {
549 case SOL_RAW:
550 break;
552 case SOL_ICMPV6:
553 if (sk->num != IPPROTO_ICMPV6)
554 return -EOPNOTSUPP;
555 return rawv6_seticmpfilter(sk, level, optname, optval,
556 optlen);
557 case SOL_IPV6:
558 if (optname == IPV6_CHECKSUM)
559 break;
560 default:
561 return ipv6_setsockopt(sk, level, optname, optval,
562 optlen);
565 if (get_user(val, (int *)optval))
566 return -EFAULT;
568 switch (optname) {
569 case IPV6_CHECKSUM:
570 if (val < 0) {
571 opt->checksum = 0;
572 } else {
573 opt->checksum = 1;
574 opt->offset = val;
577 return 0;
578 break;
580 default:
581 return(-ENOPROTOOPT);
585 static int rawv6_getsockopt(struct sock *sk, int level, int optname,
586 char *optval, int *optlen)
588 struct raw6_opt *opt = &sk->tp_pinfo.tp_raw;
589 int val, len;
591 switch(level) {
592 case SOL_RAW:
593 break;
595 case SOL_ICMPV6:
596 if (sk->num != IPPROTO_ICMPV6)
597 return -EOPNOTSUPP;
598 return rawv6_geticmpfilter(sk, level, optname, optval,
599 optlen);
600 case SOL_IPV6:
601 if (optname == IPV6_CHECKSUM)
602 break;
603 default:
604 return ipv6_getsockopt(sk, level, optname, optval,
605 optlen);
608 if (get_user(len,optlen))
609 return -EFAULT;
611 switch (optname) {
612 case IPV6_CHECKSUM:
613 if (opt->checksum == 0)
614 val = -1;
615 else
616 val = opt->offset;
618 default:
619 return -ENOPROTOOPT;
622 len=min(sizeof(int),len);
624 if (put_user(len, optlen))
625 return -EFAULT;
626 if (copy_to_user(optval,&val,len))
627 return -EFAULT;
628 return 0;
632 static void rawv6_close(struct sock *sk, long timeout)
634 bh_lock_sock(sk);
636 /* See for explanation: raw_close in ipv4/raw.c */
637 sk->state = TCP_CLOSE;
638 raw_v6_unhash(sk);
639 if (sk->num == IPPROTO_RAW)
640 ip6_ra_control(sk, -1, NULL);
641 sk->dead = 1;
642 destroy_sock(sk);
645 static int rawv6_init_sk(struct sock *sk)
647 return(0);
650 struct proto rawv6_prot = {
651 (struct sock *)&rawv6_prot, /* sklist_next */
652 (struct sock *)&rawv6_prot, /* sklist_prev */
653 rawv6_close, /* close */
654 udpv6_connect, /* connect */
655 NULL, /* accept */
656 NULL, /* retransmit */
657 NULL, /* write_wakeup */
658 NULL, /* read_wakeup */
659 datagram_poll, /* poll */
660 NULL, /* ioctl */
661 rawv6_init_sk, /* init */
662 inet6_destroy_sock, /* destroy */
663 NULL, /* shutdown */
664 rawv6_setsockopt, /* setsockopt */
665 rawv6_getsockopt, /* getsockopt */
666 rawv6_sendmsg, /* sendmsg */
667 rawv6_recvmsg, /* recvmsg */
668 rawv6_bind, /* bind */
669 rawv6_rcv_skb, /* backlog_rcv */
670 raw_v6_hash, /* hash */
671 raw_v6_unhash, /* unhash */
672 raw_v6_rehash, /* rehash */
673 NULL, /* good_socknum */
674 NULL, /* verify_bind */
675 128, /* max_header */
676 0, /* retransmits */
677 "RAW", /* name */
678 0, /* inuse */
679 0 /* highestinuse */