1 | /* SPDX-License-Identifier: BSD-3-Clause */ |
2 | /* |
3 | * Copyright (c) 1982, 1986, 1988, 1993 |
4 | * The Regents of the University of California. All rights reserved. |
5 | * |
6 | * Redistribution and use in source and binary forms, with or without |
7 | * modification, are permitted provided that the following conditions |
8 | * are met: |
9 | * 1. Redistributions of source code must retain the above copyright |
10 | * notice, this list of conditions and the following disclaimer. |
11 | * 2. Redistributions in binary form must reproduce the above copyright |
12 | * notice, this list of conditions and the following disclaimer in the |
13 | * documentation and/or other materials provided with the distribution. |
14 | * 3. Neither the name of the University nor the names of its contributors |
15 | * may be used to endorse or promote products derived from this software |
16 | * without specific prior written permission. |
17 | * |
18 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
19 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
20 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
21 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
22 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
23 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
24 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
25 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
26 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
27 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
28 | * SUCH DAMAGE. |
29 | * |
30 | * @(#)ip_input.c 8.2 (Berkeley) 1/4/94 |
31 | * ip_input.c,v 1.11 1994/11/16 10:17:08 jkh Exp |
32 | */ |
33 | |
34 | /* |
35 | * Changes and additions relating to SLiRP are |
36 | * Copyright (c) 1995 Danny Gasparovski. |
37 | */ |
38 | |
39 | #include "slirp.h" |
40 | #include "ip_icmp.h" |
41 | |
42 | static struct ip *ip_reass(Slirp *slirp, struct ip *ip, struct ipq *fp); |
43 | static void ip_freef(Slirp *slirp, struct ipq *fp); |
44 | static void ip_enq(register struct ipasfrag *p, register struct ipasfrag *prev); |
45 | static void ip_deq(register struct ipasfrag *p); |
46 | |
47 | /* |
48 | * IP initialization: fill in IP protocol switch table. |
49 | * All protocols not implemented in kernel go to raw IP protocol handler. |
50 | */ |
51 | void ip_init(Slirp *slirp) |
52 | { |
53 | slirp->ipq.ip_link.next = slirp->ipq.ip_link.prev = &slirp->ipq.ip_link; |
54 | udp_init(slirp); |
55 | tcp_init(slirp); |
56 | icmp_init(slirp); |
57 | } |
58 | |
59 | void ip_cleanup(Slirp *slirp) |
60 | { |
61 | udp_cleanup(slirp); |
62 | tcp_cleanup(slirp); |
63 | icmp_cleanup(slirp); |
64 | } |
65 | |
66 | /* |
67 | * Ip input routine. Checksum and byte swap header. If fragmented |
68 | * try to reassemble. Process options. Pass to next level. |
69 | */ |
70 | void ip_input(struct mbuf *m) |
71 | { |
72 | Slirp *slirp = m->slirp; |
73 | register struct ip *ip; |
74 | int hlen; |
75 | |
76 | if (!slirp->in_enabled) { |
77 | goto bad; |
78 | } |
79 | |
80 | DEBUG_CALL("ip_input" ); |
81 | DEBUG_ARG("m = %p" , m); |
82 | DEBUG_ARG("m_len = %d" , m->m_len); |
83 | |
84 | if (m->m_len < sizeof(struct ip)) { |
85 | goto bad; |
86 | } |
87 | |
88 | ip = mtod(m, struct ip *); |
89 | |
90 | if (ip->ip_v != IPVERSION) { |
91 | goto bad; |
92 | } |
93 | |
94 | hlen = ip->ip_hl << 2; |
95 | if (hlen < sizeof(struct ip) || hlen > m->m_len) { /* min header length */ |
96 | goto bad; /* or packet too short */ |
97 | } |
98 | |
99 | /* keep ip header intact for ICMP reply |
100 | * ip->ip_sum = cksum(m, hlen); |
101 | * if (ip->ip_sum) { |
102 | */ |
103 | if (cksum(m, hlen)) { |
104 | goto bad; |
105 | } |
106 | |
107 | /* |
108 | * Convert fields to host representation. |
109 | */ |
110 | NTOHS(ip->ip_len); |
111 | if (ip->ip_len < hlen) { |
112 | goto bad; |
113 | } |
114 | NTOHS(ip->ip_id); |
115 | NTOHS(ip->ip_off); |
116 | |
117 | /* |
118 | * Check that the amount of data in the buffers |
119 | * is as at least much as the IP header would have us expect. |
120 | * Trim mbufs if longer than we expect. |
121 | * Drop packet if shorter than we expect. |
122 | */ |
123 | if (m->m_len < ip->ip_len) { |
124 | goto bad; |
125 | } |
126 | |
127 | /* Should drop packet if mbuf too long? hmmm... */ |
128 | if (m->m_len > ip->ip_len) |
129 | m_adj(m, ip->ip_len - m->m_len); |
130 | |
131 | /* check ip_ttl for a correct ICMP reply */ |
132 | if (ip->ip_ttl == 0) { |
133 | icmp_send_error(m, ICMP_TIMXCEED, ICMP_TIMXCEED_INTRANS, 0, "ttl" ); |
134 | goto bad; |
135 | } |
136 | |
137 | /* |
138 | * If offset or IP_MF are set, must reassemble. |
139 | * Otherwise, nothing need be done. |
140 | * (We could look in the reassembly queue to see |
141 | * if the packet was previously fragmented, |
142 | * but it's not worth the time; just let them time out.) |
143 | * |
144 | * XXX This should fail, don't fragment yet |
145 | */ |
146 | if (ip->ip_off & ~IP_DF) { |
147 | register struct ipq *fp; |
148 | struct qlink *l; |
149 | /* |
150 | * Look for queue of fragments |
151 | * of this datagram. |
152 | */ |
153 | for (l = slirp->ipq.ip_link.next; l != &slirp->ipq.ip_link; |
154 | l = l->next) { |
155 | fp = container_of(l, struct ipq, ip_link); |
156 | if (ip->ip_id == fp->ipq_id && |
157 | ip->ip_src.s_addr == fp->ipq_src.s_addr && |
158 | ip->ip_dst.s_addr == fp->ipq_dst.s_addr && |
159 | ip->ip_p == fp->ipq_p) |
160 | goto found; |
161 | } |
162 | fp = NULL; |
163 | found: |
164 | |
165 | /* |
166 | * Adjust ip_len to not reflect header, |
167 | * set ip_mff if more fragments are expected, |
168 | * convert offset of this to bytes. |
169 | */ |
170 | ip->ip_len -= hlen; |
171 | if (ip->ip_off & IP_MF) |
172 | ip->ip_tos |= 1; |
173 | else |
174 | ip->ip_tos &= ~1; |
175 | |
176 | ip->ip_off <<= 3; |
177 | |
178 | /* |
179 | * If datagram marked as having more fragments |
180 | * or if this is not the first fragment, |
181 | * attempt reassembly; if it succeeds, proceed. |
182 | */ |
183 | if (ip->ip_tos & 1 || ip->ip_off) { |
184 | ip = ip_reass(slirp, ip, fp); |
185 | if (ip == NULL) |
186 | return; |
187 | m = dtom(slirp, ip); |
188 | } else if (fp) |
189 | ip_freef(slirp, fp); |
190 | |
191 | } else |
192 | ip->ip_len -= hlen; |
193 | |
194 | /* |
195 | * Switch out to protocol's input routine. |
196 | */ |
197 | switch (ip->ip_p) { |
198 | case IPPROTO_TCP: |
199 | tcp_input(m, hlen, (struct socket *)NULL, AF_INET); |
200 | break; |
201 | case IPPROTO_UDP: |
202 | udp_input(m, hlen); |
203 | break; |
204 | case IPPROTO_ICMP: |
205 | icmp_input(m, hlen); |
206 | break; |
207 | default: |
208 | m_free(m); |
209 | } |
210 | return; |
211 | bad: |
212 | m_free(m); |
213 | } |
214 | |
215 | #define iptofrag(P) ((struct ipasfrag *)(((char *)(P)) - sizeof(struct qlink))) |
216 | #define fragtoip(P) ((struct ip *)(((char *)(P)) + sizeof(struct qlink))) |
217 | /* |
218 | * Take incoming datagram fragment and try to |
219 | * reassemble it into whole datagram. If a chain for |
220 | * reassembly of this datagram already exists, then it |
221 | * is given as fp; otherwise have to make a chain. |
222 | */ |
223 | static struct ip *ip_reass(Slirp *slirp, struct ip *ip, struct ipq *fp) |
224 | { |
225 | register struct mbuf *m = dtom(slirp, ip); |
226 | register struct ipasfrag *q; |
227 | int hlen = ip->ip_hl << 2; |
228 | int i, next; |
229 | |
230 | DEBUG_CALL("ip_reass" ); |
231 | DEBUG_ARG("ip = %p" , ip); |
232 | DEBUG_ARG("fp = %p" , fp); |
233 | DEBUG_ARG("m = %p" , m); |
234 | |
235 | /* |
236 | * Presence of header sizes in mbufs |
237 | * would confuse code below. |
238 | * Fragment m_data is concatenated. |
239 | */ |
240 | m->m_data += hlen; |
241 | m->m_len -= hlen; |
242 | |
243 | /* |
244 | * If first fragment to arrive, create a reassembly queue. |
245 | */ |
246 | if (fp == NULL) { |
247 | struct mbuf *t = m_get(slirp); |
248 | |
249 | if (t == NULL) { |
250 | goto dropfrag; |
251 | } |
252 | fp = mtod(t, struct ipq *); |
253 | insque(&fp->ip_link, &slirp->ipq.ip_link); |
254 | fp->ipq_ttl = IPFRAGTTL; |
255 | fp->ipq_p = ip->ip_p; |
256 | fp->ipq_id = ip->ip_id; |
257 | fp->frag_link.next = fp->frag_link.prev = &fp->frag_link; |
258 | fp->ipq_src = ip->ip_src; |
259 | fp->ipq_dst = ip->ip_dst; |
260 | q = (struct ipasfrag *)fp; |
261 | goto insert; |
262 | } |
263 | |
264 | /* |
265 | * Find a segment which begins after this one does. |
266 | */ |
267 | for (q = fp->frag_link.next; q != (struct ipasfrag *)&fp->frag_link; |
268 | q = q->ipf_next) |
269 | if (q->ipf_off > ip->ip_off) |
270 | break; |
271 | |
272 | /* |
273 | * If there is a preceding segment, it may provide some of |
274 | * our data already. If so, drop the data from the incoming |
275 | * segment. If it provides all of our data, drop us. |
276 | */ |
277 | if (q->ipf_prev != &fp->frag_link) { |
278 | struct ipasfrag *pq = q->ipf_prev; |
279 | i = pq->ipf_off + pq->ipf_len - ip->ip_off; |
280 | if (i > 0) { |
281 | if (i >= ip->ip_len) |
282 | goto dropfrag; |
283 | m_adj(dtom(slirp, ip), i); |
284 | ip->ip_off += i; |
285 | ip->ip_len -= i; |
286 | } |
287 | } |
288 | |
289 | /* |
290 | * While we overlap succeeding segments trim them or, |
291 | * if they are completely covered, dequeue them. |
292 | */ |
293 | while (q != (struct ipasfrag *)&fp->frag_link && |
294 | ip->ip_off + ip->ip_len > q->ipf_off) { |
295 | i = (ip->ip_off + ip->ip_len) - q->ipf_off; |
296 | if (i < q->ipf_len) { |
297 | q->ipf_len -= i; |
298 | q->ipf_off += i; |
299 | m_adj(dtom(slirp, q), i); |
300 | break; |
301 | } |
302 | q = q->ipf_next; |
303 | m_free(dtom(slirp, q->ipf_prev)); |
304 | ip_deq(q->ipf_prev); |
305 | } |
306 | |
307 | insert: |
308 | /* |
309 | * Stick new segment in its place; |
310 | * check for complete reassembly. |
311 | */ |
312 | ip_enq(iptofrag(ip), q->ipf_prev); |
313 | next = 0; |
314 | for (q = fp->frag_link.next; q != (struct ipasfrag *)&fp->frag_link; |
315 | q = q->ipf_next) { |
316 | if (q->ipf_off != next) |
317 | return NULL; |
318 | next += q->ipf_len; |
319 | } |
320 | if (((struct ipasfrag *)(q->ipf_prev))->ipf_tos & 1) |
321 | return NULL; |
322 | |
323 | /* |
324 | * Reassembly is complete; concatenate fragments. |
325 | */ |
326 | q = fp->frag_link.next; |
327 | m = dtom(slirp, q); |
328 | |
329 | int was_ext = m->m_flags & M_EXT; |
330 | |
331 | q = (struct ipasfrag *)q->ipf_next; |
332 | while (q != (struct ipasfrag *)&fp->frag_link) { |
333 | struct mbuf *t = dtom(slirp, q); |
334 | q = (struct ipasfrag *)q->ipf_next; |
335 | m_cat(m, t); |
336 | } |
337 | |
338 | /* |
339 | * Create header for new ip packet by |
340 | * modifying header of first packet; |
341 | * dequeue and discard fragment reassembly header. |
342 | * Make header visible. |
343 | */ |
344 | q = fp->frag_link.next; |
345 | |
346 | /* |
347 | * If the fragments concatenated to an mbuf that's |
348 | * bigger than the total size of the fragment, then and |
349 | * m_ext buffer was alloced. But fp->ipq_next points to |
350 | * the old buffer (in the mbuf), so we must point ip |
351 | * into the new buffer. |
352 | */ |
353 | if (!was_ext && m->m_flags & M_EXT) { |
354 | int delta = (char *)q - m->m_dat; |
355 | q = (struct ipasfrag *)(m->m_ext + delta); |
356 | } |
357 | |
358 | ip = fragtoip(q); |
359 | ip->ip_len = next; |
360 | ip->ip_tos &= ~1; |
361 | ip->ip_src = fp->ipq_src; |
362 | ip->ip_dst = fp->ipq_dst; |
363 | remque(&fp->ip_link); |
364 | (void)m_free(dtom(slirp, fp)); |
365 | m->m_len += (ip->ip_hl << 2); |
366 | m->m_data -= (ip->ip_hl << 2); |
367 | |
368 | return ip; |
369 | |
370 | dropfrag: |
371 | m_free(m); |
372 | return NULL; |
373 | } |
374 | |
375 | /* |
376 | * Free a fragment reassembly header and all |
377 | * associated datagrams. |
378 | */ |
379 | static void ip_freef(Slirp *slirp, struct ipq *fp) |
380 | { |
381 | register struct ipasfrag *q, *p; |
382 | |
383 | for (q = fp->frag_link.next; q != (struct ipasfrag *)&fp->frag_link; |
384 | q = p) { |
385 | p = q->ipf_next; |
386 | ip_deq(q); |
387 | m_free(dtom(slirp, q)); |
388 | } |
389 | remque(&fp->ip_link); |
390 | (void)m_free(dtom(slirp, fp)); |
391 | } |
392 | |
393 | /* |
394 | * Put an ip fragment on a reassembly chain. |
395 | * Like insque, but pointers in middle of structure. |
396 | */ |
397 | static void ip_enq(register struct ipasfrag *p, register struct ipasfrag *prev) |
398 | { |
399 | DEBUG_CALL("ip_enq" ); |
400 | DEBUG_ARG("prev = %p" , prev); |
401 | p->ipf_prev = prev; |
402 | p->ipf_next = prev->ipf_next; |
403 | ((struct ipasfrag *)(prev->ipf_next))->ipf_prev = p; |
404 | prev->ipf_next = p; |
405 | } |
406 | |
407 | /* |
408 | * To ip_enq as remque is to insque. |
409 | */ |
410 | static void ip_deq(register struct ipasfrag *p) |
411 | { |
412 | ((struct ipasfrag *)(p->ipf_prev))->ipf_next = p->ipf_next; |
413 | ((struct ipasfrag *)(p->ipf_next))->ipf_prev = p->ipf_prev; |
414 | } |
415 | |
416 | /* |
417 | * IP timer processing; |
418 | * if a timer expires on a reassembly |
419 | * queue, discard it. |
420 | */ |
421 | void ip_slowtimo(Slirp *slirp) |
422 | { |
423 | struct qlink *l; |
424 | |
425 | DEBUG_CALL("ip_slowtimo" ); |
426 | |
427 | l = slirp->ipq.ip_link.next; |
428 | |
429 | if (l == NULL) |
430 | return; |
431 | |
432 | while (l != &slirp->ipq.ip_link) { |
433 | struct ipq *fp = container_of(l, struct ipq, ip_link); |
434 | l = l->next; |
435 | if (--fp->ipq_ttl == 0) { |
436 | ip_freef(slirp, fp); |
437 | } |
438 | } |
439 | } |
440 | |
441 | /* |
442 | * Strip out IP options, at higher |
443 | * level protocol in the kernel. |
444 | * Second argument is buffer to which options |
445 | * will be moved, and return value is their length. |
446 | * (XXX) should be deleted; last arg currently ignored. |
447 | */ |
448 | void ip_stripoptions(register struct mbuf *m, struct mbuf *mopt) |
449 | { |
450 | register int i; |
451 | struct ip *ip = mtod(m, struct ip *); |
452 | register char *opts; |
453 | int olen; |
454 | |
455 | olen = (ip->ip_hl << 2) - sizeof(struct ip); |
456 | opts = (char *)(ip + 1); |
457 | i = m->m_len - (sizeof(struct ip) + olen); |
458 | memcpy(opts, opts + olen, (unsigned)i); |
459 | m->m_len -= olen; |
460 | |
461 | ip->ip_hl = sizeof(struct ip) >> 2; |
462 | } |
463 | |