xref: /csrg-svn/sys/netinet/ip_icmp.c (revision 17059)
1 /*	ip_icmp.c	6.7	84/08/29	*/
2 
3 #include "param.h"
4 #include "systm.h"
5 #include "mbuf.h"
6 #include "protosw.h"
7 #include "socket.h"
8 #include "time.h"
9 #include "kernel.h"
10 
11 #include "../net/route.h"
12 
13 #include "in.h"
14 #include "in_systm.h"
15 #include "ip.h"
16 #include "ip_icmp.h"
17 #include "icmp_var.h"
18 
19 #ifdef ICMPPRINTFS
20 /*
21  * ICMP routines: error generation, receive packet processing, and
22  * routines to turnaround packets back to the originator, and
23  * host table maintenance routines.
24  */
25 int	icmpprintfs = 0;
26 #endif
27 
28 /*
29  * Generate an error packet of type error
30  * in response to bad packet ip.
31  */
32 icmp_error(oip, type, code)
33 	struct ip *oip;
34 	int type, code;
35 {
36 	register unsigned oiplen = oip->ip_hl << 2;
37 	register struct icmp *icp;
38 	struct mbuf *m;
39 	struct ip *nip;
40 
41 #ifdef ICMPPRINTFS
42 	if (icmpprintfs)
43 		printf("icmp_error(%x, %d, %d)\n", oip, type, code);
44 #endif
45 	icmpstat.icps_error++;
46 	/*
47 	 * Make sure that the old IP packet had 8 bytes of data to return;
48 	 * if not, don't bother.  Also don't EVER error if the old
49 	 * packet protocol was ICMP.
50 	 */
51 	if (oip->ip_len < 8) {
52 		icmpstat.icps_oldshort++;
53 		goto free;
54 	}
55 	if (oip->ip_p == IPPROTO_ICMP) {
56 		icmpstat.icps_oldicmp++;
57 		goto free;
58 	}
59 
60 	/*
61 	 * First, formulate icmp message
62 	 */
63 	m = m_get(M_DONTWAIT, MT_HEADER);
64 	if (m == NULL)
65 		goto free;
66 	m->m_len = oiplen + 8 + ICMP_MINLEN;
67 	m->m_off = MMAXOFF - m->m_len;
68 	icp = mtod(m, struct icmp *);
69 	if ((u_int)type > ICMP_IREQREPLY)
70 		panic("icmp_error");
71 	icmpstat.icps_outhist[type]++;
72 	icp->icmp_type = type;
73 	icp->icmp_void = 0;
74 	if (type == ICMP_PARAMPROB) {
75 		icp->icmp_pptr = code;
76 		code = 0;
77 	}
78 	icp->icmp_code = code;
79 	bcopy((caddr_t)oip, (caddr_t)&icp->icmp_ip, oiplen + 8);
80 	nip = &icp->icmp_ip;
81 	nip->ip_len += oiplen;
82 	nip->ip_len = htons((u_short)nip->ip_len);
83 
84 	/*
85 	 * Now, copy old ip header in front of icmp
86 	 * message.  This allows us to reuse any source
87 	 * routing info present.
88 	 */
89 	m->m_off -= oiplen;
90 	nip = mtod(m, struct ip *);
91 	bcopy((caddr_t)oip, (caddr_t)nip, oiplen);
92 	nip->ip_len = m->m_len + oiplen;
93 	nip->ip_p = IPPROTO_ICMP;
94 	/* icmp_send adds ip header to m_off and m_len, so we deduct here */
95 	m->m_off += oiplen;
96 	icmp_reflect(nip);
97 
98 free:
99 	m_freem(dtom(oip));
100 }
101 
102 static struct sockproto icmproto = { AF_INET, IPPROTO_ICMP };
103 static struct sockaddr_in icmpsrc = { AF_INET };
104 static struct sockaddr_in icmpdst = { AF_INET };
105 
106 /*
107  * Process a received ICMP message.
108  */
109 icmp_input(m)
110 	struct mbuf *m;
111 {
112 	register struct icmp *icp;
113 	register struct ip *ip = mtod(m, struct ip *);
114 	int icmplen = ip->ip_len, hlen = ip->ip_hl << 2;
115 	register int i;
116 	int (*ctlfunc)(), code;
117 	extern u_char ip_protox[];
118 	extern struct in_addr if_makeaddr();
119 
120 	/*
121 	 * Locate icmp structure in mbuf, and check
122 	 * that not corrupted and of at least minimum length.
123 	 */
124 #ifdef ICMPPRINTFS
125 	if (icmpprintfs)
126 		printf("icmp_input from %x, len %d\n", ip->ip_src, icmplen);
127 #endif
128 	if (icmplen < ICMP_MINLEN) {
129 		icmpstat.icps_tooshort++;
130 		goto free;
131 	}
132 	/* THIS LENGTH CHECK STILL MISSES ANY IP OPTIONS IN ICMP_IP */
133 	i = MIN(icmplen, ICMP_ADVLENMIN + hlen);
134  	if ((m->m_off > MMAXOFF || m->m_len < i) &&
135  		(m = m_pullup(m, i)) == 0)  {
136 		icmpstat.icps_tooshort++;
137 		return;
138 	}
139  	ip = mtod(m, struct ip *);
140 	m->m_len -= hlen;
141 	m->m_off += hlen;
142 	icp = mtod(m, struct icmp *);
143 	if (in_cksum(m, icmplen)) {
144 		icmpstat.icps_checksum++;
145 		goto free;
146 	}
147 
148 #ifdef ICMPPRINTFS
149 	/*
150 	 * Message type specific processing.
151 	 */
152 	if (icmpprintfs)
153 		printf("icmp_input, type %d code %d\n", icp->icmp_type,
154 		    icp->icmp_code);
155 #endif
156 	if (icp->icmp_type > ICMP_IREQREPLY)
157 		goto free;
158 	icmpstat.icps_inhist[icp->icmp_type]++;
159 	code = icp->icmp_code;
160 	switch (icp->icmp_type) {
161 
162 	case ICMP_UNREACH:
163 		if (code > 5)
164 			goto badcode;
165 		code += PRC_UNREACH_NET;
166 		goto deliver;
167 
168 	case ICMP_TIMXCEED:
169 		if (code > 1)
170 			goto badcode;
171 		code += PRC_TIMXCEED_INTRANS;
172 		goto deliver;
173 
174 	case ICMP_PARAMPROB:
175 		if (code)
176 			goto badcode;
177 		code = PRC_PARAMPROB;
178 		goto deliver;
179 
180 	case ICMP_SOURCEQUENCH:
181 		if (code)
182 			goto badcode;
183 		code = PRC_QUENCH;
184 	deliver:
185 		/*
186 		 * Problem with datagram; advise higher level routines.
187 		 */
188 		icp->icmp_ip.ip_len = ntohs((u_short)icp->icmp_ip.ip_len);
189 		if (icmplen < ICMP_ADVLENMIN || icmplen < ICMP_ADVLEN(icp)) {
190 			icmpstat.icps_badlen++;
191 			goto free;
192 		}
193 #ifdef ICMPPRINTFS
194 		if (icmpprintfs)
195 			printf("deliver to protocol %d\n", icp->icmp_ip.ip_p);
196 #endif
197 		if (ctlfunc = inetsw[ip_protox[icp->icmp_ip.ip_p]].pr_ctlinput)
198 			(*ctlfunc)(code, (caddr_t)icp);
199 		goto free;
200 
201 	badcode:
202 		icmpstat.icps_badcode++;
203 		goto free;
204 
205 	case ICMP_ECHO:
206 		icp->icmp_type = ICMP_ECHOREPLY;
207 		goto reflect;
208 
209 	case ICMP_TSTAMP:
210 		if (icmplen < ICMP_TSLEN) {
211 			icmpstat.icps_badlen++;
212 			goto free;
213 		}
214 		icp->icmp_type = ICMP_TSTAMPREPLY;
215 		icp->icmp_rtime = iptime();
216 		icp->icmp_ttime = icp->icmp_rtime;	/* bogus, do later! */
217 		goto reflect;
218 
219 	case ICMP_IREQ:
220 #ifdef notdef
221 		/* fill in source address zero fields! */
222 		goto reflect;
223 #else
224 		goto free;		/* not yet implemented: ignore */
225 #endif
226 
227 	case ICMP_REDIRECT:
228 		if (icmplen < ICMP_ADVLENMIN || icmplen < ICMP_ADVLEN(icp)) {
229 			icmpstat.icps_badlen++;
230 			goto free;
231 		}
232 		/*
233 		 * Short circuit routing redirects to force
234 		 * immediate change in the kernel's routing
235 		 * tables.  The message is also handed to anyone
236 		 * listening on a raw socket (e.g. the routing
237 		 * daemon for use in updating its tables).
238 		 */
239 		icmpdst.sin_addr = icp->icmp_gwaddr;
240 		if (code == ICMP_REDIRECT_NET || code == ICMP_REDIRECT_TOSNET) {
241 			icmpsrc.sin_addr =
242 			 if_makeaddr(in_netof(icp->icmp_ip.ip_dst), INADDR_ANY);
243 			rtredirect((struct sockaddr *)&icmpsrc,
244 			  (struct sockaddr *)&icmpdst, RTF_GATEWAY);
245 		} else {
246 			icmpsrc.sin_addr = icp->icmp_ip.ip_dst;
247 			rtredirect((struct sockaddr *)&icmpsrc,
248 			  (struct sockaddr *)&icmpdst, RTF_GATEWAY | RTF_HOST);
249 		}
250 		/* FALL THROUGH */
251 
252 	case ICMP_ECHOREPLY:
253 	case ICMP_TSTAMPREPLY:
254 	case ICMP_IREQREPLY:
255 		icmpsrc.sin_addr = ip->ip_src;
256 		icmpdst.sin_addr = ip->ip_dst;
257 		raw_input(dtom(icp), &icmproto, (struct sockaddr *)&icmpsrc,
258 		  (struct sockaddr *)&icmpdst);
259 		return;
260 
261 	default:
262 		goto free;
263 	}
264 reflect:
265 	ip->ip_len += hlen;		/* since ip_input deducts this */
266 	icmpstat.icps_reflect++;
267 	icmp_reflect(ip);
268 	return;
269 free:
270 	m_freem(dtom(ip));
271 }
272 
273 /*
274  * Reflect the ip packet back to the source
275  * TODO: rearrange ip source routing options.
276  */
277 icmp_reflect(ip)
278 	struct ip *ip;
279 {
280 	struct in_addr t;
281 
282 	t = ip->ip_dst;
283 	ip->ip_dst = ip->ip_src;
284 	ip->ip_src = t;
285 	icmp_send(ip);
286 }
287 
288 /*
289  * Send an icmp packet back to the ip level,
290  * after supplying a checksum.
291  */
292 icmp_send(ip)
293 	struct ip *ip;
294 {
295 	register int hlen;
296 	register struct icmp *icp;
297 	register struct mbuf *m;
298 
299 	m = dtom(ip);
300 	hlen = ip->ip_hl << 2;
301 	icp = mtod(m, struct icmp *);
302 	icp->icmp_cksum = 0;
303 	icp->icmp_cksum = in_cksum(m, ip->ip_len - hlen);
304 	m->m_off -= hlen;
305 	m->m_len += hlen;
306 #ifdef ICMPPRINTFS
307 	if (icmpprintfs)
308 		printf("icmp_send dst %x src %x\n", ip->ip_dst, ip->ip_src);
309 #endif
310 	(void) ip_output(m, (struct mbuf *)0, (struct route *)0, 0);
311 }
312 
313 n_time
314 iptime()
315 {
316 	int s = spl6();
317 	u_long t;
318 
319 	t = (time.tv_sec % (24*60*60)) * 1000 + time.tv_usec / 1000;
320 	splx(s);
321 	return (htonl(t));
322 }
323