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