diff options
Diffstat (limited to 'net/sctp/ipv6.c')
| -rw-r--r-- | net/sctp/ipv6.c | 156 | 
1 files changed, 82 insertions, 74 deletions
diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c index 1999592ba88c..0e4198ee2370 100644 --- a/net/sctp/ipv6.c +++ b/net/sctp/ipv6.c @@ -434,7 +434,7 @@ static void sctp_v6_from_sk(union sctp_addr *addr, struct sock *sk)  /* 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) { +	if (addr->sa.sa_family == AF_INET) {  		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); @@ -448,7 +448,7 @@ static void sctp_v6_to_sk_saddr(union sctp_addr *addr, struct sock *sk)  /* Initialize sk->sk_daddr from sctp_addr. */  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) { +	if (addr->sa.sa_family == AF_INET) {  		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); @@ -556,8 +556,6 @@ static int sctp_v6_available(union sctp_addr *addr, struct sctp_sock *sp)  	if (IPV6_ADDR_ANY == type)  		return 1;  	if (type == IPV6_ADDR_MAPPED) { -		if (sp && !sp->v4mapped) -			return 0;  		if (sp && ipv6_only_sock(sctp_opt2sk(sp)))  			return 0;  		sctp_v6_map_v4(addr); @@ -587,8 +585,6 @@ static int sctp_v6_addr_valid(union sctp_addr *addr,  		/* Note: This routine is used in input, so v4-mapped-v6  		 * are disallowed here when there is no sctp_sock.  		 */ -		if (!sp || !sp->v4mapped) -			return 0;  		if (sp && ipv6_only_sock(sctp_opt2sk(sp)))  			return 0;  		sctp_v6_map_v4(addr); @@ -675,11 +671,23 @@ out:  	return newsk;  } -/* Map v4 address to mapped v6 address */ -static void sctp_v6_addr_v4map(struct sctp_sock *sp, union sctp_addr *addr) +/* Format a sockaddr for return to user space. This makes sure the return is + * AF_INET or AF_INET6 depending on the SCTP_I_WANT_MAPPED_V4_ADDR option. + */ +static int sctp_v6_addr_to_user(struct sctp_sock *sp, union sctp_addr *addr)  { -	if (sp->v4mapped && AF_INET == addr->sa.sa_family) -		sctp_v4_map_v6(addr); +	if (sp->v4mapped) { +		if (addr->sa.sa_family == AF_INET) +			sctp_v4_map_v6(addr); +	} else { +		if (addr->sa.sa_family == AF_INET6 && +		    ipv6_addr_v4mapped(&addr->v6.sin6_addr)) +			sctp_v6_map_v4(addr); +	} + +	if (addr->sa.sa_family == AF_INET) +		return sizeof(struct sockaddr_in); +	return sizeof(struct sockaddr_in6);  }  /* Where did this skb come from?  */ @@ -706,82 +714,68 @@ static void sctp_v6_ecn_capable(struct sock *sk)  	inet6_sk(sk)->tclass |= INET_ECN_ECT_0;  } -/* Initialize a PF_INET6 socket msg_name. */ -static void sctp_inet6_msgname(char *msgname, int *addr_len) -{ -	struct sockaddr_in6 *sin6; - -	sin6 = (struct sockaddr_in6 *)msgname; -	sin6->sin6_family = AF_INET6; -	sin6->sin6_flowinfo = 0; -	sin6->sin6_scope_id = 0; /*FIXME */ -	*addr_len = sizeof(struct sockaddr_in6); -} -  /* Initialize a PF_INET msgname from a ulpevent. */  static void sctp_inet6_event_msgname(struct sctp_ulpevent *event,  				     char *msgname, int *addrlen)  { -	struct sockaddr_in6 *sin6, *sin6from; - -	if (msgname) { -		union sctp_addr *addr; -		struct sctp_association *asoc; - -		asoc = event->asoc; -		sctp_inet6_msgname(msgname, addrlen); -		sin6 = (struct sockaddr_in6 *)msgname; -		sin6->sin6_port = htons(asoc->peer.port); -		addr = &asoc->peer.primary_addr; +	union sctp_addr *addr; +	struct sctp_association *asoc; +	union sctp_addr *paddr; -		/* Note: If we go to a common v6 format, this code -		 * will change. -		 */ +	if (!msgname) +		return; -		/* Map ipv4 address into v4-mapped-on-v6 address.  */ -		if (sctp_sk(asoc->base.sk)->v4mapped && -		    AF_INET == addr->sa.sa_family) { -			sctp_v4_map_v6((union sctp_addr *)sin6); -			sin6->sin6_addr.s6_addr32[3] = -				addr->v4.sin_addr.s_addr; -			return; -		} +	addr = (union sctp_addr *)msgname; +	asoc = event->asoc; +	paddr = &asoc->peer.primary_addr; -		sin6from = &asoc->peer.primary_addr.v6; -		sin6->sin6_addr = sin6from->sin6_addr; -		if (ipv6_addr_type(&sin6->sin6_addr) & IPV6_ADDR_LINKLOCAL) -			sin6->sin6_scope_id = sin6from->sin6_scope_id; +	if (paddr->sa.sa_family == AF_INET) { +		addr->v4.sin_family = AF_INET; +		addr->v4.sin_port = htons(asoc->peer.port); +		addr->v4.sin_addr = paddr->v4.sin_addr; +	} else { +		addr->v6.sin6_family = AF_INET6; +		addr->v6.sin6_flowinfo = 0; +		if (ipv6_addr_type(&paddr->v6.sin6_addr) & IPV6_ADDR_LINKLOCAL) +			addr->v6.sin6_scope_id = paddr->v6.sin6_scope_id; +		else +			addr->v6.sin6_scope_id = 0; +		addr->v6.sin6_port = htons(asoc->peer.port); +		addr->v6.sin6_addr = paddr->v6.sin6_addr;  	} + +	*addrlen = sctp_v6_addr_to_user(sctp_sk(asoc->base.sk), addr);  }  /* Initialize a msg_name from an inbound skb. */  static void sctp_inet6_skb_msgname(struct sk_buff *skb, char *msgname,  				   int *addr_len)  { +	union sctp_addr *addr;  	struct sctphdr *sh; -	struct sockaddr_in6 *sin6; - -	if (msgname) { -		sctp_inet6_msgname(msgname, addr_len); -		sin6 = (struct sockaddr_in6 *)msgname; -		sh = sctp_hdr(skb); -		sin6->sin6_port = sh->source; - -		/* Map ipv4 address into v4-mapped-on-v6 address. */ -		if (sctp_sk(skb->sk)->v4mapped && -		    ip_hdr(skb)->version == 4) { -			sctp_v4_map_v6((union sctp_addr *)sin6); -			sin6->sin6_addr.s6_addr32[3] = ip_hdr(skb)->saddr; -			return; -		} -		/* Otherwise, just copy the v6 address. */ -		sin6->sin6_addr = ipv6_hdr(skb)->saddr; -		if (ipv6_addr_type(&sin6->sin6_addr) & IPV6_ADDR_LINKLOCAL) { +	if (!msgname) +		return; + +	addr = (union sctp_addr *)msgname; +	sh = sctp_hdr(skb); + +	if (ip_hdr(skb)->version == 4) { +		addr->v4.sin_family = AF_INET; +		addr->v4.sin_port = sh->source; +		addr->v4.sin_addr.s_addr =  ip_hdr(skb)->saddr; +	} else { +		addr->v6.sin6_family = AF_INET6; +		addr->v6.sin6_flowinfo = 0; +		addr->v6.sin6_port = sh->source; +		addr->v6.sin6_addr = ipv6_hdr(skb)->saddr; +		if (ipv6_addr_type(&addr->v6.sin6_addr) & IPV6_ADDR_LINKLOCAL) {  			struct sctp_ulpevent *ev = sctp_skb2event(skb); -			sin6->sin6_scope_id = ev->iif; +			addr->v6.sin6_scope_id = ev->iif;  		}  	} + +	*addr_len = sctp_v6_addr_to_user(sctp_sk(skb->sk), addr);  }  /* Do we support this AF? */ @@ -857,9 +851,6 @@ static int sctp_inet6_bind_verify(struct sctp_sock *opt, union sctp_addr *addr)  				return 0;  			}  			rcu_read_unlock(); -		} else if (type == IPV6_ADDR_MAPPED) { -			if (!opt->v4mapped) -				return 0;  		}  		af = opt->pf->af; @@ -914,6 +905,23 @@ static int sctp_inet6_supported_addrs(const struct sctp_sock *opt,  	return 1;  } +/* Handle SCTP_I_WANT_MAPPED_V4_ADDR for getpeername() and getsockname() */ +static int sctp_getname(struct socket *sock, struct sockaddr *uaddr, +			int *uaddr_len, int peer) +{ +	int rc; + +	rc = inet6_getname(sock, uaddr, uaddr_len, peer); + +	if (rc != 0) +		return rc; + +	*uaddr_len = sctp_v6_addr_to_user(sctp_sk(sock->sk), +					  (union sctp_addr *)uaddr); + +	return rc; +} +  static const struct proto_ops inet6_seqpacket_ops = {  	.family		   = PF_INET6,  	.owner		   = THIS_MODULE, @@ -922,7 +930,7 @@ static const struct proto_ops inet6_seqpacket_ops = {  	.connect	   = inet_dgram_connect,  	.socketpair	   = sock_no_socketpair,  	.accept		   = inet_accept, -	.getname	   = inet6_getname, +	.getname	   = sctp_getname,  	.poll		   = sctp_poll,  	.ioctl		   = inet6_ioctl,  	.listen		   = sctp_inet_listen, @@ -974,8 +982,6 @@ static struct sctp_af sctp_af_inet6 = {  	.copy_addrlist	   = sctp_v6_copy_addrlist,  	.from_skb	   = sctp_v6_from_skb,  	.from_sk	   = sctp_v6_from_sk, -	.to_sk_saddr	   = sctp_v6_to_sk_saddr, -	.to_sk_daddr	   = sctp_v6_to_sk_daddr,  	.from_addr_param   = sctp_v6_from_addr_param,  	.to_addr_param	   = sctp_v6_to_addr_param,  	.cmp_addr	   = sctp_v6_cmp_addr, @@ -1005,7 +1011,9 @@ static struct sctp_pf sctp_pf_inet6 = {  	.send_verify   = sctp_inet6_send_verify,  	.supported_addrs = sctp_inet6_supported_addrs,  	.create_accept_sk = sctp_v6_create_accept_sk, -	.addr_v4map    = sctp_v6_addr_v4map, +	.addr_to_user  = sctp_v6_addr_to_user, +	.to_sk_saddr   = sctp_v6_to_sk_saddr, +	.to_sk_daddr   = sctp_v6_to_sk_daddr,  	.af            = &sctp_af_inet6,  };  |