netdev
[Top] [All Lists]

Re: sin6_scope_id (Re: SIOCGIFCONF and IPv6 addresses)

To: sekiya@xxxxxxx (Yuji Sekiya)
Subject: Re: sin6_scope_id (Re: SIOCGIFCONF and IPv6 addresses)
From: kuznet@xxxxxxxxxxxxx
Date: Fri, 25 Feb 2000 17:29:41 +0300 (MSK)
Cc: yoshfuji@xxxxxxxxxxxxxxxxx, misiek@xxxxxxxxxx, netdev@xxxxxxxxxxx
In-reply-to: <xms66vejbbs.wl@orb.isi.edu> from "Yuji Sekiya" at Feb 24, 0 05:54:47 pm
Sender: owner-netdev@xxxxxxxxxxx
Hello!

> We welcome your comments and suggestions. I think we need more
> discussion and improvement. We are serious to introduce sin6_scope_id.

I append reworked patch. It is preliminary version.
Please, check and comment, if you find something wrong.

Alexey



diff -ur ../vger3-000223/linux/include/linux/in6.h linux/include/linux/in6.h
--- ../vger3-000223/linux/include/linux/in6.h   Fri Aug 20 21:32:25 1999
+++ linux/include/linux/in6.h   Wed Feb 23 21:05:44 2000
@@ -56,9 +56,9 @@
        __u16                   sin6_port;      /* Transport layer port # */
        __u32                   sin6_flowinfo;  /* IPv6 flow information */
        struct in6_addr         sin6_addr;      /* IPv6 address */
+       __u32                   sin6_scope_id;  /* scope id (new in RFC2553) */
 };
 
-
 struct ipv6_mreq {
        /* IPv6 multicast address of group */
        struct in6_addr ipv6mr_multiaddr;
@@ -156,18 +156,6 @@
 #define IPV6_NEXTHOP           9
 #define IPV6_AUTHHDR           10
 #define IPV6_FLOWINFO          11
-
-#if 0
-/* Aliases for obsolete names */
-#define IPV6_RXHOPOPTS         IPV6_HOPOPTS
-#define IPV6_RXDSTOPTS         IPV6_DSTOPTS
-#define IPV6_RXSRCRT           IPV6_RTHDR
-#endif
-
-/*
- *     Alternative names
- */
-#define SCM_SRCRT              IPV6_RXSRCRT
 
 #define IPV6_UNICAST_HOPS      16
 #define IPV6_MULTICAST_IF      17
diff -ur ../vger3-000223/linux/include/net/ipv6.h linux/include/net/ipv6.h
--- ../vger3-000223/linux/include/net/ipv6.h    Mon Jan 10 23:15:23 2000
+++ linux/include/net/ipv6.h    Wed Feb 23 21:05:43 2000
@@ -20,6 +20,8 @@
 #include <net/ndisc.h>
 #include <net/flow.h>
 
+#define SIN6_LEN_RFC2133       24
+
 /*
  *     NextHeader field of IPv6 header
  */
diff -ur ../vger3-000223/linux/net/ipv6/af_inet6.c linux/net/ipv6/af_inet6.c
--- ../vger3-000223/linux/net/ipv6/af_inet6.c   Sun Feb 13 19:12:25 2000
+++ linux/net/ipv6/af_inet6.c   Thu Feb 24 22:07:19 2000
@@ -9,6 +9,9 @@
  *
  *     $Id: af_inet6.c,v 1.54 2000/02/12 23:34:45 davem Exp $
  *
+ *     Fixes:
+ *     Hideaki YOSHIFUJI       :       sin6_scope_id support
+ *
  *     This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
  *      as published by the Free Software Foundation; either version
@@ -220,9 +223,8 @@
        if(sk->prot->bind)
                return sk->prot->bind(sk, uaddr, addr_len);
 
-       if (addr_len < sizeof(struct sockaddr_in6))
+       if (addr_len < SIN6_LEN_RFC2133)
                return -EINVAL;
-
        addr_type = ipv6_addr_type(&addr->sin6_addr);
        if ((addr_type & IPV6_ADDR_MULTICAST) && sock->type == SOCK_STREAM)
                return -EINVAL;
@@ -258,6 +260,22 @@
                return -EINVAL;
        }
 
