diff options
Diffstat (limited to 'net/sctp/ipv6.c')
| -rw-r--r-- | net/sctp/ipv6.c | 340 | 
1 files changed, 179 insertions, 161 deletions
diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c index 95e0c8eda1a..1999592ba88 100644 --- a/net/sctp/ipv6.c +++ b/net/sctp/ipv6.c @@ -21,16 +21,12 @@   * See the GNU General Public License for more details.   *   * You should have received a copy of the GNU General Public License - * along with GNU CC; see the file COPYING.  If not, write to - * the Free Software Foundation, 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. + * along with GNU CC; see the file COPYING.  If not, see + * <http://www.gnu.org/licenses/>.   *   * Please send any bug reports or fixes you make to the   * email address(es): - *    lksctp developers <lksctp-developers@lists.sourceforge.net> - * - * Or submit a bug report through the following website: - *    http://www.sf.net/projects/lksctp + *    lksctp developers <linux-sctp@vger.kernel.org>   *   * Written or modified by:   *    Le Yanqun		    <yanqun.le@nokia.com> @@ -42,9 +38,6 @@   *   * Based on:   *	linux/net/ipv6/tcp_ipv6.c - * - * Any bugs reported given to us we will try to fix... any fixes shared will - * be incorporated into the next SCTP release.   */  #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt @@ -80,6 +73,13 @@  #include <asm/uaccess.h> +static inline int sctp_v6_addr_match_len(union sctp_addr *s1, +					 union sctp_addr *s2); +static void sctp_v6_to_addr(union sctp_addr *addr, struct in6_addr *saddr, +			      __be16 port); +static int sctp_v6_cmp_addr(const union sctp_addr *addr1, +			    const union sctp_addr *addr2); +  /* Event handler for inet6 address addition/deletion events.   * The sctp_local_addr_list needs to be protocted by a spin lock since   * multiple notifiers (say IPv4 and IPv6) may be running at the same @@ -92,6 +92,7 @@ static int sctp_inet6addr_event(struct notifier_block *this, unsigned long ev,  	struct inet6_ifaddr *ifa = (struct inet6_ifaddr *)ptr;  	struct sctp_sockaddr_entry *addr = NULL;  	struct sctp_sockaddr_entry *temp; +	struct net *net = dev_net(ifa->idev->dev);  	int found = 0;  	switch (ev) { @@ -100,30 +101,32 @@ static int sctp_inet6addr_event(struct notifier_block *this, unsigned long ev,  		if (addr) {  			addr->a.v6.sin6_family = AF_INET6;  			addr->a.v6.sin6_port = 0; -			ipv6_addr_copy(&addr->a.v6.sin6_addr, &ifa->addr); +			addr->a.v6.sin6_addr = ifa->addr;  			addr->a.v6.sin6_scope_id = ifa->idev->dev->ifindex;  			addr->valid = 1; -			spin_lock_bh(&sctp_local_addr_lock); -			list_add_tail_rcu(&addr->list, &sctp_local_addr_list); -			spin_unlock_bh(&sctp_local_addr_lock); +			spin_lock_bh(&net->sctp.local_addr_lock); +			list_add_tail_rcu(&addr->list, &net->sctp.local_addr_list); +			sctp_addr_wq_mgmt(net, addr, SCTP_ADDR_NEW); +			spin_unlock_bh(&net->sctp.local_addr_lock);  		}  		break;  	case NETDEV_DOWN: -		spin_lock_bh(&sctp_local_addr_lock); +		spin_lock_bh(&net->sctp.local_addr_lock);  		list_for_each_entry_safe(addr, temp, -					&sctp_local_addr_list, list) { +					&net->sctp.local_addr_list, list) {  			if (addr->a.sa.sa_family == AF_INET6 &&  					ipv6_addr_equal(&addr->a.v6.sin6_addr,  						&ifa->addr)) { +				sctp_addr_wq_mgmt(net, addr, SCTP_ADDR_DEL);  				found = 1;  				addr->valid = 0;  				list_del_rcu(&addr->list);  				break;  			}  		} -		spin_unlock_bh(&sctp_local_addr_lock); +		spin_unlock_bh(&net->sctp.local_addr_lock);  		if (found) -			call_rcu(&addr->rcu, sctp_local_addr_free); +			kfree_rcu(addr, rcu);  		break;  	} @@ -135,16 +138,17 @@ static struct notifier_block sctp_inet6addr_notifier = {  };  /* ICMP error handler. */ -SCTP_STATIC void sctp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, -			     u8 type, u8 code, int offset, __be32 info) +static void sctp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, +			u8 type, u8 code, int offset, __be32 info)  {  	struct inet6_dev *idev;  	struct sock *sk;  	struct sctp_association *asoc;  	struct sctp_transport *transport;  	struct ipv6_pinfo *np; -	sk_buff_data_t saveip, savesctp; +	__u16 saveip, savesctp;  	int err; +	struct net *net = dev_net(skb->dev);  	idev = in6_dev_get(skb->dev); @@ -153,12 +157,12 @@ SCTP_STATIC void sctp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,  	savesctp = skb->transport_header;  	skb_reset_network_header(skb);  	skb_set_transport_header(skb, offset); -	sk = sctp_err_lookup(AF_INET6, skb, sctp_hdr(skb), &asoc, &transport); +	sk = sctp_err_lookup(net, AF_INET6, skb, sctp_hdr(skb), &asoc, &transport);  	/* Put back, the original pointers. */  	skb->network_header   = saveip;  	skb->transport_header = savesctp;  	if (!sk) { -		ICMP6_INC_STATS_BH(dev_net(skb->dev), idev, ICMP6_MIB_INERRORS); +		ICMP6_INC_STATS_BH(net, idev, ICMP6_MIB_INERRORS);  		goto out;  	} @@ -168,7 +172,8 @@ SCTP_STATIC void sctp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,  	switch (type) {  	case ICMPV6_PKT_TOOBIG: -		sctp_icmp_frag_needed(sk, asoc, transport, ntohl(info)); +		if (ip6_sk_accept_pmtu(sk)) +			sctp_icmp_frag_needed(sk, asoc, transport, ntohl(info));  		goto out_unlock;  	case ICMPV6_PARAMPROB:  		if (ICMPV6_UNK_NEXTHDR == code) { @@ -176,6 +181,9 @@ SCTP_STATIC void sctp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,  			goto out_unlock;  		}  		break; +	case NDISC_REDIRECT: +		sctp_icmp_redirect(sk, transport, skb); +		goto out_unlock;  	default:  		break;  	} @@ -196,128 +204,103 @@ out:  		in6_dev_put(idev);  } -/* Based on tcp_v6_xmit() in tcp_ipv6.c. */  static int sctp_v6_xmit(struct sk_buff *skb, struct sctp_transport *transport)  {  	struct sock *sk = skb->sk;  	struct ipv6_pinfo *np = inet6_sk(sk); -	struct flowi fl; - -	memset(&fl, 0, sizeof(fl)); - -	fl.proto = sk->sk_protocol; - -	/* Fill in the dest address from the route entry passed with the skb -	 * and the source address from the transport. -	 */ -	ipv6_addr_copy(&fl.fl6_dst, &transport->ipaddr.v6.sin6_addr); -	ipv6_addr_copy(&fl.fl6_src, &transport->saddr.v6.sin6_addr); - -	fl.fl6_flowlabel = np->flow_label; -	IP6_ECN_flow_xmit(sk, fl.fl6_flowlabel); -	if (ipv6_addr_type(&fl.fl6_src) & IPV6_ADDR_LINKLOCAL) -		fl.oif = transport->saddr.v6.sin6_scope_id; -	else -		fl.oif = sk->sk_bound_dev_if; - -	if (np->opt && np->opt->srcrt) { -		struct rt0_hdr *rt0 = (struct rt0_hdr *) np->opt->srcrt; -		ipv6_addr_copy(&fl.fl6_dst, rt0->addr); -	} +	struct flowi6 *fl6 = &transport->fl.u.ip6; -	SCTP_DEBUG_PRINTK("%s: skb:%p, len:%d, src:%pI6 dst:%pI6\n", -			  __func__, skb, skb->len, -			  &fl.fl6_src, &fl.fl6_dst); +	pr_debug("%s: skb:%p, len:%d, src:%pI6 dst:%pI6\n", __func__, skb, +		 skb->len, &fl6->saddr, &fl6->daddr); -	SCTP_INC_STATS(SCTP_MIB_OUTSCTPPACKS); +	IP6_ECN_flow_xmit(sk, fl6->flowlabel);  	if (!(transport->param_flags & SPP_PMTUD_ENABLE)) -		skb->local_df = 1; +		skb->ignore_df = 1; -	return ip6_xmit(sk, skb, &fl, np->opt); +	SCTP_INC_STATS(sock_net(sk), SCTP_MIB_OUTSCTPPACKS); + +	return ip6_xmit(sk, skb, fl6, np->opt, np->tclass);  }  /* Returns the dst cache entry for the given source and destination ip   * addresses.   */ -static struct dst_entry *sctp_v6_get_dst(struct sctp_association *asoc, -					 union sctp_addr *daddr, -					 union sctp_addr *saddr) +static void sctp_v6_get_dst(struct sctp_transport *t, union sctp_addr *saddr, +			    struct flowi *fl, struct sock *sk)  { -	struct dst_entry *dst; -	struct flowi fl; +	struct sctp_association *asoc = t->asoc; +	struct dst_entry *dst = NULL; +	struct flowi6 *fl6 = &fl->u.ip6; +	struct sctp_bind_addr *bp; +	struct ipv6_pinfo *np = inet6_sk(sk); +	struct sctp_sockaddr_entry *laddr; +	union sctp_addr *baddr = NULL; +	union sctp_addr *daddr = &t->ipaddr; +	union sctp_addr dst_saddr; +	struct in6_addr *final_p, final; +	__u8 matchlen = 0; +	__u8 bmatchlen; +	sctp_scope_t scope; -	memset(&fl, 0, sizeof(fl)); -	ipv6_addr_copy(&fl.fl6_dst, &daddr->v6.sin6_addr); +	memset(fl6, 0, sizeof(struct flowi6)); +	fl6->daddr = daddr->v6.sin6_addr; +	fl6->fl6_dport = daddr->v6.sin6_port; +	fl6->flowi6_proto = IPPROTO_SCTP;  	if (ipv6_addr_type(&daddr->v6.sin6_addr) & IPV6_ADDR_LINKLOCAL) -		fl.oif = daddr->v6.sin6_scope_id; +		fl6->flowi6_oif = daddr->v6.sin6_scope_id; +	pr_debug("%s: dst=%pI6 ", __func__, &fl6->daddr); -	SCTP_DEBUG_PRINTK("%s: DST=%pI6 ", __func__, &fl.fl6_dst); +	if (asoc) +		fl6->fl6_sport = htons(asoc->base.bind_addr.port);  	if (saddr) { -		ipv6_addr_copy(&fl.fl6_src, &saddr->v6.sin6_addr); -		SCTP_DEBUG_PRINTK("SRC=%pI6 - ", &fl.fl6_src); -	} +		fl6->saddr = saddr->v6.sin6_addr; +		fl6->fl6_sport = saddr->v6.sin6_port; -	dst = ip6_route_output(&init_net, NULL, &fl); -	if (!dst->error) { -		struct rt6_info *rt; -		rt = (struct rt6_info *)dst; -		SCTP_DEBUG_PRINTK("rt6_dst:%pI6 rt6_src:%pI6\n", -			&rt->rt6i_dst.addr, &rt->rt6i_src.addr); -		return dst; +		pr_debug("src=%pI6 - ", &fl6->saddr);  	} -	SCTP_DEBUG_PRINTK("NO ROUTE\n"); -	dst_release(dst); -	return NULL; -} - -/* Returns the number of consecutive initial bits that match in the 2 ipv6 - * addresses. - */ -static inline int sctp_v6_addr_match_len(union sctp_addr *s1, -					 union sctp_addr *s2) -{ -	return ipv6_addr_diff(&s1->v6.sin6_addr, &s2->v6.sin6_addr); -} - -/* Fills in the source address(saddr) based on the destination address(daddr) - * and asoc's bind address list. - */ -static void sctp_v6_get_saddr(struct sctp_sock *sk, -			      struct sctp_association *asoc, -			      struct dst_entry *dst, -			      union sctp_addr *daddr, -			      union sctp_addr *saddr) -{ -	struct sctp_bind_addr *bp; -	struct sctp_sockaddr_entry *laddr; -	sctp_scope_t scope; -	union sctp_addr *baddr = NULL; -	__u8 matchlen = 0; -	__u8 bmatchlen; -	SCTP_DEBUG_PRINTK("%s: asoc:%p dst:%p daddr:%pI6 ", -			  __func__, asoc, dst, &daddr->v6.sin6_addr); - -	if (!asoc) { -		ipv6_dev_get_saddr(sock_net(sctp_opt2sk(sk)), -				   dst ? ip6_dst_idev(dst)->dev : NULL, -				   &daddr->v6.sin6_addr, -				   inet6_sk(&sk->inet.sk)->srcprefs, -				   &saddr->v6.sin6_addr); -		SCTP_DEBUG_PRINTK("saddr from ipv6_get_saddr: %pI6\n", -				  &saddr->v6.sin6_addr); -		return; -	} - -	scope = sctp_scope(daddr); +	final_p = fl6_update_dst(fl6, np->opt, &final); +	dst = ip6_dst_lookup_flow(sk, fl6, final_p); +	if (!asoc || saddr) +		goto out;  	bp = &asoc->base.bind_addr; +	scope = sctp_scope(daddr); +	/* ip6_dst_lookup has filled in the fl6->saddr for us.  Check +	 * to see if we can use it. +	 */ +	if (!IS_ERR(dst)) { +		/* Walk through the bind address list and look for a bind +		 * address that matches the source address of the returned dst. +		 */ +		sctp_v6_to_addr(&dst_saddr, &fl6->saddr, htons(bp->port)); +		rcu_read_lock(); +		list_for_each_entry_rcu(laddr, &bp->address_list, list) { +			if (!laddr->valid || laddr->state == SCTP_ADDR_DEL || +			    (laddr->state != SCTP_ADDR_SRC && +			     !asoc->src_out_of_asoc_ok)) +				continue; + +			/* Do not compare against v4 addrs */ +			if ((laddr->a.sa.sa_family == AF_INET6) && +			    (sctp_v6_cmp_addr(&dst_saddr, &laddr->a))) { +				rcu_read_unlock(); +				goto out; +			} +		} +		rcu_read_unlock(); +		/* None of the bound addresses match the source address of the +		 * dst. So release it. +		 */ +		dst_release(dst); +		dst = NULL; +	} -	/* Go through the bind address list and find the best source address -	 * that matches the scope of the destination address. +	/* Walk through the bind address list and try to get the +	 * best source address for a given destination.  	 */  	rcu_read_lock();  	list_for_each_entry_rcu(laddr, &bp->address_list, list) { @@ -333,17 +316,56 @@ static void sctp_v6_get_saddr(struct sctp_sock *sk,  			}  		}  	} +	rcu_read_unlock();  	if (baddr) { -		memcpy(saddr, baddr, sizeof(union sctp_addr)); -		SCTP_DEBUG_PRINTK("saddr: %pI6\n", &saddr->v6.sin6_addr); +		fl6->saddr = baddr->v6.sin6_addr; +		fl6->fl6_sport = baddr->v6.sin6_port; +		final_p = fl6_update_dst(fl6, np->opt, &final); +		dst = ip6_dst_lookup_flow(sk, fl6, final_p); +	} + +out: +	if (!IS_ERR_OR_NULL(dst)) { +		struct rt6_info *rt; + +		rt = (struct rt6_info *)dst; +		t->dst = dst; +		t->dst_cookie = rt->rt6i_node ? rt->rt6i_node->fn_sernum : 0; +		pr_debug("rt6_dst:%pI6 rt6_src:%pI6\n", &rt->rt6i_dst.addr, +			 &fl6->saddr);  	} else { -		pr_err("%s: asoc:%p Could not find a valid source " -		       "address for the dest:%pI6\n", -		       __func__, asoc, &daddr->v6.sin6_addr); +		t->dst = NULL; + +		pr_debug("no route\n");  	} +} -	rcu_read_unlock(); +/* Returns the number of consecutive initial bits that match in the 2 ipv6 + * addresses. + */ +static inline int sctp_v6_addr_match_len(union sctp_addr *s1, +					 union sctp_addr *s2) +{ +	return ipv6_addr_diff(&s1->v6.sin6_addr, &s2->v6.sin6_addr); +} + +/* Fills in the source address(saddr) based on the destination address(daddr) + * and asoc's bind address list. + */ +static void sctp_v6_get_saddr(struct sctp_sock *sk, +			      struct sctp_transport *t, +			      struct flowi *fl) +{ +	struct flowi6 *fl6 = &fl->u.ip6; +	union sctp_addr *saddr = &t->saddr; + +	pr_debug("%s: asoc:%p dst:%p\n", __func__, t->asoc, t->dst); + +	if (t->dst) { +		saddr->v6.sin6_family = AF_INET6; +		saddr->v6.sin6_addr = fl6->saddr; +	}  }  /* Make a copy of all potential local addresses. */ @@ -363,11 +385,11 @@ static void sctp_v6_copy_addrlist(struct list_head *addrlist,  	read_lock_bh(&in6_dev->lock);  	list_for_each_entry(ifp, &in6_dev->addr_list, if_list) {  		/* Add the address to the local list.  */ -		addr = t_new(struct sctp_sockaddr_entry, GFP_ATOMIC); +		addr = kzalloc(sizeof(*addr), GFP_ATOMIC);  		if (addr) {  			addr->a.v6.sin6_family = AF_INET6;  			addr->a.v6.sin6_port = 0; -			ipv6_addr_copy(&addr->a.v6.sin6_addr, &ifp->addr); +			addr->a.v6.sin6_addr = ifp->addr;  			addr->a.v6.sin6_scope_id = dev->ifindex;  			addr->valid = 1;  			INIT_LIST_HEAD(&addr->list); @@ -380,10 +402,9 @@ static void sctp_v6_copy_addrlist(struct list_head *addrlist,  }  /* Initialize a sockaddr_storage from in incoming skb. */ -static void sctp_v6_from_skb(union sctp_addr *addr,struct sk_buff *skb, +static void sctp_v6_from_skb(union sctp_addr *addr, struct sk_buff *skb,  			     int is_saddr)  { -	void *from;  	__be16 *port;  	struct sctphdr *sh; @@ -395,12 +416,11 @@ static void sctp_v6_from_skb(union sctp_addr *addr,struct sk_buff *skb,  	sh = sctp_hdr(skb);  	if (is_saddr) {  		*port  = sh->source; -		from = &ipv6_hdr(skb)->saddr; +		addr->v6.sin6_addr = ipv6_hdr(skb)->saddr;  	} else {  		*port = sh->dest; -		from = &ipv6_hdr(skb)->daddr; +		addr->v6.sin6_addr = ipv6_hdr(skb)->daddr;  	} -	ipv6_addr_copy(&addr->v6.sin6_addr, from);  }  /* Initialize an sctp_addr from a socket. */ @@ -408,20 +428,20 @@ static void sctp_v6_from_sk(union sctp_addr *addr, struct sock *sk)  {  	addr->v6.sin6_family = AF_INET6;  	addr->v6.sin6_port = 0; -	ipv6_addr_copy(&addr->v6.sin6_addr, &inet6_sk(sk)->rcv_saddr); +	addr->v6.sin6_addr = sk->sk_v6_rcv_saddr;  }  /* Initialize sk->sk_rcv_saddr from sctp_addr. */  static void sctp_v6_to_sk_saddr(union sctp_addr *addr, struct sock *sk)  {  	if (addr->sa.sa_family == AF_INET && sctp_sk(sk)->v4mapped) { -		inet6_sk(sk)->rcv_saddr.s6_addr32[0] = 0; -		inet6_sk(sk)->rcv_saddr.s6_addr32[1] = 0; -		inet6_sk(sk)->rcv_saddr.s6_addr32[2] = htonl(0x0000ffff); -		inet6_sk(sk)->rcv_saddr.s6_addr32[3] = +		sk->sk_v6_rcv_saddr.s6_addr32[0] = 0; +		sk->sk_v6_rcv_saddr.s6_addr32[1] = 0; +		sk->sk_v6_rcv_saddr.s6_addr32[2] = htonl(0x0000ffff); +		sk->sk_v6_rcv_saddr.s6_addr32[3] =  			addr->v4.sin_addr.s_addr;  	} else { -		ipv6_addr_copy(&inet6_sk(sk)->rcv_saddr, &addr->v6.sin6_addr); +		sk->sk_v6_rcv_saddr = addr->v6.sin6_addr;  	}  } @@ -429,12 +449,12 @@ static void sctp_v6_to_sk_saddr(union sctp_addr *addr, struct sock *sk)  static void sctp_v6_to_sk_daddr(union sctp_addr *addr, struct sock *sk)  {  	if (addr->sa.sa_family == AF_INET && sctp_sk(sk)->v4mapped) { -		inet6_sk(sk)->daddr.s6_addr32[0] = 0; -		inet6_sk(sk)->daddr.s6_addr32[1] = 0; -		inet6_sk(sk)->daddr.s6_addr32[2] = htonl(0x0000ffff); -		inet6_sk(sk)->daddr.s6_addr32[3] = addr->v4.sin_addr.s_addr; +		sk->sk_v6_daddr.s6_addr32[0] = 0; +		sk->sk_v6_daddr.s6_addr32[1] = 0; +		sk->sk_v6_daddr.s6_addr32[2] = htonl(0x0000ffff); +		sk->sk_v6_daddr.s6_addr32[3] = addr->v4.sin_addr.s_addr;  	} else { -		ipv6_addr_copy(&inet6_sk(sk)->daddr, &addr->v6.sin6_addr); +		sk->sk_v6_daddr = addr->v6.sin6_addr;  	}  } @@ -446,7 +466,7 @@ static void sctp_v6_from_addr_param(union sctp_addr *addr,  	addr->v6.sin6_family = AF_INET6;  	addr->v6.sin6_port = port;  	addr->v6.sin6_flowinfo = 0; /* BUG */ -	ipv6_addr_copy(&addr->v6.sin6_addr, ¶m->v6.addr); +	addr->v6.sin6_addr = param->v6.addr;  	addr->v6.sin6_scope_id = iif;  } @@ -460,19 +480,18 @@ static int sctp_v6_to_addr_param(const union sctp_addr *addr,  	param->v6.param_hdr.type = SCTP_PARAM_IPV6_ADDRESS;  	param->v6.param_hdr.length = htons(length); -	ipv6_addr_copy(¶m->v6.addr, &addr->v6.sin6_addr); +	param->v6.addr = addr->v6.sin6_addr;  	return length;  } -/* Initialize a sctp_addr from a dst_entry. */ -static void sctp_v6_dst_saddr(union sctp_addr *addr, struct dst_entry *dst, +/* Initialize a sctp_addr from struct in6_addr. */ +static void sctp_v6_to_addr(union sctp_addr *addr, struct in6_addr *saddr,  			      __be16 port)  { -	struct rt6_info *rt = (struct rt6_info *)dst;  	addr->sa.sa_family = AF_INET6;  	addr->v6.sin6_port = port; -	ipv6_addr_copy(&addr->v6.sin6_addr, &rt->rt6i_src.addr); +	addr->v6.sin6_addr = *saddr;  }  /* Compare addresses exactly. @@ -531,7 +550,7 @@ static int sctp_v6_is_any(const union sctp_addr *addr)  static int sctp_v6_available(union sctp_addr *addr, struct sctp_sock *sp)  {  	int type; -	struct in6_addr *in6 = (struct in6_addr *)&addr->v6.sin6_addr; +	const struct in6_addr *in6 = (const struct in6_addr *)&addr->v6.sin6_addr;  	type = ipv6_addr_type(in6);  	if (IPV6_ADDR_ANY == type) @@ -547,7 +566,7 @@ static int sctp_v6_available(union sctp_addr *addr, struct sctp_sock *sp)  	if (!(type & IPV6_ADDR_UNICAST))  		return 0; -	return ipv6_chk_addr(&init_net, in6, NULL, 0); +	return ipv6_chk_addr(sock_net(&sp->inet.sk), in6, NULL, 0);  }  /* This function checks if the address is a valid address to be used for @@ -643,6 +662,8 @@ static struct sock *sctp_v6_create_accept_sk(struct sock *sk,  	 */  	sctp_v6_to_sk_daddr(&asoc->peer.primary_addr, newsk); +	newsk->sk_v6_rcv_saddr = sk->sk_v6_rcv_saddr; +  	sk_refcnt_debug_inc(newsk);  	if (newsk->sk_prot->init(newsk)) { @@ -727,7 +748,7 @@ static void sctp_inet6_event_msgname(struct sctp_ulpevent *event,  		}  		sin6from = &asoc->peer.primary_addr.v6; -		ipv6_addr_copy(&sin6->sin6_addr, &sin6from->sin6_addr); +		sin6->sin6_addr = sin6from->sin6_addr;  		if (ipv6_addr_type(&sin6->sin6_addr) & IPV6_ADDR_LINKLOCAL)  			sin6->sin6_scope_id = sin6from->sin6_scope_id;  	} @@ -755,7 +776,7 @@ static void sctp_inet6_skb_msgname(struct sk_buff *skb, char *msgname,  		}  		/* Otherwise, just copy the v6 address. */ -		ipv6_addr_copy(&sin6->sin6_addr, &ipv6_hdr(skb)->saddr); +		sin6->sin6_addr = ipv6_hdr(skb)->saddr;  		if (ipv6_addr_type(&sin6->sin6_addr) & IPV6_ADDR_LINKLOCAL) {  			struct sctp_ulpevent *ev = sctp_skb2event(skb);  			sin6->sin6_scope_id = ev->iif; @@ -824,14 +845,14 @@ static int sctp_inet6_bind_verify(struct sctp_sock *opt, union sctp_addr *addr)  		struct net_device *dev;  		if (type & IPV6_ADDR_LINKLOCAL) { +			struct net *net;  			if (!addr->v6.sin6_scope_id)  				return 0; +			net = sock_net(&opt->inet.sk);  			rcu_read_lock(); -			dev = dev_get_by_index_rcu(&init_net, -						   addr->v6.sin6_scope_id); +			dev = dev_get_by_index_rcu(net, addr->v6.sin6_scope_id);  			if (!dev || -			    !ipv6_chk_addr(&init_net, &addr->v6.sin6_addr, -					   dev, 0)) { +			    !ipv6_chk_addr(net, &addr->v6.sin6_addr, dev, 0)) {  				rcu_read_unlock();  				return 0;  			} @@ -864,7 +885,7 @@ static int sctp_inet6_send_verify(struct sctp_sock *opt, union sctp_addr *addr)  			if (!addr->v6.sin6_scope_id)  				return 0;  			rcu_read_lock(); -			dev = dev_get_by_index_rcu(&init_net, +			dev = dev_get_by_index_rcu(sock_net(&opt->inet.sk),  						   addr->v6.sin6_scope_id);  			rcu_read_unlock();  			if (!dev) @@ -922,7 +943,6 @@ static struct inet_protosw sctpv6_seqpacket_protosw = {  	.protocol      = IPPROTO_SCTP,  	.prot 	       = &sctpv6_prot,  	.ops           = &inet6_seqpacket_ops, -	.no_check      = 0,  	.flags         = SCTP_PROTOSW_FLAG  };  static struct inet_protosw sctpv6_stream_protosw = { @@ -930,7 +950,6 @@ static struct inet_protosw sctpv6_stream_protosw = {  	.protocol      = IPPROTO_SCTP,  	.prot 	       = &sctpv6_prot,  	.ops           = &inet6_seqpacket_ops, -	.no_check      = 0,  	.flags         = SCTP_PROTOSW_FLAG,  }; @@ -959,7 +978,6 @@ static struct sctp_af sctp_af_inet6 = {  	.to_sk_daddr	   = sctp_v6_to_sk_daddr,  	.from_addr_param   = sctp_v6_from_addr_param,  	.to_addr_param	   = sctp_v6_to_addr_param, -	.dst_saddr	   = sctp_v6_dst_saddr,  	.cmp_addr	   = sctp_v6_cmp_addr,  	.scope		   = sctp_v6_scope,  	.addr_valid	   = sctp_v6_addr_valid,  | 
