v_count => v_usecount; v_lastr is being used; v_mounton is gone;
[unix-history] / usr / src / sys / netinet / ip_icmp.c
... / ...
CommitLineData
1/*
2 * Copyright (c) 1982, 1986, 1988 Regents of the University of California.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms are permitted
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.
16 *
17 * @(#)ip_icmp.c 7.8.1.3 (Berkeley) %G%
18 */
19
20#include "param.h"
21#include "systm.h"
22#include "malloc.h"
23#include "mbuf.h"
24#include "protosw.h"
25#include "socket.h"
26#include "time.h"
27#include "kernel.h"
28
29#include "../net/route.h"
30#include "../net/if.h"
31
32#include "in.h"
33#include "in_systm.h"
34#include "in_var.h"
35#include "ip.h"
36#include "ip_icmp.h"
37#include "icmp_var.h"
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 */
44#ifdef ICMPPRINTFS
45int icmpprintfs = 0;
46#endif
47
48/*
49 * Generate an error packet of type error
50 * in response to bad packet ip.
51 */
52/*VARARGS3*/
53icmp_error(n, type, code, dest)
54 struct mbuf *n;
55 int type, code;
56 struct in_addr dest;
57{
58 register struct ip *oip = mtod(n, struct ip *), *nip;
59 register unsigned oiplen = oip->ip_hl << 2;
60 register struct icmp *icp;
61 register struct mbuf *m;
62 unsigned icmplen;
63
64#ifdef ICMPPRINTFS
65 if (icmpprintfs)
66 printf("icmp_error(%x, %d, %d)\n", oip, type, code);
67#endif
68 if (type != ICMP_REDIRECT)
69 icmpstat.icps_error++;
70 /*
71 * Don't send error if not the first fragment of message.
72 * Don't error if the old packet protocol was ICMP
73 * error message, only known informational types.
74 */
75 if (oip->ip_off &~ (IP_MF|IP_DF))
76 goto freeit;
77 if (oip->ip_p == IPPROTO_ICMP && type != ICMP_REDIRECT &&
78 dtom(oip)->m_len >= oiplen + ICMP_MINLEN &&
79 n->m_len >= oiplen + ICMP_MINLEN &&
80 !ICMP_INFOTYPE(((struct icmp *)((caddr_t)oip + oiplen))->icmp_type)) {
81 icmpstat.icps_oldicmp++;
82 goto freeit;
83 }
84
85 /*
86 * First, formulate icmp message
87 */
88 m = m_gethdr(M_DONTWAIT, MT_HEADER);
89 if (m == NULL)
90 goto freeit;
91 icmplen = oiplen + min(8, oip->ip_len);
92 m->m_len = icmplen + ICMP_MINLEN;
93 MH_ALIGN(m, m->m_len);
94 icp = mtod(m, struct icmp *);
95 if ((u_int)type > ICMP_MAXTYPE)
96 panic("icmp_error");
97 icmpstat.icps_outhist[type]++;
98 icp->icmp_type = type;
99 if (type == ICMP_REDIRECT)
100 icp->icmp_gwaddr = dest;
101 else
102 icp->icmp_void = 0;
103 if (type == ICMP_PARAMPROB) {
104 icp->icmp_pptr = code;
105 code = 0;
106 }
107 icp->icmp_code = code;
108 bcopy((caddr_t)oip, (caddr_t)&icp->icmp_ip, icmplen);
109 nip = &icp->icmp_ip;
110 nip->ip_len = htons((u_short)(nip->ip_len + oiplen));
111
112 /*
113 * Now, copy old ip header (without options)
114 * in front of icmp message.
115 */
116 if (m->m_data - sizeof(struct ip) < m->m_pktdat)
117 panic("icmp len");
118 m->m_data -= sizeof(struct ip);
119 m->m_len += sizeof(struct ip);
120 m->m_pkthdr.len = m->m_len;
121 m->m_pkthdr.rcvif = n->m_pkthdr.rcvif;
122 nip = mtod(m, struct ip *);
123 bcopy((caddr_t)oip, (caddr_t)nip, oiplen);
124 nip->ip_len = m->m_len;
125 nip->ip_hl = sizeof(struct ip) >> 2;
126 nip->ip_hl = sizeof(struct ip) >> 2;
127 nip->ip_p = IPPROTO_ICMP;
128 icmp_reflect(m);
129
130freeit:
131 m_freem(n);
132}
133
134static struct sockproto icmproto = { AF_INET, IPPROTO_ICMP };
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 };
139struct in_ifaddr *ifptoia();
140
141/*
142 * Process a received ICMP message.
143 */
144icmp_input(m, hlen)
145 register struct mbuf *m;
146 int hlen;
147{
148 register struct icmp *icp;
149 register struct ip *ip = mtod(m, struct ip *);
150 int icmplen = ip->ip_len;
151 register int i;
152 struct in_ifaddr *ia;
153 int (*ctlfunc)(), code;
154 extern u_char ip_protox[];
155 extern struct in_addr in_makeaddr();
156
157 /*
158 * Locate icmp structure in mbuf, and check
159 * that not corrupted and of at least minimum length.
160 */
161#ifdef ICMPPRINTFS
162 if (icmpprintfs)
163 printf("icmp_input from %x, len %d\n", ip->ip_src, icmplen);
164#endif
165 if (icmplen < ICMP_MINLEN) {
166 icmpstat.icps_tooshort++;
167 goto freeit;
168 }
169 i = hlen + MIN(icmplen, ICMP_ADVLENMIN);
170 if (m->m_len < i && (m = m_pullup(m, i)) == 0) {
171 icmpstat.icps_tooshort++;
172 return;
173 }
174 ip = mtod(m, struct ip *);
175 m->m_len -= hlen;
176 m->m_data += hlen;
177 icp = mtod(m, struct icmp *);
178 if (in_cksum(m, icmplen)) {
179 icmpstat.icps_checksum++;
180 goto freeit;
181 }
182 m->m_len += hlen;
183 m->m_data -= hlen;
184
185#ifdef ICMPPRINTFS
186 /*
187 * Message type specific processing.
188 */
189 if (icmpprintfs)
190 printf("icmp_input, type %d code %d\n", icp->icmp_type,
191 icp->icmp_code);
192#endif
193 if (icp->icmp_type > ICMP_MAXTYPE)
194 goto raw;
195 icmpstat.icps_inhist[icp->icmp_type]++;
196 code = icp->icmp_code;
197 switch (icp->icmp_type) {
198
199 case ICMP_UNREACH:
200 if (code > 5)
201 goto badcode;
202 code += PRC_UNREACH_NET;
203 goto deliver;
204
205 case ICMP_TIMXCEED:
206 if (code > 1)
207 goto badcode;
208 code += PRC_TIMXCEED_INTRANS;
209 goto deliver;
210
211 case ICMP_PARAMPROB:
212 if (code)
213 goto badcode;
214 code = PRC_PARAMPROB;
215 goto deliver;
216
217 case ICMP_SOURCEQUENCH:
218 if (code)
219 goto badcode;
220 code = PRC_QUENCH;
221 deliver:
222 /*
223 * Problem with datagram; advise higher level routines.
224 */
225 icp->icmp_ip.ip_len = ntohs((u_short)icp->icmp_ip.ip_len);
226 if (icmplen < ICMP_ADVLENMIN || icmplen < ICMP_ADVLEN(icp)) {
227 icmpstat.icps_badlen++;
228 goto freeit;
229 }
230#ifdef ICMPPRINTFS
231 if (icmpprintfs)
232 printf("deliver to protocol %d\n", icp->icmp_ip.ip_p);
233#endif
234 icmpsrc.sin_addr = icp->icmp_ip.ip_dst;
235 if (ctlfunc = inetsw[ip_protox[icp->icmp_ip.ip_p]].pr_ctlinput)
236 (*ctlfunc)(code, (struct sockaddr *)&icmpsrc);
237 break;
238
239 badcode:
240 icmpstat.icps_badcode++;
241 break;
242
243 case ICMP_ECHO:
244 icp->icmp_type = ICMP_ECHOREPLY;
245 goto reflect;
246
247 case ICMP_TSTAMP:
248 if (icmplen < ICMP_TSLEN) {
249 icmpstat.icps_badlen++;
250 break;
251 }
252 icp->icmp_type = ICMP_TSTAMPREPLY;
253 icp->icmp_rtime = iptime();
254 icp->icmp_ttime = icp->icmp_rtime; /* bogus, do later! */
255 goto reflect;
256
257 case ICMP_IREQ:
258#define satosin(sa) ((struct sockaddr_in *)(sa))
259 if (in_netof(ip->ip_src) == 0 &&
260 (ia = ifptoia(m->m_pkthdr.rcvif)))
261 ip->ip_src = in_makeaddr(in_netof(IA_SIN(ia)->sin_addr),
262 in_lnaof(ip->ip_src));
263 icp->icmp_type = ICMP_IREQREPLY;
264 goto reflect;
265
266 case ICMP_MASKREQ:
267 if (icmplen < ICMP_MASKLEN ||
268 (ia = ifptoia(m->m_pkthdr.rcvif)) == 0)
269 break;
270 icp->icmp_type = ICMP_MASKREPLY;
271 icp->icmp_mask = ia->ia_sockmask.sin_addr.s_addr;
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 }
278reflect:
279 ip->ip_len += hlen; /* since ip_input deducts this */
280 icmpstat.icps_reflect++;
281 icmpstat.icps_outhist[icp->icmp_type]++;
282 icmp_reflect(m);
283 return;
284
285 case ICMP_REDIRECT:
286 if (icmplen < ICMP_ADVLENMIN || icmplen < ICMP_ADVLEN(icp)) {
287 icmpstat.icps_badlen++;
288 break;
289 }
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
295 * daemon for use in updating its tables).
296 */
297 icmpgw.sin_addr = ip->ip_src;
298 icmpdst.sin_addr = icp->icmp_gwaddr;
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
304 if (code == ICMP_REDIRECT_NET || code == ICMP_REDIRECT_TOSNET) {
305 u_long in_netof();
306 icmpsrc.sin_addr =
307 in_makeaddr(in_netof(icp->icmp_ip.ip_dst), INADDR_ANY);
308 in_sockmaskof(icp->icmp_ip.ip_dst, &icmpmask);
309 rtredirect((struct sockaddr *)&icmpsrc,
310 (struct sockaddr *)&icmpdst,
311 (struct sockaddr *)&icmpmask, RTF_GATEWAY,
312 (struct sockaddr *)&icmpgw, (struct rtentry **)0);
313 icmpsrc.sin_addr = icp->icmp_ip.ip_dst;
314 pfctlinput(PRC_REDIRECT_NET,
315 (struct sockaddr *)&icmpsrc);
316 } else {
317 icmpsrc.sin_addr = icp->icmp_ip.ip_dst;
318 rtredirect((struct sockaddr *)&icmpsrc,
319 (struct sockaddr *)&icmpdst,
320 (struct sockaddr *)0, RTF_GATEWAY | RTF_HOST,
321 (struct sockaddr *)&icmpgw, (struct rtentry **)0);
322 pfctlinput(PRC_REDIRECT_HOST,
323 (struct sockaddr *)&icmpsrc);
324 }
325 break;
326
327 /*
328 * No kernel processing for the following;
329 * just fall through to send to raw listener.
330 */
331 case ICMP_ECHOREPLY:
332 case ICMP_TSTAMPREPLY:
333 case ICMP_IREQREPLY:
334 case ICMP_MASKREPLY:
335 default:
336 break;
337 }
338
339raw:
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);
344 return;
345
346freeit:
347 m_freem(m);
348}
349
350/*
351 * Reflect the ip packet back to the source
352 */
353icmp_reflect(m)
354 struct mbuf *m;
355{
356 register struct ip *ip = mtod(m, struct ip *);
357 register struct in_ifaddr *ia;
358 struct in_addr t;
359 struct mbuf *opts = 0, *ip_srcroute();
360 int optlen = (ip->ip_hl << 2) - sizeof(struct ip);
361
362 t = ip->ip_dst;
363 ip->ip_dst = ip->ip_src;
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)
378 ia = ifptoia(m->m_pkthdr.rcvif);
379 if (ia == (struct in_ifaddr *)0)
380 ia = in_ifaddr;
381 t = IA_SIN(ia)->sin_addr;
382 ip->ip_src = t;
383 ip->ip_ttl = MAXTTL;
384
385 if (optlen > 0) {
386 register u_char *cp;
387 struct mbuf *m = dtom(ip);
388 int opt, cnt, off;
389 u_int len;
390
391 register u_char *cp;
392 int opt, cnt;
393 u_int len;
394
395 /*
396 * Retrieve any source routing from the incoming packet;
397 * add on any record-route or timestamp options.
398 */
399 cp = (u_char *) (ip + 1);
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
411 for (cnt = optlen; cnt > 0; cnt -= len, cp += len) {
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
439 }
440 /*
441 * Now strip out original options by copying rest of first
442 * mbuf's data back, and adjust the IP length.
443 */
444 ip->ip_len -= optlen;
445 ip->ip_hl = sizeof(struct ip) >> 2;
446 m->m_len -= optlen;
447 if (m->m_flags & M_PKTHDR)
448 m->m_pkthdr.len -= optlen;
449 optlen += sizeof(struct ip);
450 bcopy((caddr_t)ip + optlen, (caddr_t)(ip + 1),
451 (unsigned)(m->m_len - sizeof(struct ip)));
452 }
453
454 icmp_send(m, opts);
455 if (opts)
456 (void)m_free(opts);
457}
458
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
471/*
472 * Send an icmp packet back to the ip level,
473 * after supplying a checksum.
474 */
475icmp_send(m, opts)
476 register struct mbuf *m;
477 struct mbuf *opts;
478{
479 register struct ip *ip = mtod(m, struct ip *);
480 register int hlen;
481 register struct icmp *icp;
482
483 hlen = ip->ip_hl << 2;
484 m->m_data += hlen;
485 m->m_len -= hlen;
486 icp = mtod(m, struct icmp *);
487 icp->icmp_cksum = 0;
488 icp->icmp_cksum = in_cksum(m, ip->ip_len - hlen);
489 m->m_data -= hlen;
490 m->m_len += hlen;
491#ifdef ICMPPRINTFS
492 if (icmpprintfs)
493 printf("icmp_send dst %x src %x\n", ip->ip_dst, ip->ip_src);
494#endif
495 (void) ip_output(m, opts, (struct route *)0, 0);
496}
497
498n_time
499iptime()
500{
501 struct timeval atv;
502 u_long t;
503
504 microtime(&atv);
505 t = (atv.tv_sec % (24*60*60)) * 1000 + atv.tv_usec / 1000;
506 return (htonl(t));
507}