+       if (addr_type & IPV6_ADDR_LINKLOCAL) {
+               if (addr_len >= sizeof(struct sockaddr_in6) &&
+                   addr->sin6_scope_id) {
+                       /* Override any existing binding, if another one
+                        * is supplied by user.
+                        */
+                       sk->bound_dev_if = addr->sin6_scope_id;
+               }
+
+               /* Binding to link-local address requires an interface */
+               if (sk->bound_dev_if == 0) {
+                       release_sock(sk);
+                       return -EINVAL;
+               }
+       }
+
        sk->rcv_saddr = v4addr;
        sk->saddr = v4addr;
 
@@ -338,6 +356,7 @@
   
        sin->sin6_family = AF_INET6;
        sin->sin6_flowinfo = 0;
+       sin->sin6_scope_id = 0;
        if (peer) {
                if (!sk->dport)
                        return -ENOTCONN;
@@ -360,7 +379,9 @@
 
                sin->sin6_port = sk->sport;
        }
-       *uaddr_len = sizeof(*sin);      
+       if (ipv6_addr_type(&sin->sin6_addr) & IPV6_ADDR_LINKLOCAL)
+               sin->sin6_scope_id = sk->bound_dev_if;
+       *uaddr_len = sizeof(*sin);
        return(0);
 }
 
diff -ur ../vger3-000223/linux/net/ipv6/datagram.c linux/net/ipv6/datagram.c
--- ../vger3-000223/linux/net/ipv6/datagram.c   Fri Aug 20 21:42:24 1999
+++ linux/net/ipv6/datagram.c   Wed Feb 23 21:05:42 2000
@@ -134,14 +134,20 @@
                sin->sin6_family = AF_INET6;
                sin->sin6_flowinfo = 0;
                sin->sin6_port = serr->port; 
+               sin->sin6_scope_id = 0;
                if (serr->ee.ee_origin == SO_EE_ORIGIN_ICMP6) {
                        memcpy(&sin->sin6_addr, skb->nh.raw + 
serr->addr_offset, 16);
                        if (sk->net_pinfo.af_inet6.sndflow)
                                sin->sin6_flowinfo = *(u32*)(skb->nh.raw + 
serr->addr_offset - 24) & IPV6_FLOWINFO_MASK;
-               } else
+                       if (ipv6_addr_type(&sin->sin6_addr) & 
IPV6_ADDR_LINKLOCAL) {
+                               struct inet6_skb_parm *opt = (struct 
inet6_skb_parm *) skb->cb;
+                               sin->sin6_scope_id = opt->iif;
+                       }
+               } else {
                        ipv6_addr_set(&sin->sin6_addr, 0, 0,
                                      __constant_htonl(0xffff),
                                      *(u32*)(skb->nh.raw + serr->addr_offset));
+               }
        }
 
        memcpy(&errhdr.ee, &serr->ee, sizeof(struct sock_extended_err));
@@ -154,6 +160,10 @@
                        memcpy(&sin->sin6_addr, &skb->nh.ipv6h->saddr, 16);
                        if (sk->net_pinfo.af_inet6.rxopt.all)
                                datagram_recv_ctl(sk, msg, skb);
