#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>
*
*/
+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);
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);
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;
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;
}
}
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;
}
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);
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);
}
out:
fl6_sock_release(flowlabel);
if (!err) {
- UDP6_INC_STATS_USER(UDP_MIB_OUTDATAGRAMS);
+ UDP6_INC_STATS_USER(UdpOutDatagrams);
return len;
}
return err;
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,