This commit was manufactured by cvs2svn to create tag
[linux-2.6.git] / net / ipv6 / udp.c
index 7133801..9a6bc46 100644 (file)
@@ -49,7 +49,6 @@
 #include <net/addrconf.h>
 #include <net/ip.h>
 #include <net/udp.h>
-#include <net/raw.h>
 #include <net/inet_common.h>
 
 #include <net/ip6_checksum.h>
@@ -204,6 +203,159 @@ static struct sock *udp_v6_lookup(struct in6_addr *saddr, u16 sport,
  *
  */
 
+int udpv6_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
+{
+       struct sockaddr_in6     *usin = (struct sockaddr_in6 *) uaddr;
+       struct inet_opt         *inet = inet_sk(sk);
+       struct ipv6_pinfo       *np = inet6_sk(sk);
+       struct in6_addr         *daddr;
+       struct dst_entry        *dst;
+       struct flowi            fl;
+       struct ip6_flowlabel    *flowlabel = NULL;
+       int                     addr_type;
+       int                     err;
+
+       if (usin->sin6_family == AF_INET) {
+               if (__ipv6_only_sock(sk))
+                       return -EAFNOSUPPORT;
+               err = udp_connect(sk, uaddr, addr_len);
+               goto ipv4_connected;
+       }
+
+       if (addr_len < SIN6_LEN_RFC2133)
+               return -EINVAL;
+
+       if (usin->sin6_family != AF_INET6) 
+               return -EAFNOSUPPORT;
+
+       memset(&fl, 0, sizeof(fl));
+       if (np->sndflow) {
+               fl.fl6_flowlabel = usin->sin6_flowinfo&IPV6_FLOWINFO_MASK;
+               if (fl.fl6_flowlabel&IPV6_FLOWLABEL_MASK) {
+                       flowlabel = fl6_sock_lookup(sk, fl.fl6_flowlabel);
+                       if (flowlabel == NULL)
+                               return -EINVAL;
+                       ipv6_addr_copy(&usin->sin6_addr, &flowlabel->dst);
+               }
+       }
+
+       addr_type = ipv6_addr_type(&usin->sin6_addr);
+
+       if (addr_type == IPV6_ADDR_ANY) {
+               /*
+                *      connect to self
+                */
+               usin->sin6_addr.s6_addr[15] = 0x01;
+       }
+
+       daddr = &usin->sin6_addr;
+
+       if (addr_type == IPV6_ADDR_MAPPED) {
+               struct sockaddr_in sin;
+
+               if (__ipv6_only_sock(sk)) {
+                       err = -ENETUNREACH;
+                       goto out;
+               }
+               sin.sin_family = AF_INET;
+               sin.sin_addr.s_addr = daddr->s6_addr32[3];
+               sin.sin_port = usin->sin6_port;
+
+               err = udp_connect(sk, (struct sockaddr*) &sin, sizeof(sin));
+
+ipv4_connected:
+               if (err)
+                       goto out;
+               
+               ipv6_addr_set(&np->daddr, 0, 0, htonl(0x0000ffff), inet->daddr);
+
+               if (ipv6_addr_any(&np->saddr)) {
+                       ipv6_addr_set(&np->saddr, 0, 0, htonl(0x0000ffff),
+                                     inet->saddr);
+               }
+
+               if (ipv6_addr_any(&np->rcv_saddr)) {
+                       ipv6_addr_set(&np->rcv_saddr, 0, 0, htonl(0x0000ffff),
+                                     inet->rcv_saddr);
+               }
+               goto out;
+       }
+
+       if (addr_type&IPV6_ADDR_LINKLOCAL) {
+               if (addr_len >= sizeof(struct sockaddr_in6) &&
+                   usin->sin6_scope_id) {
+                       if (sk->sk_bound_dev_if &&
+                           sk->sk_bound_dev_if != usin->sin6_scope_id) {
+                               err = -EINVAL;
+                               goto out;
+                       }
+                       sk->sk_bound_dev_if = usin->sin6_scope_id;
+                       if (!sk->sk_bound_dev_if &&
+                           (addr_type & IPV6_ADDR_MULTICAST))
+                               fl.oif = np->mcast_oif;
+               }
+
+               /* Connect to link-local address requires an interface */
+               if (!sk->sk_bound_dev_if) {
+                       err = -EINVAL;
+                       goto out;
+               }
+       }
+
+       ipv6_addr_copy(&np->daddr, daddr);
+       np->flow_label = fl.fl6_flowlabel;
+
+       inet->dport = usin->sin6_port;
+
+       /*
+        *      Check for a route to destination an obtain the
+        *      destination cache for it.
+        */
+
+       fl.proto = IPPROTO_UDP;
+       ipv6_addr_copy(&fl.fl6_dst, &np->daddr);
+       ipv6_addr_copy(&fl.fl6_src, &np->saddr);
+       fl.oif = sk->sk_bound_dev_if;
+       fl.fl_ip_dport = inet->dport;
+       fl.fl_ip_sport = inet->sport;
+
+       if (!fl.oif && (addr_type&IPV6_ADDR_MULTICAST))
+               fl.oif = np->mcast_oif;
+
+       if (flowlabel) {
+               if (flowlabel->opt && flowlabel->opt->srcrt) {
+                       struct rt0_hdr *rt0 = (struct rt0_hdr *) flowlabel->opt->srcrt;
+                       ipv6_addr_copy(&fl.fl6_dst, rt0->addr);
+               }
+       } else if (np->opt && np->opt->srcrt) {
+               struct rt0_hdr *rt0 = (struct rt0_hdr *)np->opt->srcrt;
+               ipv6_addr_copy(&fl.fl6_dst, rt0->addr);
+       }
+
+       err = ip6_dst_lookup(sk, &dst, &fl);
+       if (err)
+               goto out;
+
+       /* source address lookup done in ip6_dst_lookup */
+
+       if (ipv6_addr_any(&np->saddr))
+               ipv6_addr_copy(&np->saddr, &fl.fl6_src);
+
+       if (ipv6_addr_any(&np->rcv_saddr)) {
+               ipv6_addr_copy(&np->rcv_saddr, &fl.fl6_src);
+               inet->rcv_saddr = LOOPBACK4_IPV6;
+       }
+
+       ip6_dst_store(sk, dst,
+                     !ipv6_addr_cmp(&fl.fl6_dst, &np->daddr) ?
+                     &np->daddr : NULL);
+
+       sk->sk_state = TCP_ESTABLISHED;
+out:
+       fl6_sock_release(flowlabel);
+       return err;
+}
+
 static void udpv6_close(struct sock *sk, long timeout)
 {
        sk_common_release(sk);
@@ -284,10 +436,7 @@ try_again:
                                sin6->sin6_scope_id = IP6CB(skb)->iif;
                }
        }
