Line data Source code
1 : /* $OpenBSD: ip_icmp.c,v 1.177 2018/09/06 03:42:21 miko Exp $ */
2 : /* $NetBSD: ip_icmp.c,v 1.19 1996/02/13 23:42:22 christos Exp $ */
3 :
4 : /*
5 : * Copyright (c) 1982, 1986, 1988, 1993
6 : * The Regents of the University of California. All rights reserved.
7 : *
8 : * Redistribution and use in source and binary forms, with or without
9 : * modification, are permitted provided that the following conditions
10 : * are met:
11 : * 1. Redistributions of source code must retain the above copyright
12 : * notice, this list of conditions and the following disclaimer.
13 : * 2. Redistributions in binary form must reproduce the above copyright
14 : * notice, this list of conditions and the following disclaimer in the
15 : * documentation and/or other materials provided with the distribution.
16 : * 3. Neither the name of the University nor the names of its contributors
17 : * may be used to endorse or promote products derived from this software
18 : * without specific prior written permission.
19 : *
20 : * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 : * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 : * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 : * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 : * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 : * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 : * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 : * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 : * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 : * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 : * SUCH DAMAGE.
31 : *
32 : * @(#)COPYRIGHT 1.1 (NRL) 17 January 1995
33 : *
34 : * NRL grants permission for redistribution and use in source and binary
35 : * forms, with or without modification, of the software and documentation
36 : * created at NRL provided that the following conditions are met:
37 : *
38 : * 1. Redistributions of source code must retain the above copyright
39 : * notice, this list of conditions and the following disclaimer.
40 : * 2. Redistributions in binary form must reproduce the above copyright
41 : * notice, this list of conditions and the following disclaimer in the
42 : * documentation and/or other materials provided with the distribution.
43 : * 3. All advertising materials mentioning features or use of this software
44 : * must display the following acknowledgements:
45 : * This product includes software developed by the University of
46 : * California, Berkeley and its contributors.
47 : * This product includes software developed at the Information
48 : * Technology Division, US Naval Research Laboratory.
49 : * 4. Neither the name of the NRL nor the names of its contributors
50 : * may be used to endorse or promote products derived from this software
51 : * without specific prior written permission.
52 : *
53 : * THE SOFTWARE PROVIDED BY NRL IS PROVIDED BY NRL AND CONTRIBUTORS ``AS
54 : * IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
55 : * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
56 : * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL NRL OR
57 : * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
58 : * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
59 : * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
60 : * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
61 : * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
62 : * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
63 : * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
64 : *
65 : * The views and conclusions contained in the software and documentation
66 : * are those of the authors and should not be interpreted as representing
67 : * official policies, either expressed or implied, of the US Naval
68 : * Research Laboratory (NRL).
69 : */
70 :
71 : #include "carp.h"
72 : #include "pf.h"
73 :
74 : #include <sys/param.h>
75 : #include <sys/systm.h>
76 : #include <sys/mbuf.h>
77 : #include <sys/protosw.h>
78 : #include <sys/socket.h>
79 : #include <sys/sysctl.h>
80 :
81 : #include <net/if.h>
82 : #include <net/if_var.h>
83 : #include <net/route.h>
84 :
85 : #include <netinet/in.h>
86 : #include <netinet/in_systm.h>
87 : #include <netinet/in_var.h>
88 : #include <netinet/ip.h>
89 : #include <netinet/ip_icmp.h>
90 : #include <netinet/ip_var.h>
91 : #include <netinet/icmp_var.h>
92 :
93 : #if NCARP > 0
94 : #include <net/if_types.h>
95 : #include <netinet/ip_carp.h>
96 : #endif
97 :
98 : #if NPF > 0
99 : #include <net/pfvar.h>
100 : #endif
101 :
102 : /*
103 : * ICMP routines: error generation, receive packet processing, and
104 : * routines to turnaround packets back to the originator, and
105 : * host table maintenance routines.
106 : */
107 :
108 : #ifdef ICMPPRINTFS
109 : int icmpprintfs = 0; /* Settable from ddb */
110 : #endif
111 :
112 : /* values controllable via sysctl */
113 : int icmpmaskrepl = 0;
114 : int icmpbmcastecho = 0;
115 : int icmptstamprepl = 1;
116 : int icmperrppslim = 100;
117 : int icmp_rediraccept = 0;
118 : int icmp_redirtimeout = 10 * 60;
119 :
120 : static int icmperrpps_count = 0;
121 : static struct timeval icmperrppslim_last;
122 :
123 : static struct rttimer_queue *icmp_redirect_timeout_q = NULL;
124 : struct cpumem *icmpcounters;
125 :
126 : int *icmpctl_vars[ICMPCTL_MAXID] = ICMPCTL_VARS;
127 :
128 : void icmp_mtudisc_timeout(struct rtentry *, struct rttimer *);
129 : int icmp_ratelimit(const struct in_addr *, const int, const int);
130 : void icmp_redirect_timeout(struct rtentry *, struct rttimer *);
131 : int icmp_input_if(struct ifnet *, struct mbuf **, int *, int, int);
132 : int icmp_sysctl_icmpstat(void *, size_t *, void *);
133 :
134 : void
135 0 : icmp_init(void)
136 : {
137 0 : icmpcounters = counters_alloc(icps_ncounters);
138 : /*
139 : * This is only useful if the user initializes redirtimeout to
140 : * something other than zero.
141 : */
142 0 : if (icmp_redirtimeout != 0) {
143 0 : icmp_redirect_timeout_q =
144 0 : rt_timer_queue_create(icmp_redirtimeout);
145 0 : }
146 0 : }
147 :
148 : struct mbuf *
149 0 : icmp_do_error(struct mbuf *n, int type, int code, u_int32_t dest, int destmtu)
150 : {
151 0 : struct ip *oip = mtod(n, struct ip *), *nip;
152 0 : unsigned oiplen = oip->ip_hl << 2;
153 : struct icmp *icp;
154 : struct mbuf *m;
155 : unsigned icmplen, mblen;
156 :
157 : #ifdef ICMPPRINTFS
158 : if (icmpprintfs)
159 : printf("icmp_error(%x, %d, %d)\n", oip, type, code);
160 : #endif
161 0 : if (type != ICMP_REDIRECT)
162 0 : icmpstat_inc(icps_error);
163 : /*
164 : * Don't send error if not the first fragment of message.
165 : * Don't error if the old packet protocol was ICMP
166 : * error message, only known informational types.
167 : */
168 0 : if (oip->ip_off & htons(IP_OFFMASK))
169 : goto freeit;
170 0 : if (oip->ip_p == IPPROTO_ICMP && type != ICMP_REDIRECT &&
171 0 : n->m_len >= oiplen + ICMP_MINLEN &&
172 0 : !ICMP_INFOTYPE(((struct icmp *)
173 : ((caddr_t)oip + oiplen))->icmp_type)) {
174 0 : icmpstat_inc(icps_oldicmp);
175 0 : goto freeit;
176 : }
177 : /* Don't send error in response to a multicast or broadcast packet */
178 0 : if (n->m_flags & (M_BCAST|M_MCAST))
179 : goto freeit;
180 :
181 : /*
182 : * First, do a rate limitation check.
183 : */
184 0 : if (icmp_ratelimit(&oip->ip_src, type, code)) {
185 0 : icmpstat_inc(icps_toofreq);
186 0 : goto freeit;
187 : }
188 :
189 : /*
190 : * Now, formulate icmp message
191 : */
192 0 : icmplen = oiplen + min(8, ntohs(oip->ip_len));
193 : /*
194 : * Defend against mbuf chains shorter than oip->ip_len:
195 : */
196 : mblen = 0;
197 0 : for (m = n; m && (mblen < icmplen); m = m->m_next)
198 0 : mblen += m->m_len;
199 0 : icmplen = min(mblen, icmplen);
200 :
201 : /*
202 : * As we are not required to return everything we have,
203 : * we return whatever we can return at ease.
204 : *
205 : * Note that ICMP datagrams longer than 576 octets are out of spec
206 : * according to RFC1812;
207 : */
208 :
209 : KASSERT(ICMP_MINLEN <= MCLBYTES);
210 :
211 0 : if (icmplen + ICMP_MINLEN > MCLBYTES)
212 : icmplen = MCLBYTES - ICMP_MINLEN - sizeof (struct ip);
213 :
214 0 : m = m_gethdr(M_DONTWAIT, MT_HEADER);
215 0 : if (m && (sizeof (struct ip) + icmplen + ICMP_MINLEN > MHLEN)) {
216 0 : MCLGET(m, M_DONTWAIT);
217 0 : if ((m->m_flags & M_EXT) == 0) {
218 0 : m_freem(m);
219 : m = NULL;
220 0 : }
221 : }
222 0 : if (m == NULL)
223 : goto freeit;
224 : /* keep in same rtable */
225 0 : m->m_pkthdr.ph_rtableid = n->m_pkthdr.ph_rtableid;
226 0 : m->m_len = icmplen + ICMP_MINLEN;
227 0 : if ((m->m_flags & M_EXT) == 0)
228 0 : MH_ALIGN(m, m->m_len);
229 0 : icp = mtod(m, struct icmp *);
230 0 : if ((u_int)type > ICMP_MAXTYPE)
231 0 : panic("icmp_error");
232 0 : icmpstat_inc(icps_outhist + type);
233 0 : icp->icmp_type = type;
234 0 : if (type == ICMP_REDIRECT)
235 0 : icp->icmp_gwaddr.s_addr = dest;
236 : else {
237 0 : icp->icmp_void = 0;
238 : /*
239 : * The following assignments assume an overlay with the
240 : * zeroed icmp_void field.
241 : */
242 0 : if (type == ICMP_PARAMPROB) {
243 0 : icp->icmp_pptr = code;
244 : code = 0;
245 0 : } else if (type == ICMP_UNREACH &&
246 0 : code == ICMP_UNREACH_NEEDFRAG && destmtu)
247 0 : icp->icmp_nextmtu = htons(destmtu);
248 : }
249 :
250 0 : icp->icmp_code = code;
251 0 : m_copydata(n, 0, icmplen, (caddr_t)&icp->icmp_ip);
252 :
253 : /*
254 : * Now, copy old ip header (without options)
255 : * in front of icmp message.
256 : */
257 0 : if ((m->m_flags & M_EXT) == 0 &&
258 0 : m->m_data - sizeof(struct ip) < m->m_pktdat)
259 0 : panic("icmp len");
260 0 : m->m_data -= sizeof(struct ip);
261 0 : m->m_len += sizeof(struct ip);
262 0 : m->m_pkthdr.len = m->m_len;
263 0 : m->m_pkthdr.ph_ifidx = n->m_pkthdr.ph_ifidx;
264 0 : nip = mtod(m, struct ip *);
265 : /* ip_v set in ip_output */
266 0 : nip->ip_hl = sizeof(struct ip) >> 2;
267 0 : nip->ip_tos = 0;
268 0 : nip->ip_len = htons(m->m_len);
269 : /* ip_id set in ip_output */
270 0 : nip->ip_off = 0;
271 : /* ip_ttl set in icmp_reflect */
272 0 : nip->ip_p = IPPROTO_ICMP;
273 0 : nip->ip_src = oip->ip_src;
274 0 : nip->ip_dst = oip->ip_dst;
275 :
276 : /* move PF_GENERATED to new packet, if existent XXX preserve more? */
277 0 : if (n->m_pkthdr.pf.flags & PF_TAG_GENERATED)
278 0 : m->m_pkthdr.pf.flags |= PF_TAG_GENERATED;
279 :
280 0 : m_freem(n);
281 0 : return (m);
282 :
283 : freeit:
284 0 : m_freem(n);
285 0 : return (NULL);
286 0 : }
287 :
288 : /*
289 : * Generate an error packet of type error
290 : * in response to bad packet ip.
291 : *
292 : * The ip packet inside has ip_off and ip_len in host byte order.
293 : */
294 : void
295 0 : icmp_error(struct mbuf *n, int type, int code, u_int32_t dest, int destmtu)
296 : {
297 : struct mbuf *m;
298 :
299 0 : m = icmp_do_error(n, type, code, dest, destmtu);
300 0 : if (m != NULL)
301 0 : if (!icmp_reflect(m, NULL, NULL))
302 0 : icmp_send(m, NULL);
303 0 : }
304 :
305 : /*
306 : * Process a received ICMP message.
307 : */
308 : int
309 0 : icmp_input(struct mbuf **mp, int *offp, int proto, int af)
310 : {
311 : struct ifnet *ifp;
312 :
313 0 : ifp = if_get((*mp)->m_pkthdr.ph_ifidx);
314 0 : if (ifp == NULL) {
315 0 : m_freemp(mp);
316 0 : return IPPROTO_DONE;
317 : }
318 :
319 0 : proto = icmp_input_if(ifp, mp, offp, proto, af);
320 0 : if_put(ifp);
321 0 : return proto;
322 0 : }
323 :
324 : int
325 0 : icmp_input_if(struct ifnet *ifp, struct mbuf **mp, int *offp, int proto, int af)
326 : {
327 0 : struct mbuf *m = *mp;
328 0 : int hlen = *offp;
329 : struct icmp *icp;
330 0 : struct ip *ip = mtod(m, struct ip *);
331 0 : struct sockaddr_in sin;
332 : int icmplen, i, code;
333 : struct in_ifaddr *ia;
334 : void (*ctlfunc)(int, struct sockaddr *, u_int, void *);
335 0 : struct mbuf *opts;
336 :
337 : /*
338 : * Locate icmp structure in mbuf, and check
339 : * that not corrupted and of at least minimum length.
340 : */
341 0 : icmplen = ntohs(ip->ip_len) - hlen;
342 : #ifdef ICMPPRINTFS
343 : if (icmpprintfs) {
344 : char dst[INET_ADDRSTRLEN], src[INET_ADDRSTRLEN];
345 :
346 : inet_ntop(AF_INET, &ip->ip_dst, dst, sizeof(dst));
347 : inet_ntop(AF_INET, &ip->ip_src, src, sizeof(src));
348 :
349 : printf("icmp_input from %s to %s, len %d\n", src, dst, icmplen);
350 : }
351 : #endif
352 0 : if (icmplen < ICMP_MINLEN) {
353 0 : icmpstat_inc(icps_tooshort);
354 0 : goto freeit;
355 : }
356 0 : i = hlen + min(icmplen, ICMP_ADVLENMIN);
357 0 : if (m->m_len < i && (m = *mp = m_pullup(m, i)) == NULL) {
358 0 : icmpstat_inc(icps_tooshort);
359 0 : return IPPROTO_DONE;
360 : }
361 0 : ip = mtod(m, struct ip *);
362 0 : if (in4_cksum(m, 0, hlen, icmplen)) {
363 0 : icmpstat_inc(icps_checksum);
364 0 : goto freeit;
365 : }
366 :
367 0 : icp = (struct icmp *)(mtod(m, caddr_t) + hlen);
368 : #ifdef ICMPPRINTFS
369 : /*
370 : * Message type specific processing.
371 : */
372 : if (icmpprintfs)
373 : printf("icmp_input, type %d code %d\n", icp->icmp_type,
374 : icp->icmp_code);
375 : #endif
376 0 : if (icp->icmp_type > ICMP_MAXTYPE)
377 : goto raw;
378 : #if NPF > 0
379 0 : if (m->m_pkthdr.pf.flags & PF_TAG_DIVERTED) {
380 0 : switch (icp->icmp_type) {
381 : /*
382 : * As pf_icmp_mapping() considers redirects belonging to a
383 : * diverted connection, we must include it here.
384 : */
385 : case ICMP_REDIRECT:
386 : /* FALLTHROUGH */
387 : /*
388 : * These ICMP types map to other connections. They must be
389 : * delivered to pr_ctlinput() also for diverted connections.
390 : */
391 : case ICMP_UNREACH:
392 : case ICMP_TIMXCEED:
393 : case ICMP_PARAMPROB:
394 : case ICMP_SOURCEQUENCH:
395 : /*
396 : * Do not use the divert-to property of the TCP or UDP
397 : * rule when doing the PCB lookup for the raw socket.
398 : */
399 0 : m->m_pkthdr.pf.flags &=~ PF_TAG_DIVERTED;
400 : break;
401 : default:
402 : goto raw;
403 : }
404 0 : }
405 : #endif /* NPF */
406 0 : icmpstat_inc(icps_inhist + icp->icmp_type);
407 0 : code = icp->icmp_code;
408 0 : switch (icp->icmp_type) {
409 :
410 : case ICMP_UNREACH:
411 0 : switch (code) {
412 : case ICMP_UNREACH_NET:
413 : case ICMP_UNREACH_HOST:
414 : case ICMP_UNREACH_PROTOCOL:
415 : case ICMP_UNREACH_PORT:
416 : case ICMP_UNREACH_SRCFAIL:
417 0 : code += PRC_UNREACH_NET;
418 0 : break;
419 :
420 : case ICMP_UNREACH_NEEDFRAG:
421 : code = PRC_MSGSIZE;
422 0 : break;
423 :
424 : case ICMP_UNREACH_NET_UNKNOWN:
425 : case ICMP_UNREACH_NET_PROHIB:
426 : case ICMP_UNREACH_TOSNET:
427 : code = PRC_UNREACH_NET;
428 0 : break;
429 :
430 : case ICMP_UNREACH_HOST_UNKNOWN:
431 : case ICMP_UNREACH_ISOLATED:
432 : case ICMP_UNREACH_HOST_PROHIB:
433 : case ICMP_UNREACH_TOSHOST:
434 : case ICMP_UNREACH_FILTER_PROHIB:
435 : case ICMP_UNREACH_HOST_PRECEDENCE:
436 : case ICMP_UNREACH_PRECEDENCE_CUTOFF:
437 : code = PRC_UNREACH_HOST;
438 0 : break;
439 :
440 : default:
441 : goto badcode;
442 : }
443 : goto deliver;
444 :
445 : case ICMP_TIMXCEED:
446 0 : if (code > 1)
447 : goto badcode;
448 0 : code += PRC_TIMXCEED_INTRANS;
449 0 : goto deliver;
450 :
451 : case ICMP_PARAMPROB:
452 0 : if (code > 1)
453 : goto badcode;
454 : code = PRC_PARAMPROB;
455 0 : goto deliver;
456 :
457 : case ICMP_SOURCEQUENCH:
458 0 : if (code)
459 : goto badcode;
460 0 : code = PRC_QUENCH;
461 : deliver:
462 : /*
463 : * Problem with datagram; advise higher level routines.
464 : */
465 0 : if (icmplen < ICMP_ADVLENMIN || icmplen < ICMP_ADVLEN(icp) ||
466 0 : icp->icmp_ip.ip_hl < (sizeof(struct ip) >> 2)) {
467 0 : icmpstat_inc(icps_badlen);
468 0 : goto freeit;
469 : }
470 0 : if (IN_MULTICAST(icp->icmp_ip.ip_dst.s_addr))
471 : goto badcode;
472 : #ifdef INET6
473 : /* Get more contiguous data for a v6 in v4 ICMP message. */
474 0 : if (icp->icmp_ip.ip_p == IPPROTO_IPV6) {
475 0 : if (icmplen < ICMP_V6ADVLENMIN ||
476 0 : icmplen < ICMP_V6ADVLEN(icp)) {
477 0 : icmpstat_inc(icps_badlen);
478 0 : goto freeit;
479 : } else {
480 0 : if ((m = *mp = m_pullup(m, (ip->ip_hl << 2) +
481 0 : ICMP_V6ADVLEN(icp))) == NULL) {
482 0 : icmpstat_inc(icps_tooshort);
483 0 : return IPPROTO_DONE;
484 : }
485 0 : ip = mtod(m, struct ip *);
486 0 : icp = (struct icmp *)
487 0 : (m->m_data + (ip->ip_hl << 2));
488 : }
489 0 : }
490 : #endif /* INET6 */
491 : #ifdef ICMPPRINTFS
492 : if (icmpprintfs)
493 : printf("deliver to protocol %d\n", icp->icmp_ip.ip_p);
494 : #endif
495 0 : memset(&sin, 0, sizeof(sin));
496 0 : sin.sin_family = AF_INET;
497 0 : sin.sin_len = sizeof(struct sockaddr_in);
498 0 : sin.sin_addr = icp->icmp_ip.ip_dst;
499 : #if NCARP > 0
500 0 : if (carp_lsdrop(ifp, m, AF_INET, &sin.sin_addr.s_addr,
501 0 : &ip->ip_dst.s_addr, 1))
502 : goto freeit;
503 : #endif
504 : /*
505 : * XXX if the packet contains [IPv4 AH TCP], we can't make a
506 : * notification to TCP layer.
507 : */
508 0 : ctlfunc = inetsw[ip_protox[icp->icmp_ip.ip_p]].pr_ctlinput;
509 0 : if (ctlfunc)
510 0 : (*ctlfunc)(code, sintosa(&sin), m->m_pkthdr.ph_rtableid,
511 0 : &icp->icmp_ip);
512 : break;
513 :
514 : badcode:
515 0 : icmpstat_inc(icps_badcode);
516 0 : break;
517 :
518 : case ICMP_ECHO:
519 0 : if (!icmpbmcastecho &&
520 0 : (m->m_flags & (M_MCAST | M_BCAST)) != 0) {
521 0 : icmpstat_inc(icps_bmcastecho);
522 0 : break;
523 : }
524 0 : icp->icmp_type = ICMP_ECHOREPLY;
525 0 : goto reflect;
526 :
527 : case ICMP_TSTAMP:
528 0 : if (icmptstamprepl == 0)
529 : break;
530 :
531 0 : if (!icmpbmcastecho &&
532 0 : (m->m_flags & (M_MCAST | M_BCAST)) != 0) {
533 0 : icmpstat_inc(icps_bmcastecho);
534 0 : break;
535 : }
536 0 : if (icmplen < ICMP_TSLEN) {
537 0 : icmpstat_inc(icps_badlen);
538 0 : break;
539 : }
540 0 : icp->icmp_type = ICMP_TSTAMPREPLY;
541 0 : icp->icmp_rtime = iptime();
542 0 : icp->icmp_ttime = icp->icmp_rtime; /* bogus, do later! */
543 0 : goto reflect;
544 :
545 : case ICMP_MASKREQ:
546 0 : if (icmpmaskrepl == 0)
547 : break;
548 0 : if (icmplen < ICMP_MASKLEN) {
549 0 : icmpstat_inc(icps_badlen);
550 0 : break;
551 : }
552 : /*
553 : * We are not able to respond with all ones broadcast
554 : * unless we receive it over a point-to-point interface.
555 : */
556 0 : memset(&sin, 0, sizeof(sin));
557 0 : sin.sin_family = AF_INET;
558 0 : sin.sin_len = sizeof(struct sockaddr_in);
559 0 : if (ip->ip_dst.s_addr == INADDR_BROADCAST ||
560 0 : ip->ip_dst.s_addr == INADDR_ANY)
561 0 : sin.sin_addr = ip->ip_src;
562 : else
563 0 : sin.sin_addr = ip->ip_dst;
564 0 : if (ifp == NULL)
565 : break;
566 0 : ia = ifatoia(ifaof_ifpforaddr(sintosa(&sin), ifp));
567 0 : if (ia == NULL)
568 : break;
569 0 : icp->icmp_type = ICMP_MASKREPLY;
570 0 : icp->icmp_mask = ia->ia_sockmask.sin_addr.s_addr;
571 0 : if (ip->ip_src.s_addr == 0) {
572 0 : if (ifp->if_flags & IFF_BROADCAST) {
573 0 : if (ia->ia_broadaddr.sin_addr.s_addr)
574 0 : ip->ip_src = ia->ia_broadaddr.sin_addr;
575 : else
576 0 : ip->ip_src.s_addr = INADDR_BROADCAST;
577 : }
578 0 : else if (ifp->if_flags & IFF_POINTOPOINT)
579 0 : ip->ip_src = ia->ia_dstaddr.sin_addr;
580 : }
581 : reflect:
582 : #if NCARP > 0
583 0 : if (carp_lsdrop(ifp, m, AF_INET, &ip->ip_src.s_addr,
584 0 : &ip->ip_dst.s_addr, 1))
585 : goto freeit;
586 : #endif
587 0 : icmpstat_inc(icps_reflect);
588 0 : icmpstat_inc(icps_outhist + icp->icmp_type);
589 0 : if (!icmp_reflect(m, &opts, NULL)) {
590 0 : icmp_send(m, opts);
591 0 : m_free(opts);
592 0 : }
593 0 : return IPPROTO_DONE;
594 :
595 : case ICMP_REDIRECT:
596 : {
597 0 : struct sockaddr_in sdst;
598 0 : struct sockaddr_in sgw;
599 0 : struct sockaddr_in ssrc;
600 0 : struct rtentry *newrt = NULL;
601 :
602 0 : if (icmp_rediraccept == 0 || ipforwarding == 1)
603 0 : goto freeit;
604 0 : if (code > 3)
605 0 : goto badcode;
606 0 : if (icmplen < ICMP_ADVLENMIN || icmplen < ICMP_ADVLEN(icp) ||
607 0 : icp->icmp_ip.ip_hl < (sizeof(struct ip) >> 2)) {
608 0 : icmpstat_inc(icps_badlen);
609 0 : break;
610 : }
611 : /*
612 : * Short circuit routing redirects to force
613 : * immediate change in the kernel's routing
614 : * tables. The message is also handed to anyone
615 : * listening on a raw socket (e.g. the routing
616 : * daemon for use in updating its tables).
617 : */
618 0 : memset(&sdst, 0, sizeof(sdst));
619 0 : memset(&sgw, 0, sizeof(sgw));
620 0 : memset(&ssrc, 0, sizeof(ssrc));
621 0 : sdst.sin_family = sgw.sin_family = ssrc.sin_family = AF_INET;
622 0 : sdst.sin_len = sgw.sin_len = ssrc.sin_len = sizeof(sdst);
623 0 : memcpy(&sdst.sin_addr, &icp->icmp_ip.ip_dst,
624 : sizeof(sdst.sin_addr));
625 0 : memcpy(&sgw.sin_addr, &icp->icmp_gwaddr,
626 : sizeof(sgw.sin_addr));
627 0 : memcpy(&ssrc.sin_addr, &ip->ip_src,
628 : sizeof(ssrc.sin_addr));
629 :
630 : #ifdef ICMPPRINTFS
631 : if (icmpprintfs) {
632 : char gw[INET_ADDRSTRLEN], dst[INET_ADDRSTRLEN];
633 :
634 : inet_ntop(AF_INET, &icp->icmp_gwaddr, gw, sizeof(gw));
635 : inet_ntop(AF_INET, &icp->icmp_ip.ip_dst,
636 : dst, sizeof(dst));
637 :
638 : printf("redirect dst %s to %s\n", dst, gw);
639 : }
640 : #endif
641 :
642 : #if NCARP > 0
643 0 : if (carp_lsdrop(ifp, m, AF_INET, &sdst.sin_addr.s_addr,
644 0 : &ip->ip_dst.s_addr, 1))
645 0 : goto freeit;
646 : #endif
647 0 : rtredirect(sintosa(&sdst), sintosa(&sgw),
648 0 : sintosa(&ssrc), &newrt, m->m_pkthdr.ph_rtableid);
649 0 : if (newrt != NULL && icmp_redirtimeout != 0) {
650 0 : (void)rt_timer_add(newrt, icmp_redirect_timeout,
651 0 : icmp_redirect_timeout_q, m->m_pkthdr.ph_rtableid);
652 0 : }
653 0 : if (newrt != NULL)
654 0 : rtfree(newrt);
655 0 : pfctlinput(PRC_REDIRECT_HOST, sintosa(&sdst));
656 0 : break;
657 0 : }
658 : /*
659 : * No kernel processing for the following;
660 : * just fall through to send to raw listener.
661 : */
662 : case ICMP_ECHOREPLY:
663 : case ICMP_ROUTERADVERT:
664 : case ICMP_ROUTERSOLICIT:
665 : case ICMP_TSTAMPREPLY:
666 : case ICMP_IREQREPLY:
667 : case ICMP_MASKREPLY:
668 : case ICMP_TRACEROUTE:
669 : case ICMP_DATACONVERR:
670 : case ICMP_MOBILE_REDIRECT:
671 : case ICMP_IPV6_WHEREAREYOU:
672 : case ICMP_IPV6_IAMHERE:
673 : case ICMP_MOBILE_REGREQUEST:
674 : case ICMP_MOBILE_REGREPLY:
675 : case ICMP_PHOTURIS:
676 : default:
677 : break;
678 : }
679 :
680 : raw:
681 0 : return rip_input(mp, offp, proto, af);
682 :
683 : freeit:
684 0 : m_freem(m);
685 0 : return IPPROTO_DONE;
686 0 : }
687 :
688 : /*
689 : * Reflect the ip packet back to the source
690 : */
691 : int
692 0 : icmp_reflect(struct mbuf *m, struct mbuf **op, struct in_ifaddr *ia)
693 : {
694 0 : struct ip *ip = mtod(m, struct ip *);
695 : struct mbuf *opts = NULL;
696 0 : struct sockaddr_in sin;
697 : struct rtentry *rt = NULL;
698 0 : int optlen = (ip->ip_hl << 2) - sizeof(struct ip);
699 : u_int rtableid;
700 :
701 0 : if (!in_canforward(ip->ip_src) &&
702 0 : ((ip->ip_src.s_addr & IN_CLASSA_NET) !=
703 : htonl(IN_LOOPBACKNET << IN_CLASSA_NSHIFT))) {
704 0 : m_freem(m); /* Bad return address */
705 0 : return (EHOSTUNREACH);
706 : }
707 :
708 0 : if (m->m_pkthdr.ph_loopcnt++ >= M_MAXLOOP) {
709 0 : m_freem(m);
710 0 : return (ELOOP);
711 : }
712 0 : rtableid = m->m_pkthdr.ph_rtableid;
713 0 : m_resethdr(m);
714 0 : m->m_pkthdr.ph_rtableid = rtableid;
715 :
716 : /*
717 : * If the incoming packet was addressed directly to us,
718 : * use dst as the src for the reply. For broadcast, use
719 : * the address which corresponds to the incoming interface.
720 : */
721 0 : if (ia == NULL) {
722 0 : memset(&sin, 0, sizeof(sin));
723 0 : sin.sin_len = sizeof(sin);
724 0 : sin.sin_family = AF_INET;
725 0 : sin.sin_addr = ip->ip_dst;
726 :
727 0 : rt = rtalloc(sintosa(&sin), 0, rtableid);
728 0 : if (rtisvalid(rt) &&
729 0 : ISSET(rt->rt_flags, RTF_LOCAL|RTF_BROADCAST))
730 0 : ia = ifatoia(rt->rt_ifa);
731 : }
732 :
733 : /*
734 : * The following happens if the packet was not addressed to us.
735 : * Use the new source address and do a route lookup. If it fails
736 : * drop the packet as there is no path to the host.
737 : */
738 0 : if (ia == NULL) {
739 0 : rtfree(rt);
740 :
741 0 : memset(&sin, 0, sizeof(sin));
742 0 : sin.sin_len = sizeof(sin);
743 0 : sin.sin_family = AF_INET;
744 0 : sin.sin_addr = ip->ip_src;
745 :
746 : /* keep packet in the original virtual instance */
747 0 : rt = rtalloc(sintosa(&sin), RT_RESOLVE, rtableid);
748 0 : if (rt == NULL) {
749 0 : ipstat_inc(ips_noroute);
750 0 : m_freem(m);
751 0 : return (EHOSTUNREACH);
752 : }
753 :
754 0 : ia = ifatoia(rt->rt_ifa);
755 0 : }
756 :
757 0 : ip->ip_dst = ip->ip_src;
758 0 : ip->ip_ttl = MAXTTL;
759 :
760 : /* It is safe to dereference ``ia'' iff ``rt'' is valid. */
761 0 : ip->ip_src = ia->ia_addr.sin_addr;
762 0 : rtfree(rt);
763 :
764 0 : if (optlen > 0) {
765 : u_char *cp;
766 : int opt, cnt;
767 : u_int len;
768 :
769 : /*
770 : * Retrieve any source routing from the incoming packet;
771 : * add on any record-route or timestamp options.
772 : */
773 0 : cp = (u_char *) (ip + 1);
774 0 : if (op && (opts = ip_srcroute(m)) == NULL &&
775 0 : (opts = m_gethdr(M_DONTWAIT, MT_HEADER))) {
776 0 : opts->m_len = sizeof(struct in_addr);
777 0 : mtod(opts, struct in_addr *)->s_addr = 0;
778 0 : }
779 0 : if (op && opts) {
780 : #ifdef ICMPPRINTFS
781 : if (icmpprintfs)
782 : printf("icmp_reflect optlen %d rt %d => ",
783 : optlen, opts->m_len);
784 : #endif
785 0 : for (cnt = optlen; cnt > 0; cnt -= len, cp += len) {
786 0 : opt = cp[IPOPT_OPTVAL];
787 0 : if (opt == IPOPT_EOL)
788 : break;
789 0 : if (opt == IPOPT_NOP)
790 0 : len = 1;
791 : else {
792 0 : if (cnt < IPOPT_OLEN + sizeof(*cp))
793 : break;
794 0 : len = cp[IPOPT_OLEN];
795 0 : if (len < IPOPT_OLEN + sizeof(*cp) ||
796 0 : len > cnt)
797 : break;
798 : }
799 : /*
800 : * Should check for overflow, but it
801 : * "can't happen"
802 : */
803 0 : if (opt == IPOPT_RR || opt == IPOPT_TS ||
804 0 : opt == IPOPT_SECURITY) {
805 0 : memcpy(mtod(opts, caddr_t) +
806 : opts->m_len, cp, len);
807 0 : opts->m_len += len;
808 0 : }
809 : }
810 : /* Terminate & pad, if necessary */
811 0 : if ((cnt = opts->m_len % 4) != 0)
812 0 : for (; cnt < 4; cnt++) {
813 0 : *(mtod(opts, caddr_t) + opts->m_len) =
814 : IPOPT_EOL;
815 0 : opts->m_len++;
816 : }
817 : #ifdef ICMPPRINTFS
818 : if (icmpprintfs)
819 : printf("%d\n", opts->m_len);
820 : #endif
821 : }
822 0 : ip_stripoptions(m);
823 0 : }
824 0 : m->m_flags &= ~(M_BCAST|M_MCAST);
825 0 : if (op)
826 0 : *op = opts;
827 :
828 0 : return (0);
829 0 : }
830 :
831 : /*
832 : * Send an icmp packet back to the ip level
833 : */
834 : void
835 0 : icmp_send(struct mbuf *m, struct mbuf *opts)
836 : {
837 0 : struct ip *ip = mtod(m, struct ip *);
838 0 : int hlen;
839 : struct icmp *icp;
840 :
841 0 : hlen = ip->ip_hl << 2;
842 0 : icp = (struct icmp *)(mtod(m, caddr_t) + hlen);
843 0 : icp->icmp_cksum = 0;
844 0 : m->m_pkthdr.csum_flags = M_ICMP_CSUM_OUT;
845 : #ifdef ICMPPRINTFS
846 : if (icmpprintfs) {
847 : char dst[INET_ADDRSTRLEN], src[INET_ADDRSTRLEN];
848 :
849 : inet_ntop(AF_INET, &ip->ip_dst, dst, sizeof(dst));
850 : inet_ntop(AF_INET, &ip->ip_src, src, sizeof(src));
851 :
852 : printf("icmp_send dst %s src %s\n", dst, src);
853 : }
854 : #endif
855 0 : if (opts != NULL)
856 0 : m = ip_insertoptions(m, opts, &hlen);
857 :
858 0 : ip_send(m);
859 0 : }
860 :
861 : u_int32_t
862 0 : iptime(void)
863 : {
864 0 : struct timeval atv;
865 : u_long t;
866 :
867 0 : microtime(&atv);
868 0 : t = (atv.tv_sec % (24*60*60)) * 1000 + atv.tv_usec / 1000;
869 0 : return (htonl(t));
870 0 : }
871 :
872 : int
873 0 : icmp_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp,
874 : size_t newlen)
875 : {
876 : int error;
877 :
878 : /* All sysctl names at this level are terminal. */
879 0 : if (namelen != 1)
880 0 : return (ENOTDIR);
881 :
882 0 : switch (name[0]) {
883 : case ICMPCTL_REDIRTIMEOUT:
884 :
885 0 : NET_LOCK();
886 0 : error = sysctl_int(oldp, oldlenp, newp, newlen,
887 : &icmp_redirtimeout);
888 0 : if (icmp_redirect_timeout_q != NULL) {
889 0 : if (icmp_redirtimeout == 0) {
890 0 : rt_timer_queue_destroy(icmp_redirect_timeout_q);
891 0 : icmp_redirect_timeout_q = NULL;
892 0 : } else
893 0 : rt_timer_queue_change(icmp_redirect_timeout_q,
894 0 : icmp_redirtimeout);
895 0 : } else if (icmp_redirtimeout > 0) {
896 0 : icmp_redirect_timeout_q =
897 0 : rt_timer_queue_create(icmp_redirtimeout);
898 0 : }
899 0 : NET_UNLOCK();
900 0 : break;
901 :
902 : case ICMPCTL_STATS:
903 0 : error = icmp_sysctl_icmpstat(oldp, oldlenp, newp);
904 0 : break;
905 :
906 : default:
907 0 : if (name[0] < ICMPCTL_MAXID) {
908 0 : NET_LOCK();
909 0 : error = sysctl_int_arr(icmpctl_vars, name, namelen,
910 : oldp, oldlenp, newp, newlen);
911 0 : NET_UNLOCK();
912 0 : break;
913 : }
914 : error = ENOPROTOOPT;
915 0 : break;
916 : }
917 :
918 0 : return (error);
919 0 : }
920 :
921 : int
922 0 : icmp_sysctl_icmpstat(void *oldp, size_t *oldlenp, void *newp)
923 : {
924 0 : uint64_t counters[icps_ncounters];
925 0 : struct icmpstat icmpstat;
926 0 : u_long *words = (u_long *)&icmpstat;
927 : int i;
928 :
929 : CTASSERT(sizeof(icmpstat) == (nitems(counters) * sizeof(u_long)));
930 0 : memset(&icmpstat, 0, sizeof icmpstat);
931 0 : counters_read(icmpcounters, counters, nitems(counters));
932 :
933 0 : for (i = 0; i < nitems(counters); i++)
934 0 : words[i] = (u_long)counters[i];
935 :
936 0 : return (sysctl_rdstruct(oldp, oldlenp, newp,
937 : &icmpstat, sizeof(icmpstat)));
938 0 : }
939 :
940 : struct rtentry *
941 0 : icmp_mtudisc_clone(struct in_addr dst, u_int rtableid)
942 : {
943 0 : struct sockaddr_in sin;
944 : struct rtentry *rt;
945 : int error;
946 :
947 0 : memset(&sin, 0, sizeof(sin));
948 0 : sin.sin_family = AF_INET;
949 0 : sin.sin_len = sizeof(sin);
950 0 : sin.sin_addr = dst;
951 :
952 0 : rt = rtalloc(sintosa(&sin), RT_RESOLVE, rtableid);
953 :
954 : /* Check if the route is actually usable */
955 0 : if (!rtisvalid(rt) || (rt->rt_flags & (RTF_REJECT|RTF_BLACKHOLE)))
956 : goto bad;
957 :
958 : /*
959 : * No PMTU for local routes and permanent neighbors,
960 : * ARP and NDP use the same expire timer as the route.
961 : */
962 0 : if (ISSET(rt->rt_flags, RTF_LOCAL) ||
963 0 : (ISSET(rt->rt_flags, RTF_LLINFO) && rt->rt_expire == 0))
964 : goto bad;
965 :
966 : /* If we didn't get a host route, allocate one */
967 0 : if ((rt->rt_flags & RTF_HOST) == 0) {
968 0 : struct rtentry *nrt;
969 0 : struct rt_addrinfo info;
970 0 : struct sockaddr_rtlabel sa_rl;
971 :
972 0 : memset(&info, 0, sizeof(info));
973 0 : info.rti_ifa = rt->rt_ifa;
974 0 : info.rti_flags = RTF_GATEWAY | RTF_HOST | RTF_DYNAMIC;
975 0 : info.rti_info[RTAX_DST] = sintosa(&sin);
976 0 : info.rti_info[RTAX_GATEWAY] = rt->rt_gateway;
977 0 : info.rti_info[RTAX_LABEL] =
978 0 : rtlabel_id2sa(rt->rt_labelid, &sa_rl);
979 :
980 0 : error = rtrequest(RTM_ADD, &info, rt->rt_priority, &nrt,
981 : rtableid);
982 0 : if (error)
983 0 : goto bad;
984 0 : nrt->rt_rmx = rt->rt_rmx;
985 0 : rtfree(rt);
986 0 : rt = nrt;
987 0 : rtm_send(rt, RTM_ADD, 0, rtableid);
988 0 : }
989 0 : error = rt_timer_add(rt, icmp_mtudisc_timeout, ip_mtudisc_timeout_q,
990 : rtableid);
991 0 : if (error)
992 : goto bad;
993 :
994 0 : return (rt);
995 : bad:
996 0 : rtfree(rt);
997 0 : return (NULL);
998 0 : }
999 :
1000 : /* Table of common MTUs: */
1001 : static const u_short mtu_table[] = {
1002 : 65535, 65280, 32000, 17914, 9180, 8166,
1003 : 4352, 2002, 1492, 1006, 508, 296, 68, 0
1004 : };
1005 :
1006 : void
1007 0 : icmp_mtudisc(struct icmp *icp, u_int rtableid)
1008 : {
1009 : struct rtentry *rt;
1010 : struct ifnet *ifp;
1011 0 : u_long mtu = ntohs(icp->icmp_nextmtu); /* Why a long? IPv6 */
1012 :
1013 0 : rt = icmp_mtudisc_clone(icp->icmp_ip.ip_dst, rtableid);
1014 0 : if (rt == NULL)
1015 0 : return;
1016 :
1017 0 : ifp = if_get(rt->rt_ifidx);
1018 0 : if (ifp == NULL) {
1019 0 : rtfree(rt);
1020 0 : return;
1021 : }
1022 :
1023 0 : if (mtu == 0) {
1024 : int i = 0;
1025 :
1026 0 : mtu = ntohs(icp->icmp_ip.ip_len);
1027 : /* Some 4.2BSD-based routers incorrectly adjust the ip_len */
1028 0 : if (mtu > rt->rt_mtu && rt->rt_mtu != 0)
1029 0 : mtu -= (icp->icmp_ip.ip_hl << 2);
1030 :
1031 : /* If we still can't guess a value, try the route */
1032 0 : if (mtu == 0) {
1033 0 : mtu = rt->rt_mtu;
1034 :
1035 : /* If no route mtu, default to the interface mtu */
1036 :
1037 0 : if (mtu == 0)
1038 0 : mtu = ifp->if_mtu;
1039 : }
1040 :
1041 0 : for (i = 0; i < nitems(mtu_table); i++)
1042 0 : if (mtu > mtu_table[i]) {
1043 : mtu = mtu_table[i];
1044 0 : break;
1045 : }
1046 0 : }
1047 :
1048 : /*
1049 : * XXX: RTV_MTU is overloaded, since the admin can set it
1050 : * to turn off PMTU for a route, and the kernel can
1051 : * set it to indicate a serious problem with PMTU
1052 : * on a route. We should be using a separate flag
1053 : * for the kernel to indicate this.
1054 : */
1055 0 : if ((rt->rt_locks & RTV_MTU) == 0) {
1056 0 : if (mtu < 296 || mtu > ifp->if_mtu)
1057 0 : rt->rt_locks |= RTV_MTU;
1058 0 : else if (rt->rt_mtu > mtu || rt->rt_mtu == 0)
1059 0 : rt->rt_mtu = mtu;
1060 : }
1061 :
1062 0 : if_put(ifp);
1063 0 : rtfree(rt);
1064 0 : }
1065 :
1066 : void
1067 0 : icmp_mtudisc_timeout(struct rtentry *rt, struct rttimer *r)
1068 : {
1069 : struct ifnet *ifp;
1070 :
1071 0 : NET_ASSERT_LOCKED();
1072 :
1073 0 : ifp = if_get(rt->rt_ifidx);
1074 0 : if (ifp == NULL)
1075 0 : return;
1076 :
1077 0 : if ((rt->rt_flags & (RTF_DYNAMIC|RTF_HOST)) == (RTF_DYNAMIC|RTF_HOST)) {
1078 : void (*ctlfunc)(int, struct sockaddr *, u_int, void *);
1079 0 : struct sockaddr_in sin;
1080 :
1081 0 : sin = *satosin(rt_key(rt));
1082 :
1083 0 : rtdeletemsg(rt, ifp, r->rtt_tableid);
1084 :
1085 : /* Notify TCP layer of increased Path MTU estimate */
1086 0 : ctlfunc = inetsw[ip_protox[IPPROTO_TCP]].pr_ctlinput;
1087 0 : if (ctlfunc)
1088 0 : (*ctlfunc)(PRC_MTUINC, sintosa(&sin),
1089 0 : r->rtt_tableid, NULL);
1090 0 : } else {
1091 0 : if ((rt->rt_locks & RTV_MTU) == 0)
1092 0 : rt->rt_mtu = 0;
1093 : }
1094 :
1095 0 : if_put(ifp);
1096 0 : }
1097 :
1098 : /*
1099 : * Perform rate limit check.
1100 : * Returns 0 if it is okay to send the icmp packet.
1101 : * Returns 1 if the router SHOULD NOT send this icmp packet due to rate
1102 : * limitation.
1103 : *
1104 : * XXX per-destination/type check necessary?
1105 : */
1106 : int
1107 0 : icmp_ratelimit(const struct in_addr *dst, const int type, const int code)
1108 : {
1109 : /* PPS limit */
1110 0 : if (!ppsratecheck(&icmperrppslim_last, &icmperrpps_count,
1111 0 : icmperrppslim))
1112 0 : return 1; /* The packet is subject to rate limit */
1113 0 : return 0; /* okay to send */
1114 0 : }
1115 :
1116 : void
1117 0 : icmp_redirect_timeout(struct rtentry *rt, struct rttimer *r)
1118 : {
1119 : struct ifnet *ifp;
1120 :
1121 0 : NET_ASSERT_LOCKED();
1122 :
1123 0 : ifp = if_get(rt->rt_ifidx);
1124 0 : if (ifp == NULL)
1125 0 : return;
1126 :
1127 0 : if ((rt->rt_flags & (RTF_DYNAMIC|RTF_HOST)) == (RTF_DYNAMIC|RTF_HOST)) {
1128 0 : rtdeletemsg(rt, ifp, r->rtt_tableid);
1129 0 : }
1130 :
1131 0 : if_put(ifp);
1132 0 : }
1133 :
1134 : int
1135 0 : icmp_do_exthdr(struct mbuf *m, u_int16_t class, u_int8_t ctype, void *buf,
1136 : size_t len)
1137 : {
1138 0 : struct ip *ip = mtod(m, struct ip *);
1139 0 : int hlen, off;
1140 : struct mbuf *n;
1141 : struct icmp *icp;
1142 : struct icmp_ext_hdr *ieh;
1143 0 : struct {
1144 : struct icmp_ext_hdr ieh;
1145 : struct icmp_ext_obj_hdr ieo;
1146 : } hdr;
1147 :
1148 0 : hlen = ip->ip_hl << 2;
1149 0 : icp = (struct icmp *)(mtod(m, caddr_t) + hlen);
1150 0 : if (icp->icmp_type != ICMP_TIMXCEED && icp->icmp_type != ICMP_UNREACH &&
1151 0 : icp->icmp_type != ICMP_PARAMPROB)
1152 : /* exthdr not supported */
1153 0 : return (0);
1154 :
1155 0 : if (icp->icmp_length != 0)
1156 : /* exthdr already present, giving up */
1157 0 : return (0);
1158 :
1159 : /* the actual offset starts after the common ICMP header */
1160 0 : hlen += ICMP_MINLEN;
1161 : /* exthdr must start on a word boundary */
1162 0 : off = roundup(ntohs(ip->ip_len) - hlen, sizeof(u_int32_t));
1163 : /* ... and at an offset of ICMP_EXT_OFFSET or bigger */
1164 0 : off = max(off, ICMP_EXT_OFFSET);
1165 0 : icp->icmp_length = off / sizeof(u_int32_t);
1166 :
1167 0 : memset(&hdr, 0, sizeof(hdr));
1168 0 : hdr.ieh.ieh_version = ICMP_EXT_HDR_VERSION;
1169 0 : hdr.ieo.ieo_length = htons(sizeof(struct icmp_ext_obj_hdr) + len);
1170 0 : hdr.ieo.ieo_cnum = class;
1171 0 : hdr.ieo.ieo_ctype = ctype;
1172 :
1173 0 : if (m_copyback(m, hlen + off, sizeof(hdr), &hdr, M_NOWAIT) ||
1174 0 : m_copyback(m, hlen + off + sizeof(hdr), len, buf, M_NOWAIT)) {
1175 0 : m_freem(m);
1176 0 : return (ENOBUFS);
1177 : }
1178 :
1179 : /* calculate checksum */
1180 0 : n = m_getptr(m, hlen + off, &off);
1181 0 : if (n == NULL)
1182 0 : panic("icmp_do_exthdr: m_getptr failure");
1183 0 : ieh = (struct icmp_ext_hdr *)(mtod(n, caddr_t) + off);
1184 0 : ieh->ieh_cksum = in4_cksum(n, 0, off, sizeof(hdr) + len);
1185 :
1186 0 : ip->ip_len = htons(m->m_pkthdr.len);
1187 :
1188 0 : return (0);
1189 0 : }
|