1 /*
2 * INET An implementation of the TCP/IP protocol suite for the LINUX
3 * operating system. INET is implemented using the BSD Socket
4 * interface as the means of communication with the user level.
5 *
6 * The options processing module for ip.c
7 *
8 * Authors: A.N.Kuznetsov
9 *
10 */
11
12 #include <linux/config.h>
13 #include <linux/types.h>
14 #include <linux/skbuff.h>
15 #include <linux/ip.h>
16 #include <linux/icmp.h>
17 #include <linux/netdevice.h>
18 #include <net/sock.h>
19 #include <net/ip.h>
20 #include <net/icmp.h>
21
22 /*
23 * Write options to IP header, record destination address to
24 * source route option, address of outgoing interface
25 * (we should already know it, so that this function is allowed be
26 * called only after routing decision) and timestamp,
27 * if we originate this datagram.
28 */
29
30 void ip_options_build(struct sk_buff * skb, struct options * opt,
/* ![[previous]](../icons/n_left.png)
![[next]](../icons/right.png)
![[first]](../icons/n_first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
31 __u32 daddr, __u32 saddr,
32 int is_frag)
33 {
34 unsigned char * iph = (unsigned char*)skb->ip_hdr;
35
36 memcpy(skb->proto_priv, opt, sizeof(struct options));
37 memcpy(iph+sizeof(struct iphdr), opt->__data, opt->optlen);
38 opt = (struct options*)skb->proto_priv;
39 opt->is_data = 0;
40
41 if (opt->srr)
42 memcpy(iph+opt->srr+iph[opt->srr+1]-4, &daddr, 4);
43
44 if (!is_frag)
45 {
46 if (opt->rr_needaddr)
47 memcpy(iph+opt->rr+iph[opt->rr+2]-5, &saddr, 4);
48 if (opt->ts_needaddr)
49 memcpy(iph+opt->ts+iph[opt->ts+2]-9, &saddr, 4);
50 if (opt->ts_needtime)
51 {
52 struct timeval tv;
53 __u32 midtime;
54 do_gettimeofday(&tv);
55 midtime = htonl((tv.tv_sec % 86400) * 1000 + tv.tv_usec / 1000);
56 memcpy(iph+opt->ts+iph[opt->ts+2]-5, &midtime, 4);
57 }
58 return;
59 }
60 if (opt->rr)
61 {
62 memset(iph+opt->rr, IPOPT_NOP, iph[opt->rr+1]);
63 opt->rr = 0;
64 opt->rr_needaddr = 0;
65 }
66 if (opt->ts)
67 {
68 memset(iph+opt->ts, IPOPT_NOP, iph[opt->ts+1]);
69 opt->ts = 0;
70 opt->ts_needaddr = opt->ts_needtime = 0;
71 }
72 }
73
74 int ip_options_echo(struct options * dopt, struct options * sopt,
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
75 __u32 daddr, __u32 saddr,
76 struct sk_buff * skb)
77 {
78 unsigned char *sptr, *dptr;
79 int soffset, doffset;
80 int optlen;
81
82 memset(dopt, 0, sizeof(struct options));
83
84 dopt->is_data = 1;
85
86 if (!sopt)
87 sopt = (struct options*)skb->proto_priv;
88
89 if (sopt->optlen == 0)
90 {
91 dopt->optlen = 0;
92 return 0;
93 }
94
95 sptr = (sopt->is_data ? sopt->__data - sizeof(struct iphdr) :
96 (unsigned char *)skb->ip_hdr);
97 dptr = dopt->__data;
98
99 if (sopt->rr)
100 {
101 optlen = sptr[sopt->rr+1];
102 soffset = sptr[sopt->rr+2];
103 dopt->rr = dopt->optlen + sizeof(struct iphdr);
104 memcpy(dptr, sptr+sopt->rr, optlen);
105 if (sopt->rr_needaddr && soffset <= optlen) {
106 if (soffset + 3 > optlen)
107 return -EINVAL;
108 dptr[2] = soffset + 4;
109 dopt->rr_needaddr = 1;
110 }
111 dptr += optlen;
112 dopt->optlen += optlen;
113 }
114 if (sopt->ts)
115 {
116 optlen = sptr[sopt->ts+1];
117 soffset = sptr[sopt->ts+2];
118 dopt->ts = dopt->optlen + sizeof(struct iphdr);
119 memcpy(dptr, sptr+sopt->ts, optlen);
120 if (soffset <= optlen)
121 {
122 if (dopt->ts_needaddr)
123 {
124 if (soffset + 3 > optlen)
125 return -EINVAL;
126 dopt->ts_needaddr = 1;
127 soffset += 4;
128 }
129 if (dopt->ts_needtime)
130 {
131 if (soffset + 3 > optlen)
132 return -EINVAL;
133 dopt->ts_needtime = 1;
134 soffset += 4;
135 }
136 if (((struct timestamp*)(dptr+1))->flags == IPOPT_TS_PRESPEC)
137 {
138 __u32 addr;
139 memcpy(&addr, sptr+soffset-9, 4);
140 if (ip_chk_addr(addr) == 0)
141 {
142 dopt->ts_needtime = 0;
143 dopt->ts_needaddr = 0;
144 soffset -= 8;
145 }
146 }
147 dptr[2] = soffset;
148 }
149 dptr += optlen;
150 dopt->optlen += optlen;
151 }
152 if (sopt->srr)
153 {
154 unsigned char * start = sptr+sopt->srr;
155 __u32 faddr;
156
157 optlen = start[1];
158 soffset = start[2];
159 doffset = 0;
160 if (soffset > optlen)
161 soffset = optlen + 1;
162 soffset -= 4;
163 if (soffset > 3)
164 {
165 memcpy(&faddr, &start[soffset-1], 4);
166 for (soffset-=4, doffset=4; soffset > 3; soffset-=4, doffset+=4)
167 memcpy(&dptr[doffset-1], &start[soffset-1], 4);
168 /*
169 * RFC1812 requires to fix illegal source routes.
170 */
171 if (memcmp(&saddr, &start[soffset+3], 4) == 0)
172 doffset -= 4;
173 }
174 if (doffset > 3)
175 {
176 memcpy(&start[doffset-1], &daddr, 4);
177 dopt->faddr = faddr;
178 dptr[0] = start[0];
179 dptr[1] = doffset+3;
180 dptr[2] = 4;
181 dptr += doffset+3;
182 dopt->srr = dopt->optlen + sizeof(struct iphdr);
183 dopt->optlen += doffset+3;
184 dopt->is_strictroute = sopt->is_strictroute;
185 }
186 }
187 while (dopt->optlen & 3)
188 {
189 *dptr++ = IPOPT_END;
190 dopt->optlen++;
191 }
192 return 0;
193 }
194
195 void ip_options_fragment(struct sk_buff * skb)
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
196 {
197 unsigned char * optptr = (unsigned char*)skb->ip_hdr;
198 struct options * opt = (struct options*)skb->proto_priv;
199 int l = opt->optlen;
200 int optlen;
201
202 while (l > 0)
203 {
204 switch (*optptr)
205 {
206 case IPOPT_END:
207 return;
208 case IPOPT_NOOP:
209 l--;
210 optptr++;
211 continue;
212 }
213 optlen = optptr[1];
214 if (l<2 || optlen>l)
215 return;
216 if (!(*optptr & 0x80))
217 memset(optptr, IPOPT_NOOP, optlen);
218 l -= optlen;
219 optptr += optlen;
220 }
221 opt->ts = 0;
222 opt->rr = 0;
223 opt->rr_needaddr = 0;
224 opt->ts_needaddr = 0;
225 opt->ts_needtime = 0;
226 return;
227 }
228
229 /*
230 * Verify options and fill pointers in struct optinos.
231 * Caller should clear *opt, and set opt->data.
232 * If opt == NULL, then skb->data should point to IP header.
233 */
234
235 int ip_options_compile(struct options * opt, struct sk_buff * skb)
/* ![[previous]](../icons/left.png)
![[next]](../icons/n_right.png)
![[first]](../icons/first.png)
![[last]](../icons/n_last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
236 {
237 int l;
238 unsigned char * iph;
239 unsigned char * optptr;
240 int optlen;
241 unsigned char * pp_ptr = NULL;
242
243 if (!opt)
244 {
245 opt = (struct options*)skb->proto_priv;
246 memset(opt, 0, sizeof(struct options));
247 iph = (unsigned char*)skb->ip_hdr;
248 opt->optlen = ((struct iphdr *)iph)->ihl*4 - sizeof(struct iphdr);
249 optptr = iph + sizeof(struct iphdr);
250 opt->is_data = 0;
251 }
252 else
253 {
254 optptr = opt->is_data ? opt->__data : (unsigned char*)&skb->ip_hdr[1];
255 iph = optptr - sizeof(struct iphdr);
256 }
257
258 for (l = opt->optlen; l > 0; )
259 {
260 switch (*optptr)
261 {
262 case IPOPT_END:
263 for (optptr++, l--; l>0; l--)
264 {
265 if (*optptr != IPOPT_END)
266 {
267 *optptr = IPOPT_END;
268 opt->is_changed = 1;
269 }
270 }
271 goto eol;
272 case IPOPT_NOOP:
273 l--;
274 optptr++;
275 continue;
276 }
277 optlen = optptr[1];
278 if (l<2 || optlen>l)
279 {
280 pp_ptr = optptr;
281 break;
282 }
283 switch (*optptr)
284 {
285 case IPOPT_SSRR:
286 case IPOPT_LSRR:
287 if (optlen < 3)
288 {
289 pp_ptr = optptr + 1;
290 break;
291 }
292 if (optptr[2] < 4)
293 {
294 pp_ptr = optptr + 2;
295 break;
296 }
297 /* NB: cf RFC-1812 5.2.4.1 */
298 if (opt->srr)
299 {
300 pp_ptr = optptr;
301 break;
302 }
303 if (!skb)
304 {
305 if (optptr[2] != 4 || optlen < 7 || ((optlen-3) & 3))
306 {
307 pp_ptr = optptr + 1;
308 break;
309 }
310 memcpy(&opt->faddr, &optptr[3], 4);
311 if (optlen > 7)
312 memmove(&optptr[3], &optptr[7], optlen-7);
313 }
314 opt->is_strictroute = (optptr[0] == IPOPT_SSRR);
315 opt->srr = optptr - iph;
316 break;
317 case IPOPT_RR:
318 if (opt->rr)
319 {
320 pp_ptr = optptr;
321 break;
322 }
323 if (optlen < 3)
324 {
325 pp_ptr = optptr + 1;
326 break;
327 }
328 if (optptr[2] < 4)
329 {
330 pp_ptr = optptr + 2;
331 break;
332 }
333 if (optptr[2] <= optlen)
334 {
335 if (optptr[2]+3 > optlen)
336 {
337 pp_ptr = optptr + 2;
338 break;
339 }
340 if (skb)
341 {
342 memcpy(&optptr[optptr[2]-1], &skb->dev->pa_addr, 4);
343 opt->is_changed = 1;
344 }
345 optptr[2] += 4;
346 opt->rr_needaddr = 1;
347 }
348 opt->rr = optptr - iph;
349 break;
350 case IPOPT_TIMESTAMP:
351 if (opt->ts)
352 {
353 pp_ptr = optptr;
354 break;
355 }
356 if (optlen < 4)
357 {
358 pp_ptr = optptr + 1;
359 break;
360 }
361 if (optptr[2] < 5)
362 {
363 pp_ptr = optptr + 2;
364 break;
365 }
366 if (optptr[2] <= optlen)
367 {
368 struct timestamp * ts = (struct timestamp*)(optptr+1);
369 __u32 * timeptr = NULL;
370 if (ts->ptr+3 > ts->len)
371 {
372 pp_ptr = optptr + 2;
373 break;
374 }
375 switch (ts->flags)
376 {
377 case IPOPT_TS_TSONLY:
378 opt->ts = optptr - iph;
379 if (skb)
380 {
381 timeptr = (__u32*)&optptr[ts->ptr-1];
382 opt->is_changed = 1;
383 }
384 ts->ptr += 4;
385 break;
386 case IPOPT_TS_TSANDADDR:
387 if (ts->ptr+7 > ts->len)
388 {
389 pp_ptr = optptr + 2;
390 break;
391 }
392 opt->ts = optptr - iph;
393 if (skb)
394 {
395 memcpy(&optptr[ts->ptr-1], &skb->dev->pa_addr, 4);
396 timeptr = (__u32*)&optptr[ts->ptr+3];
397 }
398 opt->ts_needaddr = 1;
399 opt->ts_needtime = 1;
400 ts->ptr += 8;
401 break;
402 case IPOPT_TS_PRESPEC:
403 if (ts->ptr+7 > ts->len)
404 {
405 pp_ptr = optptr + 2;
406 break;
407 }
408 opt->ts = optptr - iph;
409 {
410 __u32 addr;
411 memcpy(&addr, &optptr[ts->ptr-1], 4);
412 if (ip_chk_addr(addr) == 0)
413 break;
414 if (skb)
415 timeptr = (__u32*)&optptr[ts->ptr+3];
416 }
417 opt->ts_needaddr = 1;
418 opt->ts_needtime = 1;
419 ts->ptr += 8;
420 break;
421 default:
422 pp_ptr = optptr + 3;
423 break;
424 }
425 if (timeptr)
426 {
427 struct timeval tv;
428 __u32 midtime;
429 do_gettimeofday(&tv);
430 midtime = htonl((tv.tv_sec % 86400) * 1000 + tv.tv_usec / 1000);
431 memcpy(timeptr, &midtime, sizeof(__u32));
432 opt->is_changed = 1;
433 }
434 }
435 else
436 {
437 struct timestamp * ts = (struct timestamp*)(optptr+1);
438 if (ts->overflow == 15)
439 {
440 pp_ptr = optptr + 3;
441 break;
442 }
443 opt->ts = optptr - iph;
444 if (skb)
445 {
446 ts->overflow++;
447 opt->is_changed = 1;
448 }
449 }
450 break;
451 case IPOPT_SEC:
452 case IPOPT_SID:
453 default:
454 if (!skb)
455 {
456 pp_ptr = optptr;
457 break;
458 }
459 break;
460 }
461 l -= optlen;
462 optptr += optlen;
463 }
464
465 eol:
466 if (!pp_ptr)
467 return 0;
468
469 if (skb)
470 {
471 icmp_send(skb, ICMP_PARAMETERPROB, 0, pp_ptr-iph, skb->dev);
472 kfree_skb(skb, FREE_READ);
473 }
474 return -EINVAL;
475 }
476