diff options
Diffstat (limited to 'net/ipv4/ip_sockglue.c')
| -rw-r--r-- | net/ipv4/ip_sockglue.c | 56 | 
1 files changed, 46 insertions, 10 deletions
diff --git a/net/ipv4/ip_sockglue.c b/net/ipv4/ip_sockglue.c index d9c4f113d70..64741b93863 100644 --- a/net/ipv4/ip_sockglue.c +++ b/net/ipv4/ip_sockglue.c @@ -56,7 +56,6 @@  /*   *	SOL_IP control messages.   */ -#define PKTINFO_SKB_CB(__skb) ((struct in_pktinfo *)((__skb)->cb))  static void ip_cmsg_recv_pktinfo(struct msghdr *msg, struct sk_buff *skb)  { @@ -187,14 +186,31 @@ void ip_cmsg_recv(struct msghdr *msg, struct sk_buff *skb)  }  EXPORT_SYMBOL(ip_cmsg_recv); -int ip_cmsg_send(struct net *net, struct msghdr *msg, struct ipcm_cookie *ipc) +int ip_cmsg_send(struct net *net, struct msghdr *msg, struct ipcm_cookie *ipc, +		 bool allow_ipv6)  { -	int err; +	int err, val;  	struct cmsghdr *cmsg;  	for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) {  		if (!CMSG_OK(msg, cmsg))  			return -EINVAL; +#if defined(CONFIG_IPV6) +		if (allow_ipv6 && +		    cmsg->cmsg_level == SOL_IPV6 && +		    cmsg->cmsg_type == IPV6_PKTINFO) { +			struct in6_pktinfo *src_info; + +			if (cmsg->cmsg_len < CMSG_LEN(sizeof(*src_info))) +				return -EINVAL; +			src_info = (struct in6_pktinfo *)CMSG_DATA(cmsg); +			if (!ipv6_addr_v4mapped(&src_info->ipi6_addr)) +				return -EINVAL; +			ipc->oif = src_info->ipi6_ifindex; +			ipc->addr = src_info->ipi6_addr.s6_addr32[3]; +			continue; +		} +#endif  		if (cmsg->cmsg_level != SOL_IP)  			continue;  		switch (cmsg->cmsg_type) { @@ -215,6 +231,24 @@ int ip_cmsg_send(struct net *net, struct msghdr *msg, struct ipcm_cookie *ipc)  			ipc->addr = info->ipi_spec_dst.s_addr;  			break;  		} +		case IP_TTL: +			if (cmsg->cmsg_len != CMSG_LEN(sizeof(int))) +				return -EINVAL; +			val = *(int *)CMSG_DATA(cmsg); +			if (val < 1 || val > 255) +				return -EINVAL; +			ipc->ttl = val; +			break; +		case IP_TOS: +			if (cmsg->cmsg_len != CMSG_LEN(sizeof(int))) +				return -EINVAL; +			val = *(int *)CMSG_DATA(cmsg); +			if (val < 0 || val > 255) +				return -EINVAL; +			ipc->tos = val; +			ipc->priority = rt_tos2priority(ipc->tos); +			break; +  		default:  			return -EINVAL;  		} @@ -368,11 +402,11 @@ void ip_local_error(struct sock *sk, int err, __be32 daddr, __be16 port, u32 inf  /*   *	Handle MSG_ERRQUEUE   */ -int ip_recv_error(struct sock *sk, struct msghdr *msg, int len) +int ip_recv_error(struct sock *sk, struct msghdr *msg, int len, int *addr_len)  {  	struct sock_exterr_skb *serr;  	struct sk_buff *skb, *skb2; -	struct sockaddr_in *sin; +	DECLARE_SOCKADDR(struct sockaddr_in *, sin, msg->msg_name);  	struct {  		struct sock_extended_err ee;  		struct sockaddr_in	 offender; @@ -398,13 +432,13 @@ int ip_recv_error(struct sock *sk, struct msghdr *msg, int len)  	serr = SKB_EXT_ERR(skb); -	sin = (struct sockaddr_in *)msg->msg_name;  	if (sin) {  		sin->sin_family = AF_INET;  		sin->sin_addr.s_addr = *(__be32 *)(skb_network_header(skb) +  						   serr->addr_offset);  		sin->sin_port = serr->port;  		memset(&sin->sin_zero, 0, sizeof(sin->sin_zero)); +		*addr_len = sizeof(*sin);  	}  	memcpy(&errhdr.ee, &serr->ee, sizeof(struct sock_extended_err)); @@ -609,7 +643,7 @@ static int do_ip_setsockopt(struct sock *sk, int level,  		inet->nodefrag = val ? 1 : 0;  		break;  	case IP_MTU_DISCOVER: -		if (val < IP_PMTUDISC_DONT || val > IP_PMTUDISC_PROBE) +		if (val < IP_PMTUDISC_DONT || val > IP_PMTUDISC_OMIT)  			goto e_inval;  		inet->pmtudisc = val;  		break; @@ -1032,13 +1066,15 @@ e_inval:   *   * To support IP_CMSG_PKTINFO option, we store rt_iif and specific   * destination in skb->cb[] before dst drop. - * This way, receiver doesnt make cache line misses to read rtable. + * This way, receiver doesn't make cache line misses to read rtable.   */ -void ipv4_pktinfo_prepare(struct sk_buff *skb) +void ipv4_pktinfo_prepare(const struct sock *sk, struct sk_buff *skb)  {  	struct in_pktinfo *pktinfo = PKTINFO_SKB_CB(skb); +	bool prepare = (inet_sk(sk)->cmsg_flags & IP_CMSG_PKTINFO) || +		       ipv6_sk_rxinfo(sk); -	if (skb_rtable(skb)) { +	if (prepare && skb_rtable(skb)) {  		pktinfo->ipi_ifindex = inet_iif(skb);  		pktinfo->ipi_spec_dst.s_addr = fib_compute_spec_dst(skb);  	} else {  | 
