diff options
Diffstat (limited to 'net/ipv4/ip_output.c')
| -rw-r--r-- | net/ipv4/ip_output.c | 69 | 
1 files changed, 33 insertions, 36 deletions
diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c index 459c011b1d4..db38c1822de 100644 --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c @@ -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, -			  __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 rtable *rt = skb_rtable(skb);  	struct iphdr *iph;  	/* 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);  	iph = ip_hdr(skb);  	iph->version  = 4; @@ -158,14 +158,14 @@ int ip_build_and_send_pkt(struct sk_buff *skb, struct sock *sk,  	else  		iph->frag_off = 0;  	iph->ttl      = ip_select_ttl(inet, &rt->dst); -	iph->daddr    = rt->rt_dst; -	iph->saddr    = rt->rt_src; +	iph->daddr    = (opt && opt->opt.srr ? opt->opt.faddr : daddr); +	iph->saddr    = saddr;  	iph->protocol = sk->sk_protocol;  	ip_select_ident(iph, &rt->dst, sk); -	if (opt && opt->optlen) { -		iph->ihl += opt->optlen>>2; -		ip_options_build(skb, opt, daddr, rt, 0); +	if (opt && opt->opt.optlen) { +		iph->ihl += opt->opt.optlen>>2; +		ip_options_build(skb, &opt->opt, daddr, rt, 0);  	}  	skb->priority = sk->sk_priority; @@ -316,7 +316,7 @@ int ip_queue_xmit(struct sk_buff *skb)  {  	struct sock *sk = skb->sk;  	struct inet_sock *inet = inet_sk(sk); -	struct ip_options *opt = inet->opt; +	struct ip_options_rcu *inet_opt;  	struct rtable *rt;  	struct iphdr *iph;  	int res; @@ -325,6 +325,7 @@ int ip_queue_xmit(struct sk_buff *skb)  	 * f.e. by something like SCTP.  	 */  	rcu_read_lock(); +	inet_opt = rcu_dereference(inet->inet_opt);  	rt = skb_rtable(skb);  	if (rt != NULL)  		goto packet_routed; @@ -332,18 +333,19 @@ int ip_queue_xmit(struct sk_buff *skb)  	/* Make sure we can route this packet. */  	rt = (struct rtable *)__sk_dst_check(sk, 0);  	if (rt == NULL) { +		struct flowi4 fl4;  		__be32 daddr;  		/* Use correct destination address if we have options. */  		daddr = inet->inet_daddr; -		if(opt && opt->srr) -			daddr = opt->faddr; +		if (inet_opt && inet_opt->opt.srr) +			daddr = inet_opt->opt.faddr;  		/* If this fails, retransmit mechanism of transport layer will  		 * keep trying until route appears or the connection times  		 * itself out.  		 */ -		rt = ip_route_output_ports(sock_net(sk), sk, +		rt = ip_route_output_ports(sock_net(sk), &fl4, sk,  					   daddr, inet->inet_saddr,  					   inet->inet_dport,  					   inet->inet_sport, @@ -357,11 +359,11 @@ int ip_queue_xmit(struct sk_buff *skb)  	skb_dst_set_noref(skb, &rt->dst);  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;  	/* 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);  	iph = ip_hdr(skb);  	*((__be16 *)iph) = htons((4 << 12) | (5 << 8) | (inet->tos & 0xff)); @@ -375,9 +377,9 @@ packet_routed:  	iph->daddr    = rt->rt_dst;  	/* Transport layer set skb->h.foo itself. */ -	if (opt && opt->optlen) { -		iph->ihl += opt->optlen >> 2; -		ip_options_build(skb, opt, inet->inet_daddr, rt, 0); +	if (inet_opt && inet_opt->opt.optlen) { +		iph->ihl += inet_opt->opt.optlen >> 2; +		ip_options_build(skb, &inet_opt->opt, inet->inet_daddr, rt, 0);  	}  	ip_select_ident_more(iph, &rt->dst, sk, @@ -1033,7 +1035,7 @@ static int ip_setup_cork(struct sock *sk, struct inet_cork *cork,  			 struct ipcm_cookie *ipc, struct rtable **rtp)  {  	struct inet_sock *inet = inet_sk(sk); -	struct ip_options *opt; +	struct ip_options_rcu *opt;  	struct rtable *rt;  	/* @@ -1047,7 +1049,7 @@ static int ip_setup_cork(struct sock *sk, struct inet_cork *cork,  			if (unlikely(cork->opt == NULL))  				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->addr = ipc->addr;  	} @@ -1451,39 +1453,34 @@ void ip_send_reply(struct sock *sk, struct sk_buff *skb, struct ip_reply_arg *ar  		   unsigned int len)  {  	struct inet_sock *inet = inet_sk(sk); -	struct { -		struct ip_options	opt; -		char			data[40]; -	} replyopts; +	struct ip_options_data replyopts;  	struct ipcm_cookie ipc;  	__be32 daddr;  	struct rtable *rt = skb_rtable(skb); -	if (ip_options_echo(&replyopts.opt, skb)) +	if (ip_options_echo(&replyopts.opt.opt, skb))  		return;  	daddr = ipc.addr = rt->rt_src;  	ipc.opt = NULL;  	ipc.tx_flags = 0; -	if (replyopts.opt.optlen) { +	if (replyopts.opt.opt.optlen) {  		ipc.opt = &replyopts.opt; -		if (ipc.opt->srr) -			daddr = replyopts.opt.faddr; +		if (replyopts.opt.opt.srr) +			daddr = replyopts.opt.opt.faddr;  	}  	{ -		struct flowi4 fl4 = { -			.flowi4_oif = arg->bound_dev_if, -			.daddr = daddr, -			.saddr = rt->rt_spec_dst, -			.flowi4_tos = RT_TOS(ip_hdr(skb)->tos), -			.fl4_sport = tcp_hdr(skb)->dest, -			.fl4_dport = tcp_hdr(skb)->source, -			.flowi4_proto = sk->sk_protocol, -			.flowi4_flags = ip_reply_arg_flowi_flags(arg), -		}; +		struct flowi4 fl4; + +		flowi4_init_output(&fl4, arg->bound_dev_if, 0, +				   RT_TOS(ip_hdr(skb)->tos), +				   RT_SCOPE_UNIVERSE, sk->sk_protocol, +				   ip_reply_arg_flowi_flags(arg), +				   daddr, rt->rt_spec_dst, +				   tcp_hdr(skb)->source, tcp_hdr(skb)->dest);  		security_skb_classify_flow(skb, flowi4_to_flowi(&fl4));  		rt = ip_route_output_key(sock_net(sk), &fl4);  		if (IS_ERR(rt))  |