-
        err = copied;
-       if (flags & MSG_TRUNC)
-               err = skb->len - sizeof(struct udphdr);
 
 out_free:
        skb_free_datagram(sk, skb);
@@ -311,7 +460,7 @@ csum_copy_err:
        skb_free_datagram(sk, skb);
 
        if (flags & MSG_DONTWAIT) {
-               UDP6_INC_STATS_USER(UDP_MIB_INERRORS);
+               UDP6_INC_STATS_USER(UdpInErrors);
                return -EAGAIN;
        }
        goto try_again;
@@ -360,7 +509,7 @@ static inline int udpv6_queue_rcv_skb(struct sock * sk, struct sk_buff *skb)
 
        if (skb->ip_summed != CHECKSUM_UNNECESSARY) {
                if ((unsigned short)csum_fold(skb_checksum(skb, 0, skb->len, skb->csum))) {
-                       UDP6_INC_STATS_BH(UDP_MIB_INERRORS);
+                       UDP6_INC_STATS_BH(UdpInErrors);
                        kfree_skb(skb);
                        return 0;
                }
@@ -368,11 +517,11 @@ static inline int udpv6_queue_rcv_skb(struct sock * sk, struct sk_buff *skb)
        }
 
        if (sock_queue_rcv_skb(sk,skb)<0) {
-               UDP6_INC_STATS_BH(UDP_MIB_INERRORS);
+               UDP6_INC_STATS_BH(UdpInErrors);
                kfree_skb(skb);
                return 0;
        }
-       UDP6_INC_STATS_BH(UDP_MIB_INDATAGRAMS);
+       UDP6_INC_STATS_BH(UdpInDatagrams);
        return 0;
 }
 
@@ -521,7 +670,7 @@ static int udpv6_rcv(struct sk_buff **pskb, unsigned int *nhoffp)
                if (skb->ip_summed != CHECKSUM_UNNECESSARY &&
                    (unsigned short)csum_fold(skb_checksum(skb, 0, skb->len, skb->csum)))
                        goto discard;
-               UDP6_INC_STATS_BH(UDP_MIB_NOPORTS);
+               UDP6_INC_STATS_BH(UdpNoPorts);
 
                icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_PORT_UNREACH, 0, dev);
 
@@ -540,7 +689,7 @@ short_packet:
                printk(KERN_DEBUG "UDP: short packet: %d/%u\n", ulen, skb->len);
 
 discard:
-       UDP6_INC_STATS_BH(UDP_MIB_INERRORS);
+       UDP6_INC_STATS_BH(UdpInErrors);
        kfree_skb(skb);
        return(0);      
 }
@@ -839,7 +988,7 @@ do_append_data:
 out:
        fl6_sock_release(flowlabel);
        if (!err) {
-               UDP6_INC_STATS_USER(UDP_MIB_OUTDATAGRAMS);
+               UDP6_INC_STATS_USER(UdpOutDatagrams);
                return len;
        }
        return err;
@@ -1025,7 +1174,7 @@ void udp6_proc_exit(void) {
 struct proto udpv6_prot = {
        .name =         "UDP",
        .close =        udpv6_close,
-       .connect =      ip6_datagram_connect,
+       .connect =      udpv6_connect,
        .disconnect =   udp_disconnect,
        .ioctl =        udp_ioctl,
        .destroy =      udpv6_destroy_sock,