forked from mirrors/linux
		
	inet: add RCU protection to inet->opt
We lack proper synchronization to manipulate inet->opt ip_options Problem is ip_make_skb() calls ip_setup_cork() and ip_setup_cork() possibly makes a copy of ipc->opt (struct ip_options), without any protection against another thread manipulating inet->opt. Another thread can change inet->opt pointer and free old one under us. Use RCU to protect inet->opt (changed to inet->inet_opt). Instead of handling atomic refcounts, just copy ip_options when necessary, to avoid cache line dirtying. We cant insert an rcu_head in struct ip_options since its included in skb->cb[], so this patch is large because I had to introduce a new ip_options_rcu structure. Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com> Cc: Herbert Xu <herbert@gondor.apana.org.au> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
		
							parent
							
								
									0a14842f5a
								
							
						
					
					
						commit
						f6d8bd051c
					
				
					 17 changed files with 241 additions and 168 deletions
				
			
		|  | @ -57,7 +57,15 @@ struct ip_options { | ||||||
| 	unsigned char	__data[0]; | 	unsigned char	__data[0]; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| #define optlength(opt) (sizeof(struct ip_options) + opt->optlen) | struct ip_options_rcu { | ||||||
|  | 	struct rcu_head rcu; | ||||||
|  | 	struct ip_options opt; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | struct ip_options_data { | ||||||
|  | 	struct ip_options_rcu	opt; | ||||||
|  | 	char			data[40]; | ||||||
|  | }; | ||||||
| 
 | 
 | ||||||
| struct inet_request_sock { | struct inet_request_sock { | ||||||
| 	struct request_sock	req; | 	struct request_sock	req; | ||||||
|  | @ -78,7 +86,7 @@ struct inet_request_sock { | ||||||
| 				acked	   : 1, | 				acked	   : 1, | ||||||
| 				no_srccheck: 1; | 				no_srccheck: 1; | ||||||
| 	kmemcheck_bitfield_end(flags); | 	kmemcheck_bitfield_end(flags); | ||||||
| 	struct ip_options	*opt; | 	struct ip_options_rcu	*opt; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| static inline struct inet_request_sock *inet_rsk(const struct request_sock *sk) | static inline struct inet_request_sock *inet_rsk(const struct request_sock *sk) | ||||||
|  | @ -140,7 +148,7 @@ struct inet_sock { | ||||||
| 	__be16			inet_sport; | 	__be16			inet_sport; | ||||||
| 	__u16			inet_id; | 	__u16			inet_id; | ||||||
| 
 | 
 | ||||||
| 	struct ip_options	*opt; | 	struct ip_options_rcu __rcu	*inet_opt; | ||||||
| 	__u8			tos; | 	__u8			tos; | ||||||
| 	__u8			min_ttl; | 	__u8			min_ttl; | ||||||
| 	__u8			mc_ttl; | 	__u8			mc_ttl; | ||||||
|  |  | ||||||
|  | @ -52,7 +52,7 @@ static inline unsigned int ip_hdrlen(const struct sk_buff *skb) | ||||||
| struct ipcm_cookie { | struct ipcm_cookie { | ||||||
| 	__be32			addr; | 	__be32			addr; | ||||||
| 	int			oif; | 	int			oif; | ||||||
| 	struct ip_options	*opt; | 	struct ip_options_rcu	*opt; | ||||||
| 	__u8			tx_flags; | 	__u8			tx_flags; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | @ -92,7 +92,7 @@ extern int		igmp_mc_proc_init(void); | ||||||
| 
 | 
 | ||||||
| extern int		ip_build_and_send_pkt(struct sk_buff *skb, struct sock *sk, | extern int		ip_build_and_send_pkt(struct sk_buff *skb, struct sock *sk, | ||||||
| 					      __be32 saddr, __be32 daddr, | 					      __be32 saddr, __be32 daddr, | ||||||
| 					      struct ip_options *opt); | 					      struct ip_options_rcu *opt); | ||||||
| extern int		ip_rcv(struct sk_buff *skb, struct net_device *dev, | extern int		ip_rcv(struct sk_buff *skb, struct net_device *dev, | ||||||
| 			       struct packet_type *pt, struct net_device *orig_dev); | 			       struct packet_type *pt, struct net_device *orig_dev); | ||||||
| extern int		ip_local_deliver(struct sk_buff *skb); | extern int		ip_local_deliver(struct sk_buff *skb); | ||||||
|  | @ -416,14 +416,15 @@ extern int ip_forward(struct sk_buff *skb); | ||||||
|  *	Functions provided by ip_options.c |  *	Functions provided by ip_options.c | ||||||
|  */ |  */ | ||||||
|   |   | ||||||
| extern void ip_options_build(struct sk_buff *skb, struct ip_options *opt, __be32 daddr, struct rtable *rt, int is_frag); | extern void ip_options_build(struct sk_buff *skb, struct ip_options *opt, | ||||||
|  | 			     __be32 daddr, struct rtable *rt, int is_frag); | ||||||
| extern int ip_options_echo(struct ip_options *dopt, struct sk_buff *skb); | extern int ip_options_echo(struct ip_options *dopt, struct sk_buff *skb); | ||||||
| extern void ip_options_fragment(struct sk_buff *skb); | extern void ip_options_fragment(struct sk_buff *skb); | ||||||
| extern int ip_options_compile(struct net *net, | extern int ip_options_compile(struct net *net, | ||||||
| 			      struct ip_options *opt, struct sk_buff *skb); | 			      struct ip_options *opt, struct sk_buff *skb); | ||||||
| extern int ip_options_get(struct net *net, struct ip_options **optp, | extern int ip_options_get(struct net *net, struct ip_options_rcu **optp, | ||||||
| 			  unsigned char *data, int optlen); | 			  unsigned char *data, int optlen); | ||||||
| extern int ip_options_get_from_user(struct net *net, struct ip_options **optp, | extern int ip_options_get_from_user(struct net *net, struct ip_options_rcu **optp, | ||||||
| 				    unsigned char __user *data, int optlen); | 				    unsigned char __user *data, int optlen); | ||||||
| extern void ip_options_undo(struct ip_options * opt); | extern void ip_options_undo(struct ip_options * opt); | ||||||
| extern void ip_forward_options(struct sk_buff *skb); | extern void ip_forward_options(struct sk_buff *skb); | ||||||
|  |  | ||||||
|  | @ -48,6 +48,7 @@ int dccp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) | ||||||
| 	struct flowi4 fl4; | 	struct flowi4 fl4; | ||||||
| 	struct rtable *rt; | 	struct rtable *rt; | ||||||
| 	int err; | 	int err; | ||||||
|  | 	struct ip_options_rcu *inet_opt; | ||||||
| 
 | 
 | ||||||
| 	dp->dccps_role = DCCP_ROLE_CLIENT; | 	dp->dccps_role = DCCP_ROLE_CLIENT; | ||||||
| 
 | 
 | ||||||
|  | @ -58,10 +59,13 @@ int dccp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) | ||||||
| 		return -EAFNOSUPPORT; | 		return -EAFNOSUPPORT; | ||||||
| 
 | 
 | ||||||
| 	nexthop = daddr = usin->sin_addr.s_addr; | 	nexthop = daddr = usin->sin_addr.s_addr; | ||||||
| 	if (inet->opt != NULL && inet->opt->srr) { | 
 | ||||||
|  | 	inet_opt = rcu_dereference_protected(inet->inet_opt, | ||||||
|  | 					     sock_owned_by_user(sk)); | ||||||
|  | 	if (inet_opt != NULL && inet_opt->opt.srr) { | ||||||
| 		if (daddr == 0) | 		if (daddr == 0) | ||||||
| 			return -EINVAL; | 			return -EINVAL; | ||||||
| 		nexthop = inet->opt->faddr; | 		nexthop = inet_opt->opt.faddr; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	orig_sport = inet->inet_sport; | 	orig_sport = inet->inet_sport; | ||||||
|  | @ -78,7 +82,7 @@ int dccp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) | ||||||
| 		return -ENETUNREACH; | 		return -ENETUNREACH; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if (inet->opt == NULL || !inet->opt->srr) | 	if (inet_opt == NULL || !inet_opt->opt.srr) | ||||||
| 		daddr = rt->rt_dst; | 		daddr = rt->rt_dst; | ||||||
| 
 | 
 | ||||||
| 	if (inet->inet_saddr == 0) | 	if (inet->inet_saddr == 0) | ||||||
|  | @ -89,8 +93,8 @@ int dccp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) | ||||||
| 	inet->inet_daddr = daddr; | 	inet->inet_daddr = daddr; | ||||||
| 
 | 
 | ||||||
| 	inet_csk(sk)->icsk_ext_hdr_len = 0; | 	inet_csk(sk)->icsk_ext_hdr_len = 0; | ||||||
| 	if (inet->opt != NULL) | 	if (inet_opt) | ||||||
| 		inet_csk(sk)->icsk_ext_hdr_len = inet->opt->optlen; | 		inet_csk(sk)->icsk_ext_hdr_len = inet_opt->opt.optlen; | ||||||
| 	/*
 | 	/*
 | ||||||
| 	 * Socket identity is still unknown (sport may be zero). | 	 * Socket identity is still unknown (sport may be zero). | ||||||
| 	 * However we set state to DCCP_REQUESTING and not releasing socket | 	 * However we set state to DCCP_REQUESTING and not releasing socket | ||||||
|  | @ -405,7 +409,7 @@ struct sock *dccp_v4_request_recv_sock(struct sock *sk, struct sk_buff *skb, | ||||||
| 	newinet->inet_daddr	= ireq->rmt_addr; | 	newinet->inet_daddr	= ireq->rmt_addr; | ||||||
| 	newinet->inet_rcv_saddr = ireq->loc_addr; | 	newinet->inet_rcv_saddr = ireq->loc_addr; | ||||||
| 	newinet->inet_saddr	= ireq->loc_addr; | 	newinet->inet_saddr	= ireq->loc_addr; | ||||||
| 	newinet->opt	   = ireq->opt; | 	newinet->inet_opt	= ireq->opt; | ||||||
| 	ireq->opt	   = NULL; | 	ireq->opt	   = NULL; | ||||||
| 	newinet->mc_index  = inet_iif(skb); | 	newinet->mc_index  = inet_iif(skb); | ||||||
| 	newinet->mc_ttl	   = ip_hdr(skb)->ttl; | 	newinet->mc_ttl	   = ip_hdr(skb)->ttl; | ||||||
|  |  | ||||||
|  | @ -573,7 +573,7 @@ static struct sock *dccp_v6_request_recv_sock(struct sock *sk, | ||||||
| 
 | 
 | ||||||
| 	   First: no IPv4 options. | 	   First: no IPv4 options. | ||||||
| 	 */ | 	 */ | ||||||
| 	newinet->opt = NULL; | 	newinet->inet_opt = NULL; | ||||||
| 
 | 
 | ||||||
| 	/* Clone RX bits */ | 	/* Clone RX bits */ | ||||||
| 	newnp->rxopt.all = np->rxopt.all; | 	newnp->rxopt.all = np->rxopt.all; | ||||||
|  |  | ||||||
|  | @ -153,7 +153,7 @@ void inet_sock_destruct(struct sock *sk) | ||||||
| 	WARN_ON(sk->sk_wmem_queued); | 	WARN_ON(sk->sk_wmem_queued); | ||||||
| 	WARN_ON(sk->sk_forward_alloc); | 	WARN_ON(sk->sk_forward_alloc); | ||||||
| 
 | 
 | ||||||
| 	kfree(inet->opt); | 	kfree(rcu_dereference_protected(inet->inet_opt, 1)); | ||||||
| 	dst_release(rcu_dereference_check(sk->sk_dst_cache, 1)); | 	dst_release(rcu_dereference_check(sk->sk_dst_cache, 1)); | ||||||
| 	sk_refcnt_debug_dec(sk); | 	sk_refcnt_debug_dec(sk); | ||||||
| } | } | ||||||
|  | @ -1106,9 +1106,12 @@ static int inet_sk_reselect_saddr(struct sock *sk) | ||||||
| 	struct flowi4 fl4; | 	struct flowi4 fl4; | ||||||
| 	struct rtable *rt; | 	struct rtable *rt; | ||||||
| 	__be32 new_saddr; | 	__be32 new_saddr; | ||||||
|  | 	struct ip_options_rcu *inet_opt; | ||||||
| 
 | 
 | ||||||
| 	if (inet->opt && inet->opt->srr) | 	inet_opt = rcu_dereference_protected(inet->inet_opt, | ||||||
| 		daddr = inet->opt->faddr; | 					     sock_owned_by_user(sk)); | ||||||
|  | 	if (inet_opt && inet_opt->opt.srr) | ||||||
|  | 		daddr = inet_opt->opt.faddr; | ||||||
| 
 | 
 | ||||||
| 	/* Query new route. */ | 	/* Query new route. */ | ||||||
| 	rt = ip_route_connect(&fl4, daddr, 0, RT_CONN_FLAGS(sk), | 	rt = ip_route_connect(&fl4, daddr, 0, RT_CONN_FLAGS(sk), | ||||||
|  | @ -1148,6 +1151,7 @@ int inet_sk_rebuild_header(struct sock *sk) | ||||||
| 	struct inet_sock *inet = inet_sk(sk); | 	struct inet_sock *inet = inet_sk(sk); | ||||||
| 	struct rtable *rt = (struct rtable *)__sk_dst_check(sk, 0); | 	struct rtable *rt = (struct rtable *)__sk_dst_check(sk, 0); | ||||||
| 	__be32 daddr; | 	__be32 daddr; | ||||||
|  | 	struct ip_options_rcu *inet_opt; | ||||||
| 	int err; | 	int err; | ||||||
| 
 | 
 | ||||||
| 	/* Route is OK, nothing to do. */ | 	/* Route is OK, nothing to do. */ | ||||||
|  | @ -1155,9 +1159,12 @@ int inet_sk_rebuild_header(struct sock *sk) | ||||||
| 		return 0; | 		return 0; | ||||||
| 
 | 
 | ||||||
| 	/* Reroute. */ | 	/* Reroute. */ | ||||||
|  | 	rcu_read_lock(); | ||||||
|  | 	inet_opt = rcu_dereference(inet->inet_opt); | ||||||
| 	daddr = inet->inet_daddr; | 	daddr = inet->inet_daddr; | ||||||
| 	if (inet->opt && inet->opt->srr) | 	if (inet_opt && inet_opt->opt.srr) | ||||||
| 		daddr = inet->opt->faddr; | 		daddr = inet_opt->opt.faddr; | ||||||
|  | 	rcu_read_unlock(); | ||||||
| 	rt = ip_route_output_ports(sock_net(sk), sk, daddr, inet->inet_saddr, | 	rt = ip_route_output_ports(sock_net(sk), sk, daddr, inet->inet_saddr, | ||||||
| 				   inet->inet_dport, inet->inet_sport, | 				   inet->inet_dport, inet->inet_sport, | ||||||
| 				   sk->sk_protocol, RT_CONN_FLAGS(sk), | 				   sk->sk_protocol, RT_CONN_FLAGS(sk), | ||||||
|  |  | ||||||
|  | @ -1857,6 +1857,11 @@ static int cipso_v4_genopt(unsigned char *buf, u32 buf_len, | ||||||
| 	return CIPSO_V4_HDR_LEN + ret_val; | 	return CIPSO_V4_HDR_LEN + ret_val; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static void opt_kfree_rcu(struct rcu_head *head) | ||||||
|  | { | ||||||
|  | 	kfree(container_of(head, struct ip_options_rcu, rcu)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| /**
 | /**
 | ||||||
|  * cipso_v4_sock_setattr - Add a CIPSO option to a socket |  * cipso_v4_sock_setattr - Add a CIPSO option to a socket | ||||||
|  * @sk: the socket |  * @sk: the socket | ||||||
|  | @ -1879,7 +1884,7 @@ int cipso_v4_sock_setattr(struct sock *sk, | ||||||
| 	unsigned char *buf = NULL; | 	unsigned char *buf = NULL; | ||||||
| 	u32 buf_len; | 	u32 buf_len; | ||||||
| 	u32 opt_len; | 	u32 opt_len; | ||||||
| 	struct ip_options *opt = NULL; | 	struct ip_options_rcu *old, *opt = NULL; | ||||||
| 	struct inet_sock *sk_inet; | 	struct inet_sock *sk_inet; | ||||||
| 	struct inet_connection_sock *sk_conn; | 	struct inet_connection_sock *sk_conn; | ||||||
| 
 | 
 | ||||||
|  | @ -1915,22 +1920,25 @@ int cipso_v4_sock_setattr(struct sock *sk, | ||||||
| 		ret_val = -ENOMEM; | 		ret_val = -ENOMEM; | ||||||
| 		goto socket_setattr_failure; | 		goto socket_setattr_failure; | ||||||
| 	} | 	} | ||||||
| 	memcpy(opt->__data, buf, buf_len); | 	memcpy(opt->opt.__data, buf, buf_len); | ||||||
| 	opt->optlen = opt_len; | 	opt->opt.optlen = opt_len; | ||||||
| 	opt->cipso = sizeof(struct iphdr); | 	opt->opt.cipso = sizeof(struct iphdr); | ||||||
| 	kfree(buf); | 	kfree(buf); | ||||||
| 	buf = NULL; | 	buf = NULL; | ||||||
| 
 | 
 | ||||||
| 	sk_inet = inet_sk(sk); | 	sk_inet = inet_sk(sk); | ||||||
|  | 
 | ||||||
|  | 	old = rcu_dereference_protected(sk_inet->inet_opt, sock_owned_by_user(sk)); | ||||||
| 	if (sk_inet->is_icsk) { | 	if (sk_inet->is_icsk) { | ||||||
| 		sk_conn = inet_csk(sk); | 		sk_conn = inet_csk(sk); | ||||||
| 		if (sk_inet->opt) | 		if (old) | ||||||
| 			sk_conn->icsk_ext_hdr_len -= sk_inet->opt->optlen; | 			sk_conn->icsk_ext_hdr_len -= old->opt.optlen; | ||||||
| 		sk_conn->icsk_ext_hdr_len += opt->optlen; | 		sk_conn->icsk_ext_hdr_len += opt->opt.optlen; | ||||||
| 		sk_conn->icsk_sync_mss(sk, sk_conn->icsk_pmtu_cookie); | 		sk_conn->icsk_sync_mss(sk, sk_conn->icsk_pmtu_cookie); | ||||||
| 	} | 	} | ||||||
| 	opt = xchg(&sk_inet->opt, opt); | 	rcu_assign_pointer(sk_inet->inet_opt, opt); | ||||||
| 	kfree(opt); | 	if (old) | ||||||
|  | 		call_rcu(&old->rcu, opt_kfree_rcu); | ||||||
| 
 | 
 | ||||||
| 	return 0; | 	return 0; | ||||||
| 
 | 
 | ||||||
|  | @ -1960,7 +1968,7 @@ int cipso_v4_req_setattr(struct request_sock *req, | ||||||
| 	unsigned char *buf = NULL; | 	unsigned char *buf = NULL; | ||||||
| 	u32 buf_len; | 	u32 buf_len; | ||||||
| 	u32 opt_len; | 	u32 opt_len; | ||||||
| 	struct ip_options *opt = NULL; | 	struct ip_options_rcu *opt = NULL; | ||||||
| 	struct inet_request_sock *req_inet; | 	struct inet_request_sock *req_inet; | ||||||
| 
 | 
 | ||||||
| 	/* We allocate the maximum CIPSO option size here so we are probably
 | 	/* We allocate the maximum CIPSO option size here so we are probably
 | ||||||
|  | @ -1988,15 +1996,16 @@ int cipso_v4_req_setattr(struct request_sock *req, | ||||||
| 		ret_val = -ENOMEM; | 		ret_val = -ENOMEM; | ||||||
| 		goto req_setattr_failure; | 		goto req_setattr_failure; | ||||||
| 	} | 	} | ||||||
| 	memcpy(opt->__data, buf, buf_len); | 	memcpy(opt->opt.__data, buf, buf_len); | ||||||
| 	opt->optlen = opt_len; | 	opt->opt.optlen = opt_len; | ||||||
| 	opt->cipso = sizeof(struct iphdr); | 	opt->opt.cipso = sizeof(struct iphdr); | ||||||
| 	kfree(buf); | 	kfree(buf); | ||||||
| 	buf = NULL; | 	buf = NULL; | ||||||
| 
 | 
 | ||||||
| 	req_inet = inet_rsk(req); | 	req_inet = inet_rsk(req); | ||||||
| 	opt = xchg(&req_inet->opt, opt); | 	opt = xchg(&req_inet->opt, opt); | ||||||
| 	kfree(opt); | 	if (opt) | ||||||
|  | 		call_rcu(&opt->rcu, opt_kfree_rcu); | ||||||
| 
 | 
 | ||||||
| 	return 0; | 	return 0; | ||||||
| 
 | 
 | ||||||
|  | @ -2016,34 +2025,34 @@ int cipso_v4_req_setattr(struct request_sock *req, | ||||||
|  * values on failure. |  * values on failure. | ||||||
|  * |  * | ||||||
|  */ |  */ | ||||||
| static int cipso_v4_delopt(struct ip_options **opt_ptr) | static int cipso_v4_delopt(struct ip_options_rcu **opt_ptr) | ||||||
| { | { | ||||||
| 	int hdr_delta = 0; | 	int hdr_delta = 0; | ||||||
| 	struct ip_options *opt = *opt_ptr; | 	struct ip_options_rcu *opt = *opt_ptr; | ||||||
| 
 | 
 | ||||||
| 	if (opt->srr || opt->rr || opt->ts || opt->router_alert) { | 	if (opt->opt.srr || opt->opt.rr || opt->opt.ts || opt->opt.router_alert) { | ||||||
| 		u8 cipso_len; | 		u8 cipso_len; | ||||||
| 		u8 cipso_off; | 		u8 cipso_off; | ||||||
| 		unsigned char *cipso_ptr; | 		unsigned char *cipso_ptr; | ||||||
| 		int iter; | 		int iter; | ||||||
| 		int optlen_new; | 		int optlen_new; | ||||||
| 
 | 
 | ||||||
| 		cipso_off = opt->cipso - sizeof(struct iphdr); | 		cipso_off = opt->opt.cipso - sizeof(struct iphdr); | ||||||
| 		cipso_ptr = &opt->__data[cipso_off]; | 		cipso_ptr = &opt->opt.__data[cipso_off]; | ||||||
| 		cipso_len = cipso_ptr[1]; | 		cipso_len = cipso_ptr[1]; | ||||||
| 
 | 
 | ||||||
| 		if (opt->srr > opt->cipso) | 		if (opt->opt.srr > opt->opt.cipso) | ||||||
| 			opt->srr -= cipso_len; | 			opt->opt.srr -= cipso_len; | ||||||
| 		if (opt->rr > opt->cipso) | 		if (opt->opt.rr > opt->opt.cipso) | ||||||
| 			opt->rr -= cipso_len; | 			opt->opt.rr -= cipso_len; | ||||||
| 		if (opt->ts > opt->cipso) | 		if (opt->opt.ts > opt->opt.cipso) | ||||||
| 			opt->ts -= cipso_len; | 			opt->opt.ts -= cipso_len; | ||||||
| 		if (opt->router_alert > opt->cipso) | 		if (opt->opt.router_alert > opt->opt.cipso) | ||||||
| 			opt->router_alert -= cipso_len; | 			opt->opt.router_alert -= cipso_len; | ||||||
| 		opt->cipso = 0; | 		opt->opt.cipso = 0; | ||||||
| 
 | 
 | ||||||
| 		memmove(cipso_ptr, cipso_ptr + cipso_len, | 		memmove(cipso_ptr, cipso_ptr + cipso_len, | ||||||
| 			opt->optlen - cipso_off - cipso_len); | 			opt->opt.optlen - cipso_off - cipso_len); | ||||||
| 
 | 
 | ||||||
| 		/* determining the new total option length is tricky because of
 | 		/* determining the new total option length is tricky because of
 | ||||||
| 		 * the padding necessary, the only thing i can think to do at | 		 * the padding necessary, the only thing i can think to do at | ||||||
|  | @ -2052,21 +2061,21 @@ static int cipso_v4_delopt(struct ip_options **opt_ptr) | ||||||
| 		 * from there we can determine the new total option length */ | 		 * from there we can determine the new total option length */ | ||||||
| 		iter = 0; | 		iter = 0; | ||||||
| 		optlen_new = 0; | 		optlen_new = 0; | ||||||
| 		while (iter < opt->optlen) | 		while (iter < opt->opt.optlen) | ||||||
| 			if (opt->__data[iter] != IPOPT_NOP) { | 			if (opt->opt.__data[iter] != IPOPT_NOP) { | ||||||
| 				iter += opt->__data[iter + 1]; | 				iter += opt->opt.__data[iter + 1]; | ||||||
| 				optlen_new = iter; | 				optlen_new = iter; | ||||||
| 			} else | 			} else | ||||||
| 				iter++; | 				iter++; | ||||||
| 		hdr_delta = opt->optlen; | 		hdr_delta = opt->opt.optlen; | ||||||
| 		opt->optlen = (optlen_new + 3) & ~3; | 		opt->opt.optlen = (optlen_new + 3) & ~3; | ||||||
| 		hdr_delta -= opt->optlen; | 		hdr_delta -= opt->opt.optlen; | ||||||
| 	} else { | 	} else { | ||||||
| 		/* only the cipso option was present on the socket so we can
 | 		/* only the cipso option was present on the socket so we can
 | ||||||
| 		 * remove the entire option struct */ | 		 * remove the entire option struct */ | ||||||
| 		*opt_ptr = NULL; | 		*opt_ptr = NULL; | ||||||
| 		hdr_delta = opt->optlen; | 		hdr_delta = opt->opt.optlen; | ||||||
| 		kfree(opt); | 		call_rcu(&opt->rcu, opt_kfree_rcu); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return hdr_delta; | 	return hdr_delta; | ||||||
|  | @ -2083,15 +2092,15 @@ static int cipso_v4_delopt(struct ip_options **opt_ptr) | ||||||
| void cipso_v4_sock_delattr(struct sock *sk) | void cipso_v4_sock_delattr(struct sock *sk) | ||||||
| { | { | ||||||
| 	int hdr_delta; | 	int hdr_delta; | ||||||
| 	struct ip_options *opt; | 	struct ip_options_rcu *opt; | ||||||
| 	struct inet_sock *sk_inet; | 	struct inet_sock *sk_inet; | ||||||
| 
 | 
 | ||||||
| 	sk_inet = inet_sk(sk); | 	sk_inet = inet_sk(sk); | ||||||
| 	opt = sk_inet->opt; | 	opt = rcu_dereference_protected(sk_inet->inet_opt, 1); | ||||||
| 	if (opt == NULL || opt->cipso == 0) | 	if (opt == NULL || opt->opt.cipso == 0) | ||||||
| 		return; | 		return; | ||||||
| 
 | 
 | ||||||
| 	hdr_delta = cipso_v4_delopt(&sk_inet->opt); | 	hdr_delta = cipso_v4_delopt(&sk_inet->inet_opt); | ||||||
| 	if (sk_inet->is_icsk && hdr_delta > 0) { | 	if (sk_inet->is_icsk && hdr_delta > 0) { | ||||||
| 		struct inet_connection_sock *sk_conn = inet_csk(sk); | 		struct inet_connection_sock *sk_conn = inet_csk(sk); | ||||||
| 		sk_conn->icsk_ext_hdr_len -= hdr_delta; | 		sk_conn->icsk_ext_hdr_len -= hdr_delta; | ||||||
|  | @ -2109,12 +2118,12 @@ void cipso_v4_sock_delattr(struct sock *sk) | ||||||
|  */ |  */ | ||||||
| void cipso_v4_req_delattr(struct request_sock *req) | void cipso_v4_req_delattr(struct request_sock *req) | ||||||
| { | { | ||||||
| 	struct ip_options *opt; | 	struct ip_options_rcu *opt; | ||||||
| 	struct inet_request_sock *req_inet; | 	struct inet_request_sock *req_inet; | ||||||
| 
 | 
 | ||||||
| 	req_inet = inet_rsk(req); | 	req_inet = inet_rsk(req); | ||||||
| 	opt = req_inet->opt; | 	opt = req_inet->opt; | ||||||
| 	if (opt == NULL || opt->cipso == 0) | 	if (opt == NULL || opt->opt.cipso == 0) | ||||||
| 		return; | 		return; | ||||||
| 
 | 
 | ||||||
| 	cipso_v4_delopt(&req_inet->opt); | 	cipso_v4_delopt(&req_inet->opt); | ||||||
|  | @ -2184,14 +2193,18 @@ static int cipso_v4_getattr(const unsigned char *cipso, | ||||||
|  */ |  */ | ||||||
| int cipso_v4_sock_getattr(struct sock *sk, struct netlbl_lsm_secattr *secattr) | int cipso_v4_sock_getattr(struct sock *sk, struct netlbl_lsm_secattr *secattr) | ||||||
| { | { | ||||||
| 	struct ip_options *opt; | 	struct ip_options_rcu *opt; | ||||||
|  | 	int res = -ENOMSG; | ||||||
| 
 | 
 | ||||||
| 	opt = inet_sk(sk)->opt; | 	rcu_read_lock(); | ||||||
| 	if (opt == NULL || opt->cipso == 0) | 	opt = rcu_dereference(inet_sk(sk)->inet_opt); | ||||||
| 		return -ENOMSG; | 	if (opt && opt->opt.cipso) | ||||||
| 
 | 		res = cipso_v4_getattr(opt->opt.__data + | ||||||
| 	return cipso_v4_getattr(opt->__data + opt->cipso - sizeof(struct iphdr), | 						opt->opt.cipso - | ||||||
| 				secattr); | 						sizeof(struct iphdr), | ||||||
|  | 				       secattr); | ||||||
|  | 	rcu_read_unlock(); | ||||||
|  | 	return res; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  |  | ||||||
|  | @ -108,8 +108,7 @@ struct icmp_bxm { | ||||||
| 		__be32	       times[3]; | 		__be32	       times[3]; | ||||||
| 	} data; | 	} data; | ||||||
| 	int head_len; | 	int head_len; | ||||||
| 	struct ip_options replyopts; | 	struct ip_options_data replyopts; | ||||||
| 	unsigned char  optbuf[40]; |  | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| /* An array of errno for error messages from dest unreach. */ | /* An array of errno for error messages from dest unreach. */ | ||||||
|  | @ -333,7 +332,7 @@ static void icmp_reply(struct icmp_bxm *icmp_param, struct sk_buff *skb) | ||||||
| 	struct inet_sock *inet; | 	struct inet_sock *inet; | ||||||
| 	__be32 daddr; | 	__be32 daddr; | ||||||
| 
 | 
 | ||||||
| 	if (ip_options_echo(&icmp_param->replyopts, skb)) | 	if (ip_options_echo(&icmp_param->replyopts.opt.opt, skb)) | ||||||
| 		return; | 		return; | ||||||
| 
 | 
 | ||||||
| 	sk = icmp_xmit_lock(net); | 	sk = icmp_xmit_lock(net); | ||||||
|  | @ -347,10 +346,10 @@ static void icmp_reply(struct icmp_bxm *icmp_param, struct sk_buff *skb) | ||||||
| 	daddr = ipc.addr = rt->rt_src; | 	daddr = ipc.addr = rt->rt_src; | ||||||
| 	ipc.opt = NULL; | 	ipc.opt = NULL; | ||||||
| 	ipc.tx_flags = 0; | 	ipc.tx_flags = 0; | ||||||
| 	if (icmp_param->replyopts.optlen) { | 	if (icmp_param->replyopts.opt.opt.optlen) { | ||||||
| 		ipc.opt = &icmp_param->replyopts; | 		ipc.opt = &icmp_param->replyopts.opt; | ||||||
| 		if (ipc.opt->srr) | 		if (ipc.opt->opt.srr) | ||||||
| 			daddr = icmp_param->replyopts.faddr; | 			daddr = icmp_param->replyopts.opt.opt.faddr; | ||||||
| 	} | 	} | ||||||
| 	{ | 	{ | ||||||
| 		struct flowi4 fl4 = { | 		struct flowi4 fl4 = { | ||||||
|  | @ -379,8 +378,8 @@ static struct rtable *icmp_route_lookup(struct net *net, struct sk_buff *skb_in, | ||||||
| 					struct icmp_bxm *param) | 					struct icmp_bxm *param) | ||||||
| { | { | ||||||
| 	struct flowi4 fl4 = { | 	struct flowi4 fl4 = { | ||||||
| 		.daddr = (param->replyopts.srr ? | 		.daddr = (param->replyopts.opt.opt.srr ? | ||||||
| 			  param->replyopts.faddr : iph->saddr), | 			  param->replyopts.opt.opt.faddr : iph->saddr), | ||||||
| 		.saddr = saddr, | 		.saddr = saddr, | ||||||
| 		.flowi4_tos = RT_TOS(tos), | 		.flowi4_tos = RT_TOS(tos), | ||||||
| 		.flowi4_proto = IPPROTO_ICMP, | 		.flowi4_proto = IPPROTO_ICMP, | ||||||
|  | @ -581,7 +580,7 @@ void icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info) | ||||||
| 					   IPTOS_PREC_INTERNETCONTROL) : | 					   IPTOS_PREC_INTERNETCONTROL) : | ||||||
| 					  iph->tos; | 					  iph->tos; | ||||||
| 
 | 
 | ||||||
| 	if (ip_options_echo(&icmp_param.replyopts, skb_in)) | 	if (ip_options_echo(&icmp_param.replyopts.opt.opt, skb_in)) | ||||||
| 		goto out_unlock; | 		goto out_unlock; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @ -597,7 +596,7 @@ void icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info) | ||||||
| 	icmp_param.offset = skb_network_offset(skb_in); | 	icmp_param.offset = skb_network_offset(skb_in); | ||||||
| 	inet_sk(sk)->tos = tos; | 	inet_sk(sk)->tos = tos; | ||||||
| 	ipc.addr = iph->saddr; | 	ipc.addr = iph->saddr; | ||||||
| 	ipc.opt = &icmp_param.replyopts; | 	ipc.opt = &icmp_param.replyopts.opt; | ||||||
| 	ipc.tx_flags = 0; | 	ipc.tx_flags = 0; | ||||||
| 
 | 
 | ||||||
| 	rt = icmp_route_lookup(net, skb_in, iph, saddr, tos, | 	rt = icmp_route_lookup(net, skb_in, iph, saddr, tos, | ||||||
|  | @ -613,7 +612,7 @@ void icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info) | ||||||
| 	room = dst_mtu(&rt->dst); | 	room = dst_mtu(&rt->dst); | ||||||
| 	if (room > 576) | 	if (room > 576) | ||||||
| 		room = 576; | 		room = 576; | ||||||
| 	room -= sizeof(struct iphdr) + icmp_param.replyopts.optlen; | 	room -= sizeof(struct iphdr) + icmp_param.replyopts.opt.opt.optlen; | ||||||
| 	room -= sizeof(struct icmphdr); | 	room -= sizeof(struct icmphdr); | ||||||
| 
 | 
 | ||||||
| 	icmp_param.data_len = skb_in->len - icmp_param.offset; | 	icmp_param.data_len = skb_in->len - icmp_param.offset; | ||||||
|  |  | ||||||
|  | @ -354,20 +354,20 @@ struct dst_entry *inet_csk_route_req(struct sock *sk, | ||||||
| { | { | ||||||
| 	struct rtable *rt; | 	struct rtable *rt; | ||||||
| 	const struct inet_request_sock *ireq = inet_rsk(req); | 	const struct inet_request_sock *ireq = inet_rsk(req); | ||||||
| 	struct ip_options *opt = inet_rsk(req)->opt; | 	struct ip_options_rcu *opt = inet_rsk(req)->opt; | ||||||
| 	struct net *net = sock_net(sk); | 	struct net *net = sock_net(sk); | ||||||
| 	struct flowi4 fl4; | 	struct flowi4 fl4; | ||||||
| 
 | 
 | ||||||
| 	flowi4_init_output(&fl4, sk->sk_bound_dev_if, sk->sk_mark, | 	flowi4_init_output(&fl4, sk->sk_bound_dev_if, sk->sk_mark, | ||||||
| 			   RT_CONN_FLAGS(sk), RT_SCOPE_UNIVERSE, | 			   RT_CONN_FLAGS(sk), RT_SCOPE_UNIVERSE, | ||||||
| 			   sk->sk_protocol, inet_sk_flowi_flags(sk), | 			   sk->sk_protocol, inet_sk_flowi_flags(sk), | ||||||
| 			   (opt && opt->srr) ? opt->faddr : ireq->rmt_addr, | 			   (opt && opt->opt.srr) ? opt->opt.faddr : ireq->rmt_addr, | ||||||
| 			   ireq->loc_addr, ireq->rmt_port, inet_sk(sk)->inet_sport); | 			   ireq->loc_addr, ireq->rmt_port, inet_sk(sk)->inet_sport); | ||||||
| 	security_req_classify_flow(req, flowi4_to_flowi(&fl4)); | 	security_req_classify_flow(req, flowi4_to_flowi(&fl4)); | ||||||
| 	rt = ip_route_output_flow(net, &fl4, sk); | 	rt = ip_route_output_flow(net, &fl4, sk); | ||||||
| 	if (IS_ERR(rt)) | 	if (IS_ERR(rt)) | ||||||
| 		goto no_route; | 		goto no_route; | ||||||
| 	if (opt && opt->is_strictroute && rt->rt_dst != rt->rt_gateway) | 	if (opt && opt->opt.is_strictroute && rt->rt_dst != rt->rt_gateway) | ||||||
| 		goto route_err; | 		goto route_err; | ||||||
| 	return &rt->dst; | 	return &rt->dst; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -36,7 +36,7 @@ | ||||||
|  * saddr is address of outgoing interface. |  * saddr is address of outgoing interface. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| void ip_options_build(struct sk_buff * skb, struct ip_options * opt, | void ip_options_build(struct sk_buff *skb, struct ip_options *opt, | ||||||
| 			    __be32 daddr, struct rtable *rt, int is_frag) | 			    __be32 daddr, struct rtable *rt, int is_frag) | ||||||
| { | { | ||||||
| 	unsigned char *iph = skb_network_header(skb); | 	unsigned char *iph = skb_network_header(skb); | ||||||
|  | @ -83,9 +83,9 @@ void ip_options_build(struct sk_buff * skb, struct ip_options * opt, | ||||||
|  * NOTE: dopt cannot point to skb. |  * NOTE: dopt cannot point to skb. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| int ip_options_echo(struct ip_options * dopt, struct sk_buff * skb) | int ip_options_echo(struct ip_options *dopt, struct sk_buff *skb) | ||||||
| { | { | ||||||
| 	struct ip_options *sopt; | 	const struct ip_options *sopt; | ||||||
| 	unsigned char *sptr, *dptr; | 	unsigned char *sptr, *dptr; | ||||||
| 	int soffset, doffset; | 	int soffset, doffset; | ||||||
| 	int	optlen; | 	int	optlen; | ||||||
|  | @ -95,10 +95,8 @@ int ip_options_echo(struct ip_options * dopt, struct sk_buff * skb) | ||||||
| 
 | 
 | ||||||
| 	sopt = &(IPCB(skb)->opt); | 	sopt = &(IPCB(skb)->opt); | ||||||
| 
 | 
 | ||||||
| 	if (sopt->optlen == 0) { | 	if (sopt->optlen == 0) | ||||||
| 		dopt->optlen = 0; |  | ||||||
| 		return 0; | 		return 0; | ||||||
| 	} |  | ||||||
| 
 | 
 | ||||||
| 	sptr = skb_network_header(skb); | 	sptr = skb_network_header(skb); | ||||||
| 	dptr = dopt->__data; | 	dptr = dopt->__data; | ||||||
|  | @ -157,7 +155,7 @@ int ip_options_echo(struct ip_options * dopt, struct sk_buff * skb) | ||||||
| 		dopt->optlen += optlen; | 		dopt->optlen += optlen; | ||||||
| 	} | 	} | ||||||
| 	if (sopt->srr) { | 	if (sopt->srr) { | ||||||
| 		unsigned char * start = sptr+sopt->srr; | 		unsigned char *start = sptr+sopt->srr; | ||||||
| 		__be32 faddr; | 		__be32 faddr; | ||||||
| 
 | 
 | ||||||
| 		optlen  = start[1]; | 		optlen  = start[1]; | ||||||
|  | @ -499,19 +497,19 @@ void ip_options_undo(struct ip_options * opt) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static struct ip_options *ip_options_get_alloc(const int optlen) | static struct ip_options_rcu *ip_options_get_alloc(const int optlen) | ||||||
| { | { | ||||||
| 	return kzalloc(sizeof(struct ip_options) + ((optlen + 3) & ~3), | 	return kzalloc(sizeof(struct ip_options_rcu) + ((optlen + 3) & ~3), | ||||||
| 		       GFP_KERNEL); | 		       GFP_KERNEL); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static int ip_options_get_finish(struct net *net, struct ip_options **optp, | static int ip_options_get_finish(struct net *net, struct ip_options_rcu **optp, | ||||||
| 				 struct ip_options *opt, int optlen) | 				 struct ip_options_rcu *opt, int optlen) | ||||||
| { | { | ||||||
| 	while (optlen & 3) | 	while (optlen & 3) | ||||||
| 		opt->__data[optlen++] = IPOPT_END; | 		opt->opt.__data[optlen++] = IPOPT_END; | ||||||
| 	opt->optlen = optlen; | 	opt->opt.optlen = optlen; | ||||||
| 	if (optlen && ip_options_compile(net, opt, NULL)) { | 	if (optlen && ip_options_compile(net, &opt->opt, NULL)) { | ||||||
| 		kfree(opt); | 		kfree(opt); | ||||||
| 		return -EINVAL; | 		return -EINVAL; | ||||||
| 	} | 	} | ||||||
|  | @ -520,29 +518,29 @@ static int ip_options_get_finish(struct net *net, struct ip_options **optp, | ||||||
| 	return 0; | 	return 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| int ip_options_get_from_user(struct net *net, struct ip_options **optp, | int ip_options_get_from_user(struct net *net, struct ip_options_rcu **optp, | ||||||
| 			     unsigned char __user *data, int optlen) | 			     unsigned char __user *data, int optlen) | ||||||
| { | { | ||||||
| 	struct ip_options *opt = ip_options_get_alloc(optlen); | 	struct ip_options_rcu *opt = ip_options_get_alloc(optlen); | ||||||
| 
 | 
 | ||||||
| 	if (!opt) | 	if (!opt) | ||||||
| 		return -ENOMEM; | 		return -ENOMEM; | ||||||
| 	if (optlen && copy_from_user(opt->__data, data, optlen)) { | 	if (optlen && copy_from_user(opt->opt.__data, data, optlen)) { | ||||||
| 		kfree(opt); | 		kfree(opt); | ||||||
| 		return -EFAULT; | 		return -EFAULT; | ||||||
| 	} | 	} | ||||||
| 	return ip_options_get_finish(net, optp, opt, optlen); | 	return ip_options_get_finish(net, optp, opt, optlen); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| int ip_options_get(struct net *net, struct ip_options **optp, | int ip_options_get(struct net *net, struct ip_options_rcu **optp, | ||||||
| 		   unsigned char *data, int optlen) | 		   unsigned char *data, int optlen) | ||||||
| { | { | ||||||
| 	struct ip_options *opt = ip_options_get_alloc(optlen); | 	struct ip_options_rcu *opt = ip_options_get_alloc(optlen); | ||||||
| 
 | 
 | ||||||
| 	if (!opt) | 	if (!opt) | ||||||
| 		return -ENOMEM; | 		return -ENOMEM; | ||||||
| 	if (optlen) | 	if (optlen) | ||||||
| 		memcpy(opt->__data, data, optlen); | 		memcpy(opt->opt.__data, data, optlen); | ||||||
| 	return ip_options_get_finish(net, optp, opt, optlen); | 	return ip_options_get_finish(net, optp, opt, optlen); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -140,14 +140,14 @@ static inline int ip_select_ttl(struct inet_sock *inet, struct dst_entry *dst) | ||||||
|  * |  * | ||||||
|  */ |  */ | ||||||
| int ip_build_and_send_pkt(struct sk_buff *skb, struct sock *sk, | int ip_build_and_send_pkt(struct sk_buff *skb, struct sock *sk, | ||||||
| 			  __be32 saddr, __be32 daddr, struct ip_options *opt) | 			  __be32 saddr, __be32 daddr, struct ip_options_rcu *opt) | ||||||
| { | { | ||||||
| 	struct inet_sock *inet = inet_sk(sk); | 	struct inet_sock *inet = inet_sk(sk); | ||||||
| 	struct rtable *rt = skb_rtable(skb); | 	struct rtable *rt = skb_rtable(skb); | ||||||
| 	struct iphdr *iph; | 	struct iphdr *iph; | ||||||
| 
 | 
 | ||||||
| 	/* Build the IP header. */ | 	/* Build the IP header. */ | ||||||
| 	skb_push(skb, sizeof(struct iphdr) + (opt ? opt->optlen : 0)); | 	skb_push(skb, sizeof(struct iphdr) + (opt ? opt->opt.optlen : 0)); | ||||||
| 	skb_reset_network_header(skb); | 	skb_reset_network_header(skb); | ||||||
| 	iph = ip_hdr(skb); | 	iph = ip_hdr(skb); | ||||||
| 	iph->version  = 4; | 	iph->version  = 4; | ||||||
|  | @ -163,9 +163,9 @@ int ip_build_and_send_pkt(struct sk_buff *skb, struct sock *sk, | ||||||
| 	iph->protocol = sk->sk_protocol; | 	iph->protocol = sk->sk_protocol; | ||||||
| 	ip_select_ident(iph, &rt->dst, sk); | 	ip_select_ident(iph, &rt->dst, sk); | ||||||
| 
 | 
 | ||||||
| 	if (opt && opt->optlen) { | 	if (opt && opt->opt.optlen) { | ||||||
| 		iph->ihl += opt->optlen>>2; | 		iph->ihl += opt->opt.optlen>>2; | ||||||
| 		ip_options_build(skb, opt, daddr, rt, 0); | 		ip_options_build(skb, &opt->opt, daddr, rt, 0); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	skb->priority = sk->sk_priority; | 	skb->priority = sk->sk_priority; | ||||||
|  | @ -316,7 +316,7 @@ int ip_queue_xmit(struct sk_buff *skb) | ||||||
| { | { | ||||||
| 	struct sock *sk = skb->sk; | 	struct sock *sk = skb->sk; | ||||||
| 	struct inet_sock *inet = inet_sk(sk); | 	struct inet_sock *inet = inet_sk(sk); | ||||||
| 	struct ip_options *opt = inet->opt; | 	struct ip_options_rcu *inet_opt; | ||||||
| 	struct rtable *rt; | 	struct rtable *rt; | ||||||
| 	struct iphdr *iph; | 	struct iphdr *iph; | ||||||
| 	int res; | 	int res; | ||||||
|  | @ -325,6 +325,7 @@ int ip_queue_xmit(struct sk_buff *skb) | ||||||
| 	 * f.e. by something like SCTP. | 	 * f.e. by something like SCTP. | ||||||
| 	 */ | 	 */ | ||||||
| 	rcu_read_lock(); | 	rcu_read_lock(); | ||||||
|  | 	inet_opt = rcu_dereference(inet->inet_opt); | ||||||
| 	rt = skb_rtable(skb); | 	rt = skb_rtable(skb); | ||||||
| 	if (rt != NULL) | 	if (rt != NULL) | ||||||
| 		goto packet_routed; | 		goto packet_routed; | ||||||
|  | @ -336,8 +337,8 @@ int ip_queue_xmit(struct sk_buff *skb) | ||||||
| 
 | 
 | ||||||
| 		/* Use correct destination address if we have options. */ | 		/* Use correct destination address if we have options. */ | ||||||
| 		daddr = inet->inet_daddr; | 		daddr = inet->inet_daddr; | ||||||
| 		if(opt && opt->srr) | 		if (inet_opt && inet_opt->opt.srr) | ||||||
| 			daddr = opt->faddr; | 			daddr = inet_opt->opt.faddr; | ||||||
| 
 | 
 | ||||||
| 		/* If this fails, retransmit mechanism of transport layer will
 | 		/* If this fails, retransmit mechanism of transport layer will
 | ||||||
| 		 * keep trying until route appears or the connection times | 		 * keep trying until route appears or the connection times | ||||||
|  | @ -357,11 +358,11 @@ int ip_queue_xmit(struct sk_buff *skb) | ||||||
| 	skb_dst_set_noref(skb, &rt->dst); | 	skb_dst_set_noref(skb, &rt->dst); | ||||||
| 
 | 
 | ||||||
| packet_routed: | packet_routed: | ||||||
| 	if (opt && opt->is_strictroute && rt->rt_dst != rt->rt_gateway) | 	if (inet_opt && inet_opt->opt.is_strictroute && rt->rt_dst != rt->rt_gateway) | ||||||
| 		goto no_route; | 		goto no_route; | ||||||
| 
 | 
 | ||||||
| 	/* OK, we know where to send it, allocate and build IP header. */ | 	/* OK, we know where to send it, allocate and build IP header. */ | ||||||
| 	skb_push(skb, sizeof(struct iphdr) + (opt ? opt->optlen : 0)); | 	skb_push(skb, sizeof(struct iphdr) + (inet_opt ? inet_opt->opt.optlen : 0)); | ||||||
| 	skb_reset_network_header(skb); | 	skb_reset_network_header(skb); | ||||||
| 	iph = ip_hdr(skb); | 	iph = ip_hdr(skb); | ||||||
| 	*((__be16 *)iph) = htons((4 << 12) | (5 << 8) | (inet->tos & 0xff)); | 	*((__be16 *)iph) = htons((4 << 12) | (5 << 8) | (inet->tos & 0xff)); | ||||||
|  | @ -375,9 +376,9 @@ int ip_queue_xmit(struct sk_buff *skb) | ||||||
| 	iph->daddr    = rt->rt_dst; | 	iph->daddr    = rt->rt_dst; | ||||||
| 	/* Transport layer set skb->h.foo itself. */ | 	/* Transport layer set skb->h.foo itself. */ | ||||||
| 
 | 
 | ||||||
| 	if (opt && opt->optlen) { | 	if (inet_opt && inet_opt->opt.optlen) { | ||||||
| 		iph->ihl += opt->optlen >> 2; | 		iph->ihl += inet_opt->opt.optlen >> 2; | ||||||
| 		ip_options_build(skb, opt, inet->inet_daddr, rt, 0); | 		ip_options_build(skb, &inet_opt->opt, inet->inet_daddr, rt, 0); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	ip_select_ident_more(iph, &rt->dst, sk, | 	ip_select_ident_more(iph, &rt->dst, sk, | ||||||
|  | @ -1033,7 +1034,7 @@ static int ip_setup_cork(struct sock *sk, struct inet_cork *cork, | ||||||
| 			 struct ipcm_cookie *ipc, struct rtable **rtp) | 			 struct ipcm_cookie *ipc, struct rtable **rtp) | ||||||
| { | { | ||||||
| 	struct inet_sock *inet = inet_sk(sk); | 	struct inet_sock *inet = inet_sk(sk); | ||||||
| 	struct ip_options *opt; | 	struct ip_options_rcu *opt; | ||||||
| 	struct rtable *rt; | 	struct rtable *rt; | ||||||
| 
 | 
 | ||||||
| 	/*
 | 	/*
 | ||||||
|  | @ -1047,7 +1048,7 @@ static int ip_setup_cork(struct sock *sk, struct inet_cork *cork, | ||||||
| 			if (unlikely(cork->opt == NULL)) | 			if (unlikely(cork->opt == NULL)) | ||||||
| 				return -ENOBUFS; | 				return -ENOBUFS; | ||||||
| 		} | 		} | ||||||
| 		memcpy(cork->opt, opt, sizeof(struct ip_options) + opt->optlen); | 		memcpy(cork->opt, &opt->opt, sizeof(struct ip_options) + opt->opt.optlen); | ||||||
| 		cork->flags |= IPCORK_OPT; | 		cork->flags |= IPCORK_OPT; | ||||||
| 		cork->addr = ipc->addr; | 		cork->addr = ipc->addr; | ||||||
| 	} | 	} | ||||||
|  | @ -1451,26 +1452,23 @@ void ip_send_reply(struct sock *sk, struct sk_buff *skb, struct ip_reply_arg *ar | ||||||
| 		   unsigned int len) | 		   unsigned int len) | ||||||
| { | { | ||||||
| 	struct inet_sock *inet = inet_sk(sk); | 	struct inet_sock *inet = inet_sk(sk); | ||||||
| 	struct { | 	struct ip_options_data replyopts; | ||||||
| 		struct ip_options	opt; |  | ||||||
| 		char			data[40]; |  | ||||||
| 	} replyopts; |  | ||||||
| 	struct ipcm_cookie ipc; | 	struct ipcm_cookie ipc; | ||||||
| 	__be32 daddr; | 	__be32 daddr; | ||||||
| 	struct rtable *rt = skb_rtable(skb); | 	struct rtable *rt = skb_rtable(skb); | ||||||
| 
 | 
 | ||||||
| 	if (ip_options_echo(&replyopts.opt, skb)) | 	if (ip_options_echo(&replyopts.opt.opt, skb)) | ||||||
| 		return; | 		return; | ||||||
| 
 | 
 | ||||||
| 	daddr = ipc.addr = rt->rt_src; | 	daddr = ipc.addr = rt->rt_src; | ||||||
| 	ipc.opt = NULL; | 	ipc.opt = NULL; | ||||||
| 	ipc.tx_flags = 0; | 	ipc.tx_flags = 0; | ||||||
| 
 | 
 | ||||||
| 	if (replyopts.opt.optlen) { | 	if (replyopts.opt.opt.optlen) { | ||||||
| 		ipc.opt = &replyopts.opt; | 		ipc.opt = &replyopts.opt; | ||||||
| 
 | 
 | ||||||
| 		if (ipc.opt->srr) | 		if (replyopts.opt.opt.srr) | ||||||
| 			daddr = replyopts.opt.faddr; | 			daddr = replyopts.opt.opt.faddr; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	{ | 	{ | ||||||
|  |  | ||||||
|  | @ -451,6 +451,11 @@ int ip_recv_error(struct sock *sk, struct msghdr *msg, int len) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | static void opt_kfree_rcu(struct rcu_head *head) | ||||||
|  | { | ||||||
|  | 	kfree(container_of(head, struct ip_options_rcu, rcu)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| /*
 | /*
 | ||||||
|  *	Socket option code for IP. This is the end of the line after any |  *	Socket option code for IP. This is the end of the line after any | ||||||
|  *	TCP,UDP etc options on an IP socket. |  *	TCP,UDP etc options on an IP socket. | ||||||
|  | @ -497,13 +502,16 @@ static int do_ip_setsockopt(struct sock *sk, int level, | ||||||
| 	switch (optname) { | 	switch (optname) { | ||||||
| 	case IP_OPTIONS: | 	case IP_OPTIONS: | ||||||
| 	{ | 	{ | ||||||
| 		struct ip_options *opt = NULL; | 		struct ip_options_rcu *old, *opt = NULL; | ||||||
|  | 
 | ||||||
| 		if (optlen > 40) | 		if (optlen > 40) | ||||||
| 			goto e_inval; | 			goto e_inval; | ||||||
| 		err = ip_options_get_from_user(sock_net(sk), &opt, | 		err = ip_options_get_from_user(sock_net(sk), &opt, | ||||||
| 					       optval, optlen); | 					       optval, optlen); | ||||||
| 		if (err) | 		if (err) | ||||||
| 			break; | 			break; | ||||||
|  | 		old = rcu_dereference_protected(inet->inet_opt, | ||||||
|  | 						sock_owned_by_user(sk)); | ||||||
| 		if (inet->is_icsk) { | 		if (inet->is_icsk) { | ||||||
| 			struct inet_connection_sock *icsk = inet_csk(sk); | 			struct inet_connection_sock *icsk = inet_csk(sk); | ||||||
| #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) | #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) | ||||||
|  | @ -512,17 +520,18 @@ static int do_ip_setsockopt(struct sock *sk, int level, | ||||||
| 			       (TCPF_LISTEN | TCPF_CLOSE)) && | 			       (TCPF_LISTEN | TCPF_CLOSE)) && | ||||||
| 			     inet->inet_daddr != LOOPBACK4_IPV6)) { | 			     inet->inet_daddr != LOOPBACK4_IPV6)) { | ||||||
| #endif | #endif | ||||||
| 				if (inet->opt) | 				if (old) | ||||||
| 					icsk->icsk_ext_hdr_len -= inet->opt->optlen; | 					icsk->icsk_ext_hdr_len -= old->opt.optlen; | ||||||
| 				if (opt) | 				if (opt) | ||||||
| 					icsk->icsk_ext_hdr_len += opt->optlen; | 					icsk->icsk_ext_hdr_len += opt->opt.optlen; | ||||||
| 				icsk->icsk_sync_mss(sk, icsk->icsk_pmtu_cookie); | 				icsk->icsk_sync_mss(sk, icsk->icsk_pmtu_cookie); | ||||||
| #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) | #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) | ||||||
| 			} | 			} | ||||||
| #endif | #endif | ||||||
| 		} | 		} | ||||||
| 		opt = xchg(&inet->opt, opt); | 		rcu_assign_pointer(inet->inet_opt, opt); | ||||||
| 		kfree(opt); | 		if (old) | ||||||
|  | 			call_rcu(&old->rcu, opt_kfree_rcu); | ||||||
| 		break; | 		break; | ||||||
| 	} | 	} | ||||||
| 	case IP_PKTINFO: | 	case IP_PKTINFO: | ||||||
|  | @ -1081,12 +1090,16 @@ static int do_ip_getsockopt(struct sock *sk, int level, int optname, | ||||||
| 	case IP_OPTIONS: | 	case IP_OPTIONS: | ||||||
| 	{ | 	{ | ||||||
| 		unsigned char optbuf[sizeof(struct ip_options)+40]; | 		unsigned char optbuf[sizeof(struct ip_options)+40]; | ||||||
| 		struct ip_options * opt = (struct ip_options *)optbuf; | 		struct ip_options *opt = (struct ip_options *)optbuf; | ||||||
|  | 		struct ip_options_rcu *inet_opt; | ||||||
|  | 
 | ||||||
|  | 		inet_opt = rcu_dereference_protected(inet->inet_opt, | ||||||
|  | 						     sock_owned_by_user(sk)); | ||||||
| 		opt->optlen = 0; | 		opt->optlen = 0; | ||||||
| 		if (inet->opt) | 		if (inet_opt) | ||||||
| 			memcpy(optbuf, inet->opt, | 			memcpy(optbuf, &inet_opt->opt, | ||||||
| 			       sizeof(struct ip_options)+ | 			       sizeof(struct ip_options) + | ||||||
| 			       inet->opt->optlen); | 			       inet_opt->opt.optlen); | ||||||
| 		release_sock(sk); | 		release_sock(sk); | ||||||
| 
 | 
 | ||||||
| 		if (opt->optlen == 0) | 		if (opt->optlen == 0) | ||||||
|  |  | ||||||
|  | @ -460,6 +460,7 @@ static int raw_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, | ||||||
| 	__be32 saddr; | 	__be32 saddr; | ||||||
| 	u8  tos; | 	u8  tos; | ||||||
| 	int err; | 	int err; | ||||||
|  | 	struct ip_options_data opt_copy; | ||||||
| 
 | 
 | ||||||
| 	err = -EMSGSIZE; | 	err = -EMSGSIZE; | ||||||
| 	if (len > 0xFFFF) | 	if (len > 0xFFFF) | ||||||
|  | @ -520,8 +521,18 @@ static int raw_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, | ||||||
| 	saddr = ipc.addr; | 	saddr = ipc.addr; | ||||||
| 	ipc.addr = daddr; | 	ipc.addr = daddr; | ||||||
| 
 | 
 | ||||||
| 	if (!ipc.opt) | 	if (!ipc.opt) { | ||||||
| 		ipc.opt = inet->opt; | 		struct ip_options_rcu *inet_opt; | ||||||
|  | 
 | ||||||
|  | 		rcu_read_lock(); | ||||||
|  | 		inet_opt = rcu_dereference(inet->inet_opt); | ||||||
|  | 		if (inet_opt) { | ||||||
|  | 			memcpy(&opt_copy, inet_opt, | ||||||
|  | 			       sizeof(*inet_opt) + inet_opt->opt.optlen); | ||||||
|  | 			ipc.opt = &opt_copy.opt; | ||||||
|  | 		} | ||||||
|  | 		rcu_read_unlock(); | ||||||
|  | 	} | ||||||
| 
 | 
 | ||||||
| 	if (ipc.opt) { | 	if (ipc.opt) { | ||||||
| 		err = -EINVAL; | 		err = -EINVAL; | ||||||
|  | @ -530,10 +541,10 @@ static int raw_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, | ||||||
| 		 */ | 		 */ | ||||||
| 		if (inet->hdrincl) | 		if (inet->hdrincl) | ||||||
| 			goto done; | 			goto done; | ||||||
| 		if (ipc.opt->srr) { | 		if (ipc.opt->opt.srr) { | ||||||
| 			if (!daddr) | 			if (!daddr) | ||||||
| 				goto done; | 				goto done; | ||||||
| 			daddr = ipc.opt->faddr; | 			daddr = ipc.opt->opt.faddr; | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	tos = RT_CONN_FLAGS(sk); | 	tos = RT_CONN_FLAGS(sk); | ||||||
|  |  | ||||||
|  | @ -321,10 +321,10 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb, | ||||||
| 	 * the ACK carries the same options again (see RFC1122 4.2.3.8) | 	 * the ACK carries the same options again (see RFC1122 4.2.3.8) | ||||||
| 	 */ | 	 */ | ||||||
| 	if (opt && opt->optlen) { | 	if (opt && opt->optlen) { | ||||||
| 		int opt_size = sizeof(struct ip_options) + opt->optlen; | 		int opt_size = sizeof(struct ip_options_rcu) + opt->optlen; | ||||||
| 
 | 
 | ||||||
| 		ireq->opt = kmalloc(opt_size, GFP_ATOMIC); | 		ireq->opt = kmalloc(opt_size, GFP_ATOMIC); | ||||||
| 		if (ireq->opt != NULL && ip_options_echo(ireq->opt, skb)) { | 		if (ireq->opt != NULL && ip_options_echo(&ireq->opt->opt, skb)) { | ||||||
| 			kfree(ireq->opt); | 			kfree(ireq->opt); | ||||||
| 			ireq->opt = NULL; | 			ireq->opt = NULL; | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|  | @ -154,6 +154,7 @@ int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) | ||||||
| 	struct flowi4 fl4; | 	struct flowi4 fl4; | ||||||
| 	struct rtable *rt; | 	struct rtable *rt; | ||||||
| 	int err; | 	int err; | ||||||
|  | 	struct ip_options_rcu *inet_opt; | ||||||
| 
 | 
 | ||||||
| 	if (addr_len < sizeof(struct sockaddr_in)) | 	if (addr_len < sizeof(struct sockaddr_in)) | ||||||
| 		return -EINVAL; | 		return -EINVAL; | ||||||
|  | @ -162,10 +163,12 @@ int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) | ||||||
| 		return -EAFNOSUPPORT; | 		return -EAFNOSUPPORT; | ||||||
| 
 | 
 | ||||||
| 	nexthop = daddr = usin->sin_addr.s_addr; | 	nexthop = daddr = usin->sin_addr.s_addr; | ||||||
| 	if (inet->opt && inet->opt->srr) { | 	inet_opt = rcu_dereference_protected(inet->inet_opt, | ||||||
|  | 					     sock_owned_by_user(sk)); | ||||||
|  | 	if (inet_opt && inet_opt->opt.srr) { | ||||||
| 		if (!daddr) | 		if (!daddr) | ||||||
| 			return -EINVAL; | 			return -EINVAL; | ||||||
| 		nexthop = inet->opt->faddr; | 		nexthop = inet_opt->opt.faddr; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	orig_sport = inet->inet_sport; | 	orig_sport = inet->inet_sport; | ||||||
|  | @ -186,7 +189,7 @@ int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) | ||||||
| 		return -ENETUNREACH; | 		return -ENETUNREACH; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if (!inet->opt || !inet->opt->srr) | 	if (!inet_opt || !inet_opt->opt.srr) | ||||||
| 		daddr = rt->rt_dst; | 		daddr = rt->rt_dst; | ||||||
| 
 | 
 | ||||||
| 	if (!inet->inet_saddr) | 	if (!inet->inet_saddr) | ||||||
|  | @ -222,8 +225,8 @@ int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) | ||||||
| 	inet->inet_daddr = daddr; | 	inet->inet_daddr = daddr; | ||||||
| 
 | 
 | ||||||
| 	inet_csk(sk)->icsk_ext_hdr_len = 0; | 	inet_csk(sk)->icsk_ext_hdr_len = 0; | ||||||
| 	if (inet->opt) | 	if (inet_opt) | ||||||
| 		inet_csk(sk)->icsk_ext_hdr_len = inet->opt->optlen; | 		inet_csk(sk)->icsk_ext_hdr_len = inet_opt->opt.optlen; | ||||||
| 
 | 
 | ||||||
| 	tp->rx_opt.mss_clamp = TCP_MSS_DEFAULT; | 	tp->rx_opt.mss_clamp = TCP_MSS_DEFAULT; | ||||||
| 
 | 
 | ||||||
|  | @ -820,17 +823,18 @@ static void syn_flood_warning(const struct sk_buff *skb) | ||||||
| /*
 | /*
 | ||||||
|  * Save and compile IPv4 options into the request_sock if needed. |  * Save and compile IPv4 options into the request_sock if needed. | ||||||
|  */ |  */ | ||||||
| static struct ip_options *tcp_v4_save_options(struct sock *sk, | static struct ip_options_rcu *tcp_v4_save_options(struct sock *sk, | ||||||
| 					      struct sk_buff *skb) | 						  struct sk_buff *skb) | ||||||
| { | { | ||||||
| 	struct ip_options *opt = &(IPCB(skb)->opt); | 	const struct ip_options *opt = &(IPCB(skb)->opt); | ||||||
| 	struct ip_options *dopt = NULL; | 	struct ip_options_rcu *dopt = NULL; | ||||||
| 
 | 
 | ||||||
| 	if (opt && opt->optlen) { | 	if (opt && opt->optlen) { | ||||||
| 		int opt_size = optlength(opt); | 		int opt_size = sizeof(*dopt) + opt->optlen; | ||||||
|  | 
 | ||||||
| 		dopt = kmalloc(opt_size, GFP_ATOMIC); | 		dopt = kmalloc(opt_size, GFP_ATOMIC); | ||||||
| 		if (dopt) { | 		if (dopt) { | ||||||
| 			if (ip_options_echo(dopt, skb)) { | 			if (ip_options_echo(&dopt->opt, skb)) { | ||||||
| 				kfree(dopt); | 				kfree(dopt); | ||||||
| 				dopt = NULL; | 				dopt = NULL; | ||||||
| 			} | 			} | ||||||
|  | @ -1411,6 +1415,7 @@ struct sock *tcp_v4_syn_recv_sock(struct sock *sk, struct sk_buff *skb, | ||||||
| #ifdef CONFIG_TCP_MD5SIG | #ifdef CONFIG_TCP_MD5SIG | ||||||
| 	struct tcp_md5sig_key *key; | 	struct tcp_md5sig_key *key; | ||||||
| #endif | #endif | ||||||
|  | 	struct ip_options_rcu *inet_opt; | ||||||
| 
 | 
 | ||||||
| 	if (sk_acceptq_is_full(sk)) | 	if (sk_acceptq_is_full(sk)) | ||||||
| 		goto exit_overflow; | 		goto exit_overflow; | ||||||
|  | @ -1431,13 +1436,14 @@ struct sock *tcp_v4_syn_recv_sock(struct sock *sk, struct sk_buff *skb, | ||||||
| 	newinet->inet_daddr   = ireq->rmt_addr; | 	newinet->inet_daddr   = ireq->rmt_addr; | ||||||
| 	newinet->inet_rcv_saddr = ireq->loc_addr; | 	newinet->inet_rcv_saddr = ireq->loc_addr; | ||||||
| 	newinet->inet_saddr	      = ireq->loc_addr; | 	newinet->inet_saddr	      = ireq->loc_addr; | ||||||
| 	newinet->opt	      = ireq->opt; | 	inet_opt	      = ireq->opt; | ||||||
|  | 	rcu_assign_pointer(newinet->inet_opt, inet_opt); | ||||||
| 	ireq->opt	      = NULL; | 	ireq->opt	      = NULL; | ||||||
| 	newinet->mc_index     = inet_iif(skb); | 	newinet->mc_index     = inet_iif(skb); | ||||||
| 	newinet->mc_ttl	      = ip_hdr(skb)->ttl; | 	newinet->mc_ttl	      = ip_hdr(skb)->ttl; | ||||||
| 	inet_csk(newsk)->icsk_ext_hdr_len = 0; | 	inet_csk(newsk)->icsk_ext_hdr_len = 0; | ||||||
| 	if (newinet->opt) | 	if (inet_opt) | ||||||
| 		inet_csk(newsk)->icsk_ext_hdr_len = newinet->opt->optlen; | 		inet_csk(newsk)->icsk_ext_hdr_len = inet_opt->opt.optlen; | ||||||
| 	newinet->inet_id = newtp->write_seq ^ jiffies; | 	newinet->inet_id = newtp->write_seq ^ jiffies; | ||||||
| 
 | 
 | ||||||
| 	tcp_mtup_init(newsk); | 	tcp_mtup_init(newsk); | ||||||
|  |  | ||||||
|  | @ -804,6 +804,7 @@ int udp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, | ||||||
| 	int corkreq = up->corkflag || msg->msg_flags&MSG_MORE; | 	int corkreq = up->corkflag || msg->msg_flags&MSG_MORE; | ||||||
| 	int (*getfrag)(void *, char *, int, int, int, struct sk_buff *); | 	int (*getfrag)(void *, char *, int, int, int, struct sk_buff *); | ||||||
| 	struct sk_buff *skb; | 	struct sk_buff *skb; | ||||||
|  | 	struct ip_options_data opt_copy; | ||||||
| 
 | 
 | ||||||
| 	if (len > 0xFFFF) | 	if (len > 0xFFFF) | ||||||
| 		return -EMSGSIZE; | 		return -EMSGSIZE; | ||||||
|  | @ -877,22 +878,32 @@ int udp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, | ||||||
| 			free = 1; | 			free = 1; | ||||||
| 		connected = 0; | 		connected = 0; | ||||||
| 	} | 	} | ||||||
| 	if (!ipc.opt) | 	if (!ipc.opt) { | ||||||
| 		ipc.opt = inet->opt; | 		struct ip_options_rcu *inet_opt; | ||||||
|  | 
 | ||||||
|  | 		rcu_read_lock(); | ||||||
|  | 		inet_opt = rcu_dereference(inet->inet_opt); | ||||||
|  | 		if (inet_opt) { | ||||||
|  | 			memcpy(&opt_copy, inet_opt, | ||||||
|  | 			       sizeof(*inet_opt) + inet_opt->opt.optlen); | ||||||
|  | 			ipc.opt = &opt_copy.opt; | ||||||
|  | 		} | ||||||
|  | 		rcu_read_unlock(); | ||||||
|  | 	} | ||||||
| 
 | 
 | ||||||
| 	saddr = ipc.addr; | 	saddr = ipc.addr; | ||||||
| 	ipc.addr = faddr = daddr; | 	ipc.addr = faddr = daddr; | ||||||
| 
 | 
 | ||||||
| 	if (ipc.opt && ipc.opt->srr) { | 	if (ipc.opt && ipc.opt->opt.srr) { | ||||||
| 		if (!daddr) | 		if (!daddr) | ||||||
| 			return -EINVAL; | 			return -EINVAL; | ||||||
| 		faddr = ipc.opt->faddr; | 		faddr = ipc.opt->opt.faddr; | ||||||
| 		connected = 0; | 		connected = 0; | ||||||
| 	} | 	} | ||||||
| 	tos = RT_TOS(inet->tos); | 	tos = RT_TOS(inet->tos); | ||||||
| 	if (sock_flag(sk, SOCK_LOCALROUTE) || | 	if (sock_flag(sk, SOCK_LOCALROUTE) || | ||||||
| 	    (msg->msg_flags & MSG_DONTROUTE) || | 	    (msg->msg_flags & MSG_DONTROUTE) || | ||||||
| 	    (ipc.opt && ipc.opt->is_strictroute)) { | 	    (ipc.opt && ipc.opt->opt.is_strictroute)) { | ||||||
| 		tos |= RTO_ONLINK; | 		tos |= RTO_ONLINK; | ||||||
| 		connected = 0; | 		connected = 0; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | @ -1469,7 +1469,7 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb, | ||||||
| 
 | 
 | ||||||
| 	   First: no IPv4 options. | 	   First: no IPv4 options. | ||||||
| 	 */ | 	 */ | ||||||
| 	newinet->opt = NULL; | 	newinet->inet_opt = NULL; | ||||||
| 	newnp->ipv6_fl_list = NULL; | 	newnp->ipv6_fl_list = NULL; | ||||||
| 
 | 
 | ||||||
| 	/* Clone RX bits */ | 	/* Clone RX bits */ | ||||||
|  |  | ||||||
|  | @ -416,7 +416,6 @@ static int l2tp_ip_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *m | ||||||
| 	int rc; | 	int rc; | ||||||
| 	struct l2tp_ip_sock *lsa = l2tp_ip_sk(sk); | 	struct l2tp_ip_sock *lsa = l2tp_ip_sk(sk); | ||||||
| 	struct inet_sock *inet = inet_sk(sk); | 	struct inet_sock *inet = inet_sk(sk); | ||||||
| 	struct ip_options *opt = inet->opt; |  | ||||||
| 	struct rtable *rt = NULL; | 	struct rtable *rt = NULL; | ||||||
| 	int connected = 0; | 	int connected = 0; | ||||||
| 	__be32 daddr; | 	__be32 daddr; | ||||||
|  | @ -471,9 +470,14 @@ static int l2tp_ip_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *m | ||||||
| 		rt = (struct rtable *) __sk_dst_check(sk, 0); | 		rt = (struct rtable *) __sk_dst_check(sk, 0); | ||||||
| 
 | 
 | ||||||
| 	if (rt == NULL) { | 	if (rt == NULL) { | ||||||
|  | 		struct ip_options_rcu *inet_opt; | ||||||
|  | 
 | ||||||
|  | 		inet_opt = rcu_dereference_protected(inet->inet_opt, | ||||||
|  | 						     sock_owned_by_user(sk)); | ||||||
|  | 
 | ||||||
| 		/* Use correct destination address if we have options. */ | 		/* Use correct destination address if we have options. */ | ||||||
| 		if (opt && opt->srr) | 		if (inet_opt && inet_opt->opt.srr) | ||||||
| 			daddr = opt->faddr; | 			daddr = inet_opt->opt.faddr; | ||||||
| 
 | 
 | ||||||
| 		/* If this fails, retransmit mechanism of transport layer will
 | 		/* If this fails, retransmit mechanism of transport layer will
 | ||||||
| 		 * keep trying until route appears or the connection times | 		 * keep trying until route appears or the connection times | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue
	
	 Eric Dumazet
						Eric Dumazet