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