From: Lawrence S. <ljs...@us...> - 2014-12-01 05:14:54
|
This is an automated email from the git hooks/post-receive script. It was generated because a ref change was pushed to the repository containing the project "A pseudo Operating System for the Dreamcast.". The branch, master has been updated via 1cd1995d9108f4913323cac74b8efc9688c8135e (commit) via 024d59caa4bdaafacacdb8a0294af7cc6629bd11 (commit) from 8c4f8f6325c130788547e3f89980cf5df67773eb (commit) Those revisions listed above that are new to this repository have not appeared on any other notification email; so we list those revisions in full, below. - Log ----------------------------------------------------------------- commit 1cd1995d9108f4913323cac74b8efc9688c8135e Author: Lawrence Sebald <ljs...@us...> Date: Mon Dec 1 00:14:22 2014 -0500 Add support for UDP Lite to the network stack. commit 024d59caa4bdaafacacdb8a0294af7cc6629bd11 Author: Lawrence Sebald <ljs...@us...> Date: Sun Nov 30 20:23:58 2014 -0500 Fix ugly indentation... ----------------------------------------------------------------------- Summary of changes: include/netinet/in.h | 24 ++++- kernel/net/net_input.c | 6 +- kernel/net/net_udp.c | 290 ++++++++++++++++++++++++++++++++++++++++-------- 3 files changed, 267 insertions(+), 53 deletions(-) diff --git a/include/netinet/in.h b/include/netinet/in.h index 4437200..f7d12e4 100644 --- a/include/netinet/in.h +++ b/include/netinet/in.h @@ -182,6 +182,9 @@ extern const struct in6_addr in6addr_loopback; /** \brief Internet Protocol Version 6. */ #define IPPROTO_IPV6 41 +/** \brief Lightweight User Datagram Protocol. */ +#define IPPROTO_UDPLITE 136 + /** \defgroup ipv4_opts IPv4 protocol level options These are the various socket-level optoins that can be accessed with the @@ -193,6 +196,7 @@ extern const struct in6_addr in6addr_loopback; \see so_opts \see ipv6_opts \see udp_opts + \see udplite_opts @{ */ @@ -210,6 +214,7 @@ extern const struct in6_addr in6addr_loopback; \see so_opts \see ipv4_opts \see udp_opts + \see udplite_opts @{ */ @@ -230,12 +235,29 @@ extern const struct in6_addr in6addr_loopback; \see so_opts \see ipv4_opts \see ipv6_opts + \see udplite_opts -@{ + @{ */ #define UDP_NOCHECKSUM 25 /**< \brief Don't calculate UDP checksums */ /** @} */ +/** \defgroup udplite_opts UDP-Lite protocol level options + + These are the various socket-level options that can be accessed with the + setsockopt() and getsockopt() functions for the IPPROTO_UDPLITE level value. + + \see so_opts + \see ipv4_opts + \see ipv6_opts + \see udp_opts + + @{ +*/ +#define UDPLITE_SEND_CSCOV 26 /**< \brief Sending checksum coverage. */ +#define UDPLITE_RECV_CSCOV 27 /**< \brief Receiving checksum coverage. */ +/** @} */ + /** \brief Test if an IPv6 Address is unspecified. This macro tests whether an IPv6 address (struct in6_addr *) is an diff --git a/kernel/net/net_input.c b/kernel/net/net_input.c index 961d6a4..90851ad 100644 --- a/kernel/net/net_input.c +++ b/kernel/net/net_input.c @@ -22,9 +22,9 @@ static int net_default_input(netif_t *nif, const uint8 *data, int len) { /* If this is bound for a multicast address, make sure we actually care about the one that it gets sent to. */ if((data[0] & 0x01) && - (data[0] != 0xFF || data[1] != 0xFF || data[2] != 0xFF || - data[3] != 0xFF || data[4] != 0xFF || data[5] != 0xFF) && - !net_multicast_check(data)) { + (data[0] != 0xFF || data[1] != 0xFF || data[2] != 0xFF || + data[3] != 0xFF || data[4] != 0xFF || data[5] != 0xFF) && + !net_multicast_check(data)) { return 0; } diff --git a/kernel/net/net_udp.c b/kernel/net/net_udp.c index ae89225..d806e20 100644 --- a/kernel/net/net_udp.c +++ b/kernel/net/net_udp.c @@ -10,6 +10,7 @@ #include <stdlib.h> #include <errno.h> #include <poll.h> +#include <stdint.h> #include <arpa/inet.h> #include <kos/net.h> #include <kos/mutex.h> @@ -44,6 +45,7 @@ struct udp_pkt { TAILQ_HEAD(udp_pkt_queue, udp_pkt); #define UDPSOCK_NO_CHECKSUM 0x00000001 +#define UDPSOCK_LITE_RCVCOV 0x00000002 struct udp_sock { LIST_ENTRY(udp_sock) sock_list; @@ -57,6 +59,11 @@ struct udp_sock { int hop_limit; file_t sock; + struct { + uint16_t send_cscov; + uint16_t recv_cscov; + } udp_lite; + struct udp_pkt_queue packets; }; @@ -69,7 +76,7 @@ static net_udp_stats_t udp_stats = { 0 }; static int net_udp_send_raw(netif_t *net, const struct sockaddr_in6 *src, const struct sockaddr_in6 *dst, const uint8 *data, size_t size, uint32_t flags, int hops, - uint32_t iflags); + uint32_t iflags, int proto, uint16_t cscov); static int net_udp_accept(net_socket_t *hnd, struct sockaddr *addr, socklen_t *addr_len) { @@ -421,7 +428,8 @@ static ssize_t net_udp_sendto(net_socket_t *hnd, const void *message, struct sockaddr_in *realaddr; struct sockaddr_in6 realaddr6; uint32_t sflags, iflags; - int hops; + int hops, proto; + uint16_t cscov; struct sockaddr_in6 local_addr; (void)flags; @@ -449,7 +457,7 @@ static ssize_t net_udp_sendto(net_socket_t *hnd, const void *message, } if(!IN6_IS_ADDR_UNSPECIFIED(&udpsock->remote_addr.sin6_addr) && - udpsock->remote_addr.sin6_port != 0) { + udpsock->remote_addr.sin6_port != 0) { if(addr) { errno = EISCONN; goto err; @@ -521,11 +529,13 @@ static ssize_t net_udp_sendto(net_socket_t *hnd, const void *message, sflags = udpsock->flags; iflags = udpsock->int_flags; hops = udpsock->hop_limit; + proto = udpsock->proto; + cscov = udpsock->udp_lite.send_cscov; mutex_unlock(&udp_mutex); return net_udp_send_raw(NULL, &local_addr, &realaddr6, (const uint8 *)message, length, sflags, hops, - iflags); + iflags, proto, cscov); err: mutex_unlock(&udp_mutex); return -1; @@ -578,7 +588,10 @@ static int net_udp_socket(net_socket_t *hnd, int domain, int type, int proto) { return -1; } - if(proto && proto != IPPROTO_UDP) { + if(!proto) { + proto = IPPROTO_UDP; + } + else if(proto != IPPROTO_UDP && proto != IPPROTO_UDPLITE) { errno = EPROTONOSUPPORT; return -1; } @@ -586,7 +599,7 @@ static int net_udp_socket(net_socket_t *hnd, int domain, int type, int proto) { memset(udpsock, 0, sizeof(struct udp_sock)); TAILQ_INIT(&udpsock->packets); udpsock->domain = domain; - udpsock->proto = IPPROTO_UDP; + udpsock->proto = proto; udpsock->hop_limit = UDP_DEFAULT_HOPS; if(irq_inside_int()) { @@ -705,11 +718,11 @@ static int net_udp_getsockopt(net_socket_t *hnd, int level, int option_name, break; case IPPROTO_UDP: + if(sock->proto != IPPROTO_UDP) + goto ret_inval; + switch(option_name) { case UDP_NOCHECKSUM: - if(sock->proto != IPPROTO_UDP) - goto ret_inval; - /* UDP/IPv6 packets must always have a checksum. */ if(sock->domain == AF_INET6) { tmp = 0; @@ -721,6 +734,22 @@ static int net_udp_getsockopt(net_socket_t *hnd, int level, int option_name, } break; + + case IPPROTO_UDPLITE: + if(sock->proto != IPPROTO_UDPLITE) + goto ret_inval; + + switch(option_name) { + case UDPLITE_SEND_CSCOV: + tmp = sock->udp_lite.send_cscov; + goto copy_int; + + case UDPLITE_RECV_CSCOV: + tmp = sock->udp_lite.recv_cscov; + goto copy_int; + } + + break; } /* If it wasn't handled, return that error. */ @@ -734,7 +763,6 @@ ret_inval: return -1; copy_int: - if(*option_len >= sizeof(int)) { memcpy(option_value, &tmp, sizeof(int)); *option_len = sizeof(int); @@ -839,15 +867,15 @@ static int net_udp_setsockopt(net_socket_t *hnd, int level, int option_name, break; case IPPROTO_UDP: + if(sock->proto != IPPROTO_UDP) + goto ret_inval; + switch(option_name) { case UDP_NOCHECKSUM: /* UDP/IPv6 packets must always have a checksum. */ if(sock->domain == AF_INET6) goto ret_inval; - if(sock->proto != IPPROTO_UDP) - goto ret_inval; - if(option_len != sizeof(int)) goto ret_inval; @@ -862,6 +890,43 @@ static int net_udp_setsockopt(net_socket_t *hnd, int level, int option_name, } break; + + case IPPROTO_UDPLITE: + if(sock->proto != IPPROTO_UDPLITE) + goto ret_inval; + + switch(option_name) { + case UDPLITE_SEND_CSCOV: + if(option_len != sizeof(int)) + goto ret_inval; + + tmp = *((int *)option_value); + + if(tmp && tmp < 8) + goto ret_inval; + else if(tmp > 0xFFFF) + tmp = 0xFFFF; + + sock->udp_lite.send_cscov = (uint16_t)tmp; + goto ret_success; + + case UDPLITE_RECV_CSCOV: + if(option_len != sizeof(int)) + goto ret_inval; + + tmp = *((int *)option_value); + + if(tmp && tmp < 8) + goto ret_inval; + else if(tmp > 0xFFFF) + tmp = 0xFFFF; + + sock->udp_lite.recv_cscov = (uint16_t)tmp; + sock->int_flags |= UDPSOCK_LITE_RCVCOV; + goto ret_success; + } + + break; } /* If it wasn't handled, return that error. */ @@ -963,7 +1028,8 @@ extern void __poll_event_trigger(int fd, short event); static int net_udp_input4(netif_t *src, const ip_hdr_t *ip, const uint8 *data, size_t size) { udp_hdr_t *hdr = (udp_hdr_t *)data; - uint16 cs; + uint16 cs, cscov = 0; + int partial = 1; struct udp_sock *sock; struct udp_pkt *pkt; @@ -975,17 +1041,44 @@ static int net_udp_input4(netif_t *src, const ip_hdr_t *ip, const uint8 *data, return -1; } - /* Calculate the checksum if one was computed by the sender. Unfortunately, - with IPv4, we don't know if a zero checksum means that the sender didn't - calculate the checksum or if it actually came out as 0xFFFF. We pretty - much have to assume the former option though. */ - if(hdr->checksum != 0) { - cs = net_ipv4_checksum_pseudo(ip->src, ip->dest, ip->protocol, size); + if(ip->protocol == IPPROTO_UDP) { + /* Calculate the checksum if one was computed by the sender. + Unfortunately, with IPv4, we don't know if a zero checksum means that + the sender didn't calculate the checksum or if it actually came out + as 0xFFFF. We pretty much have to assume the former option though. */ + if(hdr->checksum != 0) { + cs = net_ipv4_checksum_pseudo(ip->src, ip->dest, IPPROTO_UDP, size); + + /* If the checksum is right, we'll get zero back from the checksum + function */ + if(net_ipv4_checksum(data, size, cs)) { + /* The checksum was wrong, bail out */ + ++udp_stats.pkt_recv_bad_chksum; + return -1; + } + } + } + else { + cscov = ntohs(hdr->length); + + /* Make sure the checksum coverage is sane. */ + if(cscov && (cscov < 8 || cscov > size)) { + ++udp_stats.pkt_recv_bad_chksum; + return -1; + } + else if(!cscov) { + cscov = size; + partial = 0; + } + else if(cscov == size) { + partial = 0; + } + + cs = net_ipv4_checksum_pseudo(ip->src, ip->dest, IPPROTO_UDPLITE, size); /* If the checksum is right, we'll get zero back from the checksum - function */ - if(net_ipv4_checksum(data, size, cs)) { - /* The checksum was wrong, bail out */ + function. */ + if(net_ipv4_checksum(data, cscov, cs)) { ++udp_stats.pkt_recv_bad_chksum; return -1; } @@ -1008,16 +1101,32 @@ static int net_udp_input4(netif_t *src, const ip_hdr_t *ip, const uint8 *data, /* If the socket has a remote port set and it isn't the one that this packet came from, bail */ if(sock->remote_addr.sin6_port != 0 && - sock->remote_addr.sin6_port != hdr->src_port) + sock->remote_addr.sin6_port != hdr->src_port) continue; /* If we have a address specified, and its not v4-mapped or its not the address this packet came from, bail out */ if(!IN6_IS_ADDR_UNSPECIFIED(&sock->remote_addr.sin6_addr) && - (!IN6_IS_ADDR_V4MAPPED(&sock->remote_addr.sin6_addr) || - sock->remote_addr.sin6_addr.__s6_addr.__s6_addr32[3] != ip->src)) + (!IN6_IS_ADDR_V4MAPPED(&sock->remote_addr.sin6_addr) || + sock->remote_addr.sin6_addr.__s6_addr.__s6_addr32[3] != ip->src)) continue; + /* Make sure we have the right protocol */ + if(sock->proto != ip->protocol) + continue; + + /* If this packet is UDP-Lite, make sure the checksum coverage is valid + for the socket. We have to be careful here not to reject packets with + full coverage that just happen to be smaller than the coverage set by + the userspace program. Note that failing this check DOES NOT change + any of the statistics counters at all, by design. */ + if((sock->int_flags & UDPSOCK_LITE_RCVCOV) && partial && + cscov < sock->udp_lite.recv_cscov) { + /* Silently drop packets that fail the partial coverage check. */ + mutex_unlock(&udp_mutex); + return 0; + } + if(!(pkt = (struct udp_pkt *)malloc(sizeof(struct udp_pkt)))) { mutex_unlock(&udp_mutex); return -1; @@ -1059,7 +1168,8 @@ static int net_udp_input4(netif_t *src, const ip_hdr_t *ip, const uint8 *data, static int net_udp_input6(netif_t *src, const ipv6_hdr_t *ip, const uint8 *data, size_t size) { udp_hdr_t *hdr = (udp_hdr_t *)data; - uint16 cs; + uint16 cs, cscov = 0; + int partial = 1; struct udp_sock *sock; struct udp_pkt *pkt; @@ -1071,20 +1181,45 @@ static int net_udp_input6(netif_t *src, const ipv6_hdr_t *ip, const uint8 *data, return -1; } - /* Calculate the checksum of the packet. Note that this is optional for IPv4 - but required for IPv6. Thus, for IPv6, a zero checksum in the packet - implies that the value was actually calculated as 0xFFFF (whereas with - IPv4, that means that either the value was calculated as 0xFFFF or no - checksum was calculated). */ - cs = net_ipv6_checksum_pseudo(&ip->src_addr, &ip->dst_addr, size, - IPPROTO_UDP); + if(ip->next_header == IPPROTO_UDP) { + /* Calculate the checksum of the packet. Note that this is optional for + IPv4 but required for IPv6. */ + cs = net_ipv6_checksum_pseudo(&ip->src_addr, &ip->dst_addr, size, + IPPROTO_UDP); - /* If the checksum is right, we'll get zero back from the checksum - function */ - if(net_ipv4_checksum(data, size, cs)) { - /* The checksum was wrong, bail out */ - ++udp_stats.pkt_recv_bad_chksum; - return -1; + /* If the checksum is right, we'll get zero back from the checksum + function. */ + if(net_ipv4_checksum(data, size, cs)) { + /* The checksum was wrong, bail out */ + ++udp_stats.pkt_recv_bad_chksum; + return -1; + } + } + else { + cscov = ntohs(hdr->length); + + /* Make sure the checksum coverage is sane. */ + if(cscov && (cscov < 8 || cscov > size)) { + ++udp_stats.pkt_recv_bad_chksum; + return -1; + } + else if(!cscov) { + cscov = size; + partial = 0; + } + else if(cscov == size) { + partial = 0; + } + + cs = net_ipv6_checksum_pseudo(&ip->src_addr, &ip->dst_addr, size, + IPPROTO_UDPLITE); + + /* If the checksum is right, we'll get zero back from the checksum + function. */ + if(net_ipv4_checksum(data, cscov, cs)) { + ++udp_stats.pkt_recv_bad_chksum; + return -1; + } } if(mutex_trylock(&udp_mutex)) @@ -1104,16 +1239,32 @@ static int net_udp_input6(netif_t *src, const ipv6_hdr_t *ip, const uint8 *data, /* If the socket has a remote port set and it isn't the one that this packet came from, bail */ if(sock->remote_addr.sin6_port != 0 && - sock->remote_addr.sin6_port != hdr->src_port) + sock->remote_addr.sin6_port != hdr->src_port) continue; /* If we have a address specified and it is not the address this packet came from, bail out */ if(!IN6_IS_ADDR_UNSPECIFIED(&sock->remote_addr.sin6_addr) && - memcmp(&sock->remote_addr.sin6_addr, &ip->src_addr, - sizeof(struct in6_addr))) + memcmp(&sock->remote_addr.sin6_addr, &ip->src_addr, + sizeof(struct in6_addr))) continue; + /* Make sure we have the right protocol */ + if(sock->proto != ip->next_header) + continue; + + /* If this packet is UDP-Lite, make sure the checksum coverage is valid + for the socket. We have to be careful here not to reject packets with + full coverage that just happen to be smaller than the coverage set by + the userspace program. Note that failing this check DOES NOT change + any of the statistics counters at all, by design. */ + if((sock->int_flags & UDPSOCK_LITE_RCVCOV) && partial && + cscov < sock->udp_lite.recv_cscov) { + /* Silently drop packets that fail the partial coverage check. */ + mutex_unlock(&udp_mutex); + return 0; + } + if(!(pkt = (struct udp_pkt *)malloc(sizeof(struct udp_pkt)))) { mutex_unlock(&udp_mutex); return -1; @@ -1164,10 +1315,11 @@ static int net_udp_input(netif_t *src, int domain, const void *hdr, return -1; } +/* XXX */ static int net_udp_send_raw(netif_t *net, const struct sockaddr_in6 *src, const struct sockaddr_in6 *dst, const uint8 *data, size_t size, uint32_t flags, int hops, - uint32_t iflags) { + uint32_t iflags, int proto, uint16_t cscov) { uint8 buf[size + sizeof(udp_hdr_t)]; udp_hdr_t *hdr = (udp_hdr_t *)buf; uint16 cs; @@ -1220,18 +1372,36 @@ static int net_udp_send_raw(netif_t *net, const struct sockaddr_in6 *src, memcpy(buf + sizeof(udp_hdr_t), data, size); size += sizeof(udp_hdr_t); - cs = net_ipv6_checksum_pseudo(&srcaddr, &dst->sin6_addr, size, IPPROTO_UDP); ...<truncated>... hooks/post-receive -- A pseudo Operating System for the Dreamcast. |