xref: /original-bsd/sys/netinet/ip_icmp.c (revision 9a96b58b)
1 /*	ip_icmp.c	4.16	82/05/02	*/
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/clock.h"
9 #include "../net/in.h"
10 #include "../net/in_systm.h"
11 #include "../net/ip.h"
12 #include "../net/ip_icmp.h"
13 
14 /*
15  * ICMP routines: error generation, receive packet processing, and
16  * routines to turnaround packets back to the originator, and
17  * host table maintenance routines.
18  */
19 int	icmpprintfs = 0;
20 
21 /*
22  * Generate an error packet of type error
23  * in response to bad packet ip.
24  */
25 icmp_error(oip, type, code)
26 	struct ip *oip;
27 	int type, code;
28 {
29 	register unsigned oiplen = oip->ip_hl << 2;
30 	register struct icmp *icp;
31 	struct mbuf *m;
32 	struct ip *nip;
33 COUNT(ICMP_ERROR);
34 
35 	if (icmpprintfs)
36 		printf("icmp_error(%x, %d, %d)\n", oip, type, code);
37 	/*
38 	 * Make sure that the old IP packet had 8 bytes of data to return;
39 	 * if not, don't bother.  Also don't EVER error if the old
40 	 * packet protocol was ICMP.
41 	 */
42 	if (oip->ip_len < 8 || oip->ip_p == IPPROTO_ICMP)
43 		goto free;
44 
45 	/*
46 	 * First, formulate icmp message
47 	 */
48 	m = m_get(M_DONTWAIT);
49 	if (m == 0)
50 		goto free;
51 	m->m_len = oiplen + 8 + ICMP_MINLEN;
52 	m->m_off = MMAXOFF - m->m_len;
53 	icp = mtod(m, struct icmp *);
54 	icp->icmp_type = type;
55 	icp->icmp_void = 0;
56 	if (type == ICMP_PARAMPROB) {
57 		icp->icmp_pptr = code;
58 		code = 0;
59 	}
60 	icp->icmp_code = code;
61 	bcopy((caddr_t)oip, (caddr_t)&icp->icmp_ip, oiplen + 8);
62 	nip = &icp->icmp_ip;
63 	nip->ip_len += oiplen;
64 #if vax || pdp11
65 	nip->ip_len = htons((u_short)nip->ip_len);
66 #endif
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 COUNT(ICMP_INPUT);
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 #if vax || pdp11
148 		icp->icmp_ip.ip_len = ntohs(icp->icmp_ip.ip_len);
149 #endif
150 		if (icmplen < ICMP_ADVLENMIN || icmplen < ICMP_ADVLEN(icp))
151 			goto free;
152 		if (icmpprintfs)
153 			printf("deliver to protocol %d\n", icp->icmp_ip.ip_p);
154 		if (ctlfunc = protosw[ip_protox[icp->icmp_ip.ip_p]].pr_ctlinput)
155 			(*ctlfunc)(icmpmap[i] + icp->icmp_code, (caddr_t)icp);
156 		goto free;
157 
158 	case ICMP_ECHO:
159 		icp->icmp_type = ICMP_ECHOREPLY;
160 		goto reflect;
161 
162 	case ICMP_TSTAMP:
163 		if (icmplen < ICMP_TSLEN)
164 			goto free;
165 		icp->icmp_type = ICMP_TSTAMPREPLY;
166 		icp->icmp_rtime = iptime();
167 		icp->icmp_ttime = icp->icmp_rtime;	/* bogus, do later! */
168 		goto reflect;
169 
170 	case ICMP_IREQ:
171 		/* fill in source address zero fields! */
172 		goto reflect;
173 
174 	case ICMP_ECHOREPLY:
175 	case ICMP_TSTAMPREPLY:
176 	case ICMP_IREQREPLY:
177 		if (icmplen < ICMP_ADVLENMIN || icmplen < ICMP_ADVLEN(icp))
178 			goto free;
179 		icmpsrc.sin_addr = ip->ip_src;
180 		icmpdst.sin_addr = ip->ip_dst;
181 		raw_input(dtom(icp), &icmproto, (struct sockaddr *)&icmpsrc,
182 		  (struct sockaddr *)&icmpdst);
183 		goto free;
184 
185 	default:
186 		goto free;
187 	}
188 reflect:
189 	icmp_reflect(ip);
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 COUNT(ICMP_REFLECT);
203 
204 	t = ip->ip_dst;
205 	ip->ip_dst = ip->ip_src;
206 	ip->ip_src = t;
207 	icmp_send(ip);
208 }
209 
210 int	generateicmpmsgs = 1;
211 
212 /*
213  * Send an icmp packet back to the ip level,
214  * after supplying a checksum.
215  */
216 icmp_send(ip)
217 	struct ip *ip;
218 {
219 	register int hlen = ip->ip_hl << 2;
220 	register struct icmp *icp;
221 	register struct mbuf *m = dtom(ip);
222 
223 COUNT(ICMP_SEND);
224 	if (!generateicmpmsgs)
225 		return;
226 	icp = mtod(m, struct icmp *);
227 	icp->icmp_cksum = 0;
228 	icp->icmp_cksum = in_cksum(m, ip->ip_len - hlen);
229 	m->m_off -= hlen;
230 	m->m_len += hlen;
231 	if (icmpprintfs)
232 		printf("icmp_send dst %x src %x\n", ip->ip_dst, ip->ip_src);
233 	(void) ip_output(m, 0, 0, 0);
234 }
235 
236 n_time
237 iptime()
238 {
239 	int s = spl6();
240 	u_long t;
241 
242 COUNT(IPTIME);
243 	t = (time % SECDAY) * 1000 + lbolt * hz;
244 	splx(s);
245 	return (htonl(t));
246 }
247