v_count => v_usecount; v_lastr is being used; v_mounton is gone;
[unix-history] / usr / src / sys / netinet / ip_icmp.c
CommitLineData
8ae0e4b4 1/*
0ebd7d37 2 * Copyright (c) 1982, 1986, 1988 Regents of the University of California.
2b6b6284 3 * All rights reserved.
8ae0e4b4 4 *
2b6b6284 5 * Redistribution and use in source and binary forms are permitted
616d42db
KB
6 * provided that the above copyright notice and this paragraph are
7 * duplicated in all such forms and that any documentation,
8 * advertising materials, and other materials related to such
9 * distribution and use acknowledge that the software was developed
10 * by the University of California, Berkeley. The name of the
11 * University may not be used to endorse or promote products derived
12 * from this software without specific prior written permission.
13 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
14 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
15 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
2b6b6284 16 *
89369b13 17 * @(#)ip_icmp.c 7.8.1.3 (Berkeley) %G%
8ae0e4b4 18 */
81a36a8d 19
20666ad3
JB
20#include "param.h"
21#include "systm.h"
9d91b170 22#include "malloc.h"
20666ad3
JB
23#include "mbuf.h"
24#include "protosw.h"
25#include "socket.h"
26#include "time.h"
27#include "kernel.h"
6e7edb25
BJ
28
29#include "../net/route.h"
c175a1bd 30#include "../net/if.h"
f4d55810 31
20666ad3
JB
32#include "in.h"
33#include "in_systm.h"
c175a1bd 34#include "in_var.h"
20666ad3
JB
35#include "ip.h"
36#include "ip_icmp.h"
37#include "icmp_var.h"
81a36a8d
BJ
38
39/*
40 * ICMP routines: error generation, receive packet processing, and
41 * routines to turnaround packets back to the originator, and
42 * host table maintenance routines.
43 */
7a372964 44#ifdef ICMPPRINTFS
67387c9c 45int icmpprintfs = 0;
a60889ab 46#endif
81a36a8d
BJ
47
48/*
72e4f44e
SL
49 * Generate an error packet of type error
50 * in response to bad packet ip.
81a36a8d 51 */
9d91b170
MK
52/*VARARGS3*/
53icmp_error(n, type, code, dest)
54 struct mbuf *n;
755d8841 55 int type, code;
f22d98c3 56 struct in_addr dest;
81a36a8d 57{
9d91b170 58 register struct ip *oip = mtod(n, struct ip *), *nip;
72e4f44e
SL
59 register unsigned oiplen = oip->ip_hl << 2;
60 register struct icmp *icp;
9d91b170 61 register struct mbuf *m;
8011f5df 62 unsigned icmplen;
81a36a8d 63
a60889ab 64#ifdef ICMPPRINTFS
39674d5f
SL
65 if (icmpprintfs)
66 printf("icmp_error(%x, %d, %d)\n", oip, type, code);
a60889ab 67#endif
cd86d39a
MK
68 if (type != ICMP_REDIRECT)
69 icmpstat.icps_error++;
81a36a8d 70 /*
f22d98c3 71 * Don't send error if not the first fragment of message.
99fca325
MK
72 * Don't error if the old packet protocol was ICMP
73 * error message, only known informational types.
81a36a8d 74 */
f22d98c3 75 if (oip->ip_off &~ (IP_MF|IP_DF))
b4dc7708 76 goto freeit;
99fca325 77 if (oip->ip_p == IPPROTO_ICMP && type != ICMP_REDIRECT &&
0ebd7d37 78 dtom(oip)->m_len >= oiplen + ICMP_MINLEN &&
9a63a2be 79 n->m_len >= oiplen + ICMP_MINLEN &&
99fca325 80 !ICMP_INFOTYPE(((struct icmp *)((caddr_t)oip + oiplen))->icmp_type)) {
7b66c1fd 81 icmpstat.icps_oldicmp++;
b4dc7708 82 goto freeit;
7b66c1fd 83 }
81a36a8d
BJ
84
85 /*
72e4f44e 86 * First, formulate icmp message
81a36a8d 87 */
9d91b170 88 m = m_gethdr(M_DONTWAIT, MT_HEADER);
7b66c1fd 89 if (m == NULL)
b4dc7708 90 goto freeit;
9d91b170 91 icmplen = oiplen + min(8, oip->ip_len);
f22d98c3 92 m->m_len = icmplen + ICMP_MINLEN;
9d91b170 93 MH_ALIGN(m, m->m_len);
72e4f44e 94 icp = mtod(m, struct icmp *);
f22d98c3 95 if ((u_int)type > ICMP_MAXTYPE)
7b66c1fd
SL
96 panic("icmp_error");
97 icmpstat.icps_outhist[type]++;
81a36a8d 98 icp->icmp_type = type;
f22d98c3
MK
99 if (type == ICMP_REDIRECT)
100 icp->icmp_gwaddr = dest;
101 else
102 icp->icmp_void = 0;
81a36a8d 103 if (type == ICMP_PARAMPROB) {
81a36a8d 104 icp->icmp_pptr = code;
72e4f44e
SL
105 code = 0;
106 }
107 icp->icmp_code = code;
f22d98c3 108 bcopy((caddr_t)oip, (caddr_t)&icp->icmp_ip, icmplen);
39674d5f 109 nip = &icp->icmp_ip;
9d91b170 110 nip->ip_len = htons((u_short)(nip->ip_len + oiplen));
81a36a8d
BJ
111
112 /*
9a63a2be
MK
113 * Now, copy old ip header (without options)
114 * in front of icmp message.
81a36a8d 115 */
9a63a2be 116 if (m->m_data - sizeof(struct ip) < m->m_pktdat)
f22d98c3 117 panic("icmp len");
9a63a2be
MK
118 m->m_data -= sizeof(struct ip);
119 m->m_len += sizeof(struct ip);
9d91b170
MK
120 m->m_pkthdr.len = m->m_len;
121 m->m_pkthdr.rcvif = n->m_pkthdr.rcvif;
72e4f44e
SL
122 nip = mtod(m, struct ip *);
123 bcopy((caddr_t)oip, (caddr_t)nip, oiplen);
76a1d7bb 124 nip->ip_len = m->m_len;
9a63a2be 125 nip->ip_hl = sizeof(struct ip) >> 2;
0ebd7d37 126 nip->ip_hl = sizeof(struct ip) >> 2;
72e4f44e 127 nip->ip_p = IPPROTO_ICMP;
9d91b170 128 icmp_reflect(m);
81a36a8d 129
b4dc7708 130freeit:
9d91b170 131 m_freem(n);
81a36a8d
BJ
132}
133
72e4f44e 134static struct sockproto icmproto = { AF_INET, IPPROTO_ICMP };
9a63a2be
MK
135static struct sockaddr_in icmpsrc = { sizeof (struct sockaddr_in), AF_INET };
136static struct sockaddr_in icmpdst = { sizeof (struct sockaddr_in), AF_INET };
137static struct sockaddr_in icmpgw = { sizeof (struct sockaddr_in), AF_INET };
138struct sockaddr_in icmpmask = { 8, 0 };
f22d98c3 139struct in_ifaddr *ifptoia();
72e4f44e 140
81a36a8d 141/*
d52566dd 142 * Process a received ICMP message.
81a36a8d 143 */
9d91b170 144icmp_input(m, hlen)
166e6158 145 register struct mbuf *m;
9d91b170 146 int hlen;
81a36a8d 147{
81a36a8d 148 register struct icmp *icp;
d52566dd 149 register struct ip *ip = mtod(m, struct ip *);
9d91b170 150 int icmplen = ip->ip_len;
8046f415 151 register int i;
f22d98c3 152 struct in_ifaddr *ia;
8046f415 153 int (*ctlfunc)(), code;
b454c3ea 154 extern u_char ip_protox[];
c175a1bd 155 extern struct in_addr in_makeaddr();
81a36a8d
BJ
156
157 /*
158 * Locate icmp structure in mbuf, and check
159 * that not corrupted and of at least minimum length.
160 */
a60889ab 161#ifdef ICMPPRINTFS
39674d5f
SL
162 if (icmpprintfs)
163 printf("icmp_input from %x, len %d\n", ip->ip_src, icmplen);
a60889ab 164#endif
4d75f980 165 if (icmplen < ICMP_MINLEN) {
7b66c1fd 166 icmpstat.icps_tooshort++;
b4dc7708 167 goto freeit;
7b66c1fd 168 }
f22d98c3 169 i = hlen + MIN(icmplen, ICMP_ADVLENMIN);
9d91b170 170 if (m->m_len < i && (m = m_pullup(m, i)) == 0) {
167351c2
MK
171 icmpstat.icps_tooshort++;
172 return;
173 }
8046f415 174 ip = mtod(m, struct ip *);
81a36a8d 175 m->m_len -= hlen;
9d91b170 176 m->m_data += hlen;
72e4f44e 177 icp = mtod(m, struct icmp *);
8046f415 178 if (in_cksum(m, icmplen)) {
7b66c1fd 179 icmpstat.icps_checksum++;
b4dc7708 180 goto freeit;
72e4f44e 181 }
76a1d7bb 182 m->m_len += hlen;
9d91b170 183 m->m_data -= hlen;
81a36a8d 184
a60889ab 185#ifdef ICMPPRINTFS
81a36a8d
BJ
186 /*
187 * Message type specific processing.
188 */
39674d5f
SL
189 if (icmpprintfs)
190 printf("icmp_input, type %d code %d\n", icp->icmp_type,
a60889ab
KM
191 icp->icmp_code);
192#endif
f22d98c3 193 if (icp->icmp_type > ICMP_MAXTYPE)
166e6158 194 goto raw;
7b66c1fd 195 icmpstat.icps_inhist[icp->icmp_type]++;
a60889ab 196 code = icp->icmp_code;
7b66c1fd 197 switch (icp->icmp_type) {
81a36a8d
BJ
198
199 case ICMP_UNREACH:
a60889ab
KM
200 if (code > 5)
201 goto badcode;
202 code += PRC_UNREACH_NET;
203 goto deliver;
204
81a36a8d 205 case ICMP_TIMXCEED:
a60889ab
KM
206 if (code > 1)
207 goto badcode;
208 code += PRC_TIMXCEED_INTRANS;
209 goto deliver;
210
81a36a8d 211 case ICMP_PARAMPROB:
a60889ab
KM
212 if (code)
213 goto badcode;
214 code = PRC_PARAMPROB;
215 goto deliver;
216
72e4f44e 217 case ICMP_SOURCEQUENCH:
a60889ab
KM
218 if (code)
219 goto badcode;
220 code = PRC_QUENCH;
221 deliver:
81a36a8d 222 /*
a60889ab 223 * Problem with datagram; advise higher level routines.
81a36a8d 224 */
a1edc12b 225 icp->icmp_ip.ip_len = ntohs((u_short)icp->icmp_ip.ip_len);
7b66c1fd
SL
226 if (icmplen < ICMP_ADVLENMIN || icmplen < ICMP_ADVLEN(icp)) {
227 icmpstat.icps_badlen++;
b4dc7708 228 goto freeit;
7b66c1fd 229 }
a60889ab 230#ifdef ICMPPRINTFS
39674d5f
SL
231 if (icmpprintfs)
232 printf("deliver to protocol %d\n", icp->icmp_ip.ip_p);
a60889ab 233#endif
f22d98c3 234 icmpsrc.sin_addr = icp->icmp_ip.ip_dst;
59965020 235 if (ctlfunc = inetsw[ip_protox[icp->icmp_ip.ip_p]].pr_ctlinput)
f22d98c3 236 (*ctlfunc)(code, (struct sockaddr *)&icmpsrc);
76a1d7bb 237 break;
a60889ab
KM
238
239 badcode:
240 icmpstat.icps_badcode++;
76a1d7bb 241 break;
81a36a8d
BJ
242
243 case ICMP_ECHO:
244 icp->icmp_type = ICMP_ECHOREPLY;
245 goto reflect;
246
247 case ICMP_TSTAMP:
7b66c1fd
SL
248 if (icmplen < ICMP_TSLEN) {
249 icmpstat.icps_badlen++;
76a1d7bb 250 break;
7b66c1fd 251 }
81a36a8d 252 icp->icmp_type = ICMP_TSTAMPREPLY;
2b4b57cd 253 icp->icmp_rtime = iptime();
81a36a8d
BJ
254 icp->icmp_ttime = icp->icmp_rtime; /* bogus, do later! */
255 goto reflect;
256
257 case ICMP_IREQ:
c175a1bd 258#define satosin(sa) ((struct sockaddr_in *)(sa))
9d91b170
MK
259 if (in_netof(ip->ip_src) == 0 &&
260 (ia = ifptoia(m->m_pkthdr.rcvif)))
f22d98c3 261 ip->ip_src = in_makeaddr(in_netof(IA_SIN(ia)->sin_addr),
c175a1bd
MK
262 in_lnaof(ip->ip_src));
263 icp->icmp_type = ICMP_IREQREPLY;
81a36a8d
BJ
264 goto reflect;
265
f22d98c3 266 case ICMP_MASKREQ:
9d91b170
MK
267 if (icmplen < ICMP_MASKLEN ||
268 (ia = ifptoia(m->m_pkthdr.rcvif)) == 0)
76a1d7bb 269 break;
9b8941dc 270 icp->icmp_type = ICMP_MASKREPLY;
9a63a2be 271 icp->icmp_mask = ia->ia_sockmask.sin_addr.s_addr;
f22d98c3
MK
272 if (ip->ip_src.s_addr == 0) {
273 if (ia->ia_ifp->if_flags & IFF_BROADCAST)
274 ip->ip_src = satosin(&ia->ia_broadaddr)->sin_addr;
275 else if (ia->ia_ifp->if_flags & IFF_POINTOPOINT)
276 ip->ip_src = satosin(&ia->ia_dstaddr)->sin_addr;
277 }
76a1d7bb
MK
278reflect:
279 ip->ip_len += hlen; /* since ip_input deducts this */
280 icmpstat.icps_reflect++;
281 icmpstat.icps_outhist[icp->icmp_type]++;
9d91b170 282 icmp_reflect(m);
76a1d7bb 283 return;
f22d98c3 284
a469458a 285 case ICMP_REDIRECT:
7b66c1fd
SL
286 if (icmplen < ICMP_ADVLENMIN || icmplen < ICMP_ADVLEN(icp)) {
287 icmpstat.icps_badlen++;
76a1d7bb 288 break;
7b66c1fd 289 }
a469458a
SL
290 /*
291 * Short circuit routing redirects to force
292 * immediate change in the kernel's routing
293 * tables. The message is also handed to anyone
294 * listening on a raw socket (e.g. the routing
fbc81152 295 * daemon for use in updating its tables).
a469458a 296 */
f22d98c3 297 icmpgw.sin_addr = ip->ip_src;
167351c2 298 icmpdst.sin_addr = icp->icmp_gwaddr;
f22d98c3
MK
299#ifdef ICMPPRINTFS
300 if (icmpprintfs)
301 printf("redirect dst %x to %x\n", icp->icmp_ip.ip_dst,
302 icp->icmp_gwaddr);
303#endif
fbc81152 304 if (code == ICMP_REDIRECT_NET || code == ICMP_REDIRECT_TOSNET) {
9a63a2be 305 u_long in_netof();
fbc81152 306 icmpsrc.sin_addr =
c175a1bd 307 in_makeaddr(in_netof(icp->icmp_ip.ip_dst), INADDR_ANY);
7a372964 308 in_sockmaskof(icp->icmp_ip.ip_dst, &icmpmask);
fbc81152 309 rtredirect((struct sockaddr *)&icmpsrc,
9a63a2be
MK
310 (struct sockaddr *)&icmpdst,
311 (struct sockaddr *)&icmpmask, RTF_GATEWAY,
b4dc7708 312 (struct sockaddr *)&icmpgw, (struct rtentry **)0);
166e6158 313 icmpsrc.sin_addr = icp->icmp_ip.ip_dst;
f22d98c3
MK
314 pfctlinput(PRC_REDIRECT_NET,
315 (struct sockaddr *)&icmpsrc);
fbc81152
MK
316 } else {
317 icmpsrc.sin_addr = icp->icmp_ip.ip_dst;
318 rtredirect((struct sockaddr *)&icmpsrc,
9a63a2be
MK
319 (struct sockaddr *)&icmpdst,
320 (struct sockaddr *)0, RTF_GATEWAY | RTF_HOST,
b4dc7708 321 (struct sockaddr *)&icmpgw, (struct rtentry **)0);
f22d98c3
MK
322 pfctlinput(PRC_REDIRECT_HOST,
323 (struct sockaddr *)&icmpsrc);
fbc81152 324 }
76a1d7bb 325 break;
167351c2 326
76a1d7bb
MK
327 /*
328 * No kernel processing for the following;
329 * just fall through to send to raw listener.
330 */
167351c2
MK
331 case ICMP_ECHOREPLY:
332 case ICMP_TSTAMPREPLY:
333 case ICMP_IREQREPLY:
f22d98c3 334 case ICMP_MASKREPLY:
81a36a8d 335 default:
76a1d7bb 336 break;
81a36a8d 337 }
76a1d7bb 338
166e6158 339raw:
76a1d7bb
MK
340 icmpsrc.sin_addr = ip->ip_src;
341 icmpdst.sin_addr = ip->ip_dst;
342 raw_input(m, &icmproto, (struct sockaddr *)&icmpsrc,
343 (struct sockaddr *)&icmpdst);
af8f6a21 344 return;
76a1d7bb 345
b4dc7708 346freeit:
76a1d7bb 347 m_freem(m);
81a36a8d
BJ
348}
349
350/*
351 * Reflect the ip packet back to the source
352 */
9d91b170
MK
353icmp_reflect(m)
354 struct mbuf *m;
81a36a8d 355{
9d91b170 356 register struct ip *ip = mtod(m, struct ip *);
c175a1bd 357 register struct in_ifaddr *ia;
f22d98c3 358 struct in_addr t;
5ba846a2 359 struct mbuf *opts = 0, *ip_srcroute();
76a1d7bb 360 int optlen = (ip->ip_hl << 2) - sizeof(struct ip);
81a36a8d 361
72e4f44e
SL
362 t = ip->ip_dst;
363 ip->ip_dst = ip->ip_src;
f22d98c3
MK
364 /*
365 * If the incoming packet was addressed directly to us,
366 * use dst as the src for the reply. Otherwise (broadcast
367 * or anonymous), use the address which corresponds
368 * to the incoming interface.
369 */
370 for (ia = in_ifaddr; ia; ia = ia->ia_next) {
371 if (t.s_addr == IA_SIN(ia)->sin_addr.s_addr)
372 break;
373 if ((ia->ia_ifp->if_flags & IFF_BROADCAST) &&
374 t.s_addr == satosin(&ia->ia_broadaddr)->sin_addr.s_addr)
375 break;
376 }
377 if (ia == (struct in_ifaddr *)0)
9d91b170 378 ia = ifptoia(m->m_pkthdr.rcvif);
76751082
MK
379 if (ia == (struct in_ifaddr *)0)
380 ia = in_ifaddr;
381 t = IA_SIN(ia)->sin_addr;
72e4f44e 382 ip->ip_src = t;
2bc7b126 383 ip->ip_ttl = MAXTTL;
f22d98c3 384
76a1d7bb 385 if (optlen > 0) {
9a63a2be 386 register u_char *cp;
89369b13 387 struct mbuf *m = dtom(ip);
9a63a2be
MK
388 int opt, cnt, off;
389 u_int len;
390
0ebd7d37 391 register u_char *cp;
b4dc7708 392 int opt, cnt;
0ebd7d37
MK
393 u_int len;
394
cd86d39a 395 /*
9a63a2be
MK
396 * Retrieve any source routing from the incoming packet;
397 * add on any record-route or timestamp options.
cd86d39a 398 */
9a63a2be 399 cp = (u_char *) (ip + 1);
7a372964
MK
400 if ((opts = ip_srcroute()) == 0 &&
401 (opts = m_gethdr(M_DONTWAIT, MT_HEADER))) {
402 opts->m_len = sizeof(struct in_addr);
403 mtod(opts, struct in_addr *)->s_addr = 0;
404 }
405 if (opts) {
406#ifdef ICMPPRINTFS
407 if (icmpprintfs)
408 printf("icmp_reflect optlen %d rt %d => ",
409 optlen, opts->m_len);
410#endif
9a63a2be 411 for (cnt = optlen; cnt > 0; cnt -= len, cp += len) {
7a372964
MK
412 opt = cp[IPOPT_OPTVAL];
413 if (opt == IPOPT_EOL)
414 break;
415 if (opt == IPOPT_NOP)
416 len = 1;
417 else {
418 len = cp[IPOPT_OLEN];
419 if (len <= 0 || len > cnt)
420 break;
421 }
422 /*
423 * should check for overflow, but it "can't happen"
424 */
425 if (opt == IPOPT_RR || opt == IPOPT_TS) {
426 bcopy((caddr_t)cp,
427 mtod(opts, caddr_t) + opts->m_len, len);
428 opts->m_len += len;
429 }
430 }
431 if (opts->m_len % 4 != 0) {
432 *(mtod(opts, caddr_t) + opts->m_len) = IPOPT_EOL;
433 opts->m_len++;
434 }
435#ifdef ICMPPRINTFS
436 if (icmpprintfs)
437 printf("%d\n", opts->m_len);
438#endif
9a63a2be 439 }
7a372964
MK
440 /*
441 * Now strip out original options by copying rest of first
442 * mbuf's data back, and adjust the IP length.
443 */
9a63a2be
MK
444 ip->ip_len -= optlen;
445 ip->ip_hl = sizeof(struct ip) >> 2;
7a372964 446 m->m_len -= optlen;
9d91b170
MK
447 if (m->m_flags & M_PKTHDR)
448 m->m_pkthdr.len -= optlen;
7a372964
MK
449 optlen += sizeof(struct ip);
450 bcopy((caddr_t)ip + optlen, (caddr_t)(ip + 1),
b4dc7708 451 (unsigned)(m->m_len - sizeof(struct ip)));
f22d98c3 452 }
424b2707 453
9d91b170 454 icmp_send(m, opts);
cd86d39a 455 if (opts)
8011f5df 456 (void)m_free(opts);
81a36a8d
BJ
457}
458
f22d98c3
MK
459struct in_ifaddr *
460ifptoia(ifp)
461 struct ifnet *ifp;
462{
463 register struct in_ifaddr *ia;
464
465 for (ia = in_ifaddr; ia; ia = ia->ia_next)
466 if (ia->ia_ifp == ifp)
467 return (ia);
468 return ((struct in_ifaddr *)0);
469}
470
81a36a8d 471/*
72e4f44e
SL
472 * Send an icmp packet back to the ip level,
473 * after supplying a checksum.
81a36a8d 474 */
9d91b170
MK
475icmp_send(m, opts)
476 register struct mbuf *m;
cd86d39a 477 struct mbuf *opts;
81a36a8d 478{
9d91b170 479 register struct ip *ip = mtod(m, struct ip *);
af8f6a21 480 register int hlen;
72e4f44e 481 register struct icmp *icp;
d52566dd 482
af8f6a21 483 hlen = ip->ip_hl << 2;
9d91b170 484 m->m_data += hlen;
76a1d7bb 485 m->m_len -= hlen;
72e4f44e
SL
486 icp = mtod(m, struct icmp *);
487 icp->icmp_cksum = 0;
488 icp->icmp_cksum = in_cksum(m, ip->ip_len - hlen);
9d91b170 489 m->m_data -= hlen;
72e4f44e 490 m->m_len += hlen;
a60889ab 491#ifdef ICMPPRINTFS
39674d5f
SL
492 if (icmpprintfs)
493 printf("icmp_send dst %x src %x\n", ip->ip_dst, ip->ip_src);
a60889ab 494#endif
cd86d39a 495 (void) ip_output(m, opts, (struct route *)0, 0);
d52566dd
BJ
496}
497
cdad2eb1 498n_time
2b4b57cd 499iptime()
d52566dd 500{
b3f3ec92 501 struct timeval atv;
2752c877 502 u_long t;
d52566dd 503
b3f3ec92
MK
504 microtime(&atv);
505 t = (atv.tv_sec % (24*60*60)) * 1000 + atv.tv_usec / 1000;
cdad2eb1 506 return (htonl(t));
d52566dd 507}