2 * Copyright (c) 1992 Regents of the University of California.
5 * This software was developed by the Computer Systems Engineering group
6 * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
7 * contributed to Berkeley.
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 * 3. Neither the name of the University nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35 * The send and receive functions were originally implemented in udp.c and
36 * moved here. Also it is likely some more cleanup can be done, especially
37 * once we will implement the support for tcp.
40 #include <sys/cdefs.h>
42 #include <sys/param.h>
43 #include <sys/socket.h>
44 #include <sys/queue.h>
49 #include <netinet/in.h>
50 #include <netinet/if_ether.h>
51 #include <netinet/in_systm.h>
53 #include <netinet/ip.h>
54 #include <netinet/ip_var.h>
55 #include <netinet/udp.h>
56 #include <netinet/udp_var.h>
61 typedef STAILQ_HEAD(ipqueue
, ip_queue
) ip_queue_t
;
65 STAILQ_ENTRY(ip_queue
) ipq_next
;
69 * Fragment re-assembly queue.
72 struct in_addr ip_src
;
73 struct in_addr ip_dst
;
81 STAILQ_ENTRY(ip_reasm
) ip_next
;
84 STAILQ_HEAD(ire_list
, ip_reasm
) ire_list
= STAILQ_HEAD_INITIALIZER(ire_list
);
86 /* Caller must leave room for ethernet and ip headers in front!! */
88 sendip(struct iodesc
*d
, void *pkt
, size_t len
, uint8_t proto
)
96 printf("sendip: proto: %x d=%p called.\n", proto
, (void *)d
);
98 printf("saddr: %s:%d",
99 inet_ntoa(d
->myip
), ntohs(d
->myport
));
100 printf(" daddr: %s:%d\n",
101 inet_ntoa(d
->destip
), ntohs(d
->destport
));
106 ip
= (struct ip
*)pkt
- 1;
109 bzero(ip
, sizeof(*ip
));
111 ip
->ip_v
= IPVERSION
; /* half-char */
112 ip
->ip_hl
= sizeof(*ip
) >> 2; /* half-char */
113 ip
->ip_len
= htons(len
);
114 ip
->ip_p
= proto
; /* char */
115 ip
->ip_ttl
= IPDEFTTL
; /* char */
116 ip
->ip_src
= d
->myip
;
117 ip
->ip_dst
= d
->destip
;
118 ip
->ip_sum
= in_cksum(ip
, sizeof(*ip
)); /* short, but special */
120 if (ip
->ip_dst
.s_addr
== INADDR_BROADCAST
|| ip
->ip_src
.s_addr
== 0 ||
121 netmask
== 0 || SAMENET(ip
->ip_src
, ip
->ip_dst
, netmask
))
122 ea
= arpwhohas(d
, ip
->ip_dst
);
124 ea
= arpwhohas(d
, gateip
);
126 cc
= sendether(d
, ip
, len
, ea
, ETHERTYPE_IP
);
130 panic("sendip: bad write (%zd != %zd)", cc
, len
);
131 return (cc
- sizeof(*ip
));
135 ip_reasm_free(struct ip_reasm
*ipr
)
137 struct ip_queue
*ipq
;
139 while ((ipq
= STAILQ_FIRST(&ipr
->ip_queue
)) != NULL
) {
140 STAILQ_REMOVE_HEAD(&ipr
->ip_queue
, ipq_next
);
149 ip_reasm_add(struct ip_reasm
*ipr
, void *pkt
, struct ip
*ip
)
151 struct ip_queue
*ipq
, *prev
, *p
;
153 if ((ipq
= calloc(1, sizeof (*ipq
))) == NULL
)
160 STAILQ_FOREACH(p
, &ipr
->ip_queue
, ipq_next
) {
161 if ((ntohs(p
->ipq_hdr
->ip_off
) & IP_OFFMASK
) <
162 (ntohs(ip
->ip_off
) & IP_OFFMASK
)) {
169 STAILQ_INSERT_AFTER(&ipr
->ip_queue
, prev
, ipq
, ipq_next
);
172 STAILQ_INSERT_HEAD(&ipr
->ip_queue
, ipq
, ipq_next
);
177 * Receive a IP packet and validate it is for us.
180 readipv4(struct iodesc
*d
, void **pkt
, void **payload
, time_t tleft
,
185 struct ether_header
*eh
;
188 uint16_t etype
; /* host order */
190 struct ip_reasm
*ipr
;
191 struct ip_queue
*ipq
, *last
;
195 printf("readip: called\n");
200 n
= readether(d
, (void **)&ptr
, (void **)&ip
, tleft
, &etype
);
201 if (n
== -1 || n
< sizeof(*ip
) + sizeof(*uh
)) {
206 /* Ethernet address checks now in readether() */
208 /* Need to respond to ARP requests. */
209 if (etype
== ETHERTYPE_ARP
) {
210 struct arphdr
*ah
= (void *)ip
;
211 if (ah
->ar_op
== htons(ARPOP_REQUEST
)) {
216 errno
= EAGAIN
; /* Call me again. */
220 if (etype
!= ETHERTYPE_IP
) {
223 printf("readip: not IP. ether_type=%x\n", etype
);
229 /* Check ip header */
230 if (ip
->ip_v
!= IPVERSION
|| /* half char */
234 printf("readip: IP version or proto. ip_v=%d ip_p=%d\n",
242 hlen
= ip
->ip_hl
<< 2;
243 if (hlen
< sizeof(*ip
) ||
244 in_cksum(ip
, hlen
) != 0) {
247 printf("readip: short hdr or bad cksum.\n");
252 if (n
< ntohs(ip
->ip_len
)) {
255 printf("readip: bad length %d < %d.\n",
256 (int)n
, ntohs(ip
->ip_len
));
261 if (d
->myip
.s_addr
&& ip
->ip_dst
.s_addr
!= d
->myip
.s_addr
) {
264 printf("readip: bad saddr %s != ", inet_ntoa(d
->myip
));
265 printf("%s\n", inet_ntoa(ip
->ip_dst
));
272 /* Unfragmented packet. */
273 if ((ntohs(ip
->ip_off
) & IP_MF
) == 0 &&
274 (ntohs(ip
->ip_off
) & IP_OFFMASK
) == 0) {
275 uh
= (struct udphdr
*)((uintptr_t)ip
+ sizeof (*ip
));
276 /* If there were ip options, make them go away */
277 if (hlen
!= sizeof(*ip
)) {
278 bcopy(((u_char
*)ip
) + hlen
, uh
, uh
->uh_ulen
- hlen
);
279 ip
->ip_len
= htons(sizeof(*ip
));
280 n
-= hlen
- sizeof(*ip
);
283 n
= (n
> (ntohs(ip
->ip_len
) - sizeof(*ip
))) ?
284 ntohs(ip
->ip_len
) - sizeof(*ip
) : n
;
286 *payload
= (void *)((uintptr_t)ip
+ sizeof(*ip
));
290 STAILQ_FOREACH(ipr
, &ire_list
, ip_next
) {
291 if (ipr
->ip_src
.s_addr
== ip
->ip_src
.s_addr
&&
292 ipr
->ip_dst
.s_addr
== ip
->ip_dst
.s_addr
&&
293 ipr
->ip_id
== ip
->ip_id
&&
294 ipr
->ip_proto
== ip
->ip_p
)
298 /* Allocate new reassembly entry */
300 if ((ipr
= calloc(1, sizeof (*ipr
))) == NULL
) {
305 ipr
->ip_src
= ip
->ip_src
;
306 ipr
->ip_dst
= ip
->ip_dst
;
307 ipr
->ip_id
= ip
->ip_id
;
308 ipr
->ip_proto
= ip
->ip_p
;
309 ipr
->ip_ttl
= MAXTTL
;
310 STAILQ_INIT(&ipr
->ip_queue
);
311 STAILQ_INSERT_TAIL(&ire_list
, ipr
, ip_next
);
314 if (ip_reasm_add(ipr
, ptr
, ip
) != 0) {
315 STAILQ_REMOVE(&ire_list
, ipr
, ip_reasm
, ip_next
);
321 if ((ntohs(ip
->ip_off
) & IP_MF
) == 0) {
322 ipr
->ip_total_size
= (8 * (ntohs(ip
->ip_off
) & IP_OFFMASK
));
323 ipr
->ip_total_size
+= n
+ sizeof (*ip
);
324 ipr
->ip_total_size
+= sizeof (struct ether_header
);
326 ipr
->ip_pkt
= malloc(ipr
->ip_total_size
+ 2);
327 if (ipr
->ip_pkt
== NULL
) {
328 STAILQ_REMOVE(&ire_list
, ipr
, ip_reasm
, ip_next
);
335 * If we do not have re-assembly buffer ipr->ip_pkt, we are still
336 * missing fragments, so just restart the read.
338 if (ipr
->ip_pkt
== NULL
) {
344 * Walk the packet list in reassembly queue, if we got all the
345 * fragments, build the packet.
349 STAILQ_FOREACH(ipq
, &ipr
->ip_queue
, ipq_next
) {
350 if ((ntohs(ipq
->ipq_hdr
->ip_off
) & IP_OFFMASK
) != n
/ 8) {
351 STAILQ_REMOVE(&ire_list
, ipr
, ip_reasm
, ip_next
);
356 n
+= ntohs(ipq
->ipq_hdr
->ip_len
) - (ipq
->ipq_hdr
->ip_hl
<< 2);
359 if ((ntohs(last
->ipq_hdr
->ip_off
) & IP_MF
) != 0) {
364 ipq
= STAILQ_FIRST(&ipr
->ip_queue
);
365 /* Fabricate ethernet header */
366 eh
= (struct ether_header
*)((uintptr_t)ipr
->ip_pkt
+ 2);
367 bcopy((void *)((uintptr_t)ipq
->ipq_pkt
+ 2), eh
, sizeof (*eh
));
369 /* Fabricate IP header */
370 ipr
->ip_hdr
= (struct ip
*)((uintptr_t)eh
+ sizeof (*eh
));
371 bcopy(ipq
->ipq_hdr
, ipr
->ip_hdr
, sizeof (*ipr
->ip_hdr
));
372 ipr
->ip_hdr
->ip_hl
= sizeof (*ipr
->ip_hdr
) >> 2;
373 ipr
->ip_hdr
->ip_len
= htons(n
);
374 ipr
->ip_hdr
->ip_sum
= 0;
375 ipr
->ip_hdr
->ip_sum
= in_cksum(ipr
->ip_hdr
, sizeof (*ipr
->ip_hdr
));
378 ptr
= (char *)((uintptr_t)ipr
->ip_hdr
+ sizeof (*ipr
->ip_hdr
));
379 STAILQ_FOREACH(ipq
, &ipr
->ip_queue
, ipq_next
) {
383 hlen
= ipq
->ipq_hdr
->ip_hl
<< 2;
384 len
= ntohs(ipq
->ipq_hdr
->ip_len
) - hlen
;
385 data
= (char *)((uintptr_t)ipq
->ipq_hdr
+ hlen
);
387 bcopy(data
, ptr
+ n
, len
);
392 ipr
->ip_pkt
= NULL
; /* Avoid free from ip_reasm_free() */
395 /* Clean up the reassembly list */
396 while ((ipr
= STAILQ_FIRST(&ire_list
)) != NULL
) {
397 STAILQ_REMOVE_HEAD(&ire_list
, ip_next
);
404 * Receive a IP packet.
407 readip(struct iodesc
*d
, void **pkt
, void **payload
, time_t tleft
,
414 while ((getsecs() - t
) < tleft
) {
416 ret
= readipv4(d
, pkt
, payload
, tleft
, proto
);
419 /* Bubble up the error if it wasn't successful */
423 /* We've exhausted tleft; timeout */