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