+                       if (ipv6_addr_type(&sin->sin6_addr) & 
IPV6_ADDR_LINKLOCAL) {
+                               struct inet6_skb_parm *opt = (struct 
inet6_skb_parm *) skb->cb;
+                               sin->sin6_scope_id = opt->iif;
+                       }
                } else {
                        ipv6_addr_set(&sin->sin6_addr, 0, 0,
                                      __constant_htonl(0xffff),
diff -ur ../vger3-000223/linux/net/ipv6/ip6_input.c linux/net/ipv6/ip6_input.c
--- ../vger3-000223/linux/net/ipv6/ip6_input.c  Wed Feb 23 17:52:05 2000
+++ linux/net/ipv6/ip6_input.c  Wed Feb 23 20:05:35 2000
@@ -26,6 +26,9 @@
 #include <linux/in6.h>
 #include <linux/icmpv6.h>
 
+#include <linux/netfilter.h>
+#include <linux/netfilter_ipv6.h>
+
 #include <net/sock.h>
 #include <net/snmp.h>
 
@@ -38,6 +41,16 @@
 #include <net/addrconf.h>
 
 
+
+static inline int ip6_rcv_finish( struct sk_buff *skb) 
+{
+
+       if (skb->dst == NULL)
+               ip6_route_input(skb);
+
+       return skb->dst->input(skb);
+}
+
 int ipv6_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type 
*pt)
 {
        struct ipv6hdr *hdr;
@@ -77,12 +90,7 @@
                        return 0;
                }
        }
-
-       if (skb->dst == NULL)
-               ip6_route_input(skb);
-
-       return skb->dst->input(skb);
-
+       return NF_HOOK(PF_INET6,NF_IP6_PRE_ROUTING, skb, dev, NULL, 
ip6_rcv_finish);
 truncated:
        IP6_INC_STATS_BH(Ip6InTruncatedPkts);
 err:
@@ -97,7 +105,8 @@
  *     Deliver the packet to the host
  */
 
-int ip6_input(struct sk_buff *skb)
+
+static inline int ip6_input_finish(struct sk_buff *skb)
 {
        struct ipv6hdr *hdr = skb->nh.ipv6h;
        struct inet6_protocol *ipprot;
@@ -178,6 +187,12 @@
        }
 
        return 0;
+}
+
+
+int ip6_input(struct sk_buff *skb)
+{
+       return NF_HOOK(PF_INET6,NF_IP6_LOCAL_IN, skb, skb->dev, NULL, 
ip6_input_finish);
 }
 
 int ip6_mc_input(struct sk_buff *skb)
diff -ur ../vger3-000223/linux/net/ipv6/ip6_output.c linux/net/ipv6/ip6_output.c
--- ../vger3-000223/linux/net/ipv6/ip6_output.c Mon Jan 10 23:16:39 2000
+++ linux/net/ipv6/ip6_output.c Wed Feb 23 21:11:58 2000
@@ -34,6 +34,9 @@
 #include <linux/in6.h>
 #include <linux/route.h>
 
+#include <linux/netfilter.h>
+#include <linux/netfilter_ipv6.h>
+
 #include <net/sock.h>
 #include <net/snmp.h>
 
@@ -57,11 +60,30 @@
        spin_unlock_bh(&ip6_id_lock);
 }
 
+static inline int ip6_output_finish(struct sk_buff *skb)
+{
+
+       struct dst_entry *dst = skb->dst;
+       struct hh_cache *hh = dst->hh;
+
+       if (hh) {
+               read_lock_bh(&hh->hh_lock);
+               memcpy(skb->data - 16, hh->hh_data, 16);
+               read_unlock_bh(&hh->hh_lock);
+               skb_push(skb, hh->hh_len);
+               return hh->hh_output(skb);
+       } else if (dst->neighbour)
+               return dst->neighbour->output(skb);
+
+       kfree_skb(skb);
+       return -EINVAL;
+
+}
+
 int ip6_output(struct sk_buff *skb)
 {
        struct dst_entry *dst = skb->dst;
        struct net_device *dev = dst->dev;
-       struct hh_cache *hh = dst->hh;
 
        skb->protocol = __constant_htons(ETH_P_IPV6);
        skb->dev = dev;
@@ -84,18 +106,29 @@
                IP6_INC_STATS(Ip6OutMcastPkts);
        }
 
-       if (hh) {
-               read_lock_bh(&hh->hh_lock);
-               memcpy(skb->data - 16, hh->hh_data, 16);
-               read_unlock_bh(&hh->hh_lock);
-               skb_push(skb, hh->hh_len);
-               return hh->hh_output(skb);
-       } else if (dst->neighbour)
-               return dst->neighbour->output(skb);
+       return NF_HOOK(PF_INET6, NF_IP6_POST_ROUTING, skb,NULL, 
skb->dev,ip6_output_finish);
 
