Blame view

net/ipv4/ip_options.c 14.8 KB
b24413180   Greg Kroah-Hartman   License cleanup: ...
1
  // SPDX-License-Identifier: GPL-2.0
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2
3
4
5
6
7
8
  /*
   * INET		An implementation of the TCP/IP protocol suite for the LINUX
   *		operating system.  INET is implemented using the  BSD Socket
   *		interface as the means of communication with the user level.
   *
   *		The options processing module for ip.c
   *
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
9
   * Authors:	A.N.Kuznetsov
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
10
   *
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
11
   */
afd465030   Joe Perches   net: ipv4: Standa...
12
  #define pr_fmt(fmt) "IPv4: " fmt
4fc268d24   Randy Dunlap   [PATCH] capable/c...
13
  #include <linux/capability.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
14
  #include <linux/module.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
15
  #include <linux/slab.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
16
  #include <linux/types.h>
7c0f6ba68   Linus Torvalds   Replace <asm/uacc...
17
  #include <linux/uaccess.h>
48bdf072c   Chris Metcalf   ip_options_compil...
18
  #include <asm/unaligned.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
19
20
21
22
23
24
25
26
  #include <linux/skbuff.h>
  #include <linux/ip.h>
  #include <linux/icmp.h>
  #include <linux/netdevice.h>
  #include <linux/rtnetlink.h>
  #include <net/sock.h>
  #include <net/ip.h>
  #include <net/icmp.h>
14c850212   Arnaldo Carvalho de Melo   [INET_SOCK]: Move...
27
  #include <net/route.h>
11a03f78f   Paul Moore   [NetLabel]: core ...
28
  #include <net/cipso_ipv4.h>
35ebf65e8   David S. Miller   ipv4: Create and ...
29
  #include <net/ip_fib.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
30

e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
31
  /*
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
32
33
34
35
36
37
38
39
40
   * Write options to IP header, record destination address to
   * source route option, address of outgoing interface
   * (we should already know it, so that this  function is allowed be
   * called only after routing decision) and timestamp,
   * if we originate this datagram.
   *
   * daddr is real destination address, next hop is recorded in IP header.
   * saddr is address of outgoing interface.
   */
f6d8bd051   Eric Dumazet   inet: add RCU pro...
41
  void ip_options_build(struct sk_buff *skb, struct ip_options *opt,
8e36360ae   David S. Miller   ipv4: Remove rout...
42
  		      __be32 daddr, struct rtable *rt, int is_frag)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
