Blame view

net/ipv4/ip_options.c 15.2 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
7
  /*
   * 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
8
   * Authors:	A.N.Kuznetsov
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
9
   *
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
10
   */
afd465030   Joe Perches   net: ipv4: Standa...
11
  #define pr_fmt(fmt) "IPv4: " fmt
4fc268d24   Randy Dunlap   [PATCH] capable/c...
12
  #include <linux/capability.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
13
  #include <linux/module.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
14
  #include <linux/slab.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
15
16
  #include <linux/types.h>
  #include <asm/uaccess.h>
48bdf072c   Chris Metcalf   ip_options_compil...
17
  #include <asm/unaligned.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
18
19
20
21
22
23
24
25
  #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...
26
  #include <net/route.h>
11a03f78f   Paul Moore   [NetLabel]: core ...
27
  #include <net/cipso_ipv4.h>
35ebf65e8   David S. Miller   ipv4: Create and ...
28
  #include <net/ip_fib.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
29

e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
30
  /*
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
31
32
33
34
35
36
37
38
39
   * 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...
40
  void ip_options_build(struct sk_buff *skb, struct ip_options *opt,
8e36360ae   David S. Miller   ipv4: Remove rout...
41
  		      __be32 daddr, struct rtable *rt, int is_frag)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
42
  {
d56f90a7c   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
43
  	unsigned char *iph = skb_network_header(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
44
45
46
47
  
  	memcpy(&(IPCB(skb)->opt), opt, sizeof(struct ip_options));
  	memcpy(iph+sizeof(struct iphdr), opt->__data, opt->optlen);
  	opt = &(IPCB(skb)->opt);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
48
49
50
51
52
53
  
  	if (opt->srr)
  		memcpy(iph+opt->srr+iph[opt->srr+1]-4, &daddr, 4);
  
  	if (!is_frag) {
  		if (opt->rr_needaddr)
8e36360ae   David S. Miller   ipv4: Remove rout...
54
  			ip_rt_get_source(iph+opt->rr+iph[opt->rr+2]-5, skb, rt);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
55
  		if (opt->ts_needaddr)
8e36360ae   David S. Miller   ipv4: Remove rout...
56
  			ip_rt_get_source(iph+opt->ts+iph[opt->ts+2]-9, skb, rt);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
57
  		if (opt->ts_needtime) {
f25c3d613   YOSHIFUJI Hideaki   [IPV4]: Convert d...
58
  			struct timespec tv;
e25d2ca6b   Al Viro   [IPV4]: trivial i...
59
  			__be32 midtime;
f25c3d613   YOSHIFUJI Hideaki   [IPV4]: Convert d...
60
61
  			getnstimeofday(&tv);
  			midtime = htonl((tv.tv_sec % 86400) * MSEC_PER_SEC + tv.tv_nsec / NSEC_PER_MSEC);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
  			memcpy(iph+opt->ts+iph[opt->ts+2]-5, &midtime, 4);
  		}
  		return;
  	}
  	if (opt->rr) {
  		memset(iph+opt->rr, IPOPT_NOP, iph[opt->rr+1]);
  		opt->rr = 0;
  		opt->rr_needaddr = 0;
  	}
  	if (opt->ts) {
  		memset(iph+opt->ts, IPOPT_NOP, iph[opt->ts+1]);
  		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.
   */
f6d8bd051   Eric Dumazet   inet: add RCU pro...
85
  int ip_options_echo(struct ip_options *dopt, struct sk_buff *skb)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
86
  {
f6d8bd051   Eric Dumazet   inet: add RCU pro...
87
  	const struct ip_options *sopt;
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));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
93
  	sopt = &(IPCB(skb)->opt);
f6d8bd051   Eric Dumazet   inet: add RCU pro...
94
  	if (sopt->optlen == 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
95
  		return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
96

d56f90a7c   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
97
  	sptr = skb_network_header(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
98
  	dptr = dopt->__data;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
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
132
  	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...
133
  					if (soffset + 7 <= optlen) {
fd6832220   Al Viro   [IPV4]: inet_addr...
134
  						__be32 addr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
135

8628bd8af   Jan Luebbe   ipv4: Fix IP time...
136
137
  						memcpy(&addr, dptr+soffset-1, 4);
  						if (inet_addr_type(dev_net(skb_dst(skb)->dev), addr) != RTN_UNICAST) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
138
139
140
141
142
143
144
145
146
147
148
149
  							dopt->ts_needtime = 1;
  							soffset += 8;
  						}
  					}
  				}
  			}
  			dptr[2] = soffset;
  		}
  		dptr += optlen;
  		dopt->optlen += optlen;
  	}
  	if (sopt->srr) {
f6d8bd051   Eric Dumazet   inet: add RCU pro...
150
  		unsigned char *start = sptr+sopt->srr;
3ca3c68e7   Al Viro   [IPV4]: struct ip...
151
  		__be32 faddr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
152
153
154
155
156
157
158
159
160
  
  		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...
161
  			for (soffset -= 4, doffset = 4; soffset > 3; soffset -= 4, doffset += 4)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
162
163
164
165
  				memcpy(&dptr[doffset-1], &start[soffset-1], 4);
  			/*
  			 * RFC1812 requires to fix illegal source routes.
  			 */
eddc9ec53   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
166
167
  			if (memcmp(&ip_hdr(skb)->saddr,
  				   &start[soffset + 3], 4) == 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
168
169
170
  				doffset -= 4;
  		}
  		if (doffset > 3) {
6255e5ead   Julian Anastasov   ipv4: optimize fi...
171
  			__be32 daddr = fib_compute_spec_dst(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
172
173
174
175
176
177
178
179
180
181
182
  			memcpy(&start[doffset-1], &daddr, 4);
  			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 ...
183
184
185
186
187
188
189
  	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
190
191
192
193
194
195
196
197
198
199
200
201
  	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...
202
  void ip_options_fragment(struct sk_buff *skb)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
203
  {
d56f90a7c   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
204
  	unsigned char *optptr = skb_network_header(skb) + sizeof(struct iphdr);
5e73ea1a3   Daniel Baluta   ipv4: fix checkpa...
205
  	struct ip_options *opt = &(IPCB(skb)->opt);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
206
207
208
209
210
211
212
213
214
215
216
217
218
  	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...
219
  		if (optlen < 2 || optlen > l)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
220
221
222
223
224
225
226
227
228
229
230
  		  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
231
  }
bf5e53e37   Eric Dumazet   ipv4: defer fib_c...
232
233
234
235
236
237
238
239
  /* 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
240
241
242
243
244
  /*
   * 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.
   */
0e6bd4a1c   Denis V. Lunev   [NETNS]: Add name...
245
  int ip_options_compile(struct net *net,
5e73ea1a3   Daniel Baluta   ipv4: fix checkpa...
246
  		       struct ip_options *opt, struct sk_buff *skb)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
247
  {
bf5e53e37   Eric Dumazet   ipv4: defer fib_c...
248
  	__be32 spec_dst = htonl(INADDR_ANY);
5e73ea1a3   Daniel Baluta   ipv4: fix checkpa...
249
  	unsigned char *pp_ptr = NULL;
11604721a   David S. Miller   ipv4: Fix crashes...
250
  	struct rtable *rt = NULL;
35ebf65e8   David S. Miller   ipv4: Create and ...
251
252
253
  	unsigned char *optptr;
  	unsigned char *iph;
  	int optlen, l;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
254

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

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