net: Add support for v4-mapping-to-v6 sockets
This allows IPv4 and IPv6 share the same port space. User can still control the behavior of the v4-mapping-to-v6 by using the IPV6_V6ONLY socket option at runtime. Currently the IPv4 mapping to IPv6 is turned off by default, and also the IPV6_V6ONLY is true by default which means that IPv4 and IPv6 do not share the port space. Only way to use v4-mapping-to-v6 is to enable the Kconfig option and turn off the v6only socket option. Signed-off-by: Jukka Rissanen <jukka.rissanen@nordicsemi.no>
This commit is contained in:
parent
256d5fac4f
commit
4f37d63ed1
|
@ -328,6 +328,9 @@ __net_socket struct net_context {
|
|||
#endif
|
||||
#if defined(CONFIG_NET_CONTEXT_REUSEPORT)
|
||||
bool reuseport;
|
||||
#endif
|
||||
#if defined(CONFIG_NET_IPV4_MAPPING_TO_IPV6)
|
||||
bool ipv6_v6only;
|
||||
#endif
|
||||
} options;
|
||||
|
||||
|
@ -1081,6 +1084,7 @@ enum net_context_option {
|
|||
NET_OPT_DSCP_ECN = 8,
|
||||
NET_OPT_REUSEADDR = 9,
|
||||
NET_OPT_REUSEPORT = 10,
|
||||
NET_OPT_IPV6_V6ONLY = 11,
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -143,6 +143,18 @@ source "subsys/net/ip/Kconfig.ipv6"
|
|||
|
||||
source "subsys/net/ip/Kconfig.ipv4"
|
||||
|
||||
config NET_IPV4_MAPPING_TO_IPV6
|
||||
bool "Support IPv4 mapped on IPv6 addresses"
|
||||
depends on NET_NATIVE_IPV6
|
||||
help
|
||||
Support v4-mapped-on-v6 address type. This allows IPv4 and IPv6
|
||||
to share a local port space. When the application gets an IPv4
|
||||
connection or packet to an IPv6 socket, its source address will
|
||||
be mapped to IPv6. This is turned off by default which means
|
||||
that IPV6_V6ONLY socket option is always on. If you enable this
|
||||
option, then you can still control the behaviour of the socket
|
||||
via the IPV6_V6ONLY option at runtime.
|
||||
|
||||
config NET_SHELL
|
||||
bool "Network shell utilities"
|
||||
select SHELL
|
||||
|
|
|
@ -380,6 +380,8 @@ int net_conn_register(uint16_t proto, uint8_t family,
|
|||
|
||||
conn_set_used(conn);
|
||||
|
||||
conn->v6only = net_context_is_v6only_set(context);
|
||||
|
||||
conn_register_debug(conn, remote_port, local_port);
|
||||
|
||||
return 0;
|
||||
|
@ -668,7 +670,17 @@ enum net_verdict net_conn_input(struct net_pkt *pkt,
|
|||
raw_pkt_continue = true;
|
||||
}
|
||||
}
|
||||
continue; /* wrong protocol family */
|
||||
|
||||
if (IS_ENABLED(CONFIG_NET_IPV4_MAPPING_TO_IPV6)) {
|
||||
if (!(conn->family == AF_INET6 && pkt_family == AF_INET &&
|
||||
!conn->v6only)) {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
continue; /* wrong protocol family */
|
||||
}
|
||||
|
||||
/* We might have a match for v4-to-v6 mapping, check more */
|
||||
}
|
||||
|
||||
/* Is the candidate connection matching the packet's protocol wihin the family? */
|
||||
|
@ -740,7 +752,26 @@ enum net_verdict net_conn_input(struct net_pkt *pkt,
|
|||
|
||||
if ((conn->flags & NET_CONN_LOCAL_ADDR_SET) &&
|
||||
!conn_addr_cmp(pkt, ip_hdr, &conn->local_addr, false)) {
|
||||
continue; /* wrong local address */
|
||||
|
||||
/* Check if we could do a v4-mapping-to-v6 and the IPv6 socket
|
||||
* has no IPV6_V6ONLY option set and if the local IPV6 address
|
||||
* is unspecified, then we could accept a connection from IPv4
|
||||
* address by mapping it to IPv6 address.
|
||||
*/
|
||||
if (IS_ENABLED(CONFIG_NET_IPV4_MAPPING_TO_IPV6)) {
|
||||
if (!(conn->family == AF_INET6 && pkt_family == AF_INET &&
|
||||
!conn->v6only &&
|
||||
net_ipv6_is_addr_unspecified(
|
||||
&net_sin6(&conn->local_addr)->sin6_addr))) {
|
||||
continue; /* wrong local address */
|
||||
}
|
||||
} else {
|
||||
continue; /* wrong local address */
|
||||
}
|
||||
|
||||
/* We might have a match for v4-to-v6 mapping,
|
||||
* continue with rank checking.
|
||||
*/
|
||||
}
|
||||
|
||||
if (best_rank < NET_CONN_RANK(conn->flags)) {
|
||||
|
|
|
@ -79,6 +79,9 @@ struct net_conn {
|
|||
|
||||
/** Flags for the connection */
|
||||
uint8_t flags;
|
||||
|
||||
/** Is v4-mapping-to-v6 enabled for this connection */
|
||||
uint8_t v6only : 1;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -78,6 +78,17 @@ bool net_context_is_reuseport_set(struct net_context *context)
|
|||
#endif
|
||||
}
|
||||
|
||||
bool net_context_is_v6only_set(struct net_context *context)
|
||||
{
|
||||
#if defined(CONFIG_NET_IPV4_MAPPING_TO_IPV6)
|
||||
return context->options.ipv6_v6only;
|
||||
#else
|
||||
ARG_UNUSED(context);
|
||||
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(CONFIG_NET_UDP) || defined(CONFIG_NET_TCP)
|
||||
static inline bool is_in_tcp_listen_state(struct net_context *context)
|
||||
{
|
||||
|
@ -115,7 +126,6 @@ static int check_used_port(enum net_ip_protocol proto,
|
|||
const struct sockaddr *local_addr,
|
||||
bool reuseaddr_set,
|
||||
bool reuseport_set)
|
||||
|
||||
{
|
||||
int i;
|
||||
|
||||
|
@ -188,8 +198,14 @@ static int check_used_port(enum net_ip_protocol proto,
|
|||
}
|
||||
} else if (IS_ENABLED(CONFIG_NET_IPV4) &&
|
||||
local_addr->sa_family == AF_INET) {
|
||||
/* If there is an IPv6 socket already bound and
|
||||
* if v6only option is enabled, then it is possible to
|
||||
* bind IPv4 address to it.
|
||||
*/
|
||||
if (net_sin_ptr(&contexts[i].local)->sin_addr == NULL ||
|
||||
net_sin_ptr(&contexts[i].local)->sin_family != AF_INET) {
|
||||
((IS_ENABLED(CONFIG_NET_IPV4_MAPPING_TO_IPV6) ?
|
||||
net_context_is_v6only_set(&contexts[i]) : true) &&
|
||||
net_sin_ptr(&contexts[i].local)->sin_family != AF_INET)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -416,7 +432,10 @@ int net_context_get(sa_family_t family, enum net_sock_type type, uint16_t proto,
|
|||
#if defined(CONFIG_NET_CONTEXT_SNDTIMEO)
|
||||
contexts[i].options.sndtimeo = K_FOREVER;
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_NET_IPV4_MAPPING_TO_IPV6)
|
||||
/* By default IPv4 and IPv6 are in different port spaces */
|
||||
contexts[i].options.ipv6_v6only = true;
|
||||
#endif
|
||||
if (IS_ENABLED(CONFIG_NET_IP)) {
|
||||
(void)memset(&contexts[i].remote, 0, sizeof(struct sockaddr));
|
||||
(void)memset(&contexts[i].local, 0, sizeof(struct sockaddr_ptr));
|
||||
|
@ -1496,6 +1515,36 @@ static int get_context_reuseport(struct net_context *context,
|
|||
#endif
|
||||
}
|
||||
|
||||
static int get_context_ipv6_v6only(struct net_context *context,
|
||||
void *value, size_t *len)
|
||||
{
|
||||
#if defined(CONFIG_NET_IPV4_MAPPING_TO_IPV6)
|
||||
if (!value || !len) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (*len != sizeof(int)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (context->options.ipv6_v6only == true) {
|
||||
*((int *)value) = (int) true;
|
||||
} else {
|
||||
*((int *)value) = (int) false;
|
||||
}
|
||||
|
||||
*len = sizeof(int);
|
||||
|
||||
return 0;
|
||||
#else
|
||||
ARG_UNUSED(context);
|
||||
ARG_UNUSED(value);
|
||||
ARG_UNUSED(len);
|
||||
|
||||
return -ENOTSUP;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* If buf is not NULL, then use it. Otherwise read the data to be written
|
||||
* to net_pkt from msghdr.
|
||||
*/
|
||||
|
@ -1726,8 +1775,21 @@ static int context_sendto(struct net_context *context,
|
|||
|
||||
} else if (IS_ENABLED(CONFIG_NET_IPV4) &&
|
||||
net_context_get_family(context) == AF_INET) {
|
||||
const struct sockaddr_in *addr4 =
|
||||
(const struct sockaddr_in *)dst_addr;
|
||||
const struct sockaddr_in *addr4 = (const struct sockaddr_in *)dst_addr;
|
||||
struct sockaddr_in mapped;
|
||||
|
||||
/* Get the destination address from the mapped IPv6 address */
|
||||
if (IS_ENABLED(CONFIG_NET_IPV4_MAPPING_TO_IPV6) &&
|
||||
addr4->sin_family == AF_INET6 &&
|
||||
net_ipv6_addr_is_v4_mapped(&net_sin6(dst_addr)->sin6_addr)) {
|
||||
struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)dst_addr;
|
||||
|
||||
mapped.sin_port = addr6->sin6_port;
|
||||
mapped.sin_family = AF_INET;
|
||||
net_ipaddr_copy(&mapped.sin_addr,
|
||||
(struct in_addr *)(&addr6->sin6_addr.s6_addr32[3]));
|
||||
addr4 = &mapped;
|
||||
}
|
||||
|
||||
if (msghdr) {
|
||||
addr4 = msghdr->msg_name;
|
||||
|
@ -2591,6 +2653,32 @@ static int set_context_reuseport(struct net_context *context,
|
|||
#endif
|
||||
}
|
||||
|
||||
static int set_context_ipv6_v6only(struct net_context *context,
|
||||
const void *value, size_t len)
|
||||
{
|
||||
#if defined(CONFIG_NET_IPV4_MAPPING_TO_IPV6)
|
||||
bool v6only = false;
|
||||
|
||||
if (len != sizeof(int)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (*((int *) value) != 0) {
|
||||
v6only = true;
|
||||
}
|
||||
|
||||
context->options.ipv6_v6only = v6only;
|
||||
|
||||
return 0;
|
||||
#else
|
||||
ARG_UNUSED(context);
|
||||
ARG_UNUSED(value);
|
||||
ARG_UNUSED(len);
|
||||
|
||||
return -ENOTSUP;
|
||||
#endif
|
||||
}
|
||||
|
||||
int net_context_set_option(struct net_context *context,
|
||||
enum net_context_option option,
|
||||
const void *value, size_t len)
|
||||
|
@ -2636,6 +2724,9 @@ int net_context_set_option(struct net_context *context,
|
|||
case NET_OPT_REUSEPORT:
|
||||
ret = set_context_reuseport(context, value, len);
|
||||
break;
|
||||
case NET_OPT_IPV6_V6ONLY:
|
||||
ret = set_context_ipv6_v6only(context, value, len);
|
||||
break;
|
||||
}
|
||||
|
||||
k_mutex_unlock(&context->lock);
|
||||
|
@ -2688,6 +2779,9 @@ int net_context_get_option(struct net_context *context,
|
|||
case NET_OPT_REUSEPORT:
|
||||
ret = get_context_reuseport(context, value, len);
|
||||
break;
|
||||
case NET_OPT_IPV6_V6ONLY:
|
||||
ret = get_context_ipv6_v6only(context, value, len);
|
||||
break;
|
||||
}
|
||||
|
||||
k_mutex_unlock(&context->lock);
|
||||
|
|
|
@ -71,6 +71,7 @@ extern void net_context_init(void);
|
|||
extern const char *net_context_state(struct net_context *context);
|
||||
extern bool net_context_is_reuseaddr_set(struct net_context *context);
|
||||
extern bool net_context_is_reuseport_set(struct net_context *context);
|
||||
extern bool net_context_is_v6only_set(struct net_context *context);
|
||||
extern void net_pkt_init(void);
|
||||
extern void net_tc_tx_init(void);
|
||||
extern void net_tc_rx_init(void);
|
||||
|
|
|
@ -2532,6 +2532,36 @@ next_state:
|
|||
break;
|
||||
}
|
||||
|
||||
net_ipaddr_copy(&conn->context->remote, &conn->dst.sa);
|
||||
|
||||
/* Check if v4-mapping-to-v6 needs to be done for
|
||||
* the accepted socket.
|
||||
*/
|
||||
if (IS_ENABLED(CONFIG_NET_IPV4_MAPPING_TO_IPV6) &&
|
||||
net_context_get_family(conn->context) == AF_INET &&
|
||||
net_context_get_family(context) == AF_INET6 &&
|
||||
!net_context_is_v6only_set(context)) {
|
||||
struct in6_addr mapped;
|
||||
|
||||
net_ipv6_addr_create_v4_mapped(
|
||||
&net_sin(&conn->context->remote)->sin_addr,
|
||||
&mapped);
|
||||
net_ipaddr_copy(&net_sin6(&conn->context->remote)->sin6_addr,
|
||||
&mapped);
|
||||
|
||||
net_sin6(&conn->context->remote)->sin6_family = AF_INET6;
|
||||
|
||||
NET_DBG("Setting v4 mapped address %s",
|
||||
net_sprint_ipv6_addr(&mapped));
|
||||
|
||||
/* Note that we cannot set the local address to IPv6 one
|
||||
* as that is used to match the connection, and not just
|
||||
* for printing. The remote address is only used for
|
||||
* passing it to accept() and printing it by "net conn"
|
||||
* command.
|
||||
*/
|
||||
}
|
||||
|
||||
accept_cb(conn->context, &context->remote,
|
||||
sizeof(struct sockaddr), 0, context);
|
||||
|
||||
|
|
|
@ -2070,6 +2070,22 @@ int zsock_getsockopt_ctx(struct net_context *ctx, int level, int optname,
|
|||
|
||||
case IPPROTO_IPV6:
|
||||
switch (optname) {
|
||||
case IPV6_V6ONLY:
|
||||
if (IS_ENABLED(CONFIG_NET_IPV4_MAPPING_TO_IPV6)) {
|
||||
ret = net_context_get_option(ctx,
|
||||
NET_OPT_IPV6_V6ONLY,
|
||||
optval,
|
||||
optlen);
|
||||
if (ret < 0) {
|
||||
errno = -ret;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case IPV6_TCLASS:
|
||||
if (IS_ENABLED(CONFIG_NET_CONTEXT_DSCP_ECN)) {
|
||||
ret = net_context_get_option(ctx,
|
||||
|
@ -2407,9 +2423,17 @@ int zsock_setsockopt_ctx(struct net_context *ctx, int level, int optname,
|
|||
case IPPROTO_IPV6:
|
||||
switch (optname) {
|
||||
case IPV6_V6ONLY:
|
||||
/* Ignore for now. Provided to let port
|
||||
* existing apps.
|
||||
*/
|
||||
if (IS_ENABLED(CONFIG_NET_IPV4_MAPPING_TO_IPV6)) {
|
||||
ret = net_context_set_option(ctx,
|
||||
NET_OPT_IPV6_V6ONLY,
|
||||
optval,
|
||||
optlen);
|
||||
if (ret < 0) {
|
||||
errno = -ret;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
case IPV6_TCLASS:
|
||||
|
|
Loading…
Reference in a new issue