43
  {
d56f90a7c   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
44
  	unsigned char *iph = skb_network_header(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
45
46
  
  	memcpy(&(IPCB(skb)->opt), opt, sizeof(struct ip_options));
343d8c601   Miaohe Lin   net: clean up cod...
47
  	memcpy(iph + sizeof(struct iphdr), opt->__data, opt->optlen);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
48
  	opt = &(IPCB(skb)->opt);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
49
50
  
  	if (opt->srr)
343d8c601   Miaohe Lin   net: clean up cod...
51
  		memcpy(iph + opt->srr + iph[opt->srr + 1] - 4, &daddr, 4);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
52
53
54
  
  	if (!is_frag) {
  		if (opt->rr_needaddr)
343d8c601   Miaohe Lin   net: clean up cod...
55
  			ip_rt_get_source(iph + opt->rr + iph[opt->rr + 2] - 5, skb, rt);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
56
  		if (opt->ts_needaddr)
343d8c601   Miaohe Lin   net: clean up cod...
57
  			ip_rt_get_source(iph + opt->ts + iph[opt->ts + 2] - 9, skb, rt);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
58
  		if (opt->ts_needtime) {
e25d2ca6b   Al Viro   [IPV4]: trivial i...
59
  			__be32 midtime;
822c86853   Deepa Dinamani   net: ipv4: Conver...
60
61
  
  			midtime = inet_current_timestamp();
343d8c601   Miaohe Lin   net: clean up cod...
62
  			memcpy(iph + opt->ts + iph[opt->ts + 2] - 5, &midtime, 4);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
63
64
65
66
  		}
  		return;
  	}
  	if (opt->rr) {
343d8c601   Miaohe Lin   net: clean up cod...
67
  		memset(iph + opt->rr, IPOPT_NOP, iph[opt->rr + 1]);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
68
69
70
71
  		opt->rr = 0;
  		opt->rr_needaddr = 0;
  	}
  	if (opt->ts) {
343d8c601   Miaohe Lin   net: clean up cod...
72
  		memset(iph + opt->ts, IPOPT_NOP, iph[opt->ts + 1]);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
73
74
75
76
  		opt->ts = 0;
  		opt->ts_needaddr = opt->ts_needtime = 0;
  	}
  }
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
77
  /*
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
78
79
80
81
82
83
84
   * Provided (sopt, skb) points to received options,
   * build in dopt compiled option set appropriate for answering.
   * i.e. invert SRR option, copy anothers,
   * and grab room in RR/TS options.
   *
   * NOTE: dopt cannot point to skb.
   */
91ed1e666   Paolo Abeni   ip/options: expli...
85
86
  int __ip_options_echo(struct net *net, struct ip_options *dopt,
  		      struct sk_buff *skb, const struct ip_options *sopt)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
87
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
88
89
90
  	unsigned char *sptr, *dptr;
  	int soffset, doffset;
  	int	optlen;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
91
92
  
  	memset(dopt, 0, sizeof(struct ip_options));
f6d8bd051   Eric Dumazet   inet: add RCU pro...
93
  	if (sopt->optlen == 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
94
  		return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
95

d56f90a7c   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
96
  	sptr = skb_network_header(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
97
  	dptr = dopt->__data;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
  	if (sopt->rr) {
  		optlen  = sptr[sopt->rr+1];
  		soffset = sptr[sopt->rr+2];
  		dopt->rr = dopt->optlen + sizeof(struct iphdr);
  		memcpy(dptr, sptr+sopt->rr, optlen);
  		if (sopt->rr_needaddr && soffset <= optlen) {
  			if (soffset + 3 > optlen)
  				return -EINVAL;
  			dptr[2] = soffset + 4;
  			dopt->rr_needaddr = 1;
  		}
  		dptr += optlen;
  		dopt->optlen += optlen;
  	}
  	if (sopt->ts) {
  		optlen = sptr[sopt->ts+1];
  		soffset = sptr[sopt->ts+2];
  		dopt->ts = dopt->optlen + sizeof(struct iphdr);
  		memcpy(dptr, sptr+sopt->ts, optlen);
  		if (soffset <= optlen) {
  			if (sopt->ts_needaddr) {
  				if (soffset + 3 > optlen)
  					return -EINVAL;
  				dopt->ts_needaddr = 1;
  				soffset += 4;
  			}
  			if (sopt->ts_needtime) {
  				if (soffset + 3 > optlen)
  					return -EINVAL;
  				if ((dptr[3]&0xF) != IPOPT_TS_PRESPEC) {
  					dopt->ts_needtime = 1;
  					soffset += 4;
  				} else {
  					dopt->ts_needtime = 0;
8628bd8af   Jan Luebbe   ipv4: Fix IP time...
132
  					if (soffset + 7 <= optlen) {
fd6832220   Al Viro   [IPV4]: inet_addr...
133
  						__be32 addr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
134

8628bd8af   Jan Luebbe   ipv4: Fix IP time...
135
  						memcpy(&addr, dptr+soffset-1, 4);
91ed1e666   Paolo Abeni   ip/options: expli...
136
  						if (inet_addr_type(net, addr) != RTN_UNICAST) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
137
138
139
140
141
142
143
144
145
146
147
148
  							dopt->ts_needtime = 1;
  							soffset += 8;
  						}
  					}
  				}
  			}
  			dptr[2] = soffset;
  		}
  		dptr += optlen;
  		dopt->optlen += optlen;
  	}
  	if (sopt->srr) {
f6d8bd051   Eric Dumazet   inet: add RCU pro...
149
  		unsigned char *start = sptr+sopt->srr;
3ca3c68e7   Al Viro   [IPV4]: struct ip...
150
  		__be32 faddr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
151
152
153
154
155
156
157
158
159
  
  		optlen  = start[1];
  		soffset = start[2];
  		doffset = 0;
  		if (soffset > optlen)
  			soffset = optlen + 1;
  		soffset -= 4;
  		if (soffset > 3) {
  			memcpy(&faddr, &start[soffset-1], 4);
a22318e83   Weilong Chen   ipv4: do clean up...
160
  			for (soffset -= 4, doffset = 4; soffset > 3; soffset -= 4, doffset += 4)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
161
162
163
164
  				memcpy(&dptr[doffset-1], &start[soffset-1], 4);
  			/*
  			 * RFC1812 requires to fix illegal source routes.
  			 */
eddc9ec53   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
165
166
  			if (memcmp(&ip_hdr(skb)->saddr,
  				   &start[soffset + 3], 4) == 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
167
168
169
  				doffset -= 4;
  		}
  		if (doffset > 3) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
170
171
172
173
174
175
176
177
178
179
  			dopt->faddr = faddr;
  			dptr[0] = start[0];
  			dptr[1] = doffset+3;
  			dptr[2] = 4;
  			dptr += doffset+3;
  			dopt->srr = dopt->optlen + sizeof(struct iphdr);
  			dopt->optlen += doffset+3;
  			dopt->is_strictroute = sopt->is_strictroute;
  		}
  	}
11a03f78f   Paul Moore   [NetLabel]: core ...
180
181
182
183
184
185
186
  	if (sopt->cipso) {
  		optlen  = sptr[sopt->cipso+1];
  		dopt->cipso = dopt->optlen+sizeof(struct iphdr);
  		memcpy(dptr, sptr+sopt->cipso, optlen);
  		dptr += optlen;
  		dopt->optlen += optlen;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
187
188
189
190
191
192
193
194
195
196
197
198
  	while (dopt->optlen & 3) {
  		*dptr++ = IPOPT_END;
  		dopt->optlen++;
  	}
  	return 0;
  }
  
  /*
   *	Options "fragmenting", just fill options not
   *	allowed in fragments with NOOPs.
   *	Simple and stupid 8), but the most efficient way.
   */
5e73ea1a3   Daniel Baluta   ipv4: fix checkpa...
199
  void ip_options_fragment(struct sk_buff *skb)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
200
  {
d56f90a7c   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
201
  	unsigned char *optptr = skb_network_header(skb) + sizeof(struct iphdr);
5e73ea1a3   Daniel Baluta   ipv4: fix checkpa...
202
  	struct ip_options *opt = &(IPCB(skb)->opt);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
203
204
205
206
207
208
209
210
211
212
213
214
215
  	int  l = opt->optlen;
  	int  optlen;
  
  	while (l > 0) {
  		switch (*optptr) {
  		case IPOPT_END:
  			return;
  		case IPOPT_NOOP:
  			l--;
  			optptr++;
  			continue;
  		}
  		optlen = optptr[1];
a22318e83   Weilong Chen   ipv4: do clean up...
216
  		if (optlen < 2 || optlen > l)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
217
218
219
220
221
222
223
224
225
226
227
  		  return;
  		if (!IPOPT_COPIED(*optptr))
  			memset(optptr, IPOPT_NOOP, optlen);
  		l -= optlen;
  		optptr += optlen;
  	}
  	opt->ts = 0;
  	opt->rr = 0;
  	opt->rr_needaddr = 0;
  	opt->ts_needaddr = 0;
  	opt->ts_needtime = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
228
  }
bf5e53e37   Eric Dumazet   ipv4: defer fib_c...
229
230
231
232
233
234
235
236
  /* helper used by ip_options_compile() to call fib_compute_spec_dst()
   * at most one time.
   */
  static void spec_dst_fill(__be32 *spec_dst, struct sk_buff *skb)
  {
  	if (*spec_dst == htonl(INADDR_ANY))
  		*spec_dst = fib_compute_spec_dst(skb);
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
237
238
239
240
241
  /*
   * Verify options and fill pointers in struct options.
   * Caller should clear *opt, and set opt->data.
   * If opt == NULL, then skb->data should point to IP header.
   */
3da1ed7ac   Nazarov Sergey   net: avoid use IP...
242
243
244
  int __ip_options_compile(struct net *net,
  			 struct ip_options *opt, struct sk_buff *skb,
  			 __be32 *info)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
245
  {
bf5e53e37   Eric Dumazet   ipv4: defer fib_c...
246
  	__be32 spec_dst = htonl(INADDR_ANY);
5e73ea1a3   Daniel Baluta   ipv4: fix checkpa...
247
  	unsigned char *pp_ptr = NULL;
11604721a   David S. Miller   ipv4: Fix crashes...
248
  	struct rtable *rt = NULL;
35ebf65e8   David S. Miller   ipv4: Create and ...
249
250
251
  	unsigned char *optptr;
  	unsigned char *iph;
  	int optlen, l;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
252

00db41243   Ian Morris   ipv4: coding styl...
253
  	if (skb) {
11604721a   David S. Miller   ipv4: Fix crashes...
254
  		rt = skb_rtable(skb);
22aba383c   Denis V. Lunev   [IPV4]: Always pa...
255
256
  		optptr = (unsigned char *)&(ip_hdr(skb)[1]);
  	} else
10fe7d85e   Denis V. Lunev   [IPV4]: Remove un...
257
  		optptr = opt->__data;
22aba383c   Denis V. Lunev   [IPV4]: Always pa...
258
  	iph = optptr - sizeof(struct iphdr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
259
260
261
  
  	for (l = opt->optlen; l > 0; ) {
  		switch (*optptr) {
dd9b45598   Weilong Chen   ipv4: switch and ...
262
  		case IPOPT_END:
a22318e83   Weilong Chen   ipv4: do clean up...
263
  			for (optptr++, l--; l > 0; optptr++, l--) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
264
265
266
267
268
269
  				if (*optptr != IPOPT_END) {
  					*optptr = IPOPT_END;
  					opt->is_changed = 1;
  				}
  			}
  			goto eol;
dd9b45598   Weilong Chen   ipv4: switch and ...
270
  		case IPOPT_NOOP:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
271
272
273
274
  			l--;
  			optptr++;
  			continue;
  		}
10ec9472f   Eric Dumazet   ipv4: fix buffer ...
275
276
277
278
  		if (unlikely(l < 2)) {
  			pp_ptr = optptr;
  			goto error;
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
279
  		optlen = optptr[1];
a22318e83   Weilong Chen   ipv4: do clean up...
280
  		if (optlen < 2 || optlen > l) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
281
282
283
284
  			pp_ptr = optptr;
  			goto error;
  		}
  		switch (*optptr) {
dd9b45598   Weilong Chen   ipv4: switch and ...
285
286
  		case IPOPT_SSRR:
  		case IPOPT_LSRR:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
  			if (optlen < 3) {
  				pp_ptr = optptr + 1;
  				goto error;
  			}
  			if (optptr[2] < 4) {
  				pp_ptr = optptr + 2;
  				goto error;
  			}
  			/* NB: cf RFC-1812 5.2.4.1 */
  			if (opt->srr) {
  				pp_ptr = optptr;
  				goto error;
  			}
  			if (!skb) {
  				if (optptr[2] != 4 || optlen < 7 || ((optlen-3) & 3)) {
  					pp_ptr = optptr + 1;
  					goto error;
  				}
  				memcpy(&opt->faddr, &optptr[3], 4);
  				if (optlen > 7)
  					memmove(&optptr[3], &optptr[7], optlen-7);
  			}
  			opt->is_strictroute = (optptr[0] == IPOPT_SSRR);
  			opt->srr = optptr - iph;
  			break;
dd9b45598   Weilong Chen   ipv4: switch and ...
312
  		case IPOPT_RR:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
  			if (opt->rr) {
  				pp_ptr = optptr;
  				goto error;
  			}
  			if (optlen < 3) {
  				pp_ptr = optptr + 1;
  				goto error;
  			}
  			if (optptr[2] < 4) {
  				pp_ptr = optptr + 2;
  				goto error;
  			}
  			if (optptr[2] <= optlen) {
  				if (optptr[2]+3 > optlen) {
  					pp_ptr = optptr + 2;
  					goto error;
  				}
11604721a   David S. Miller   ipv4: Fix crashes...
330
  				if (rt) {
bf5e53e37   Eric Dumazet   ipv4: defer fib_c...
331
  					spec_dst_fill(&spec_dst, skb);
35ebf65e8   David S. Miller   ipv4: Create and ...
332
  					memcpy(&optptr[optptr[2]-1], &spec_dst, 4);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
333
334
335
336
337
338
339
  					opt->is_changed = 1;
  				}
  				optptr[2] += 4;
  				opt->rr_needaddr = 1;
  			}
  			opt->rr = optptr - iph;
  			break;
dd9b45598   Weilong Chen   ipv4: switch and ...
340
  		case IPOPT_TIMESTAMP:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
341
342
343
344
345
346
347
348
349
350
351
352
353
  			if (opt->ts) {
  				pp_ptr = optptr;
  				goto error;
  			}
  			if (optlen < 4) {
  				pp_ptr = optptr + 1;
  				goto error;
  			}
  			if (optptr[2] < 5) {
  				pp_ptr = optptr + 2;
  				goto error;
  			}
  			if (optptr[2] <= optlen) {
48bdf072c   Chris Metcalf   ip_options_compil...
354
  				unsigned char *timeptr = NULL;
5a2b646ff   Hisao Tanabe   ipv4: Use predefi...
355
  				if (optptr[2]+3 > optlen) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
356
357
358
359
  					pp_ptr = optptr + 2;
  					goto error;
  				}
  				switch (optptr[3]&0xF) {
dd9b45598   Weilong Chen   ipv4: switch and ...
360
  				case IPOPT_TS_TSONLY:
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
361
  					if (skb)
48bdf072c   Chris Metcalf   ip_options_compil...
362
  						timeptr = &optptr[optptr[2]-1];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
363
364
365
  					opt->ts_needtime = 1;
  					optptr[2] += 4;
  					break;
dd9b45598   Weilong Chen   ipv4: switch and ...
366
  				case IPOPT_TS_TSANDADDR:
5a2b646ff   Hisao Tanabe   ipv4: Use predefi...
367
  					if (optptr[2]+7 > optlen) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
368
369
370
  						pp_ptr = optptr + 2;
  						goto error;
  					}
11604721a   David S. Miller   ipv4: Fix crashes...
371
  					if (rt)  {
bf5e53e37   Eric Dumazet   ipv4: defer fib_c...
372
  						spec_dst_fill(&spec_dst, skb);
35ebf65e8   David S. Miller   ipv4: Create and ...
373
  						memcpy(&optptr[optptr[2]-1], &spec_dst, 4);
48bdf072c   Chris Metcalf   ip_options_compil...
374
  						timeptr = &optptr[optptr[2]+3];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
375
376
377
378
379
  					}
  					opt->ts_needaddr = 1;
  					opt->ts_needtime = 1;
  					optptr[2] += 8;
  					break;
dd9b45598   Weilong Chen   ipv4: switch and ...
380
  				case IPOPT_TS_PRESPEC:
5a2b646ff   Hisao Tanabe   ipv4: Use predefi...
381
  					if (optptr[2]+7 > optlen) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
382
383
384
  						pp_ptr = optptr + 2;
  						goto error;
  					}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
385
  					{
fd6832220   Al Viro   [IPV4]: inet_addr...
386
  						__be32 addr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
387
  						memcpy(&addr, &optptr[optptr[2]-1], 4);
0e6bd4a1c   Denis V. Lunev   [NETNS]: Add name...
388
  						if (inet_addr_type(net, addr) == RTN_UNICAST)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
389
390
  							break;
  						if (skb)
48bdf072c   Chris Metcalf   ip_options_compil...
391
  							timeptr = &optptr[optptr[2]+3];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
392
393
394
395
  					}
  					opt->ts_needtime = 1;
  					optptr[2] += 8;
  					break;
dd9b45598   Weilong Chen   ipv4: switch and ...
396
  				default:
52e804c6d   Eric W. Biederman   net: Allow userns...
397
  					if (!skb && !ns_capable(net->user_ns, CAP_NET_RAW)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
398
399
400
401
402
403
  						pp_ptr = optptr + 3;
  						goto error;
  					}
  					break;
  				}
  				if (timeptr) {
822c86853   Deepa Dinamani   net: ipv4: Conver...
404
405
406
407
  					__be32 midtime;
  
  					midtime = inet_current_timestamp();
  					memcpy(timeptr, &midtime, 4);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
408
409
  					opt->is_changed = 1;
  				}
fa2b04f45   David Ward   net/ipv4: Timesta...
410
  			} else if ((optptr[3]&0xF) != IPOPT_TS_PRESPEC) {
95c961747   Eric Dumazet   net: cleanup unsi...
411
  				unsigned int overflow = optptr[3]>>4;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
412
413
414
415
  				if (overflow == 15) {
  					pp_ptr = optptr + 3;
  					goto error;
  				}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
416
417
418
419
420
  				if (skb) {
  					optptr[3] = (optptr[3]&0xF)|((overflow+1)<<4);
  					opt->is_changed = 1;
  				}
  			}
4660c7f49   David Ward   net/ipv4: Ensure ...
421
  			opt->ts = optptr - iph;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
422
  			break;
dd9b45598   Weilong Chen   ipv4: switch and ...
423
  		case IPOPT_RA:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
424
425
426
427
428
429
430
  			if (optlen < 4) {
  				pp_ptr = optptr + 1;
  				goto error;
  			}
  			if (optptr[2] == 0 && optptr[3] == 0)
  				opt->router_alert = optptr - iph;
  			break;
dd9b45598   Weilong Chen   ipv4: switch and ...
431
  		case IPOPT_CIPSO:
52e804c6d   Eric W. Biederman   net: Allow userns...
432
  			if ((!skb && !ns_capable(net->user_ns, CAP_NET_RAW)) || opt->cipso) {
11a03f78f   Paul Moore   [NetLabel]: core ...
433
434
435
436
  				pp_ptr = optptr;
  				goto error;
  			}
  			opt->cipso = optptr - iph;
15c45f7b2   Paul Moore   cipso: Add suppor...
437
  			if (cipso_v4_validate(skb, &optptr)) {
11a03f78f   Paul Moore   [NetLabel]: core ...
438
439
440
441
  				pp_ptr = optptr;
  				goto error;
  			}
  			break;
dd9b45598   Weilong Chen   ipv4: switch and ...
442
443
444
  		case IPOPT_SEC:
  		case IPOPT_SID:
  		default:
52e804c6d   Eric W. Biederman   net: Allow userns...
445
  			if (!skb && !ns_capable(net->user_ns, CAP_NET_RAW)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
446
447
448
449
450
451
452
453
454
455
456
457
458
459
  				pp_ptr = optptr;
  				goto error;
  			}
  			break;
  		}
  		l -= optlen;
  		optptr += optlen;
  	}
  
  eol:
  	if (!pp_ptr)
  		return 0;
  
  error:
3da1ed7ac   Nazarov Sergey   net: avoid use IP...
460
461
  	if (info)
  		*info = htonl((pp_ptr-iph)<<24);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
462
463
  	return -EINVAL;
  }
