Blame view

net/ipv4/ip_options.c 14.9 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
   */
4fc268d24   Randy Dunlap   [PATCH] capable/c...
11
  #include <linux/capability.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
12
  #include <linux/module.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
13
  #include <linux/slab.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
14
15
  #include <linux/types.h>
  #include <asm/uaccess.h>
48bdf072c   Chris Metcalf   ip_options_compil...
16
  #include <asm/unaligned.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
17
18
19
20
21
22
23
24
  #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...
25
  #include <net/route.h>
11a03f78f   Paul Moore   [NetLabel]: core ...
26
  #include <net/cipso_ipv4.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
27

e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
28
  /*
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
29
30
31
32
33
34
35
36
37
   * 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...
38
  void ip_options_build(struct sk_buff *skb, struct ip_options *opt,
8e36360ae   David S. Miller   ipv4: Remove rout...
39
  		      __be32 daddr, struct rtable *rt, int is_frag)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
40
  {
d56f90a7c   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
41
  	unsigned char *iph = skb_network_header(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
42
43
44
45
  
  	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
46
47
48
49
50
51
  
  	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...
52
  			ip_rt_get_source(iph+opt->rr+iph[opt->rr+2]-5, skb, rt);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
53
  		if (opt->ts_needaddr)
8e36360ae   David S. Miller   ipv4: Remove rout...
54
  			ip_rt_get_source(iph+opt->ts+iph[opt->ts+2]-9, skb, rt);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
55
  		if (opt->ts_needtime) {
f25c3d613   YOSHIFUJI Hideaki   [IPV4]: Convert d...
56
  			struct timespec tv;
e25d2ca6b   Al Viro   [IPV4]: trivial i...
57
  			__be32 midtime;
f25c3d613   YOSHIFUJI Hideaki   [IPV4]: Convert d...
58
59
  			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
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
  			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...
75
  /*
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
76
77
78
79
80
81
82
   * 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...
83
  int ip_options_echo(struct ip_options *dopt, struct sk_buff *skb)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
84
  {
f6d8bd051   Eric Dumazet   inet: add RCU pro...
85
  	const struct ip_options *sopt;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
86
87
88
  	unsigned char *sptr, *dptr;
  	int soffset, doffset;
  	int	optlen;
e25d2ca6b   Al Viro   [IPV4]: trivial i...
89
  	__be32	daddr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
90
91
  
  	memset(dopt, 0, sizeof(struct ip_options));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
92
  	sopt = &(IPCB(skb)->opt);
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;
511c3f92a   Eric Dumazet   net: skb->rtable ...
98
  	daddr = skb_rtable(skb)->rt_spec_dst;
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
133
  
  	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...
134
  					if (soffset + 7 <= optlen) {
fd6832220   Al Viro   [IPV4]: inet_addr...
135
  						__be32 addr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
136

8628bd8af   Jan Luebbe   ipv4: Fix IP time...
137
138
  						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
139
140
141
142
143
144
145
146
147
148
149
150
  							dopt->ts_needtime = 1;
  							soffset += 8;
  						}
  					}
  				}
  			}
  			dptr[2] = soffset;
  		}
  		dptr += optlen;
  		dopt->optlen += optlen;
  	}
  	if (sopt->srr) {
f6d8bd051   Eric Dumazet   inet: add RCU pro...
151
  		unsigned char *start = sptr+sopt->srr;
3ca3c68e7   Al Viro   [IPV4]: struct ip...
152
  		__be32 faddr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
153
154
155
156
157
158
159
160
161
162
163
164
165
166
  
  		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);
  			for (soffset-=4, doffset=4; soffset > 3; soffset-=4, doffset+=4)
  				memcpy(&dptr[doffset-1], &start[soffset-1], 4);
  			/*
  			 * RFC1812 requires to fix illegal source routes.
  			 */