-       kfree_skb(skb);
+}
+
+#ifdef CONFIG_NETFILTER
+static int route6_me_harder(struct sk_buff *skb)
+{
        return -EINVAL;
 }
+#endif /* CONFIG_NETFILTER */
+
+static inline int ip6_maybe_reroute(struct sk_buff *skb)
+{
+#ifdef CONFIG_NETFILTER
+       if (skb->nfcache & NFC_ALTERED){
+               if (route6_me_harder(skb) != 0){
+                       kfree_skb(skb);
+                       return -EINVAL;
+               }
+       }
+#endif /* CONFIG_NETFILTER */
+       return skb->dst->output(skb);
+}
 
 /*
  *     xmit an sk_buff (used by TCP)
@@ -159,7 +192,7 @@
 
        if (skb->len <= dst->pmtu) {
                IP6_INC_STATS(Ip6OutRequests);
-               return dst->output(skb);
+               return NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, skb, NULL, dst->dev, 
ip6_maybe_reroute);
        }
 
        printk(KERN_DEBUG "IPv6: sending pkt_too_big to self\n");
@@ -388,7 +421,7 @@
 
                        IP6_INC_STATS(Ip6FragCreates);
                        IP6_INC_STATS(Ip6OutRequests);
-                       err = dst->output(skb);
+                       err = NF_HOOK(PF_INET6,NF_IP6_LOCAL_OUT, skb, NULL, 
dst->dev, ip6_maybe_reroute);
                        if (err) {
                                kfree_skb(last_skb);
                                return err;
@@ -414,7 +447,7 @@
        IP6_INC_STATS(Ip6FragCreates);
        IP6_INC_STATS(Ip6FragOKs);
        IP6_INC_STATS(Ip6OutRequests);
-       return dst->output(last_skb);
+       return NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, last_skb, NULL,dst->dev, 
ip6_maybe_reroute);
 }
 
 int ip6_build_xmit(struct sock *sk, inet_getfrag_t getfrag, const void *data,
@@ -582,7 +615,7 @@
 
                if (!err) {
                        IP6_INC_STATS(Ip6OutRequests);
-                       err = dst->output(skb);
+                       err = NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, skb, NULL, 
dst->dev, ip6_maybe_reroute);
                } else {
                        err = -EFAULT;
                        kfree_skb(skb);
@@ -636,6 +669,11 @@
        return 0;
 }
 
+static inline int ip6_forward_finish(struct sk_buff *skb)
+{
+       return skb->dst->output(skb);
+}
+
 int ip6_forward(struct sk_buff *skb)
 {
        struct dst_entry *dst = skb->dst;
@@ -726,7 +764,7 @@
        hdr->hop_limit--;
 
        IP6_INC_STATS_BH(Ip6OutForwDatagrams);
-       return dst->output(skb);
+       return NF_HOOK(PF_INET6,NF_IP6_FORWARD, skb, skb->dev, dst->dev, 
ip6_forward_finish);
 
 drop:
        IP6_INC_STATS_BH(Ip6InAddrErrors);
diff -ur ../vger3-000223/linux/net/ipv6/ipv6_sockglue.c 
linux/net/ipv6/ipv6_sockglue.c
--- ../vger3-000223/linux/net/ipv6/ipv6_sockglue.c      Tue Feb  1 22:16:23 2000
+++ linux/net/ipv6/ipv6_sockglue.c      Wed Feb 23 20:00:57 2000
@@ -35,6 +35,7 @@
 #include <linux/if_arp.h>
 #include <linux/init.h>
 #include <linux/sysctl.h>
+#include <linux/netfilter.h>
 
 #include <net/sock.h>
 #include <net/snmp.h>
@@ -371,6 +372,14 @@
        case IPV6_FLOWLABEL_MGR:
                retv = ipv6_flowlabel_opt(sk, optval, optlen);
                break;
+
+#ifdef CONFIG_NETFILTER
+       default:
+               retv = nf_setsockopt(sk, PF_INET6, optname, optval, 
+                                           optlen);
+               break;
+#endif
+
        }
        release_sock(sk);
 
@@ -450,7 +459,17 @@
                break;
        }
        default:
+#ifdef CONFIG_NETFILTER
+               lock_sock(sk);
+               val = nf_getsockopt(sk, PF_INET6, optname, optval, 
+                                   &len);
+               release_sock(sk);
+               if (val >= 0)
+                       val = put_user(len, optlen);
+               return val;
+#else
                return -EINVAL;
+#endif
        }
        len=min(sizeof(int),len);
        if(put_user(len, optlen))
diff -ur ../vger3-000223/linux/net/ipv6/raw.c linux/net/ipv6/raw.c
--- ../vger3-000223/linux/net/ipv6/raw.c        Tue Jan 18 22:57:10 2000
+++ linux/net/ipv6/raw.c        Thu Feb 24 22:26:53 2000
@@ -9,6 +9,9 @@
  *
  *     $Id: raw.c,v 1.33 2000/01/18 08:24:22 davem Exp $
  *
+ *     Fixes:
+ *     Hideaki YOSHIFUJI       :       sin6_scope_id support
+ *
  *     This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
  *      as published by the Free Software Foundation; either version
@@ -183,9 +186,8 @@
        int addr_type;
        int err;
 
-       if (addr_len < sizeof(struct sockaddr_in6))
+       if (addr_len < SIN6_LEN_RFC2133)
                return -EINVAL;
-
        addr_type = ipv6_addr_type(&addr->sin6_addr);
 
        /* Raw sockets are IPv6 only */
