net: context: Assign a random port number when context is created

Current behaviour has an issue when UDP context is created with local
port number 0, net_conn_input() happens to treat zero port as
a wildcard ("receive packets for all ports"). net_context_bind()
for a UDP context doesn't affect its existing connection in any way.

Proposed solution is, context should be created with a random free
port assigned and bind() updates connection information from context.

Jira: ZEP-1644

Change-Id: Idb3592b58c831d986763312077b0dcdd95850bc9
Signed-off-by: Ravi kumar Veeramally <ravikumar.veeramally@linux.intel.com>
This commit is contained in:
Ravi kumar Veeramally 2017-02-03 13:49:00 +02:00 committed by Jukka Rissanen
commit df201a0e4b
2 changed files with 96 additions and 74 deletions

View file

@ -419,8 +419,9 @@ static inline void net_context_set_iface(struct net_context *context,
*
* @details Network context is used to define the connection 5-tuple
* (protocol, remote address, remote port, source address and source
* port). This is similar as BSD socket() function. The context will
* be created with a reference count of 1.
* port). Random free port number will be assigned to source port when
* context is created. This is similar as BSD socket() function.
* The context will be created with a reference count of 1.
*
* @param family IP address family (AF_INET or AF_INET6)
* @param type Type of the socket, SOCK_STREAM or SOCK_DGRAM
@ -659,6 +660,11 @@ int net_context_sendto(struct net_buf *buf,
* used. If CONFIG_NET_CONTEXT_SYNC_RECV is not set, then the timeout parameter
* value is ignored.
* This is similar as BSD recv() function.
* Note that net_context_bind() should be called before net_context_recv().
* Default random port number is assigned to local port. Only bind() will
* updates connection information from context. If recv() is called before
* bind() call, it may refuse to bind to a context which already has
* a connection associated.
*
* @param context The network context to use.
* @param cb Caller supplied callback function.

View file

@ -105,6 +105,68 @@ static struct sockaddr *create_sockaddr(struct net_buf *buf,
}
#endif
static int check_used_port(enum net_ip_protocol ip_proto,
uint16_t local_port,
const struct sockaddr *local_addr)
{
int i;
for (i = 0; i < NET_MAX_CONTEXT; i++) {
if (!net_context_is_used(&contexts[i])) {
continue;
}
if (!(net_context_get_ip_proto(&contexts[i]) == ip_proto &&
net_sin((struct sockaddr *)&
contexts[i].local)->sin_port == local_port)) {
continue;
}
if (local_addr->family == AF_INET6) {
if (net_ipv6_addr_cmp(
net_sin6_ptr(&contexts[i].local)->
sin6_addr,
&((struct sockaddr_in6 *)
local_addr)->sin6_addr)) {
return -EEXIST;
}
} else {
if (net_ipv4_addr_cmp(
net_sin_ptr(&contexts[i].local)->
sin_addr,
&((struct sockaddr_in *)
local_addr)->sin_addr)) {
return -EEXIST;
}
}
}
return 0;
}
static uint16_t find_available_port(struct net_context *context,
const struct sockaddr *addr)
{
if (!net_sin(addr)->sin_port) {
uint16_t local_port;
do {
local_port = sys_rand32_get() | 0x8000;
if (local_port <= 1023) {
/* 0 - 1023 ports are reserved */
continue;
}
} while (check_used_port(
net_context_get_ip_proto(context),
htons(local_port), addr) == -EEXIST);
return htons(local_port);
}
return net_sin(addr)->sin_port;
}
int net_context_get(sa_family_t family,
enum net_sock_type type,
enum net_ip_protocol ip_proto,
@ -220,6 +282,32 @@ int net_context_get(sa_family_t family,
memset(&contexts[i].remote, 0, sizeof(struct sockaddr));
memset(&contexts[i].local, 0, sizeof(struct sockaddr_ptr));
#if defined(CONFIG_NET_IPV6)
if (family == AF_INET6) {
struct sockaddr_in6 *addr6 = (struct sockaddr_in6
*)&contexts[i].local;
addr6->sin6_port = find_available_port(&contexts[i],
(struct sockaddr *)addr6);
if (!addr6->sin6_port) {
return -EADDRINUSE;
}
}
#endif
#if defined(CONFIG_NET_IPV4)
if (family == AF_INET) {
struct sockaddr_in *addr = (struct sockaddr_in
*)&contexts[i].local;
addr->sin_port = find_available_port(&contexts[i],
(struct sockaddr *)addr);
if (!addr->sin_port) {
return -EADDRINUSE;
}
}
#endif
#if defined(CONFIG_NET_CONTEXT_SYNC_RECV)
k_sem_init(&contexts[i].recv_data_wait, 0, UINT_MAX);
k_sem_give(&contexts[i].recv_data_wait);
@ -347,66 +435,6 @@ int net_context_put(struct net_context *context)
return 0;
}
static int check_used_port(enum net_ip_protocol ip_proto,
uint16_t local_port,
const struct sockaddr *local_addr)
{
int i;
for (i = 0; i < NET_MAX_CONTEXT; i++) {
if (!net_context_is_used(&contexts[i])) {
continue;
}
if (net_context_get_ip_proto(&contexts[i]) == ip_proto &&
net_sin((struct sockaddr *)&contexts[i].local)->sin_port ==
local_port) {
if (local_addr->family == AF_INET6) {
if (net_ipv6_addr_cmp(
net_sin6_ptr(&contexts[i].local)->
sin6_addr,
&((struct sockaddr_in6 *)
local_addr)->sin6_addr)) {
return -EEXIST;
}
} else {
if (net_ipv4_addr_cmp(
net_sin_ptr(&contexts[i].local)->
sin_addr,
&((struct sockaddr_in *)
local_addr)->sin_addr)) {
return -EEXIST;
}
}
}
}
return 0;
}
static uint16_t find_available_port(struct net_context *context,
const struct sockaddr *addr)
{
if (!net_sin(addr)->sin_port) {
uint16_t local_port;
do {
local_port = sys_rand32_get() | 0x8000;
if (local_port <= 1023) {
/* 0 - 1023 ports are reserved */
continue;
}
} while (check_used_port(
net_context_get_ip_proto(context),
htons(local_port), addr) == -EEXIST);
return htons(local_port);
}
return net_sin(addr)->sin_port;
}
int net_context_bind(struct net_context *context, const struct sockaddr *addr,
socklen_t addrlen)
{
@ -468,12 +496,6 @@ int net_context_bind(struct net_context *context, const struct sockaddr *addr,
}
#endif /* CONFIG_NET_L2_OFFLOAD_IP */
addr6->sin6_port = find_available_port(context,
(struct sockaddr *)addr6);
if (!addr6->sin6_port) {
return -EADDRINUSE;
}
net_context_set_iface(context, iface);
net_sin6_ptr(&context->local)->sin6_family = AF_INET6;
@ -531,12 +553,6 @@ int net_context_bind(struct net_context *context, const struct sockaddr *addr,
}
#endif /* CONFIG_NET_L2_OFFLOAD_IP */
addr4->sin_port = find_available_port(context,
(struct sockaddr *)addr4);
if (!addr4->sin_port) {
return -EADDRINUSE;
}
net_context_set_iface(context, iface);
net_sin_ptr(&context->local)->sin_family = AF_INET;