Skip to content

Commit

Permalink
ipv6: Support IPv4/IPv6 mapped PCBs
Browse files Browse the repository at this point in the history
This change also drops UDP IPv4 packet received on IPv6-only socket.

Ref IDF-4845
  • Loading branch information
freakyxue authored and david-cermak committed Aug 16, 2022
1 parent 9752eb5 commit c0fa16f
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 1 deletion.
41 changes: 41 additions & 0 deletions src/api/api_msg.c
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,16 @@ recv_udp(void *arg, struct udp_pcb *pcb, struct pbuf *p,
return;
}

#if ESP_LWIP && 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 /* ESP_LWIP && LWIP_IPV6 */

buf = (struct netbuf *)memp_malloc(MEMP_NETBUF);
if (buf == NULL) {
pbuf_free(p);
Expand Down Expand Up @@ -668,6 +678,15 @@ pcb_new(struct api_msg *msg)
if (msg->conn->pcb.ip == NULL) {
msg->err = ERR_MEM;
}
#if ESP_LWIP && LWIP_IPV4 && LWIP_IPV6
else {
if (NETCONNTYPE_ISIPV6(msg->conn->type)) {
/* Convert IPv4 PCB manually to an IPv6 PCB */
IP_SET_TYPE_VAL(msg->conn->pcb.ip->local_ip, IPADDR_TYPE_V6);
IP_SET_TYPE_VAL(msg->conn->pcb.ip->remote_ip, IPADDR_TYPE_V6);
}
}
#endif /* ESP_LWIP && LWIP_IPV4 && LWIP_IPV6 */
}

/**
Expand Down Expand Up @@ -1233,6 +1252,20 @@ lwip_netconn_do_bind(void *m)
err_t err;

if (msg->conn->pcb.tcp != NULL) {
#if ESP_LWIP && LWIP_IPV4 && LWIP_IPV6
/* "Socket API like" dual-stack support: If IP to bind to is IP6_ADDR_ANY,
* and NETCONN_FLAG_IPV6_V6ONLY is NOT set, use IP_ANY_TYPE to bind
*/
if (ip_addr_cmp(API_EXPR_REF(msg->msg.bc.ipaddr), IP6_ADDR_ANY) &&
(netconn_get_ipv6only(msg->conn) == 0)) {
/* change PCB type to IPADDR_TYPE_ANY */
IP_SET_TYPE_VAL(msg->conn->pcb.ip->local_ip, IPADDR_TYPE_ANY);
IP_SET_TYPE_VAL(msg->conn->pcb.ip->remote_ip, IPADDR_TYPE_ANY);

/* bind to IPADDR_TYPE_ANY */
API_EXPR_REF(msg->msg.bc.ipaddr) = IP_ANY_TYPE;
}
#endif /* ESP_LWIP && LWIP_IPV4 && LWIP_IPV6 */
switch (NETCONNTYPE_GROUP(msg->conn->type)) {
#if LWIP_RAW
case NETCONN_RAW:
Expand Down Expand Up @@ -1548,6 +1581,14 @@ lwip_netconn_do_send(void *m)
struct api_msg *msg = (struct api_msg *)m;

err_t err = netconn_err(msg->conn);

#if ESP_LWIP && LWIP_IPV4 && LWIP_IPV6
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 /* ESP_LWIP && LWIP_IPV4 && LWIP_IPV6 */

if (err == ERR_OK) {
if (msg->conn->pcb.tcp != NULL) {
switch (NETCONNTYPE_GROUP(msg->conn->type)) {
Expand Down
13 changes: 13 additions & 0 deletions src/api/netdb.c
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,11 @@ lwip_getaddrinfo(const char *nodename, const char *servname,
type = NETCONN_DNS_IPV4;
} else if (ai_family == AF_INET6) {
type = NETCONN_DNS_IPV6;
#if ESP_LWIP
if (hints->ai_flags & AI_V4MAPPED) {
type = NETCONN_DNS_IPV6_IPV4;
}
#endif /* ESP_LWIP */
}
#endif /* LWIP_IPV4 && LWIP_IPV6 */
err = netconn_gethostbyname_addrtype(nodename, &addr, type);
Expand All @@ -351,6 +356,14 @@ lwip_getaddrinfo(const char *nodename, const char *servname,
}
}

#if ESP_LWIP && 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 */
ip4_2_ipv4_mapped_ipv6(ip_2_ip6(&addr), ip_2_ip4(&addr));
}
#endif /* ESP_LWIP && LWIP_IPV4 && LWIP_IPV6 */

total_size = sizeof(struct addrinfo) + sizeof(struct sockaddr_storage);
if (nodename != NULL) {
namelen = strlen(nodename);
Expand Down
13 changes: 12 additions & 1 deletion src/core/udp.c
Original file line number Diff line number Diff line change
Expand Up @@ -540,9 +540,20 @@ udp_sendto_chksum(struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *dst_ip,
if (!IP_ADDR_PCB_VERSION_MATCH(pcb, dst_ip)) {
return ERR_VAL;
}
#if ESP_LWIP && 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;
unmap_ipv4_mapped_ipv6(ip_2_ip4(&dest_ipv4), ip_2_ip6(dst_ip));
#if LWIP_CHECKSUM_ON_COPY
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 */
}
#endif /* ESP_LWIP && LWIP_IPV4 && LWIP_IPV6 */

LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_send\n"));

if (pcb->netif_idx != NETIF_NO_INDEX) {
netif = netif_get_by_index(pcb->netif_idx);
} else {
Expand Down
5 changes: 5 additions & 0 deletions src/include/lwip/ip_addr.h
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,11 @@ extern const ip_addr_t ip_addr_any_type;
ip_2_ip6(ipaddr)->addr[3] = 0; \
ip6_addr_clear_zone(ip_2_ip6(ipaddr)); }while(0)

#if ESP_LWIP && LWIP_IPV6
#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))
#endif /* ESP_LWIP && LWIP_IPV6 */

/** @ingroup ipaddr */
#define ip_addr_copy(dest, src) do{ IP_SET_TYPE_VAL(dest, IP_GET_TYPE(&src)); if(IP_IS_V6_VAL(src)){ \
ip6_addr_copy(*ip_2_ip6(&(dest)), *ip_2_ip6(&(src))); }else{ \
Expand Down

0 comments on commit c0fa16f

Please sign in to comment.