@@ -198,6 +200,20 @@
        if (sk->state != TCP_CLOSE)
                goto out;
 
+       if (addr_type & IPV6_ADDR_LINKLOCAL) {
+               if (addr_len >= sizeof(struct sockaddr_in6) &&
+                   addr->sin6_scope_id) {
+                       /* Override any existing binding, if another one
+                        * is supplied by user.
+                        */
+                       sk->bound_dev_if = addr->sin6_scope_id;
+               }
+
+               /* Binding to link-local address requires an interface */
+               if (sk->bound_dev_if == 0)
+                       goto out;
+       }
+
        /* Check if the address belongs to the host. */
        if (addr_type != IPV6_ADDR_ANY) {
                /* ipv4 addr of the socket is invalid.  Only the
@@ -325,6 +341,11 @@
                memcpy(&sin6->sin6_addr, &skb->nh.ipv6h->saddr, 
                       sizeof(struct in6_addr));
                sin6->sin6_flowinfo = 0;
+               sin6->sin6_scope_id = 0;
+               if (ipv6_addr_type(&sin6->sin6_addr) & IPV6_ADDR_LINKLOCAL) {
+                       struct inet6_skb_parm *opt = (struct inet6_skb_parm *) 
skb->cb;
+                       sin6->sin6_scope_id = opt->iif;
+               }
        }
 
        if (sk->net_pinfo.af_inet6.rxopt.all)
@@ -429,14 +450,15 @@
         */
 
        fl.fl6_flowlabel = 0;
+       fl.oif = 0;
 
        if (sin6) {
-               if (addr_len < sizeof(struct sockaddr_in6)) 
-                       return(-EINVAL);
+               if (addr_len < SIN6_LEN_RFC2133) 
+                       return -EINVAL;
 
                if (sin6->sin6_family && sin6->sin6_family != AF_INET6) 
                        return(-EINVAL);
-               
+
                /* port is the proto value [0..255] carried in nexthdr */
                proto = ntohs(sin6->sin6_port);
 
@@ -457,11 +479,15 @@
                        }
                }
 
-
                /* Otherwise it will be difficult to maintain sk->dst_cache. */
                if (sk->state == TCP_ESTABLISHED &&
                    !ipv6_addr_cmp(daddr, &sk->net_pinfo.af_inet6.daddr))
                        daddr = &sk->net_pinfo.af_inet6.daddr;
