date and time created 88/12/14 15:29:27 by sklower
[unix-history] / usr / src / sys / netinet / ip_icmp.c
CommitLineData
8ae0e4b4 1/*
0880b18e 2 * Copyright (c) 1982, 1986 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 *
9d91b170 17 * @(#)ip_icmp.c 7.9 (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 38
a60889ab 39#ifdef ICMPPRINTFS
81a36a8d
BJ
40/*
41 * ICMP routines: error generation, receive packet processing, and
42 * routines to turnaround packets back to the originator, and
43 * host table maintenance routines.
44 */
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))
81a36a8d 76 goto free;
99fca325
MK
77 if (oip->ip_p == IPPROTO_ICMP && type != ICMP_REDIRECT &&
78 !ICMP_INFOTYPE(((struct icmp *)((caddr_t)oip + oiplen))->icmp_type)) {
7b66c1fd
SL
79 icmpstat.icps_oldicmp++;
80 goto free;
81 }
81a36a8d
BJ
82
83 /*
72e4f44e 84 * First, formulate icmp message
81a36a8d 85 */
9d91b170 86 m = m_gethdr(M_DONTWAIT, MT_HEADER);
7b66c1fd 87 if (m == NULL)
81a36a8d 88 goto free;
9d91b170 89 icmplen = oiplen + min(8, oip->ip_len);
f22d98c3 90 m->m_len = icmplen + ICMP_MINLEN;
9d91b170 91 MH_ALIGN(m, m->m_len);
72e4f44e 92 icp = mtod(m, struct icmp *);
f22d98c3 93 if ((u_int)type > ICMP_MAXTYPE)
7b66c1fd
SL
94 panic("icmp_error");
95 icmpstat.icps_outhist[type]++;
81a36a8d 96 icp->icmp_type = type;
f22d98c3
MK
97 if (type == ICMP_REDIRECT)
98 icp->icmp_gwaddr = dest;
99 else
100 icp->icmp_void = 0;
81a36a8d 101 if (type == ICMP_PARAMPROB) {
81a36a8d 102 icp->icmp_pptr = code;
72e4f44e
SL
103 code = 0;
104 }
105 icp->icmp_code = code;
f22d98c3 106 bcopy((caddr_t)oip, (caddr_t)&icp->icmp_ip, icmplen);
39674d5f 107 nip = &icp->icmp_ip;
9d91b170 108 nip->ip_len = htons((u_short)(nip->ip_len + oiplen));
81a36a8d
BJ
109
110 /*
76a1d7bb 111 * Now, copy old ip header in front of icmp message.
81a36a8d 112 */
9d91b170 113 if (m->m_len + oiplen > MHLEN)
f22d98c3 114 oiplen = sizeof(struct ip);
9d91b170 115 if (m->m_data - oiplen < m->m_pktdat)
f22d98c3 116 panic("icmp len");
9d91b170 117 m->m_data -= oiplen;
76a1d7bb 118 m->m_len += oiplen;
9d91b170
MK
119 m->m_pkthdr.len = m->m_len;
120 m->m_pkthdr.rcvif = n->m_pkthdr.rcvif;
72e4f44e
SL
121 nip = mtod(m, struct ip *);
122 bcopy((caddr_t)oip, (caddr_t)nip, oiplen);
76a1d7bb 123 nip->ip_len = m->m_len;
72e4f44e 124 nip->ip_p = IPPROTO_ICMP;
9d91b170 125 icmp_reflect(m);
81a36a8d 126
def04636 127free:
9d91b170 128 m_freem(n);
81a36a8d
BJ
129}
130
72e4f44e
SL
131static struct sockproto icmproto = { AF_INET, IPPROTO_ICMP };
132static struct sockaddr_in icmpsrc = { AF_INET };
133static struct sockaddr_in icmpdst = { AF_INET };
f22d98c3
MK
134static struct sockaddr_in icmpgw = { AF_INET };
135struct in_ifaddr *ifptoia();
72e4f44e 136
81a36a8d 137/*
d52566dd 138 * Process a received ICMP message.
81a36a8d 139 */
9d91b170 140icmp_input(m, hlen)
166e6158 141 register struct mbuf *m;
9d91b170 142 int hlen;
81a36a8d 143{
81a36a8d 144 register struct icmp *icp;
d52566dd 145 register struct ip *ip = mtod(m, struct ip *);
9d91b170 146 int icmplen = ip->ip_len;
8046f415 147 register int i;
f22d98c3 148 struct in_ifaddr *ia;
8046f415 149 int (*ctlfunc)(), code;
b454c3ea 150 extern u_char ip_protox[];
c175a1bd 151 extern struct in_addr in_makeaddr();
81a36a8d
BJ
152
153 /*
154 * Locate icmp structure in mbuf, and check
155 * that not corrupted and of at least minimum length.
156 */
a60889ab 157#ifdef ICMPPRINTFS
39674d5f
SL
158 if (icmpprintfs)
159 printf("icmp_input from %x, len %d\n", ip->ip_src, icmplen);
a60889ab 160#endif
4d75f980 161 if (icmplen < ICMP_MINLEN) {
7b66c1fd 162 icmpstat.icps_tooshort++;
72e4f44e 163 goto free;
7b66c1fd 164 }
f22d98c3 165 i = hlen + MIN(icmplen, ICMP_ADVLENMIN);
9d91b170 166 if (m->m_len < i && (m = m_pullup(m, i)) == 0) {
167351c2
MK
167 icmpstat.icps_tooshort++;
168 return;
169 }
8046f415 170 ip = mtod(m, struct ip *);
81a36a8d 171 m->m_len -= hlen;
9d91b170 172 m->m_data += hlen;
72e4f44e 173 icp = mtod(m, struct icmp *);
8046f415 174 if (in_cksum(m, icmplen)) {
7b66c1fd 175 icmpstat.icps_checksum++;
d52566dd 176 goto free;
72e4f44e 177 }
76a1d7bb 178 m->m_len += hlen;
9d91b170 179 m->m_data -= hlen;
81a36a8d 180
a60889ab 181#ifdef ICMPPRINTFS
81a36a8d
BJ
182 /*
183 * Message type specific processing.
184 */
39674d5f
SL
185 if (icmpprintfs)
186 printf("icmp_input, type %d code %d\n", icp->icmp_type,
a60889ab
KM
187 icp->icmp_code);
188#endif
f22d98c3 189 if (icp->icmp_type > ICMP_MAXTYPE)
166e6158 190 goto raw;
7b66c1fd 191 icmpstat.icps_inhist[icp->icmp_type]++;
a60889ab 192 code = icp->icmp_code;
7b66c1fd 193 switch (icp->icmp_type) {
81a36a8d
BJ
194
195 case ICMP_UNREACH:
a60889ab
KM
196 if (code > 5)
197 goto badcode;
198 code += PRC_UNREACH_NET;
199 goto deliver;
200
81a36a8d 201 case ICMP_TIMXCEED:
a60889ab
KM
202 if (code > 1)
203 goto badcode;
204 code += PRC_TIMXCEED_INTRANS;
205 goto deliver;
206
81a36a8d 207 case ICMP_PARAMPROB:
a60889ab
KM
208 if (code)
209 goto badcode;
210 code = PRC_PARAMPROB;
211 goto deliver;
212
72e4f44e 213 case ICMP_SOURCEQUENCH:
a60889ab
KM
214 if (code)
215 goto badcode;
216 code = PRC_QUENCH;
217 deliver:
81a36a8d 218 /*
a60889ab 219 * Problem with datagram; advise higher level routines.
81a36a8d 220 */
a1edc12b 221 icp->icmp_ip.ip_len = ntohs((u_short)icp->icmp_ip.ip_len);
7b66c1fd
SL
222 if (icmplen < ICMP_ADVLENMIN || icmplen < ICMP_ADVLEN(icp)) {
223 icmpstat.icps_badlen++;
d52566dd 224 goto free;
7b66c1fd 225 }
a60889ab 226#ifdef ICMPPRINTFS
39674d5f
SL
227 if (icmpprintfs)
228 printf("deliver to protocol %d\n", icp->icmp_ip.ip_p);
a60889ab 229#endif
f22d98c3 230 icmpsrc.sin_addr = icp->icmp_ip.ip_dst;
59965020 231 if (ctlfunc = inetsw[ip_protox[icp->icmp_ip.ip_p]].pr_ctlinput)
f22d98c3 232 (*ctlfunc)(code, (struct sockaddr *)&icmpsrc);
76a1d7bb 233 break;
a60889ab
KM
234
235 badcode:
236 icmpstat.icps_badcode++;
76a1d7bb 237 break;
81a36a8d
BJ
238
239 case ICMP_ECHO:
240 icp->icmp_type = ICMP_ECHOREPLY;
241 goto reflect;
242
243 case ICMP_TSTAMP:
7b66c1fd
SL
244 if (icmplen < ICMP_TSLEN) {
245 icmpstat.icps_badlen++;
76a1d7bb 246 break;
7b66c1fd 247 }
81a36a8d 248 icp->icmp_type = ICMP_TSTAMPREPLY;
2b4b57cd 249 icp->icmp_rtime = iptime();
81a36a8d
BJ
250 icp->icmp_ttime = icp->icmp_rtime; /* bogus, do later! */
251 goto reflect;
252
253 case ICMP_IREQ:
c175a1bd 254#define satosin(sa) ((struct sockaddr_in *)(sa))
9d91b170
MK
255 if (in_netof(ip->ip_src) == 0 &&
256 (ia = ifptoia(m->m_pkthdr.rcvif)))
f22d98c3 257 ip->ip_src = in_makeaddr(in_netof(IA_SIN(ia)->sin_addr),
c175a1bd
MK
258 in_lnaof(ip->ip_src));
259 icp->icmp_type = ICMP_IREQREPLY;
81a36a8d
BJ
260 goto reflect;
261
f22d98c3 262 case ICMP_MASKREQ:
9d91b170
MK
263 if (icmplen < ICMP_MASKLEN ||
264 (ia = ifptoia(m->m_pkthdr.rcvif)) == 0)
76a1d7bb 265 break;
9b8941dc 266 icp->icmp_type = ICMP_MASKREPLY;
3baec204 267 icp->icmp_mask = htonl(ia->ia_subnetmask);
f22d98c3
MK
268 if (ip->ip_src.s_addr == 0) {
269 if (ia->ia_ifp->if_flags & IFF_BROADCAST)
270 ip->ip_src = satosin(&ia->ia_broadaddr)->sin_addr;
271 else if (ia->ia_ifp->if_flags & IFF_POINTOPOINT)
272 ip->ip_src = satosin(&ia->ia_dstaddr)->sin_addr;
273 }
76a1d7bb
MK
274reflect:
275 ip->ip_len += hlen; /* since ip_input deducts this */
276 icmpstat.icps_reflect++;
277 icmpstat.icps_outhist[icp->icmp_type]++;
9d91b170 278 icmp_reflect(m);
76a1d7bb 279 return;
f22d98c3 280
a469458a 281 case ICMP_REDIRECT:
7b66c1fd
SL
282 if (icmplen < ICMP_ADVLENMIN || icmplen < ICMP_ADVLEN(icp)) {
283 icmpstat.icps_badlen++;
76a1d7bb 284 break;
7b66c1fd 285 }
a469458a
SL
286 /*
287 * Short circuit routing redirects to force
288 * immediate change in the kernel's routing
289 * tables. The message is also handed to anyone
290 * listening on a raw socket (e.g. the routing
fbc81152 291 * daemon for use in updating its tables).
a469458a 292 */
f22d98c3 293 icmpgw.sin_addr = ip->ip_src;
167351c2 294 icmpdst.sin_addr = icp->icmp_gwaddr;
f22d98c3
MK
295#ifdef ICMPPRINTFS
296 if (icmpprintfs)
297 printf("redirect dst %x to %x\n", icp->icmp_ip.ip_dst,
298 icp->icmp_gwaddr);
299#endif
fbc81152
MK
300 if (code == ICMP_REDIRECT_NET || code == ICMP_REDIRECT_TOSNET) {
301 icmpsrc.sin_addr =
c175a1bd 302 in_makeaddr(in_netof(icp->icmp_ip.ip_dst), INADDR_ANY);
fbc81152 303 rtredirect((struct sockaddr *)&icmpsrc,
f22d98c3
MK
304 (struct sockaddr *)&icmpdst, RTF_GATEWAY,
305 (struct sockaddr *)&icmpgw);
166e6158 306 icmpsrc.sin_addr = icp->icmp_ip.ip_dst;
f22d98c3
MK
307 pfctlinput(PRC_REDIRECT_NET,
308 (struct sockaddr *)&icmpsrc);
fbc81152
MK
309 } else {
310 icmpsrc.sin_addr = icp->icmp_ip.ip_dst;
311 rtredirect((struct sockaddr *)&icmpsrc,
f22d98c3
MK
312 (struct sockaddr *)&icmpdst, RTF_GATEWAY | RTF_HOST,
313 (struct sockaddr *)&icmpgw);
314 pfctlinput(PRC_REDIRECT_HOST,
315 (struct sockaddr *)&icmpsrc);
fbc81152 316 }
76a1d7bb 317 break;
167351c2 318
76a1d7bb
MK
319 /*
320 * No kernel processing for the following;
321 * just fall through to send to raw listener.
322 */
167351c2
MK
323 case ICMP_ECHOREPLY:
324 case ICMP_TSTAMPREPLY:
325 case ICMP_IREQREPLY:
f22d98c3 326 case ICMP_MASKREPLY:
81a36a8d 327 default:
76a1d7bb 328 break;
81a36a8d 329 }
76a1d7bb 330
166e6158 331raw:
76a1d7bb
MK
332 icmpsrc.sin_addr = ip->ip_src;
333 icmpdst.sin_addr = ip->ip_dst;
334 raw_input(m, &icmproto, (struct sockaddr *)&icmpsrc,
335 (struct sockaddr *)&icmpdst);
af8f6a21 336 return;
76a1d7bb 337
81a36a8d 338free:
76a1d7bb 339 m_freem(m);
81a36a8d
BJ
340}
341
342/*
343 * Reflect the ip packet back to the source
344 */
9d91b170
MK
345icmp_reflect(m)
346 struct mbuf *m;
81a36a8d 347{
9d91b170 348 register struct ip *ip = mtod(m, struct ip *);
c175a1bd 349 register struct in_ifaddr *ia;
f22d98c3 350 struct in_addr t;
5ba846a2 351 struct mbuf *opts = 0, *ip_srcroute();
76a1d7bb 352 int optlen = (ip->ip_hl << 2) - sizeof(struct ip);
81a36a8d 353
72e4f44e
SL
354 t = ip->ip_dst;
355 ip->ip_dst = ip->ip_src;
f22d98c3
MK
356 /*
357 * If the incoming packet was addressed directly to us,
358 * use dst as the src for the reply. Otherwise (broadcast
359 * or anonymous), use the address which corresponds
360 * to the incoming interface.
361 */
362 for (ia = in_ifaddr; ia; ia = ia->ia_next) {
363 if (t.s_addr == IA_SIN(ia)->sin_addr.s_addr)
364 break;
365 if ((ia->ia_ifp->if_flags & IFF_BROADCAST) &&
366 t.s_addr == satosin(&ia->ia_broadaddr)->sin_addr.s_addr)
367 break;
368 }
369 if (ia == (struct in_ifaddr *)0)
9d91b170 370 ia = ifptoia(m->m_pkthdr.rcvif);
76751082
MK
371 if (ia == (struct in_ifaddr *)0)
372 ia = in_ifaddr;
373 t = IA_SIN(ia)->sin_addr;
72e4f44e 374 ip->ip_src = t;
2bc7b126 375 ip->ip_ttl = MAXTTL;
f22d98c3 376
76a1d7bb 377 if (optlen > 0) {
cd86d39a
MK
378 /*
379 * Retrieve any source routing from the incoming packet
76a1d7bb 380 * and strip out other options. Adjust the IP length.
cd86d39a
MK
381 */
382 opts = ip_srcroute();
9d91b170
MK
383 bcopy((caddr_t)ip + ip->ip_len, (caddr_t)ip + sizeof(struct ip),
384 (unsigned)m->m_len - ip->ip_len);
cd86d39a 385 ip->ip_len -= optlen;
9d91b170
MK
386 m->m_len -= optlen;
387 if (m->m_flags & M_PKTHDR)
388 m->m_pkthdr.len -= optlen;
f22d98c3 389 }
9d91b170 390 icmp_send(m, opts);
cd86d39a 391 if (opts)
8011f5df 392 (void)m_free(opts);
81a36a8d
BJ
393}
394
f22d98c3
MK
395struct in_ifaddr *
396ifptoia(ifp)
397 struct ifnet *ifp;
398{
399 register struct in_ifaddr *ia;
400
401 for (ia = in_ifaddr; ia; ia = ia->ia_next)
402 if (ia->ia_ifp == ifp)
403 return (ia);
404 return ((struct in_ifaddr *)0);
405}
406
81a36a8d 407/*
72e4f44e
SL
408 * Send an icmp packet back to the ip level,
409 * after supplying a checksum.
81a36a8d 410 */
9d91b170
MK
411icmp_send(m, opts)
412 register struct mbuf *m;
cd86d39a 413 struct mbuf *opts;
81a36a8d 414{
9d91b170 415 register struct ip *ip = mtod(m, struct ip *);
af8f6a21 416 register int hlen;
72e4f44e 417 register struct icmp *icp;
d52566dd 418
af8f6a21 419 hlen = ip->ip_hl << 2;
9d91b170 420 m->m_data += hlen;
76a1d7bb 421 m->m_len -= hlen;
72e4f44e
SL
422 icp = mtod(m, struct icmp *);
423 icp->icmp_cksum = 0;
424 icp->icmp_cksum = in_cksum(m, ip->ip_len - hlen);
9d91b170 425 m->m_data -= hlen;
72e4f44e 426 m->m_len += hlen;
a60889ab 427#ifdef ICMPPRINTFS
39674d5f
SL
428 if (icmpprintfs)
429 printf("icmp_send dst %x src %x\n", ip->ip_dst, ip->ip_src);
a60889ab 430#endif
cd86d39a 431 (void) ip_output(m, opts, (struct route *)0, 0);
d52566dd
BJ
432}
433
cdad2eb1 434n_time
2b4b57cd 435iptime()
d52566dd 436{
b3f3ec92 437 struct timeval atv;
2752c877 438 u_long t;
d52566dd 439
b3f3ec92
MK
440 microtime(&atv);
441 t = (atv.tv_sec % (24*60*60)) * 1000 + atv.tv_usec / 1000;
cdad2eb1 442 return (htonl(t));
d52566dd 443}