dbb5281a1   Stephen Suryaputra   netfilter: nf_tab...
464
  EXPORT_SYMBOL(__ip_options_compile);
3da1ed7ac   Nazarov Sergey   net: avoid use IP...
465
466
467
468
469
470
471
472
473
474
475
476
  
  int ip_options_compile(struct net *net,
  		       struct ip_options *opt, struct sk_buff *skb)
  {
  	int ret;
  	__be32 info;
  
  	ret = __ip_options_compile(net, opt, skb, &info);
  	if (ret != 0 && skb)
  		icmp_send(skb, ICMP_PARAMETERPROB, 0, info);
  	return ret;
  }
462fb2af9   Bandan Das   bridge : Sanitize...
477
  EXPORT_SYMBOL(ip_options_compile);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
478
479
480
481
  
  /*
   *	Undo all the changes done by ip_options_compile().
   */
5e73ea1a3   Daniel Baluta   ipv4: fix checkpa...
482
  void ip_options_undo(struct ip_options *opt)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
483
484
  {
  	if (opt->srr) {
343d8c601   Miaohe Lin   net: clean up cod...
485
486
487
488
  		unsigned char *optptr = opt->__data + opt->srr - sizeof(struct iphdr);
  
  		memmove(optptr + 7, optptr + 3, optptr[1] - 7);
  		memcpy(optptr + 3, &opt->faddr, 4);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
489
490
  	}
  	if (opt->rr_needaddr) {
343d8c601   Miaohe Lin   net: clean up cod...
491
  		unsigned char *optptr = opt->__data + opt->rr - sizeof(struct iphdr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
492
  		optptr[2] -= 4;
343d8c601   Miaohe Lin   net: clean up cod...
493
  		memset(&optptr[optptr[2] - 1], 0, 4);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
494
495
  	}
  	if (opt->ts) {
343d8c601   Miaohe Lin   net: clean up cod...
496
  		unsigned char *optptr = opt->__data + opt->ts - sizeof(struct iphdr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
497
498
  		if (opt->ts_needtime) {
  			optptr[2] -= 4;
343d8c601   Miaohe Lin   net: clean up cod...
499
500
  			memset(&optptr[optptr[2] - 1], 0, 4);
  			if ((optptr[3] & 0xF) == IPOPT_TS_PRESPEC)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
501
502
503
504
  				optptr[2] -= 4;
  		}
  		if (opt->ts_needaddr) {
  			optptr[2] -= 4;
343d8c601   Miaohe Lin   net: clean up cod...
505
  			memset(&optptr[optptr[2] - 1], 0, 4);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
506
507
508
  		}
  	}
  }
de40a3e88   Christoph Hellwig   net/ipv4: merge i...
509
510
  int ip_options_get(struct net *net, struct ip_options_rcu **optp,
  		   sockptr_t data, int optlen)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
511
  {
de40a3e88   Christoph Hellwig   net/ipv4: merge i...
512
513
514
  	struct ip_options_rcu *opt;
  
  	opt = kzalloc(sizeof(struct ip_options_rcu) + ((optlen + 3) & ~3),
376407039   Mariusz Kozlowski   [IPV4] ip_options...
515
  		       GFP_KERNEL);
de40a3e88   Christoph Hellwig   net/ipv4: merge i...
516
517
518
519
520
521
  	if (!opt)
  		return -ENOMEM;
  	if (optlen && copy_from_sockptr(opt->opt.__data, data, optlen)) {
  		kfree(opt);
  		return -EFAULT;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
522

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
523
  	while (optlen & 3)
f6d8bd051   Eric Dumazet   inet: add RCU pro...
524
525
526
  		opt->opt.__data[optlen++] = IPOPT_END;
  	opt->opt.optlen = optlen;
  	if (optlen && ip_options_compile(net, &opt->opt, NULL)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
527
528
529
  		kfree(opt);
  		return -EINVAL;
  	}
a51482bde   Jesper Juhl   [NET]: kfree cleanup
530
  	kfree(*optp);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
531
532
533
534
535
536
  	*optp = opt;
  	return 0;
  }
  
  void ip_forward_options(struct sk_buff *skb)
  {
5e73ea1a3   Daniel Baluta   ipv4: fix checkpa...
537
538
  	struct   ip_options *opt	= &(IPCB(skb)->opt);
  	unsigned char *optptr;
511c3f92a   Eric Dumazet   net: skb->rtable ...
539
  	struct rtable *rt = skb_rtable(skb);
d56f90a7c   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
540
  	unsigned char *raw = skb_network_header(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
541
542
543
  
  	if (opt->rr_needaddr) {
  		optptr = (unsigned char *)raw + opt->rr;
8e36360ae   David S. Miller   ipv4: Remove rout...
544
  		ip_rt_get_source(&optptr[optptr[2]-5], skb, rt);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
545
546
547
548
549
550
  		opt->is_changed = 1;
  	}
  	if (opt->srr_is_hit) {
  		int srrptr, srrspace;
  
  		optptr = raw + opt->srr;
a22318e83   Weilong Chen   ipv4: do clean up...
551
  		for ( srrptr = optptr[2], srrspace = optptr[1];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
552
553
554
555
556
  		     srrptr <= srrspace;
  		     srrptr += 4
  		     ) {
  			if (srrptr + 3 > srrspace)
  				break;
ac8a48106   Li Wei   ipv4: Save nextho...
557
  			if (memcmp(&opt->nexthop, &optptr[srrptr-1], 4) == 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
558
559
560
561
  				break;
  		}
  		if (srrptr + 3 <= srrspace) {
  			opt->is_changed = 1;
ac8a48106   Li Wei   ipv4: Save nextho...
562
  			ip_hdr(skb)->daddr = opt->nexthop;
5dc7883f2   Li Wei   ipv4: Fix wrong o...
563
  			ip_rt_get_source(&optptr[srrptr-1], skb, rt);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
564
  			optptr[2] = srrptr+4;
e87cc4728   Joe Perches   net: Convert net_...
565
566
567
568
569
  		} else {
  			net_crit_ratelimited("%s(): Argh! Destination lost!
  ",
  					     __func__);
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
570
571
  		if (opt->ts_needaddr) {
  			optptr = raw + opt->ts;
8e36360ae   David S. Miller   ipv4: Remove rout...
572
  			ip_rt_get_source(&optptr[optptr[2]-9], skb, rt);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
573
574
575
576
577
  			opt->is_changed = 1;
  		}
  	}
  	if (opt->is_changed) {
  		opt->is_changed = 0;
eddc9ec53   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
578
  		ip_send_check(ip_hdr(skb));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
579
580
  	}
  }
8c83f2df9   Stephen Suryaputra   vrf: check accept...
581
  int ip_options_rcv_srr(struct sk_buff *skb, struct net_device *dev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
582
583
584
  {
  	struct ip_options *opt = &(IPCB(skb)->opt);
  	int srrspace, srrptr;
9e12bb22e   Al Viro   [IPV4]: ip_route_...
585
  	__be32 nexthop;
eddc9ec53   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
586
  	struct iphdr *iph = ip_hdr(skb);
d56f90a7c   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
587
  	unsigned char *optptr = skb_network_header(skb) + opt->srr;
511c3f92a   Eric Dumazet   net: skb->rtable ...
588
  	struct rtable *rt = skb_rtable(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
589
  	struct rtable *rt2;
7fee226ad   Eric Dumazet   net: add a noref ...
590
  	unsigned long orefdst;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
591
  	int err;
10949550b   David S. Miller   ipv4: Kill spurio...
592
  	if (!rt)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
593
594
595
596
597
598
599
600
601
602
603
604
  		return 0;
  
  	if (skb->pkt_type != PACKET_HOST)
  		return -EINVAL;
  	if (rt->rt_type == RTN_UNICAST) {
  		if (!opt->is_strictroute)
  			return 0;
  		icmp_send(skb, ICMP_PARAMETERPROB, 0, htonl(16<<24));
  		return -EINVAL;
  	}
  	if (rt->rt_type != RTN_LOCAL)
  		return -EINVAL;
a22318e83   Weilong Chen   ipv4: do clean up...
605
  	for (srrptr = optptr[2], srrspace = optptr[1]; srrptr <= srrspace; srrptr += 4) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
606
607
608
609
610
  		if (srrptr + 3 > srrspace) {
  			icmp_send(skb, ICMP_PARAMETERPROB, 0, htonl((opt->srr+2)<<24));
  			return -EINVAL;
  		}
  		memcpy(&nexthop, &optptr[srrptr-1], 4);
7fee226ad   Eric Dumazet   net: add a noref ...
611
  		orefdst = skb->_skb_refdst;
adf30907d   Eric Dumazet   net: skb->dst acc...
612
  		skb_dst_set(skb, NULL);
8c83f2df9   Stephen Suryaputra   vrf: check accept...
613
  		err = ip_route_input(skb, nexthop, iph->saddr, iph->tos, dev);
511c3f92a   Eric Dumazet   net: skb->rtable ...
614
  		rt2 = skb_rtable(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
615
  		if (err || (rt2->rt_type != RTN_UNICAST && rt2->rt_type != RTN_LOCAL)) {
7fee226ad   Eric Dumazet   net: add a noref ...
616
617
  			skb_dst_drop(skb);
  			skb->_skb_refdst = orefdst;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
618
619
  			return -EINVAL;
  		}
7fee226ad   Eric Dumazet   net: add a noref ...
620
  		refdst_drop(orefdst);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
621
622
623
  		if (rt2->rt_type != RTN_LOCAL)
  			break;
  		/* Superfast 8) loopback forward */
c30883bdf   David S. Miller   ipv4: Simplify ip...
624
  		iph->daddr = nexthop;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
625
626
627
628
  		opt->is_changed = 1;
  	}
  	if (srrptr <= srrspace) {
  		opt->srr_is_hit = 1;
ac8a48106   Li Wei   ipv4: Save nextho...
629
  		opt->nexthop = nexthop;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
630
631
632
633
  		opt->is_changed = 1;
  	}
  	return 0;
  }
462fb2af9   Bandan Das   bridge : Sanitize...
634
  EXPORT_SYMBOL(ip_options_rcv_srr);