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