+
+               if (addr_len >= sizeof(struct sockaddr_in6) &&
+                   sin6->sin6_scope_id &&
+                   ipv6_addr_type(daddr)&IPV6_ADDR_LINKLOCAL)
+                       fl.oif = sin6->sin6_scope_id;
        } else {
                if (sk->state != TCP_ESTABLISHED) 
                        return(-EINVAL);
@@ -479,7 +505,8 @@
                return(-EINVAL);
        }
 
-       fl.oif = sk->bound_dev_if;
+       if (fl.oif == 0)
+               fl.oif = sk->bound_dev_if;
        fl.fl6_src = NULL;
 
        if (msg->msg_controllen) {
diff -ur ../vger3-000223/linux/net/ipv6/tcp_ipv6.c linux/net/ipv6/tcp_ipv6.c
--- ../vger3-000223/linux/net/ipv6/tcp_ipv6.c   Tue Feb  1 22:16:24 2000
+++ linux/net/ipv6/tcp_ipv6.c   Thu Feb 24 22:07:19 2000
@@ -12,6 +12,9 @@
  *     linux/net/ipv4/tcp_input.c
  *     linux/net/ipv4/tcp_output.c
  *
+ *     Fixes:
+ *     Hideaki YOSHIFUJI       :       sin6_scope_id support
+ *
  *     This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
  *      as published by the Free Software Foundation; either version
@@ -509,8 +512,8 @@
        int addr_type;
        int err;
 
-       if (addr_len < sizeof(struct sockaddr_in6)) 
-               return(-EINVAL);
+       if (addr_len < SIN6_LEN_RFC2133) 
+               return -EINVAL;
 
        if (usin->sin6_family != AF_INET6) 
                return(-EAFNOSUPPORT);
@@ -540,6 +543,24 @@
        if(addr_type & IPV6_ADDR_MULTICAST)
                return -ENETUNREACH;
 
+       if (addr_type&IPV6_ADDR_LINKLOCAL) {
+               if (addr_len >= sizeof(struct sockaddr_in6) &&
+                   usin->sin6_scope_id) {
+                       /* If interface is set while binding, indices
+                        * must coincide.
+                        */
+                       if (sk->bound_dev_if &&
+                           sk->bound_dev_if != usin->sin6_scope_id)
+                               return -EINVAL;
+
+                       sk->bound_dev_if = usin->sin6_scope_id;
+               }
+
+               /* Connect to link-local address requires an interface */
+               if (sk->bound_dev_if == 0)
+                       return -EINVAL;
+       }
+
        if (tp->ts_recent_stamp && ipv6_addr_cmp(&np->daddr, &usin->sin6_addr)) 
{
                tp->ts_recent = 0;
                tp->ts_recent_stamp = 0;
@@ -605,15 +626,6 @@
                goto failure;
        }
 
-       if (fl.oif == 0 && addr_type&IPV6_ADDR_LINKLOCAL) {
-               /* Ough! This guy tries to connect to link local
-                * address and did not specify interface.
-                * Actually we should kick him out, but
-                * we will be patient :) --ANK
-                */
-               sk->bound_dev_if = dst->dev->ifindex;
-       }
-
        ip6_dst_store(sk, dst, NULL);
 
        if (saddr == NULL) {
@@ -1723,6 +1735,9 @@
        sin6->sin6_port = sk->dport;
        /* We do not store received flowlabel for TCP */
        sin6->sin6_flowinfo = 0;
+       sin6->sin6_scope_id = 0;
+       if (sk->bound_dev_if && 
ipv6_addr_type(&sin6->sin6_addr)&IPV6_ADDR_LINKLOCAL)
+               sin6->sin6_scope_id = sk->bound_dev_if;
 }
 
 static int tcp_v6_remember_stamp(struct sock *sk)
diff -ur ../vger3-000223/linux/net/ipv6/udp.c linux/net/ipv6/udp.c
--- ../vger3-000223/linux/net/ipv6/udp.c        Tue Jan 18 22:57:13 2000
+++ linux/net/ipv6/udp.c        Thu Feb 24 22:07:18 2000
@@ -9,6 +9,9 @@
  *
  *     $Id: udp.c,v 1.50 2000/01/18 08:24:24 davem Exp $
  *
+ *     Fixes:
+ *     Hideaki YOSHIFUJI       :       sin6_scope_id support
+ *
  *     This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
  *      as published by the Free Software Foundation; either version
@@ -218,7 +221,7 @@
                goto ipv4_connected;
        }
 
