diff options
Diffstat (limited to 'net/ipv4/udp.c')
| -rw-r--r-- | net/ipv4/udp.c | 41 | 
1 files changed, 23 insertions, 18 deletions
diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index f87a8eb76f3..544f435d1af 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -578,7 +578,7 @@ found:  void __udp4_lib_err(struct sk_buff *skb, u32 info, struct udp_table *udptable)  {  	struct inet_sock *inet; -	struct iphdr *iph = (struct iphdr *)skb->data; +	const struct iphdr *iph = (const struct iphdr *)skb->data;  	struct udphdr *uh = (struct udphdr *)(skb->data+(iph->ihl<<2));  	const int type = icmp_hdr(skb)->type;  	const int code = icmp_hdr(skb)->code; @@ -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 (*getfrag)(void *, char *, int, int, int, struct sk_buff *);  	struct sk_buff *skb; +	struct ip_options_data opt_copy;  	if (len > 0xFFFF)  		return -EMSGSIZE; @@ -877,22 +878,32 @@ int udp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,  			free = 1;  		connected = 0;  	} -	if (!ipc.opt) -		ipc.opt = inet->opt; +	if (!ipc.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;  	ipc.addr = faddr = daddr; -	if (ipc.opt && ipc.opt->srr) { +	if (ipc.opt && ipc.opt->opt.srr) {  		if (!daddr)  			return -EINVAL; -		faddr = ipc.opt->faddr; +		faddr = ipc.opt->opt.faddr;  		connected = 0;  	}  	tos = RT_TOS(inet->tos);  	if (sock_flag(sk, SOCK_LOCALROUTE) ||  	    (msg->msg_flags & MSG_DONTROUTE) || -	    (ipc.opt && ipc.opt->is_strictroute)) { +	    (ipc.opt && ipc.opt->opt.is_strictroute)) {  		tos |= RTO_ONLINK;  		connected = 0;  	} @@ -909,20 +920,14 @@ int udp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,  		rt = (struct rtable *)sk_dst_check(sk, 0);  	if (rt == NULL) { -		struct flowi4 fl4 = { -			.flowi4_oif = ipc.oif, -			.flowi4_mark = sk->sk_mark, -			.daddr = faddr, -			.saddr = saddr, -			.flowi4_tos = tos, -			.flowi4_proto = sk->sk_protocol, -			.flowi4_flags = (inet_sk_flowi_flags(sk) | -					 FLOWI_FLAG_CAN_SLEEP), -			.fl4_sport = inet->inet_sport, -			.fl4_dport = dport, -		}; +		struct flowi4 fl4;  		struct net *net = sock_net(sk); +		flowi4_init_output(&fl4, ipc.oif, sk->sk_mark, tos, +				   RT_SCOPE_UNIVERSE, sk->sk_protocol, +				   inet_sk_flowi_flags(sk)|FLOWI_FLAG_CAN_SLEEP, +				   faddr, saddr, dport, inet->inet_sport); +  		security_sk_classify_flow(sk, flowi4_to_flowi(&fl4));  		rt = ip_route_output_flow(net, &fl4, sk);  		if (IS_ERR(rt)) {  |