* added 0.99 linux version
[mascara-docs.git] / i386 / linux / linux-2.3.21 / net / ipv6 / udp.c
blobe167c7e78c0c96a3e7d68b94e5f1874261996a56
1 /*
2 * UDP over IPv6
3 * Linux INET6 implementation
5 * Authors:
6 * Pedro Roque <roque@di.fc.ul.pt>
8 * Based on linux/ipv4/udp.c
10 * $Id: udp.c,v 1.45 1999/08/20 11:06:32 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/config.h>
19 #include <linux/errno.h>
20 #include <linux/types.h>
21 #include <linux/socket.h>
22 #include <linux/sockios.h>
23 #include <linux/sched.h>
24 #include <linux/net.h>
25 #include <linux/in6.h>
26 #include <linux/netdevice.h>
27 #include <linux/if_arp.h>
28 #include <linux/ipv6.h>
29 #include <linux/icmpv6.h>
30 #include <linux/init.h>
31 #include <asm/uaccess.h>
33 #include <net/sock.h>
34 #include <net/snmp.h>
36 #include <net/ipv6.h>
37 #include <net/ndisc.h>
38 #include <net/protocol.h>
39 #include <net/transp_v6.h>
40 #include <net/ip6_route.h>
41 #include <net/addrconf.h>
42 #include <net/ip.h>
43 #include <net/udp.h>
44 #include <net/inet_common.h>
46 #include <net/checksum.h>
48 struct udp_mib udp_stats_in6;
50 /* Grrr, addr_type already calculated by caller, but I don't want
51 * to add some silly "cookie" argument to this method just for that.
53 static int udp_v6_get_port(struct sock *sk, unsigned short snum)
55 write_lock_bh(&udp_hash_lock);
56 if (snum == 0) {
57 int best_size_so_far, best, result, i;
59 if (udp_port_rover > sysctl_local_port_range[1] ||
60 udp_port_rover < sysctl_local_port_range[0])
61 udp_port_rover = sysctl_local_port_range[0];
62 best_size_so_far = 32767;
63 best = result = udp_port_rover;
64 for (i = 0; i < UDP_HTABLE_SIZE; i++, result++) {
65 struct sock *sk;
66 int size;
68 sk = udp_hash[result & (UDP_HTABLE_SIZE - 1)];
69 if (!sk) {
70 if (result > sysctl_local_port_range[1])
71 result = sysctl_local_port_range[0] +
72 ((result - sysctl_local_port_range[0]) &
73 (UDP_HTABLE_SIZE - 1));
74 goto gotit;
76 size = 0;
77 do {
78 if (++size >= best_size_so_far)
79 goto next;
80 } while ((sk = sk->next) != NULL);
81 best_size_so_far = size;
82 best = result;
83 next:
85 result = best;
86 for(;; result += UDP_HTABLE_SIZE) {
87 if (result > sysctl_local_port_range[1])
88 result = sysctl_local_port_range[0]
89 + ((result - sysctl_local_port_range[0]) &
90 (UDP_HTABLE_SIZE - 1));
91 if (!udp_lport_inuse(result))
92 break;
94 gotit:
95 udp_port_rover = snum = result;
96 } else {
97 struct sock *sk2;
98 int addr_type = ipv6_addr_type(&sk->net_pinfo.af_inet6.rcv_saddr);
100 for (sk2 = udp_hash[snum & (UDP_HTABLE_SIZE - 1)];
101 sk2 != NULL;
102 sk2 = sk2->next) {
103 if (sk2->num == snum &&
104 sk2 != sk &&
105 sk2->bound_dev_if == sk->bound_dev_if &&
106 (!sk2->rcv_saddr ||
107 addr_type == IPV6_ADDR_ANY ||
108 !ipv6_addr_cmp(&sk->net_pinfo.af_inet6.rcv_saddr,
109 &sk2->net_pinfo.af_inet6.rcv_saddr)) &&
110 (!sk2->reuse || !sk->reuse))
111 goto fail;
115 sk->num = snum;
116 write_unlock_bh(&udp_hash_lock);
117 return 0;
119 fail:
120 write_unlock_bh(&udp_hash_lock);
121 return 1;
124 static void udp_v6_hash(struct sock *sk)
126 struct sock **skp = &udp_hash[sk->num & (UDP_HTABLE_SIZE - 1)];
128 write_lock_bh(&udp_hash_lock);
129 if ((sk->next = *skp) != NULL)
130 (*skp)->pprev = &sk->next;
131 *skp = sk;
132 sk->pprev = skp;
133 sk->prot->inuse++;
134 if(sk->prot->highestinuse < sk->prot->inuse)
135 sk->prot->highestinuse = sk->prot->inuse;
136 sock_hold(sk);
137 write_unlock_bh(&udp_hash_lock);
140 static void udp_v6_unhash(struct sock *sk)
142 write_lock_bh(&udp_hash_lock);
143 if (sk->pprev) {
144 if (sk->next)
145 sk->next->pprev = sk->pprev;
146 *sk->pprev = sk->next;
147 sk->pprev = NULL;
148 sk->prot->inuse--;
149 __sock_put(sk);
151 write_unlock_bh(&udp_hash_lock);
154 static struct sock *udp_v6_lookup(struct in6_addr *saddr, u16 sport,
155 struct in6_addr *daddr, u16 dport, int dif)
157 struct sock *sk, *result = NULL;
158 unsigned short hnum = ntohs(dport);
159 int badness = -1;
161 read_lock(&udp_hash_lock);
162 for(sk = udp_hash[hnum & (UDP_HTABLE_SIZE - 1)]; sk != NULL; sk = sk->next) {
163 if((sk->num == hnum) &&
164 (sk->family == PF_INET6)) {
165 struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
166 int score = 0;
167 if(sk->dport) {
168 if(sk->dport != sport)
169 continue;
170 score++;
172 if(!ipv6_addr_any(&np->rcv_saddr)) {
173 if(ipv6_addr_cmp(&np->rcv_saddr, daddr))
174 continue;
175 score++;
177 if(!ipv6_addr_any(&np->daddr)) {
178 if(ipv6_addr_cmp(&np->daddr, saddr))
179 continue;
180 score++;
182 if(sk->bound_dev_if) {
183 if(sk->bound_dev_if != dif)
184 continue;
185 score++;
187 if(score == 4) {
188 result = sk;
189 break;
190 } else if(score > badness) {
191 result = sk;
192 badness = score;
196 if (result)
197 sock_hold(result);
198 read_unlock(&udp_hash_lock);
199 return result;
206 int udpv6_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
208 struct sockaddr_in6 *usin = (struct sockaddr_in6 *) uaddr;
209 struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
210 struct in6_addr *daddr;
211 struct in6_addr saddr;
212 struct dst_entry *dst;
213 struct flowi fl;
214 struct ip6_flowlabel *flowlabel = NULL;
215 int addr_type;
216 int err;
218 if (usin->sin6_family == AF_INET) {
219 err = udp_connect(sk, uaddr, addr_len);
220 goto ipv4_connected;
223 if (addr_len < sizeof(*usin))
224 return -EINVAL;
226 if (usin->sin6_family != AF_INET6)
227 return -EAFNOSUPPORT;
229 fl.fl6_flowlabel = 0;
230 if (np->sndflow) {
231 fl.fl6_flowlabel = usin->sin6_flowinfo&IPV6_FLOWINFO_MASK;
232 if (fl.fl6_flowlabel&IPV6_FLOWLABEL_MASK) {
233 flowlabel = fl6_sock_lookup(sk, fl.fl6_flowlabel);
234 if (flowlabel == NULL)
235 return -EINVAL;
236 ipv6_addr_copy(&usin->sin6_addr, &flowlabel->dst);
240 addr_type = ipv6_addr_type(&usin->sin6_addr);
242 if (addr_type == IPV6_ADDR_ANY) {
244 * connect to self
246 usin->sin6_addr.s6_addr[15] = 0x01;
249 daddr = &usin->sin6_addr;
251 if (addr_type == IPV6_ADDR_MAPPED) {
252 struct sockaddr_in sin;
254 sin.sin_family = AF_INET;
255 sin.sin_addr.s_addr = daddr->s6_addr32[3];
256 sin.sin_port = usin->sin6_port;
258 err = udp_connect(sk, (struct sockaddr*) &sin, sizeof(sin));
260 ipv4_connected:
261 if (err < 0)
262 return err;
264 ipv6_addr_set(&np->daddr, 0, 0,
265 __constant_htonl(0x0000ffff),
266 sk->daddr);
268 if(ipv6_addr_any(&np->saddr)) {
269 ipv6_addr_set(&np->saddr, 0, 0,
270 __constant_htonl(0x0000ffff),
271 sk->saddr);
275 if(ipv6_addr_any(&np->rcv_saddr)) {
276 ipv6_addr_set(&np->rcv_saddr, 0, 0,
277 __constant_htonl(0x0000ffff),
278 sk->rcv_saddr);
280 return 0;
283 ipv6_addr_copy(&np->daddr, daddr);
284 np->flow_label = fl.fl6_flowlabel;
286 sk->dport = usin->sin6_port;
289 * Check for a route to destination an obtain the
290 * destination cache for it.
293 fl.proto = IPPROTO_UDP;
294 fl.fl6_dst = &np->daddr;
295 fl.fl6_src = &saddr;
296 fl.oif = sk->bound_dev_if;
297 fl.uli_u.ports.dport = sk->dport;
298 fl.uli_u.ports.sport = sk->sport;
300 if (flowlabel) {
301 if (flowlabel->opt && flowlabel->opt->srcrt) {
302 struct rt0_hdr *rt0 = (struct rt0_hdr *) flowlabel->opt->srcrt;
303 fl.fl6_dst = rt0->addr;
305 } else if (np->opt && np->opt->srcrt) {
306 struct rt0_hdr *rt0 = (struct rt0_hdr *) np->opt->srcrt;
307 fl.fl6_dst = rt0->addr;
310 dst = ip6_route_output(sk, &fl);
312 if ((err = dst->error) != 0) {
313 dst_release(dst);
314 fl6_sock_release(flowlabel);
315 return err;
318 ip6_dst_store(sk, dst, fl.fl6_dst);
320 /* get the source adddress used in the apropriate device */
322 err = ipv6_get_saddr(dst, daddr, &saddr);
324 if (err == 0) {
325 if(ipv6_addr_any(&np->saddr))
326 ipv6_addr_copy(&np->saddr, &saddr);
328 if(ipv6_addr_any(&np->rcv_saddr)) {
329 ipv6_addr_copy(&np->rcv_saddr, &saddr);
330 sk->rcv_saddr = 0xffffffff;
332 sk->state = TCP_ESTABLISHED;
334 fl6_sock_release(flowlabel);
336 return err;
339 static void udpv6_close(struct sock *sk, long timeout)
341 inet_sock_release(sk);
344 #ifndef HAVE_CSUM_COPY_USER
345 #undef CONFIG_UDP_DELAY_CSUM
346 #endif
349 * This should be easy, if there is something there we
350 * return it, otherwise we block.
353 int udpv6_recvmsg(struct sock *sk, struct msghdr *msg, int len,
354 int noblock, int flags, int *addr_len)
356 struct sk_buff *skb;
357 int copied, err;
359 if (addr_len)
360 *addr_len=sizeof(struct sockaddr_in6);
362 if (flags & MSG_ERRQUEUE)
363 return ipv6_recv_error(sk, msg, len);
365 skb = skb_recv_datagram(sk, flags, noblock, &err);
366 if (!skb)
367 goto out;
369 copied = skb->len - sizeof(struct udphdr);
370 if (copied > len) {
371 copied = len;
372 msg->msg_flags |= MSG_TRUNC;
375 #ifndef CONFIG_UDP_DELAY_CSUM
376 err = skb_copy_datagram_iovec(skb, sizeof(struct udphdr),
377 msg->msg_iov, copied);
378 #else
379 if (skb->ip_summed==CHECKSUM_UNNECESSARY) {
380 err = skb_copy_datagram_iovec(skb, sizeof(struct udphdr), msg->msg_iov,
381 copied);
382 } else if (copied > msg->msg_iov[0].iov_len || (msg->msg_flags&MSG_TRUNC)) {
383 if ((unsigned short)csum_fold(csum_partial(skb->h.raw, skb->len, skb->csum))) {
384 /* Clear queue. */
385 if (flags&MSG_PEEK) {
386 int clear = 0;
387 spin_lock_irq(&sk->receive_queue.lock);
388 if (skb == skb_peek(&sk->receive_queue)) {
389 __skb_unlink(skb, &sk->receive_queue);
390 clear = 1;
392 spin_unlock_irq(&sk->receive_queue.lock);
393 if (clear)
394 kfree_skb(skb);
397 /* Error for blocking case is chosen to masquerade
398 as some normal condition.
400 err = (flags&MSG_DONTWAIT) ? -EAGAIN : -EHOSTUNREACH;
401 udp_stats_in6.UdpInErrors++;
402 goto out_free;
404 err = skb_copy_datagram_iovec(skb, sizeof(struct udphdr), msg->msg_iov,
405 copied);
406 } else {
407 unsigned int csum = csum_partial(skb->h.raw, sizeof(struct udphdr), skb->csum);
409 err = 0;
410 csum = csum_and_copy_to_user((char*)&skb->h.uh[1], msg->msg_iov[0].iov_base, copied, csum, &err);
411 if (err)
412 goto out_free;
413 if ((unsigned short)csum_fold(csum)) {
414 /* Error for blocking case is chosen to masquerade
415 as some normal condition.
417 err = (flags&MSG_DONTWAIT) ? -EAGAIN : -EHOSTUNREACH;
418 udp_stats_in6.UdpInErrors++;
419 goto out_free;
422 #endif
423 if (err)
424 goto out_free;
426 sk->stamp=skb->stamp;
428 /* Copy the address. */
429 if (msg->msg_name) {
430 struct sockaddr_in6 *sin6;
432 sin6 = (struct sockaddr_in6 *) msg->msg_name;
433 sin6->sin6_family = AF_INET6;
434 sin6->sin6_port = skb->h.uh->source;
435 sin6->sin6_flowinfo = 0;
437 if (skb->protocol == __constant_htons(ETH_P_IP)) {
438 ipv6_addr_set(&sin6->sin6_addr, 0, 0,
439 __constant_htonl(0xffff), skb->nh.iph->saddr);
440 if (sk->protinfo.af_inet.cmsg_flags)
441 ip_cmsg_recv(msg, skb);
442 } else {
443 memcpy(&sin6->sin6_addr, &skb->nh.ipv6h->saddr,
444 sizeof(struct in6_addr));
446 if (sk->net_pinfo.af_inet6.rxopt.all)
447 datagram_recv_ctl(sk, msg, skb);
450 err = copied;
452 out_free:
453 skb_free_datagram(sk, skb);
454 out:
455 return err;
458 void udpv6_err(struct sk_buff *skb, struct ipv6hdr *hdr,
459 struct inet6_skb_parm *opt,
460 int type, int code, unsigned char *buff, __u32 info)
462 struct net_device *dev = skb->dev;
463 struct in6_addr *saddr = &hdr->saddr;
464 struct in6_addr *daddr = &hdr->daddr;
465 struct sock *sk;
466 struct udphdr *uh;
467 int err;
469 if (buff + sizeof(struct udphdr) > skb->tail)
470 return;
472 uh = (struct udphdr *) buff;
474 sk = udp_v6_lookup(daddr, uh->dest, saddr, uh->source, dev->ifindex);
476 if (sk == NULL)
477 return;
479 if (!icmpv6_err_convert(type, code, &err) &&
480 !sk->net_pinfo.af_inet6.recverr)
481 goto out;
483 if (sk->bsdism && sk->state!=TCP_ESTABLISHED &&
484 !sk->net_pinfo.af_inet6.recverr)
485 goto out;
487 if (sk->net_pinfo.af_inet6.recverr)
488 ipv6_icmp_error(sk, skb, err, uh->dest, ntohl(info), (u8 *)(uh+1));
490 sk->err = err;
491 sk->error_report(sk);
492 out:
493 sock_put(sk);
496 static inline int udpv6_queue_rcv_skb(struct sock * sk, struct sk_buff *skb)
498 #if defined(CONFIG_FILTER) && defined(CONFIG_UDP_DELAY_CSUM)
499 if (sk->filter && skb->ip_summed != CHECKSUM_UNNECESSARY) {
500 if ((unsigned short)csum_fold(csum_partial(skb->h.raw, skb->len, skb->csum))) {
501 udp_stats_in6.UdpInErrors++;
502 ipv6_statistics.Ip6InDiscards++;
503 kfree_skb(skb);
504 return 0;
506 skb->ip_summed = CHECKSUM_UNNECESSARY;
508 #endif
509 if (sock_queue_rcv_skb(sk,skb)<0) {
510 udp_stats_in6.UdpInErrors++;
511 ipv6_statistics.Ip6InDiscards++;
512 kfree_skb(skb);
513 return 0;
515 ipv6_statistics.Ip6InDelivers++;
516 udp_stats_in6.UdpInDatagrams++;
517 return 0;
520 static struct sock *udp_v6_mcast_next(struct sock *sk,
521 u16 loc_port, struct in6_addr *loc_addr,
522 u16 rmt_port, struct in6_addr *rmt_addr,
523 int dif)
525 struct sock *s = sk;
526 unsigned short num = ntohs(loc_port);
527 for(; s; s = s->next) {
528 if(s->num == num) {
529 struct ipv6_pinfo *np = &s->net_pinfo.af_inet6;
530 if(s->dport) {
531 if(s->dport != rmt_port)
532 continue;
534 if(!ipv6_addr_any(&np->daddr) &&
535 ipv6_addr_cmp(&np->daddr, rmt_addr))
536 continue;
538 if (s->bound_dev_if && s->bound_dev_if != dif)
539 continue;
541 if(!ipv6_addr_any(&np->rcv_saddr)) {
542 if(ipv6_addr_cmp(&np->rcv_saddr, loc_addr) == 0)
543 return s;
545 if(!inet6_mc_check(s, loc_addr))
546 continue;
547 return s;
550 return NULL;
554 * Note: called only from the BH handler context,
555 * so we don't need to lock the hashes.
557 static void udpv6_mcast_deliver(struct udphdr *uh,
558 struct in6_addr *saddr, struct in6_addr *daddr,
559 struct sk_buff *skb)
561 struct sock *sk, *sk2;
562 struct sk_buff *buff;
563 int dif;
565 read_lock(&udp_hash_lock);
566 sk = udp_hash[ntohs(uh->dest) & (UDP_HTABLE_SIZE - 1)];
567 dif = skb->dev->ifindex;
568 sk = udp_v6_mcast_next(sk, uh->dest, daddr, uh->source, saddr, dif);
569 if (!sk)
570 goto free_skb;
572 buff = NULL;
573 sk2 = sk;
574 while((sk2 = udp_v6_mcast_next(sk2->next, uh->dest, saddr,
575 uh->source, daddr, dif))) {
576 if (!buff) {
577 buff = skb_clone(skb, GFP_ATOMIC);
578 if (!buff)
579 continue;
581 if (sock_queue_rcv_skb(sk2, buff) >= 0)
582 buff = NULL;
584 if (buff)
585 kfree_skb(buff);
586 if (sock_queue_rcv_skb(sk, skb) < 0) {
587 free_skb:
588 kfree_skb(skb);
590 read_unlock(&udp_hash_lock);
593 int udpv6_rcv(struct sk_buff *skb, unsigned long len)
595 struct sock *sk;
596 struct udphdr *uh;
597 struct net_device *dev = skb->dev;
598 struct in6_addr *saddr = &skb->nh.ipv6h->saddr;
599 struct in6_addr *daddr = &skb->nh.ipv6h->daddr;
600 u32 ulen;
602 uh = skb->h.uh;
603 __skb_pull(skb, skb->h.raw - skb->data);
605 ulen = ntohs(uh->len);
607 /* Check for jumbo payload */
608 if (ulen == 0 && skb->nh.ipv6h->payload_len == 0)
609 ulen = len;
611 if (ulen > len || len < sizeof(*uh)) {
612 if (net_ratelimit())
613 printk(KERN_DEBUG "UDP: short packet: %d/%ld\n", ulen, len);
614 udp_stats_in6.UdpInErrors++;
615 kfree_skb(skb);
616 return(0);
619 if (uh->check == 0) {
620 /* IPv6 draft-v2 section 8.1 says that we SHOULD log
621 this error. Well, it is reasonable.
623 if (net_ratelimit())
624 printk(KERN_INFO "IPv6: udp checksum is 0\n");
625 goto discard;
628 skb_trim(skb, ulen);
630 #ifndef CONFIG_UDP_DELAY_CSUM
631 switch (skb->ip_summed) {
632 case CHECKSUM_NONE:
633 skb->csum = csum_partial((char*)uh, ulen, 0);
634 case CHECKSUM_HW:
635 if (csum_ipv6_magic(saddr, daddr, ulen, IPPROTO_UDP, skb->csum)) {
636 printk(KERN_DEBUG "IPv6: udp checksum error\n");
637 goto discard;
640 #else
641 if (skb->ip_summed==CHECKSUM_HW) {
642 if (csum_ipv6_magic(saddr, daddr, ulen, IPPROTO_UDP, skb->csum))
643 goto discard;
644 skb->ip_summed = CHECKSUM_UNNECESSARY;
645 } else if (skb->ip_summed != CHECKSUM_UNNECESSARY)
646 skb->csum = ~csum_ipv6_magic(saddr, daddr, ulen, IPPROTO_UDP, 0);
647 #endif
649 len = ulen;
652 * Multicast receive code
654 if (ipv6_addr_type(daddr) & IPV6_ADDR_MULTICAST) {
655 udpv6_mcast_deliver(uh, saddr, daddr, skb);
656 return 0;
659 /* Unicast */
662 * check socket cache ... must talk to Alan about his plans
663 * for sock caches... i'll skip this for now.
666 sk = udp_v6_lookup(saddr, uh->source, daddr, uh->dest, dev->ifindex);
668 if (sk == NULL) {
669 #ifdef CONFIG_UDP_DELAY_CSUM
670 if (skb->ip_summed != CHECKSUM_UNNECESSARY &&
671 (unsigned short)csum_fold(csum_partial((char*)uh, len, skb->csum)))
672 goto discard;
673 #endif
674 udp_stats_in6.UdpNoPorts++;
676 icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_PORT_UNREACH, 0, dev);
678 kfree_skb(skb);
679 return(0);
681 if (0/*sk->user_callback &&
682 sk->user_callback(sk->user_data, skb) == 0*/) {
683 udp_stats_in6.UdpInDatagrams++;
684 sock_put(sk);
685 return(0);
688 /* deliver */
690 udpv6_queue_rcv_skb(sk, skb);
691 sock_put(sk);
692 return(0);
694 discard:
695 udp_stats_in6.UdpInErrors++;
696 kfree_skb(skb);
697 return(0);
701 * Sending
704 struct udpv6fakehdr
706 struct udphdr uh;
707 struct iovec *iov;
708 __u32 wcheck;
709 __u32 pl_len;
710 struct in6_addr *daddr;
714 * with checksum
717 static int udpv6_getfrag(const void *data, struct in6_addr *addr,
718 char *buff, unsigned int offset, unsigned int len)
720 struct udpv6fakehdr *udh = (struct udpv6fakehdr *) data;
721 char *dst;
722 int final = 0;
723 int clen = len;
725 dst = buff;
727 if (offset) {
728 offset -= sizeof(struct udphdr);
729 } else {
730 dst += sizeof(struct udphdr);
731 final = 1;
732 clen -= sizeof(struct udphdr);
735 if (csum_partial_copy_fromiovecend(dst, udh->iov, offset,
736 clen, &udh->wcheck))
737 return -EFAULT;
739 if (final) {
740 struct in6_addr *daddr;
742 udh->wcheck = csum_partial((char *)udh, sizeof(struct udphdr),
743 udh->wcheck);
745 if (udh->daddr) {
746 daddr = udh->daddr;
747 } else {
749 * use packet destination address
750 * this should improve cache locality
752 daddr = addr + 1;
754 udh->uh.check = csum_ipv6_magic(addr, daddr,
755 udh->pl_len, IPPROTO_UDP,
756 udh->wcheck);
757 if (udh->uh.check == 0)
758 udh->uh.check = -1;
760 memcpy(buff, udh, sizeof(struct udphdr));
762 return 0;
765 static int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, int ulen)
767 struct ipv6_txoptions opt_space;
768 struct udpv6fakehdr udh;
769 struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
770 struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) msg->msg_name;
771 struct ipv6_txoptions *opt = NULL;
772 struct ip6_flowlabel *flowlabel = NULL;
773 struct flowi fl;
774 int addr_len = msg->msg_namelen;
775 struct in6_addr *daddr;
776 int len = ulen + sizeof(struct udphdr);
777 int addr_type;
778 int hlimit = -1;
780 int err;
782 /* Rough check on arithmetic overflow,
783 better check is made in ip6_build_xmit
785 if (ulen < 0 || ulen > INT_MAX - sizeof(struct udphdr))
786 return -EMSGSIZE;
788 fl.fl6_flowlabel = 0;
790 if (sin6) {
791 if (sin6->sin6_family == AF_INET)
792 return udp_sendmsg(sk, msg, ulen);
794 if (addr_len < sizeof(*sin6))
795 return -EINVAL;
797 if (sin6->sin6_family && sin6->sin6_family != AF_INET6)
798 return -EINVAL;
800 if (sin6->sin6_port == 0)
801 return -EINVAL;
803 udh.uh.dest = sin6->sin6_port;
804 daddr = &sin6->sin6_addr;
806 if (np->sndflow) {
807 fl.fl6_flowlabel = sin6->sin6_flowinfo&IPV6_FLOWINFO_MASK;
808 if (fl.fl6_flowlabel&IPV6_FLOWLABEL_MASK) {
809 flowlabel = fl6_sock_lookup(sk, fl.fl6_flowlabel);
810 if (flowlabel == NULL)
811 return -EINVAL;
812 daddr = &flowlabel->dst;
816 /* Otherwise it will be difficult to maintain sk->dst_cache. */
817 if (sk->state == TCP_ESTABLISHED &&
818 !ipv6_addr_cmp(daddr, &sk->net_pinfo.af_inet6.daddr))
819 daddr = &sk->net_pinfo.af_inet6.daddr;
820 } else {
821 if (sk->state != TCP_ESTABLISHED)
822 return -ENOTCONN;
824 udh.uh.dest = sk->dport;
825 daddr = &sk->net_pinfo.af_inet6.daddr;
826 fl.fl6_flowlabel = np->flow_label;
829 addr_type = ipv6_addr_type(daddr);
831 if (addr_type == IPV6_ADDR_MAPPED) {
832 struct sockaddr_in sin;
834 sin.sin_family = AF_INET;
835 sin.sin_addr.s_addr = daddr->s6_addr32[3];
836 sin.sin_port = udh.uh.dest;
837 msg->msg_name = (struct sockaddr *)(&sin);
838 msg->msg_namelen = sizeof(sin);
839 fl6_sock_release(flowlabel);
841 return udp_sendmsg(sk, msg, ulen);
844 udh.daddr = NULL;
845 fl.oif = sk->bound_dev_if;
846 fl.fl6_src = NULL;
848 if (msg->msg_controllen) {
849 opt = &opt_space;
850 memset(opt, 0, sizeof(struct ipv6_txoptions));
852 err = datagram_send_ctl(msg, &fl, opt, &hlimit);
853 if (err < 0) {
854 fl6_sock_release(flowlabel);
855 return err;
857 if ((fl.fl6_flowlabel&IPV6_FLOWLABEL_MASK) && !flowlabel) {
858 flowlabel = fl6_sock_lookup(sk, fl.fl6_flowlabel);
859 if (flowlabel == NULL)
860 return -EINVAL;
862 if (!(opt->opt_nflen|opt->opt_flen))
863 opt = NULL;
865 if (opt == NULL)
866 opt = np->opt;
867 if (flowlabel)
868 opt = fl6_merge_options(&opt_space, flowlabel, opt);
869 if (opt && opt->srcrt)
870 udh.daddr = daddr;
872 udh.uh.source = sk->sport;
873 udh.uh.len = len < 0x10000 ? htons(len) : 0;
874 udh.uh.check = 0;
875 udh.iov = msg->msg_iov;
876 udh.wcheck = 0;
877 udh.pl_len = len;
879 fl.proto = IPPROTO_UDP;
880 fl.fl6_dst = daddr;
881 fl.uli_u.ports.dport = udh.uh.dest;
882 fl.uli_u.ports.sport = udh.uh.source;
884 err = ip6_build_xmit(sk, udpv6_getfrag, &udh, &fl, len, opt, hlimit,
885 msg->msg_flags);
887 fl6_sock_release(flowlabel);
889 if (err < 0)
890 return err;
892 udp_stats_in6.UdpOutDatagrams++;
893 return ulen;
896 static struct inet6_protocol udpv6_protocol =
898 udpv6_rcv, /* UDP handler */
899 udpv6_err, /* UDP error control */
900 NULL, /* next */
901 IPPROTO_UDP, /* protocol ID */
902 0, /* copy */
903 NULL, /* data */
904 "UDPv6" /* name */
907 #define LINE_LEN 190
908 #define LINE_FMT "%-190s\n"
910 static void get_udp6_sock(struct sock *sp, char *tmpbuf, int i)
912 struct in6_addr *dest, *src;
913 __u16 destp, srcp;
914 int timer_active;
915 unsigned long timer_expires;
917 dest = &sp->net_pinfo.af_inet6.daddr;
918 src = &sp->net_pinfo.af_inet6.rcv_saddr;
919 destp = ntohs(sp->dport);
920 srcp = ntohs(sp->sport);
921 timer_active = (sp->timer.prev != NULL) ? 2 : 0;
922 timer_expires = (timer_active == 2 ? sp->timer.expires : jiffies);
923 sprintf(tmpbuf,
924 "%4d: %08X%08X%08X%08X:%04X %08X%08X%08X%08X:%04X "
925 "%02X %08X:%08X %02X:%08lX %08X %5d %8d %ld %d %p",
927 src->s6_addr32[0], src->s6_addr32[1],
928 src->s6_addr32[2], src->s6_addr32[3], srcp,
929 dest->s6_addr32[0], dest->s6_addr32[1],
930 dest->s6_addr32[2], dest->s6_addr32[3], destp,
931 sp->state,
932 atomic_read(&sp->wmem_alloc), atomic_read(&sp->rmem_alloc),
933 timer_active, timer_expires-jiffies, 0,
934 sp->socket->inode->i_uid, 0,
935 sp->socket ? sp->socket->inode->i_ino : 0,
936 atomic_read(&sp->refcnt), sp);
939 int udp6_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
941 int len = 0, num = 0, i;
942 off_t pos = 0;
943 off_t begin;
944 char tmpbuf[LINE_LEN+2];
946 if (offset < LINE_LEN+1)
947 len += sprintf(buffer, LINE_FMT,
948 " sl " /* 6 */
949 "local_address " /* 38 */
950 "remote_address " /* 38 */
951 "st tx_queue rx_queue tr tm->when retrnsmt" /* 41 */
952 " uid timeout inode"); /* 21 */
953 /*----*/
954 /*144 */
955 pos = LINE_LEN+1;
956 read_lock(&udp_hash_lock);
957 for (i = 0; i < UDP_HTABLE_SIZE; i++) {
958 struct sock *sk;
960 for (sk = udp_hash[i]; sk; sk = sk->next, num++) {
961 if (sk->family != PF_INET6)
962 continue;
963 pos += LINE_LEN+1;
964 if (pos < offset)
965 continue;
966 get_udp6_sock(sk, tmpbuf, i);
967 len += sprintf(buffer+len, LINE_FMT, tmpbuf);
968 if(len >= length)
969 goto out;
972 out:
973 read_unlock(&udp_hash_lock);
974 begin = len - (pos - offset);
975 *start = buffer + begin;
976 len -= begin;
977 if(len > length)
978 len = length;
979 if (len < 0)
980 len = 0;
981 return len;
984 struct proto udpv6_prot = {
985 udpv6_close, /* close */
986 udpv6_connect, /* connect */
987 udp_disconnect, /* disconnect */
988 NULL, /* accept */
989 NULL, /* retransmit */
990 NULL, /* write_wakeup */
991 NULL, /* read_wakeup */
992 datagram_poll, /* poll */
993 udp_ioctl, /* ioctl */
994 NULL, /* init */
995 inet6_destroy_sock, /* destroy */
996 NULL, /* shutdown */
997 ipv6_setsockopt, /* setsockopt */
998 ipv6_getsockopt, /* getsockopt */
999 udpv6_sendmsg, /* sendmsg */
1000 udpv6_recvmsg, /* recvmsg */
1001 NULL, /* bind */
1002 udpv6_queue_rcv_skb, /* backlog_rcv */
1003 udp_v6_hash, /* hash */
1004 udp_v6_unhash, /* unhash */
1005 udp_v6_get_port, /* get_port */
1006 128, /* max_header */
1007 0, /* retransmits */
1008 "UDP", /* name */
1009 0, /* inuse */
1010 0 /* highestinuse */
1013 void __init udpv6_init(void)
1015 inet6_add_protocol(&udpv6_protocol);