eddc9ec53   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
167
168
  			if (memcmp(&ip_hdr(skb)->saddr,
  				   &start[soffset + 3], 4) == 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
169
170
171
172
173
174
175
176
177
178
179
180
181
182
  				doffset -= 4;
  		}
  		if (doffset > 3) {
  			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.
   */
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
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);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
  	struct ip_options * opt = &(IPCB(skb)->opt);
  	int  l = opt->optlen;
  	int  optlen;
  
  	while (l > 0) {
  		switch (*optptr) {
  		case IPOPT_END:
  			return;
  		case IPOPT_NOOP:
  			l--;
  			optptr++;
  			continue;
  		}
  		optlen = optptr[1];
  		if (optlen<2 || optlen>l)
  		  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
232
233
234
235
236
237
  }
  
  /*
   * 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...
238
239
  int ip_options_compile(struct net *net,
  		       struct ip_options * opt, struct sk_buff * skb)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
240
241
242
243
244
245
  {
  	int l;
  	unsigned char * iph;
  	unsigned char * optptr;
  	int optlen;
  	unsigned char * pp_ptr = NULL;
22aba383c   Denis V. Lunev   [IPV4]: Always pa...
246
  	struct rtable *rt = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
247

22aba383c   Denis V. Lunev   [IPV4]: Always pa...
248
  	if (skb != NULL) {
511c3f92a   Eric Dumazet   net: skb->rtable ...
249
  		rt = skb_rtable(skb);
22aba383c   Denis V. Lunev   [IPV4]: Always pa...
250
251
  		optptr = (unsigned char *)&(ip_hdr(skb)[1]);
  	} else
10fe7d85e   Denis V. Lunev   [IPV4]: Remove un...
252
  		optptr = opt->__data;
22aba383c   Denis V. Lunev   [IPV4]: Always pa...
253
  	iph = optptr - sizeof(struct iphdr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
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
310
311
312
313
314
315
316
317
318
319
320
  
  	for (l = opt->optlen; l > 0; ) {
  		switch (*optptr) {
  		      case IPOPT_END:
  			for (optptr++, l--; l>0; optptr++, l--) {
  				if (*optptr != IPOPT_END) {
  					*optptr = IPOPT_END;
  					opt->is_changed = 1;
  				}
  			}
  			goto eol;
  		      case IPOPT_NOOP:
  			l--;
  			optptr++;
  			continue;
  		}
  		optlen = optptr[1];
  		if (optlen<2 || optlen>l) {
  			pp_ptr = optptr;
  			goto error;
  		}
  		switch (*optptr) {
  		      case IPOPT_SSRR:
  		      case IPOPT_LSRR:
  			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;
  		      case IPOPT_RR:
  			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;
  				}
c65353daf   Eric Dumazet   ip: ip_options_co...
321
  				if (rt) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
  					memcpy(&optptr[optptr[2]-1], &rt->rt_spec_dst, 4);
  					opt->is_changed = 1;
  				}
  				optptr[2] += 4;
  				opt->rr_needaddr = 1;
  			}
  			opt->rr = optptr - iph;
  			break;
  		      case IPOPT_TIMESTAMP:
  			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...
344
  				unsigned char *timeptr = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
345
346
347
348
349
350
351
  				if (optptr[2]+3 > optptr[1]) {
  					pp_ptr = optptr + 2;
  					goto error;
  				}
  				switch (optptr[3]&0xF) {
  				      case IPOPT_TS_TSONLY:
  					opt->ts = optptr - iph;
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
352
  					if (skb)
48bdf072c   Chris Metcalf   ip_options_compil...
353
  						timeptr = &optptr[optptr[2]-1];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
354
355
356
357
358
359
360
361
362
  					opt->ts_needtime = 1;
  					optptr[2] += 4;
  					break;
  				      case IPOPT_TS_TSANDADDR:
  					if (optptr[2]+7 > optptr[1]) {
  						pp_ptr = optptr + 2;
  						goto error;
  					}
  					opt->ts = optptr - iph;
c65353daf   Eric Dumazet   ip: ip_options_co...
363
  					if (rt)  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
364
  						memcpy(&optptr[optptr[2]-1], &rt->rt_spec_dst, 4);
48bdf072c   Chris Metcalf   ip_options_compil...
365
  						timeptr = &optptr[optptr[2]+3];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
366
367
368
369
370
371
372
373
374
375
376
377
  					}
  					opt->ts_needaddr = 1;
  					opt->ts_needtime = 1;
  					optptr[2] += 8;
  					break;
  				      case IPOPT_TS_PRESPEC:
  					if (optptr[2]+7 > optptr[1]) {
  						pp_ptr = optptr + 2;
  						goto error;
  					}
  					opt->ts = optptr - iph;
  					{
fd6832220   Al Viro   [IPV4]: inet_addr...
378
  						__be32 addr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
379
  						memcpy(&addr, &optptr[optptr[2]-1], 4);
0e6bd4a1c   Denis V. Lunev   [NETNS]: Add name...
380
  						if (inet_addr_type(net, addr) == RTN_UNICAST)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
381
382
  							break;
  						if (skb)
48bdf072c   Chris Metcalf   ip_options_compil...
383
  							timeptr = &optptr[optptr[2]+3];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
384
385
386
387
388
389
390
391
392
393
394
395
  					}
  					opt->ts_needtime = 1;
  					optptr[2] += 8;
  					break;
  				      default:
  					if (!skb && !capable(CAP_NET_RAW)) {
  						pp_ptr = optptr + 3;
  						goto error;
  					}
  					break;
  				}
  				if (timeptr) {
f25c3d613   YOSHIFUJI Hideaki   [IPV4]: Convert d...
396
  					struct timespec tv;
48bdf072c   Chris Metcalf   ip_options_compil...
397
  					u32  midtime;
f25c3d613   YOSHIFUJI Hideaki   [IPV4]: Convert d...
398
  					getnstimeofday(&tv);
48bdf072c   Chris Metcalf   ip_options_compil...
399
400
  					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
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
  					opt->is_changed = 1;
  				}
  			} else {
  				unsigned overflow = optptr[3]>>4;
  				if (overflow == 15) {
  					pp_ptr = optptr + 3;
  					goto error;
  				}
  				opt->ts = optptr - iph;
  				if (skb) {
  					optptr[3] = (optptr[3]&0xF)|((overflow+1)<<4);
  					opt->is_changed = 1;
  				}
  			}
  			break;
  		      case IPOPT_RA:
  			if (optlen < 4) {
  				pp_ptr = optptr + 1;
  				goto error;
  			}
  			if (optptr[2] == 0 && optptr[3] == 0)
  				opt->router_alert = optptr - iph;
  			break;
11a03f78f   Paul Moore   [NetLabel]: core ...
424
  		      case IPOPT_CIPSO:
f8687afef   Paul Moore   [NetLabel]: prote...
425
  			if ((!skb && !capable(CAP_NET_RAW)) || opt->cipso) {
11a03f78f   Paul Moore   [NetLabel]: core ...
426
427
428
429
  				pp_ptr = optptr;
  				goto error;
  			}
  			opt->cipso = optptr - iph;
15c45f7b2   Paul Moore   cipso: Add suppor...
430
  			if (cipso_v4_validate(skb, &optptr)) {
11a03f78f   Paul Moore   [NetLabel]: core ...
431
432
433
434
  				pp_ptr = optptr;
  				goto error;
  			}
  			break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
  		      case IPOPT_SEC:
  		      case IPOPT_SID:
  		      default:
  			if (!skb && !capable(CAP_NET_RAW)) {
  				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...
458
  EXPORT_SYMBOL(ip_options_compile);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
  
  /*
   *	Undo all the changes done by ip_options_compile().
   */
  
  void ip_options_undo(struct ip_options * opt)
  {
  	if (opt->srr) {
  		unsigned  char * optptr = opt->__data+opt->srr-sizeof(struct  iphdr);
  		memmove(optptr+7, optptr+3, optptr[1]-7);
  		memcpy(optptr+3, &opt->faddr, 4);
  	}
  	if (opt->rr_needaddr) {
  		unsigned  char * optptr = opt->__data+opt->rr-sizeof(struct  iphdr);
  		optptr[2] -= 4;
  		memset(&optptr[optptr[2]-1], 0, 4);
  	}
  	if (opt->ts) {
  		unsigned  char * optptr = opt->__data+opt->ts-sizeof(struct  iphdr);
  		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...
490
  static struct ip_options_rcu *ip_options_get_alloc(const int optlen)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
491
  {
f6d8bd051   Eric Dumazet   inet: add RCU pro...
492
  	return kzalloc(sizeof(struct ip_options_rcu) + ((optlen + 3) & ~3),
376407039   Mariusz Kozlowski   [IPV4] ip_options...
493
  		       GFP_KERNEL);
4c6ea29d8   Arnaldo Carvalho de Melo   [IP]: Introduce i...
494
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
495

f6d8bd051   Eric Dumazet   inet: add RCU pro...
496
497
  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...
498
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
499
  	while (optlen & 3)
f6d8bd051   Eric Dumazet   inet: add RCU pro...
500
501
502
  		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
503
504
505
  		kfree(opt);
  		return -EINVAL;
  	}
a51482bde   Jesper Juhl   [NET]: kfree cleanup
506
  	kfree(*optp);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
507
508
509
  	*optp = opt;
  	return 0;
  }
f6d8bd051   Eric Dumazet   inet: add RCU pro...
510
  int ip_options_get_from_user(struct net *net, struct ip_options_rcu **optp,
f2c4802b3   Denis V. Lunev   [NETNS]: Add name...
511
  			     unsigned char __user *data, int optlen)
4c6ea29d8   Arnaldo Carvalho de Melo   [IP]: Introduce i...
512
  {
f6d8bd051   Eric Dumazet   inet: add RCU pro...
513
  	struct ip_options_rcu *opt = ip_options_get_alloc(optlen);
4c6ea29d8   Arnaldo Carvalho de Melo   [IP]: Introduce i...
514
515
516
  
  	if (!opt)
  		return -ENOMEM;
f6d8bd051   Eric Dumazet   inet: add RCU pro...
517
  	if (optlen && copy_from_user(opt->opt.__data, data, optlen)) {
4c6ea29d8   Arnaldo Carvalho de Melo   [IP]: Introduce i...
518
519
520
  		kfree(opt);
  		return -EFAULT;
  	}
f2c4802b3   Denis V. Lunev   [NETNS]: Add name...
521
  	return ip_options_get_finish(net, optp, opt, optlen);
4c6ea29d8   Arnaldo Carvalho de Melo   [IP]: Introduce i...
522
  }
f6d8bd051   Eric Dumazet   inet: add RCU pro...
523
  int ip_options_get(struct net *net, struct ip_options_rcu **optp,
f2c4802b3   Denis V. Lunev   [NETNS]: Add name...
524
  		   unsigned char *data, int optlen)
4c6ea29d8   Arnaldo Carvalho de Melo   [IP]: Introduce i...
525
  {
f6d8bd051   Eric Dumazet   inet: add RCU pro...
526
  	struct ip_options_rcu *opt = ip_options_get_alloc(optlen);
4c6ea29d8   Arnaldo Carvalho de Melo   [IP]: Introduce i...
527
528
529
530
  
  	if (!opt)
  		return -ENOMEM;
  	if (optlen)
f6d8bd051   Eric Dumazet   inet: add RCU pro...
531
  		memcpy(opt->opt.__data, data, optlen);
f2c4802b3   Denis V. Lunev   [NETNS]: Add name...
532
  	return ip_options_get_finish(net, optp, opt, optlen);
4c6ea29d8   Arnaldo Carvalho de Melo   [IP]: Introduce i...
533
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
534
535
536
537
  void ip_forward_options(struct sk_buff *skb)
  {
  	struct   ip_options * opt	= &(IPCB(skb)->opt);
  	unsigned char * optptr;
511c3f92a   Eric Dumazet   net: skb->rtable ...
538
  	struct rtable *rt = skb_rtable(skb);
d56f90a7c   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
539
  	unsigned char *raw = skb_network_header(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
540
541
542
  
  	if (opt->rr_needaddr) {
  		optptr = (unsigned char *)raw + opt->rr;
8e36360ae   David S. Miller   ipv4: Remove rout...
543
  		ip_rt_get_source(&optptr[optptr[2]-5], skb, rt);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
544
545
546
547
548
549
550
551
552
553
554
555
556
  		opt->is_changed = 1;
  	}
  	if (opt->srr_is_hit) {
  		int srrptr, srrspace;
  
  		optptr = raw + opt->srr;
  
  		for ( srrptr=optptr[2], srrspace = optptr[1];
  		     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;
8e36360ae   David S. Miller   ipv4: Remove rout...
562
  			ip_rt_get_source(&optptr[srrptr-1], skb, rt);
ac8a48106   Li Wei   ipv4: Save nextho...
563
  			ip_hdr(skb)->daddr = opt->nexthop;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
564
565
566
567
568
569
  			optptr[2] = srrptr+4;
  		} else if (net_ratelimit())
  			printk(KERN_CRIT "ip_forward(): Argh! Destination lost!
  ");
  		if (opt->ts_needaddr) {
  			optptr = raw + opt->ts;
8e36360ae   David S. Miller   ipv4: Remove rout...
570
  			ip_rt_get_source(&optptr[optptr[2]-9], skb, rt);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
571
572
573
574
575
  			opt->is_changed = 1;
  		}
  	}
  	if (opt->is_changed) {
  		opt->is_changed = 0;
eddc9ec53   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
576
  		ip_send_check(ip_hdr(skb));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
577
578
579
580
581
582
583
  	}
  }
  
  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_...
584
  	__be32 nexthop;
eddc9ec53   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
585
  	struct iphdr *iph = ip_hdr(skb);
d56f90a7c   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
586
  	unsigned char *optptr = skb_network_header(skb) + opt->srr;
511c3f92a   Eric Dumazet   net: skb->rtable ...
587
  	struct rtable *rt = skb_rtable(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
588
  	struct rtable *rt2;
7fee226ad   Eric Dumazet   net: add a noref ...
589
  	unsigned long orefdst;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
590
  	int err;
10949550b   David S. Miller   ipv4: Kill spurio...
591
  	if (!rt)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
  		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;
  
  	for (srrptr=optptr[2], srrspace = optptr[1]; srrptr <= srrspace; srrptr += 4) {
  		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);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
613
  		err = ip_route_input(skb, nexthop, iph->saddr, iph->tos, skb->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);