Index: head/lib/libstand/Makefile =================================================================== --- head/lib/libstand/Makefile +++ head/lib/libstand/Makefile @@ -141,7 +141,7 @@ fstat.c close.c lseek.c open.c read.c write.c readdir.c # network routines -SRCS+= arp.c ether.c inet_ntoa.c in_cksum.c net.c udp.c netif.c rpc.c +SRCS+= arp.c ether.c ip.c inet_ntoa.c in_cksum.c net.c udp.c netif.c rpc.c # network info services: SRCS+= bootp.c rarp.c bootparam.c Index: head/lib/libstand/ip.c =================================================================== --- head/lib/libstand/ip.c +++ head/lib/libstand/ip.c @@ -0,0 +1,422 @@ +/* + * Copyright (c) 1992 Regents of the University of California. + * All rights reserved. + * + * This software was developed by the Computer Systems Engineering group + * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and + * contributed to Berkeley. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * The send and receive functions were originally implemented in udp.c and + * moved here. Also it is likely some more cleanup can be done, especially + * once we will implement the support for tcp. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "stand.h" +#include "net.h" + +typedef STAILQ_HEAD(ipqueue, ip_queue) ip_queue_t; +struct ip_queue { + void *ipq_pkt; + struct ip *ipq_hdr; + STAILQ_ENTRY(ip_queue) ipq_next; +}; + +/* + * Fragment re-assembly queue. + */ +struct ip_reasm { + struct in_addr ip_src; + struct in_addr ip_dst; + uint16_t ip_id; + uint8_t ip_proto; + uint8_t ip_ttl; + size_t ip_total_size; + ip_queue_t ip_queue; + void *ip_pkt; + struct ip *ip_hdr; + STAILQ_ENTRY(ip_reasm) ip_next; +}; + +STAILQ_HEAD(ire_list, ip_reasm) ire_list = STAILQ_HEAD_INITIALIZER(ire_list); + +/* Caller must leave room for ethernet and ip headers in front!! */ +ssize_t +sendip(struct iodesc *d, void *pkt, size_t len, uint8_t proto) +{ + ssize_t cc; + struct ip *ip; + u_char *ea; + +#ifdef NET_DEBUG + if (debug) { + printf("sendip: proto: %x d=%p called.\n", proto, (void *)d); + if (d) { + printf("saddr: %s:%d", + inet_ntoa(d->myip), ntohs(d->myport)); + printf(" daddr: %s:%d\n", + inet_ntoa(d->destip), ntohs(d->destport)); + } + } +#endif + + ip = (struct ip *)pkt - 1; + len += sizeof(*ip); + + bzero(ip, sizeof(*ip)); + + ip->ip_v = IPVERSION; /* half-char */ + ip->ip_hl = sizeof(*ip) >> 2; /* half-char */ + ip->ip_len = htons(len); + ip->ip_p = proto; /* char */ + ip->ip_ttl = IPDEFTTL; /* char */ + ip->ip_src = d->myip; + ip->ip_dst = d->destip; + ip->ip_sum = in_cksum(ip, sizeof(*ip)); /* short, but special */ + + if (ip->ip_dst.s_addr == INADDR_BROADCAST || ip->ip_src.s_addr == 0 || + netmask == 0 || SAMENET(ip->ip_src, ip->ip_dst, netmask)) + ea = arpwhohas(d, ip->ip_dst); + else + ea = arpwhohas(d, gateip); + + cc = sendether(d, ip, len, ea, ETHERTYPE_IP); + if (cc == -1) + return (-1); + if (cc != len) + panic("sendip: bad write (%zd != %zd)", cc, len); + return (cc - sizeof(*ip)); +} + +static void +ip_reasm_free(struct ip_reasm *ipr) +{ + struct ip_queue *ipq; + + while ((ipq = STAILQ_FIRST(&ipr->ip_queue)) != NULL) { + STAILQ_REMOVE_HEAD(&ipr->ip_queue, ipq_next); + free(ipq->ipq_pkt); + free(ipq); + } + free(ipr->ip_pkt); + free(ipr); +} + +static int +ip_reasm_add(struct ip_reasm *ipr, void *pkt, struct ip *ip) +{ + struct ip_queue *ipq, *prev, *p; + + if ((ipq = calloc(1, sizeof (*ipq))) == NULL) + return (1); + + ipq->ipq_pkt = pkt; + ipq->ipq_hdr = ip; + + prev = NULL; + STAILQ_FOREACH(p, &ipr->ip_queue, ipq_next) { + if ((ntohs(p->ipq_hdr->ip_off) & IP_OFFMASK) < + (ntohs(ip->ip_off) & IP_OFFMASK)) { + prev = p; + continue; + } + if (prev == NULL) + break; + + STAILQ_INSERT_AFTER(&ipr->ip_queue, prev, ipq, ipq_next); + return (0); + } + STAILQ_INSERT_HEAD(&ipr->ip_queue, ipq, ipq_next); + return (0); +} + +/* + * Receive a IP packet and validate it is for us. + */ +static ssize_t +readipv4(struct iodesc *d, void **pkt, void **payload, time_t tleft, + uint8_t proto) +{ + ssize_t n; + size_t hlen; + struct ether_header *eh; + struct ip *ip; + struct udphdr *uh; + uint16_t etype; /* host order */ + char *ptr; + struct ip_reasm *ipr; + struct ip_queue *ipq, *last; + +#ifdef NET_DEBUG + if (debug) + printf("readip: called\n"); +#endif + + ip = NULL; + ptr = NULL; + n = readether(d, (void **)&ptr, (void **)&ip, tleft, &etype); + if (n == -1 || n < sizeof(*ip) + sizeof(*uh)) { + free(ptr); + return (-1); + } + + /* Ethernet address checks now in readether() */ + + /* Need to respond to ARP requests. */ + if (etype == ETHERTYPE_ARP) { + struct arphdr *ah = (void *)ip; + if (ah->ar_op == htons(ARPOP_REQUEST)) { + /* Send ARP reply */ + arp_reply(d, ah); + } + free(ptr); + errno = EAGAIN; /* Call me again. */ + return (-1); + } + + if (etype != ETHERTYPE_IP) { +#ifdef NET_DEBUG + if (debug) + printf("readip: not IP. ether_type=%x\n", etype); +#endif + free(ptr); + return (-1); + } + + /* Check ip header */ + if (ip->ip_v != IPVERSION || + ip->ip_p != proto) { /* half char */ +#ifdef NET_DEBUG + if (debug) { + printf("readip: IP version or proto. ip_v=%d ip_p=%d\n", + ip->ip_v, ip->ip_p); + } +#endif + free(ptr); + return (-1); + } + + hlen = ip->ip_hl << 2; + if (hlen < sizeof(*ip) || + in_cksum(ip, hlen) != 0) { +#ifdef NET_DEBUG + if (debug) + printf("readip: short hdr or bad cksum.\n"); +#endif + free(ptr); + return (-1); + } + if (n < ntohs(ip->ip_len)) { +#ifdef NET_DEBUG + if (debug) + printf("readip: bad length %d < %d.\n", + (int)n, ntohs(ip->ip_len)); +#endif + free(ptr); + return (-1); + } + if (d->myip.s_addr && ip->ip_dst.s_addr != d->myip.s_addr) { +#ifdef NET_DEBUG + if (debug) { + printf("readip: bad saddr %s != ", inet_ntoa(d->myip)); + printf("%s\n", inet_ntoa(ip->ip_dst)); + } +#endif + free(ptr); + return (-1); + } + + /* Unfragmented packet. */ + if ((ntohs(ip->ip_off) & IP_MF) == 0 && + (ntohs(ip->ip_off) & IP_OFFMASK) == 0) { + uh = (struct udphdr *)((uintptr_t)ip + sizeof (*ip)); + /* If there were ip options, make them go away */ + if (hlen != sizeof(*ip)) { + bcopy(((u_char *)ip) + hlen, uh, uh->uh_ulen - hlen); + ip->ip_len = htons(sizeof(*ip)); + n -= hlen - sizeof(*ip); + } + + n = (n > (ntohs(ip->ip_len) - sizeof(*ip))) ? + ntohs(ip->ip_len) - sizeof(*ip) : n; + *pkt = ptr; + *payload = (void *)((uintptr_t)ip + sizeof(*ip)); + return (n); + } + + STAILQ_FOREACH(ipr, &ire_list, ip_next) { + if (ipr->ip_src.s_addr == ip->ip_src.s_addr && + ipr->ip_dst.s_addr == ip->ip_dst.s_addr && + ipr->ip_id == ip->ip_id && + ipr->ip_proto == ip->ip_p) + break; + } + + /* Allocate new reassembly entry */ + if (ipr == NULL) { + if ((ipr = calloc(1, sizeof (*ipr))) == NULL) { + free(ptr); + return (-1); + } + + ipr->ip_src = ip->ip_src; + ipr->ip_dst = ip->ip_dst; + ipr->ip_id = ip->ip_id; + ipr->ip_proto = ip->ip_p; + ipr->ip_ttl = MAXTTL; + STAILQ_INIT(&ipr->ip_queue); + STAILQ_INSERT_TAIL(&ire_list, ipr, ip_next); + } + + if (ip_reasm_add(ipr, ptr, ip) != 0) { + STAILQ_REMOVE(&ire_list, ipr, ip_reasm, ip_next); + free(ipr); + free(ptr); + return (-1); + } + + if ((ntohs(ip->ip_off) & IP_MF) == 0) { + ipr->ip_total_size = (8 * (ntohs(ip->ip_off) & IP_OFFMASK)); + ipr->ip_total_size += n + sizeof (*ip); + ipr->ip_total_size += sizeof (struct ether_header); + + ipr->ip_pkt = malloc(ipr->ip_total_size + 2); + if (ipr->ip_pkt == NULL) { + STAILQ_REMOVE(&ire_list, ipr, ip_reasm, ip_next); + ip_reasm_free(ipr); + return (-1); + } + } + + /* + * If we do not have re-assembly buffer ipr->ip_pkt, we are still + * missing fragments, so just restart the read. + */ + if (ipr->ip_pkt == NULL) { + errno = EAGAIN; + return (-1); + } + + /* + * Walk the packet list in reassembly queue, if we got all the + * fragments, build the packet. + */ + n = 0; + last = NULL; + STAILQ_FOREACH(ipq, &ipr->ip_queue, ipq_next) { + if ((ntohs(ipq->ipq_hdr->ip_off) & IP_OFFMASK) != n / 8) { + errno = EAGAIN; + return (-1); + } + + n += ntohs(ipq->ipq_hdr->ip_len) - (ipq->ipq_hdr->ip_hl << 2); + last = ipq; + } + if ((ntohs(last->ipq_hdr->ip_off) & IP_MF) != 0) { + errno = EAGAIN; + return (-1); + } + + ipq = STAILQ_FIRST(&ipr->ip_queue); + /* Fabricate ethernet header */ + eh = (struct ether_header *)((uintptr_t)ipr->ip_pkt + 2); + bcopy((void *)((uintptr_t)ipq->ipq_pkt + 2), eh, sizeof (*eh)); + + /* Fabricate IP header */ + ipr->ip_hdr = (struct ip *)((uintptr_t)eh + sizeof (*eh)); + bcopy(ipq->ipq_hdr, ipr->ip_hdr, sizeof (*ipr->ip_hdr)); + ipr->ip_hdr->ip_hl = sizeof (*ipr->ip_hdr) >> 2; + ipr->ip_hdr->ip_len = htons(n); + ipr->ip_hdr->ip_sum = 0; + ipr->ip_hdr->ip_sum = in_cksum(ipr->ip_hdr, sizeof (*ipr->ip_hdr)); + + n = 0; + ptr = (char *)((uintptr_t)ipr->ip_hdr + sizeof (*ipr->ip_hdr)); + STAILQ_FOREACH(ipq, &ipr->ip_queue, ipq_next) { + char *data; + size_t len; + + hlen = ipq->ipq_hdr->ip_hl << 2; + len = ntohs(ipq->ipq_hdr->ip_len) - hlen; + data = (char *)((uintptr_t)ipq->ipq_hdr + hlen); + + bcopy(data, ptr + n, len); + n += len; + } + + *pkt = ipr->ip_pkt; + ipr->ip_pkt = NULL; /* Avoid free from ip_reasm_free() */ + *payload = ptr; + + /* Clean up the reassembly list */ + while ((ipr = STAILQ_FIRST(&ire_list)) != NULL) { + STAILQ_REMOVE_HEAD(&ire_list, ip_next); + ip_reasm_free(ipr); + } + return (n); +} + +/* + * Receive a IP packet. + */ +ssize_t +readip(struct iodesc *d, void **pkt, void **payload, time_t tleft, + uint8_t proto) +{ + time_t t; + ssize_t ret = -1; + + t = getsecs(); + while ((getsecs() - t) < tleft) { + errno = 0; + ret = readipv4(d, pkt, payload, tleft, proto); + if (errno != EAGAIN) + break; + } + return (ret); +} Index: head/lib/libstand/net.h =================================================================== --- head/lib/libstand/net.h +++ head/lib/libstand/net.h @@ -108,6 +108,8 @@ u_char *dea, int etype); ssize_t readether(struct iodesc *, void **, void **, time_t, uint16_t *); +ssize_t sendip(struct iodesc *, void *, size_t, uint8_t); +ssize_t readip(struct iodesc *, void **, void **, time_t, uint8_t); ssize_t sendudp(struct iodesc *, void *, size_t); ssize_t readudp(struct iodesc *, void **, void **, time_t); ssize_t sendrecv(struct iodesc *, Index: head/lib/libstand/udp.c =================================================================== --- head/lib/libstand/udp.c +++ head/lib/libstand/udp.c @@ -62,9 +62,8 @@ sendudp(struct iodesc *d, void *pkt, size_t len) { ssize_t cc; - struct ip *ip; + struct udpiphdr *ui; struct udphdr *uh; - u_char *ea; #ifdef NET_DEBUG if (debug) { @@ -78,52 +77,31 @@ } #endif - uh = (struct udphdr *)pkt - 1; - ip = (struct ip *)uh - 1; - len += sizeof(*ip) + sizeof(*uh); - - bzero(ip, sizeof(*ip) + sizeof(*uh)); + ui = (struct udpiphdr *)pkt - 1; + bzero(ui, sizeof(*ui)); - ip->ip_v = IPVERSION; /* half-char */ - ip->ip_hl = sizeof(*ip) >> 2; /* half-char */ - ip->ip_len = htons(len); - ip->ip_p = IPPROTO_UDP; /* char */ - ip->ip_ttl = IPDEFTTL; /* char */ - ip->ip_src = d->myip; - ip->ip_dst = d->destip; - ip->ip_sum = in_cksum(ip, sizeof(*ip)); /* short, but special */ + uh = (struct udphdr *)pkt - 1; + len += sizeof(*uh); uh->uh_sport = d->myport; uh->uh_dport = d->destport; - uh->uh_ulen = htons(len - sizeof(*ip)); + uh->uh_ulen = htons(len); -#ifndef UDP_NO_CKSUM - { - struct udpiphdr *ui; - struct ip tip; + ui->ui_pr = IPPROTO_UDP; + ui->ui_len = uh->uh_ulen; + ui->ui_src = d->myip; + ui->ui_dst = d->destip; - /* Calculate checksum (must save and restore ip header) */ - tip = *ip; - ui = (struct udpiphdr *)ip; - bzero(&ui->ui_x1, sizeof(ui->ui_x1)); - ui->ui_len = uh->uh_ulen; - uh->uh_sum = in_cksum(ui, len); - *ip = tip; - } +#ifndef UDP_NO_CKSUM + uh->uh_sum = in_cksum(ui, len + sizeof (struct ip)); #endif - if (ip->ip_dst.s_addr == INADDR_BROADCAST || ip->ip_src.s_addr == 0 || - netmask == 0 || SAMENET(ip->ip_src, ip->ip_dst, netmask)) - ea = arpwhohas(d, ip->ip_dst); - else - ea = arpwhohas(d, gateip); - - cc = sendether(d, ip, len, ea, ETHERTYPE_IP); + cc = sendip(d, uh, len, IPPROTO_UDP); if (cc == -1) return (-1); if (cc != len) panic("sendudp: bad write (%zd != %zd)", cc, len); - return (cc - (sizeof(*ip) + sizeof(*uh))); + return (cc - sizeof(*uh)); } /* @@ -133,10 +111,7 @@ readudp(struct iodesc *d, void **pkt, void **payload, time_t tleft) { ssize_t n; - size_t hlen; - struct ip *ip; struct udphdr *uh; - uint16_t etype; /* host order */ void *ptr; #ifdef NET_DEBUG @@ -144,84 +119,14 @@ printf("readudp: called\n"); #endif - ip = NULL; + uh = NULL; ptr = NULL; - n = readether(d, &ptr, (void **)&ip, tleft, &etype); - if (n == -1 || n < sizeof(*ip) + sizeof(*uh)) { + n = readip(d, &ptr, (void **)&uh, tleft, IPPROTO_UDP); + if (n == -1 || n < sizeof(*uh) || n != ntohs(uh->uh_ulen)) { free(ptr); return (-1); } - /* Ethernet address checks now in readether() */ - - /* Need to respond to ARP requests. */ - if (etype == ETHERTYPE_ARP) { - struct arphdr *ah = (void *)ip; - if (ah->ar_op == htons(ARPOP_REQUEST)) { - /* Send ARP reply */ - arp_reply(d, ah); - } - free(ptr); - return (-1); - } - - if (etype != ETHERTYPE_IP) { -#ifdef NET_DEBUG - if (debug) - printf("readudp: not IP. ether_type=%x\n", etype); -#endif - free(ptr); - return (-1); - } - - /* Check ip header */ - if (ip->ip_v != IPVERSION || - ip->ip_p != IPPROTO_UDP) { /* half char */ -#ifdef NET_DEBUG - if (debug) - printf("readudp: IP version or not UDP. ip_v=%d ip_p=%d\n", ip->ip_v, ip->ip_p); -#endif - free(ptr); - return (-1); - } - - hlen = ip->ip_hl << 2; - if (hlen < sizeof(*ip) || - in_cksum(ip, hlen) != 0) { -#ifdef NET_DEBUG - if (debug) - printf("readudp: short hdr or bad cksum.\n"); -#endif - free(ptr); - return (-1); - } - if (n < ntohs(ip->ip_len)) { -#ifdef NET_DEBUG - if (debug) - printf("readudp: bad length %d < %d.\n", - (int)n, ntohs(ip->ip_len)); -#endif - free(ptr); - return (-1); - } - if (d->myip.s_addr && ip->ip_dst.s_addr != d->myip.s_addr) { -#ifdef NET_DEBUG - if (debug) { - printf("readudp: bad saddr %s != ", inet_ntoa(d->myip)); - printf("%s\n", inet_ntoa(ip->ip_dst)); - } -#endif - free(ptr); - return (-1); - } - - uh = (struct udphdr *)((uintptr_t)ip + sizeof (*ip)); - /* If there were ip options, make them go away */ - if (hlen != sizeof(*ip)) { - bcopy(((u_char *)ip) + hlen, uh, uh->uh_ulen - hlen); - ip->ip_len = htons(sizeof(*ip)); - n -= hlen - sizeof(*ip); - } if (uh->uh_dport != d->myport) { #ifdef NET_DEBUG if (debug) @@ -235,16 +140,13 @@ #ifndef UDP_NO_CKSUM if (uh->uh_sum) { struct udpiphdr *ui; + struct ip *ip; struct ip tip; n = ntohs(uh->uh_ulen) + sizeof(*ip); - if (n > RECV_SIZE - ETHER_SIZE) { - printf("readudp: huge packet, udp len %d\n", (int)n); - free(ptr); - return (-1); - } /* Check checksum (must save and restore ip header) */ + ip = (struct ip *)uh - 1; tip = *ip; ui = (struct udpiphdr *)ip; bzero(&ui->ui_x1, sizeof(ui->ui_x1));