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