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.
This commit is contained in:
Grant Erickson
2012-12-19 18:21:07 -08:00
committed by Simon Goldschmidt
parent e2c2afbbe0
commit d74464e091
4 changed files with 102 additions and 1 deletions

View File

@@ -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;