-       if (addr_len < sizeof(*usin)) 
+       if (addr_len < SIN6_LEN_RFC2133)
                return -EINVAL;
 
        if (usin->sin6_family != AF_INET6) 
@@ -278,6 +281,21 @@
                return 0;
        }
 
+       if (addr_type&IPV6_ADDR_LINKLOCAL) {
+               if (addr_len >= sizeof(struct sockaddr_in6) &&
+                   usin->sin6_scope_id) {
+                       if (sk->bound_dev_if && sk->bound_dev_if != 
usin->sin6_scope_id) {
+                               fl6_sock_release(flowlabel);
+                               return -EINVAL;
+                       }
+                       sk->bound_dev_if = usin->sin6_scope_id;
+               }
+
+               /* Connect to link-local address requires an interface */
+               if (sk->bound_dev_if == 0)
+                       return -EINVAL;
+       }
+
        ipv6_addr_copy(&np->daddr, daddr);
        np->flow_label = fl.fl6_flowlabel;
 
@@ -392,6 +410,7 @@
                sin6->sin6_family = AF_INET6;
                sin6->sin6_port = skb->h.uh->source;
                sin6->sin6_flowinfo = 0;
+               sin6->sin6_scope_id = 0;
 
                if (skb->protocol == __constant_htons(ETH_P_IP)) {
                        ipv6_addr_set(&sin6->sin6_addr, 0, 0,
@@ -404,6 +423,10 @@
 
                        if (sk->net_pinfo.af_inet6.rxopt.all)
                                datagram_recv_ctl(sk, msg, skb);
+                       if (ipv6_addr_type(&sin6->sin6_addr) & 
IPV6_ADDR_LINKLOCAL) {
+                               struct inet6_skb_parm *opt = (struct 
inet6_skb_parm *) skb->cb;
+                               sin6->sin6_scope_id = opt->iif;
+                       }
                }
        }
        err = copied;
@@ -746,12 +769,13 @@
                return -EMSGSIZE;
        
        fl.fl6_flowlabel = 0;
+       fl.oif = 0;
 
        if (sin6) {
                if (sin6->sin6_family == AF_INET)
                        return udp_sendmsg(sk, msg, ulen);
 
-               if (addr_len < sizeof(*sin6))
+               if (addr_len < SIN6_LEN_RFC2133)
                        return -EINVAL;
 
                if (sin6->sin6_family && sin6->sin6_family != AF_INET6)
@@ -777,6 +801,11 @@
                if (sk->state == TCP_ESTABLISHED &&
                    !ipv6_addr_cmp(daddr, &sk->net_pinfo.af_inet6.daddr))
                        daddr = &sk->net_pinfo.af_inet6.daddr;
+
+               if (addr_len >= sizeof(struct sockaddr_in6) &&
+                   sin6->sin6_scope_id &&
+                   ipv6_addr_type(daddr)&IPV6_ADDR_LINKLOCAL)
+                       fl.oif = sin6->sin6_scope_id;
        } else {
                if (sk->state != TCP_ESTABLISHED)
                        return -ENOTCONN;
@@ -802,7 +831,8 @@
        }
 
        udh.daddr = NULL;
-       fl.oif = sk->bound_dev_if;
+       if (!fl.oif)
+               fl.oif = sk->bound_dev_if;
        fl.fl6_src = NULL;
 
        if (msg->msg_controllen) {

<Prev in Thread] Current Thread [Next in Thread>