From d74464e0919ce6f2e7b16712fac6e641cfdf2cf8 Mon Sep 17 00:00:00 2001 From: Grant Erickson Date: Wed, 19 Dec 2012 18:21:07 -0800 Subject: [PATCH] Add RFC3542-style checksum compuation on raw, IPv6 sockets This patch adds support for RFC3542-style checksum computation on raw, IPv6 sockets via the IPV6_CHECKSUM socket option. This allows the development of application-layer utilities such as ping6 which are unable to compute the raw packet checksum without a prior knowledge of the source address selection. --- src/api/sockets.c | 84 ++++++++++++++++++++++++++++++++++++++ src/core/raw.c | 11 +++++ src/include/lwip/raw.h | 5 +++ src/include/lwip/sockets.h | 3 +- 4 files changed, 102 insertions(+), 1 deletion(-) diff --git a/src/api/sockets.c b/src/api/sockets.c index 22905539..096b9c5c 100644 --- a/src/api/sockets.c +++ b/src/api/sockets.c @@ -1703,6 +1703,22 @@ lwip_getsockopt(int s, int level, int optname, void *optval, socklen_t *optlen) } /* switch (optname) */ break; #endif /* LWIP_UDP && LWIP_UDPLITE*/ + /* Level: IPPROTO_RAW */ + case IPPROTO_RAW: + switch (optname) { +#if LWIP_IPV6 + case IPV6_CHECKSUM: + if (*optlen < sizeof(int)) { + err = EINVAL; + break; + } +#endif /* LWIP_IPV6 */ + default: + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_RAW, UNIMPL: optname=0x%x, ..)\n", + s, optname)); + err = ENOPROTOOPT; + } /* switch (optname) */ + break; /* UNDEFINED LEVEL */ default: LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, level=0x%x, UNIMPL: optname=0x%x, ..)\n", @@ -1955,6 +1971,24 @@ lwip_getsockopt_internal(void *arg) } /* switch (optname) */ break; #endif /* LWIP_UDP */ + /* Level: IPPROTO_RAW */ + case IPPROTO_RAW: + switch (optname) { +#if LWIP_IPV6 + case IPV6_CHECKSUM: + if (sock->conn->pcb.raw->chksum_reqd == 0) + *(int *)optval = -1; + else + *(int *)optval = sock->conn->pcb.raw->chksum_offset; + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_RAW, IPV6_CHECKSUM) = %d\n", + s, (*(int*)optval)) ); + break; +#endif /* LWIP_IPV6 */ + default: + LWIP_ASSERT("unhandled optname", 0); + break; + } /* switch (optname) */ + break; default: LWIP_ASSERT("unhandled level", 0); break; @@ -2129,12 +2163,26 @@ lwip_setsockopt(int s, int level, int optname, const void *optval, socklen_t opt return 0; break; + case IPV6_CHECKSUM: + err = EINVAL; + break; default: LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IPV6, UNIMPL: optname=0x%x, ..)\n", s, optname)); err = ENOPROTOOPT; } /* switch (optname) */ break; + case IPPROTO_ICMPV6: + switch (optname) { + case IPV6_CHECKSUM: + err = EINVAL; + break; + default: + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_ICMPV6, UNIMPL: optname=0x%x, ..)\n", + s, optname)); + err = ENOPROTOOPT; + } /* switch (optname) */ + break; #endif /* LWIP_IPV6 */ #if LWIP_UDP && LWIP_UDPLITE @@ -2161,6 +2209,22 @@ lwip_setsockopt(int s, int level, int optname, const void *optval, socklen_t opt } /* switch (optname) */ break; #endif /* LWIP_UDP && LWIP_UDPLITE */ +/* Level: IPPROTO_RAW */ + case IPPROTO_RAW: + switch (optname) { +#if LWIP_IPV6 + case IPV6_CHECKSUM: + /* Per RFC3542, odd offsets are not allowed */ + if ((*(int *)optval > 0) && (*(int *)optval & 1)) + err = EINVAL; + break; +#endif /* LWIP_IPV6 */ + default: + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_RAW, UNIMPL: optname=0x%x, ..)\n", + s, optname)); + err = ENOPROTOOPT; + } /* switch (optname) */ + break; /* UNDEFINED LEVEL */ default: LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, level=0x%x, UNIMPL: optname=0x%x, ..)\n", @@ -2419,6 +2483,26 @@ lwip_setsockopt_internal(void *arg) } /* switch (optname) */ break; #endif /* LWIP_UDP */ + /* Level: IPPROTO_RAW */ + case IPPROTO_RAW: + switch (optname) { +#if LWIP_IPV6 + case IPV6_CHECKSUM: + if (*(int *)optval < 0) { + sock->conn->pcb.raw->chksum_reqd = 0; + } else { + sock->conn->pcb.raw->chksum_reqd = 1; + sock->conn->pcb.raw->chksum_offset = *(int *)optval; + } + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_RAW, IPV6_CHECKSUM, ..) -> %d\n", + s, sock->conn->pcb.raw->chksum_reqd)); + break; +#endif /* LWIP_IPV6 */ + default: + LWIP_ASSERT("unhandled optname", 0); + break; + } /* switch (optname) */ + break; default: LWIP_ASSERT("unhandled level", 0); break; diff --git a/src/core/raw.c b/src/core/raw.c index 68b23c61..90eda30b 100644 --- a/src/core/raw.c +++ b/src/core/raw.c @@ -51,6 +51,7 @@ #include "arch/perf.h" #include "lwip/ip6.h" #include "lwip/ip6_addr.h" +#include "lwip/inet_chksum.h" #include @@ -313,6 +314,16 @@ raw_sendto(struct raw_pcb *pcb, struct pbuf *p, ip_addr_t *ipaddr) src_ip = &pcb->local_ip; } +#if LWIP_IPV6 + /* If requested, based on the IPV6_CHECKSUM socket option per RFC3542, */ + /* compute the checksum and update the checksum in the payload. */ + if (PCB_ISIPV6(pcb) && pcb->chksum_reqd) { + u16_t chksum; + chksum = ip6_chksum_pseudo(q, pcb->protocol, q->tot_len, ipX_2_ip6(src_ip), ipX_2_ip6(dst_ip)); + *(u16_t *)(((u8_t *)q->payload) + pcb->chksum_offset) = chksum; + } +#endif + NETIF_SET_HWADDRHINT(netif, &pcb->addr_hint); err = ipX_output_if(PCB_ISIPV6(pcb), q, ipX_2_ip(src_ip), ipX_2_ip(dst_ip), pcb->ttl, pcb->tos, pcb->protocol, netif); NETIF_SET_HWADDRHINT(netif, NULL); diff --git a/src/include/lwip/raw.h b/src/include/lwip/raw.h index f0c8ed47..e1412489 100644 --- a/src/include/lwip/raw.h +++ b/src/include/lwip/raw.h @@ -97,6 +97,11 @@ struct raw_pcb { } recv; /* user-supplied argument for the recv callback */ void *recv_arg; +#if LWIP_IPV6 + /* fields for handling checksum computations as per RFC3542. */ + u8_t chksum_reqd; + u16_t chksum_offset; +#endif }; /* The following functions is the application layer interface to the diff --git a/src/include/lwip/sockets.h b/src/include/lwip/sockets.h index ba4ca7b6..e2a36a46 100644 --- a/src/include/lwip/sockets.h +++ b/src/include/lwip/sockets.h @@ -230,7 +230,8 @@ struct linger { /* * Options for level IPPROTO_IPV6 */ -#define IPV6_V6ONLY 27 /* RFC3493: boolean control to restrict AF_INET6 sockets to IPv6 communications only. */ +#define IPV6_CHECKSUM 7 /* RFC3542: calculate and insert the ICMPv6 checksum for raw sockets. */ +#define IPV6_V6ONLY 27 /* RFC3493: boolean control to restrict AF_INET6 sockets to IPv6 communications only. */ #endif /* LWIP_IPV6 */ #if LWIP_UDP && LWIP_UDPLITE