xref: /original-bsd/sys/netinet/ip_icmp.c (revision f0fd5f8a)
1 /*	ip_icmp.c	4.26	82/12/14	*/
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, MT_HEADER);
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;
108 	int i, (*ctlfunc)(), type;
109 	extern u_char ip_protox[];
110 
111 	/*
112 	 * Locate icmp structure in mbuf, and check
113 	 * that not corrupted and of at least minimum length.
114 	 */
115 	if (icmpprintfs)
116 		printf("icmp_input from %x, len %d\n", ip->ip_src, icmplen);
117 	if (icmplen < ICMP_MINLEN)
118 		goto free;
119 	m->m_len -= hlen;
120 	m->m_off += hlen;
121 	/* need routine to make sure header is in this mbuf here */
122 	icp = mtod(m, struct icmp *);
123 	i = icp->icmp_cksum;
124 	icp->icmp_cksum = 0;
125 	if (i != in_cksum(m, icmplen)) {
126 		printf("icmp: cksum %x\n", i);
127 		goto free;
128 	}
129 
130 	/*
131 	 * Message type specific processing.
132 	 */
133 	if (icmpprintfs)
134 		printf("icmp_input, type %d code %d\n", icp->icmp_type,
135 			icp->icmp_code);
136 	switch (i = icp->icmp_type) {
137 
138 	case ICMP_UNREACH:
139 	case ICMP_TIMXCEED:
140 	case ICMP_PARAMPROB:
141 	case ICMP_REDIRECT:
142 	case ICMP_SOURCEQUENCH:
143 		/*
144 		 * Problem with previous datagram; advise
145 		 * higher level routines.
146 		 */
147 		icp->icmp_ip.ip_len = ntohs((u_short)icp->icmp_ip.ip_len);
148 		if (icmplen < ICMP_ADVLENMIN || icmplen < ICMP_ADVLEN(icp))
149 			goto free;
150 		if (icmpprintfs)
151 			printf("deliver to protocol %d\n", icp->icmp_ip.ip_p);
152 		type = i == ICMP_PARAMPROB ? 0 : icp->icmp_code;
153 		if (ctlfunc = inetsw[ip_protox[icp->icmp_ip.ip_p]].pr_ctlinput)
154 			(*ctlfunc)(icmpmap[i] + type, (caddr_t)icp);
155 		goto free;
156 
157 	case ICMP_ECHO:
158 		icp->icmp_type = ICMP_ECHOREPLY;
159 		goto reflect;
160 
161 	case ICMP_TSTAMP:
162 		if (icmplen < ICMP_TSLEN)
163 			goto free;
164 		icp->icmp_type = ICMP_TSTAMPREPLY;
165 		icp->icmp_rtime = iptime();
166 		icp->icmp_ttime = icp->icmp_rtime;	/* bogus, do later! */
167 		goto reflect;
168 
169 	case ICMP_IREQ:
170 		/* fill in source address zero fields! */
171 		goto reflect;
172 
173 	case ICMP_ECHOREPLY:
174 	case ICMP_TSTAMPREPLY:
175 	case ICMP_IREQREPLY:
176 		if (icmplen < ICMP_ADVLENMIN || icmplen < ICMP_ADVLEN(icp))
177 			goto free;
178 		icmpsrc.sin_addr = ip->ip_src;
179 		icmpdst.sin_addr = ip->ip_dst;
180 		raw_input(dtom(icp), &icmproto, (struct sockaddr *)&icmpsrc,
181 		  (struct sockaddr *)&icmpdst);
182 		goto free;
183 
184 	default:
185 		goto free;
186 	}
187 reflect:
188 	icmp_reflect(ip);
189 	return;
190 free:
191 	m_freem(dtom(ip));
192 }
193 
194 /*
195  * Reflect the ip packet back to the source
196  * TODO: rearrange ip source routing options.
197  */
198 icmp_reflect(ip)
199 	struct ip *ip;
200 {
201 	struct in_addr t;
202 
203 	t = ip->ip_dst;
204 	ip->ip_dst = ip->ip_src;
205 	ip->ip_src = t;
206 	icmp_send(ip);
207 }
208 
209 /*
210  * Send an icmp packet back to the ip level,
211  * after supplying a checksum.
212  */
213 icmp_send(ip)
214 	struct ip *ip;
215 {
216 	register int hlen;
217 	register struct icmp *icp;
218 	register struct mbuf *m;
219 
220 	m = dtom(ip);
221 	hlen = ip->ip_hl << 2;
222 	icp = mtod(m, struct icmp *);
223 	icp->icmp_cksum = 0;
224 	icp->icmp_cksum = in_cksum(m, ip->ip_len - hlen);
225 	m->m_off -= hlen;
226 	m->m_len += hlen;
227 	if (icmpprintfs)
228 		printf("icmp_send dst %x src %x\n", ip->ip_dst, ip->ip_src);
229 	(void) ip_output(m, (struct mbuf *)0, (struct route *)0, 0);
230 }
231 
232 n_time
233 iptime()
234 {
235 	int s = spl6();
236 	u_long t;
237 
238 	t = (time.tv_sec % (24*60*60)) * 1000 + time.tv_usec / 1000;
239 	splx(s);
240 	return (htonl(t));
241 }
242