lwip: Enable IPV6_ONLY option for UDP sockets (BSD & netconn)

* setsockopt(s, IPV6_ONLY, &one, sizeof(int)) will disable IPV6-only
  mode. Incoming/outgoing IPV4 packets are dropped.

* Otherwise, sockets bound to IPV6_ANY_ADDR can receive unicast packets
  for IPV4 or IPV6.

* sendto() a IPV6-mapped-IPV4 address on a UDP socket works correctly
  (not supported for RAW or TCP sockets.)

* getaddrinfo() option AI_V4MAPPED is implemented.

As well as extending support to TCP & RAW, there is some potential improvement
to dropping incoming packets - the drop happens a bit late in the process and
there is no "ICMP port unreachable" response sent.
This commit is contained in:
Angus Gratton 2017-07-07 19:27:00 +08:00 committed by Angus Gratton
parent 961180617e
commit 04a2cefb26
5 changed files with 67 additions and 11 deletions

View File

@ -180,6 +180,16 @@ recv_udp(void *arg, struct udp_pcb *pcb, struct pbuf *p,
return;
}
#if LWIP_IPV6
/* This should be eventually moved to a flag on the UDP PCB, and this drop can happen
more correctly in udp_input(). This will also allow icmp_dest_unreach() to be called. */
if (conn->flags & NETCONN_FLAG_IPV6_V6ONLY && !ip_current_is_v6()) {
LWIP_DEBUGF(API_MSG_DEBUG, ("recv_udp: Dropping IPv4 UDP packet (IPv6-only socket)"));
pbuf_free(p);
return;
}
#endif
buf = (struct netbuf *)memp_malloc(MEMP_NETBUF);
if (buf == NULL) {
pbuf_free(p);
@ -1388,6 +1398,12 @@ lwip_netconn_do_send(void *m)
if (ERR_IS_FATAL(msg->conn->last_err)) {
msg->err = msg->conn->last_err;
#if LWIP_IPV4 && LWIP_IPV6
} else if ((msg->conn->flags & NETCONN_FLAG_IPV6_V6ONLY) &&
IP_IS_V4MAPPEDV6(&msg->msg.b->addr)) {
LWIP_DEBUGF(API_MSG_DEBUG, ("lwip_netconn_do_send: Dropping IPv4 packet on IPv6-only socket"));
msg->err = ERR_VAL;
#endif /* LWIP_IPV4 && LWIP_IPV6 */
} else {
msg->err = ERR_CONN;
if (msg->conn->pcb.tcp != NULL) {

View File

@ -261,7 +261,7 @@ lwip_freeaddrinfo(struct addrinfo *ai)
* @param res pointer to a pointer where to store the result (set to NULL on failure)
* @return 0 on success, non-zero on failure
*
* @todo: implement AI_V4MAPPED, AI_ADDRCONFIG
* @todo: implement AI_ADDRCONFIG
*/
int
lwip_getaddrinfo(const char *nodename, const char *servname,
@ -330,6 +330,9 @@ lwip_getaddrinfo(const char *nodename, const char *servname,
type = NETCONN_DNS_IPV4;
} else if (ai_family == AF_INET6) {
type = NETCONN_DNS_IPV6;
if (hints->ai_flags & AI_V4MAPPED) {
type = NETCONN_DNS_IPV6_IPV4;
}
}
#endif /* LWIP_IPV4 && LWIP_IPV6 */
err = netconn_gethostbyname_addrtype(nodename, &addr, type);
@ -346,6 +349,14 @@ lwip_getaddrinfo(const char *nodename, const char *servname,
}
}
#if LWIP_IPV4 && LWIP_IPV6
if (ai_family == AF_INET6 && (hints->ai_flags & AI_V4MAPPED)
&& IP_GET_TYPE(&addr) == IPADDR_TYPE_V4) {
/* Convert native V4 address to a V4-mapped IPV6 address */
ip_addr_make_ip4_mapped_ip6(&addr, &addr);
}
#endif
total_size = sizeof(struct addrinfo) + sizeof(struct sockaddr_storage);
if (nodename != NULL) {
namelen = strlen(nodename);

View File

@ -2373,10 +2373,6 @@ lwip_getsockopt_impl(int s, int level, int optname, void *optval, socklen_t *opt
switch (optname) {
case IPV6_V6ONLY:
LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, *optlen, int);
/* @todo: this does not work for datagram sockets, yet */
if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) {
return ENOPROTOOPT;
}
*(int*)optval = (netconn_get_ipv6only(sock->conn) ? 1 : 0);
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IPV6, IPV6_V6ONLY) = %d\n",
s, *(int *)optval));
@ -2811,12 +2807,7 @@ lwip_setsockopt_impl(int s, int level, int optname, const void *optval, socklen_
case IPPROTO_IPV6:
switch (optname) {
case IPV6_V6ONLY:
/* @todo: this does not work for datagram sockets, yet */
#if CONFIG_MDNS
//LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, int, NETCONN_TCP);
#else
LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, int, NETCONN_TCP);
#endif
LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, optlen, int);
if (*(const int*)optval) {
netconn_set_ipv6only(sock->conn, 1);
} else {

View File

@ -542,6 +542,19 @@ udp_sendto_chksum(struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *dst_ip,
LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_send\n"));
#if LWIP_IPV4 && LWIP_IPV6
/* Unwrap IPV4-mapped IPV6 addresses and convert to native IPV4 here */
if (IP_IS_V4MAPPEDV6(dst_ip)) {
ip_addr_t dest_ipv4;
ip_addr_ip4_from_mapped_ip6(&dest_ipv4, dst_ip);
#if LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UP
return udp_sendto_chksum(pcb, p, &dest_ipv4, dst_port, have_chksum, chksum);
#else
return udp_sendto(pcb, p, &dest_ipv4, dst_port);
#endif /* LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UP */
}
#endif /* LWIP_IPV4 && LWIP_IPV6 */
#if LWIP_IPV6 || (LWIP_IPV4 && LWIP_MULTICAST_TX_OPTIONS)
if (ip_addr_ismulticast(dst_ip_route)) {
#if LWIP_IPV6

View File

@ -69,6 +69,10 @@ extern const ip_addr_t ip_addr_any_type;
#define IP_IS_V6_VAL(ipaddr) (IP_GET_TYPE(&ipaddr) == IPADDR_TYPE_V6)
#define IP_IS_V6(ipaddr) (((ipaddr) != NULL) && IP_IS_V6_VAL(*(ipaddr)))
#define IP_V6_EQ_PART(ipaddr, WORD, VAL) (ip_2_ip6(ipaddr)->addr[WORD] == htonl(VAL))
#define IP_IS_V4MAPPEDV6(ipaddr) (IP_IS_V6(ipaddr) && IP_V6_EQ_PART(ipaddr, 0, 0) && IP_V6_EQ_PART(ipaddr, 1, 0) && IP_V6_EQ_PART(ipaddr, 2, 0x0000FFFF))
#define IP_SET_TYPE_VAL(ipaddr, iptype) do { (ipaddr).type = (iptype); }while(0)
#define IP_SET_TYPE(ipaddr, iptype) do { if((ipaddr) != NULL) { IP_SET_TYPE_VAL(*(ipaddr), iptype); }}while(0)
#define IP_GET_TYPE(ipaddr) ((ipaddr)->type)
@ -156,6 +160,27 @@ extern const ip_addr_t ip_addr_any_type;
((IP_IS_V6(addr)) ? ip6addr_ntoa_r(ip_2_ip6(addr), buf, buflen) : ip4addr_ntoa_r(ip_2_ip4(addr), buf, buflen)))
int ipaddr_aton(const char *cp, ip_addr_t *addr);
/* Map an IPv4 ip_addr into an IPV6 ip_addr, using format
defined in RFC4291 2.5.5.2.
Safe to call when dest==src.
*/
#define ip_addr_make_ip4_mapped_ip6(dest, src) do { \
u32_t tmp = ip_2_ip4(src)->addr; \
IP_ADDR6((dest), 0x0, 0x0, htonl(0x0000FFFF), tmp); \
} while(0)
/* Convert an IPv4 mapped V6 address to an IPV4 address.
Check IP_IS_V4MAPPEDV6(src) before using this.
Safe to call when dest == src.
*/
#define ip_addr_ip4_from_mapped_ip6(dest, src) do { \
ip_2_ip4(dest)->addr = ip_2_ip6(src)->addr[3]; \
IP_SET_TYPE(dest, IPADDR_TYPE_V4); \
} while(0)
#else /* LWIP_IPV4 && LWIP_IPV6 */
#define IP_ADDR_PCB_VERSION_MATCH(addr, pcb) 1