Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Backport v3.7-branch] net: Update IP address refcount properly when address already exists #85659

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions include/zephyr/net/net_if.h
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,10 @@ struct net_if_addr {
*/
uint8_t is_temporary : 1;

uint8_t _unused : 4;
/** Was this address added or not */
uint8_t is_added : 1;

uint8_t _unused : 3;
};

/**
Expand Down Expand Up @@ -1180,9 +1183,10 @@ int net_if_set_link_addr_locked(struct net_if *iface,
extern int net_if_addr_unref_debug(struct net_if *iface,
sa_family_t family,
const void *addr,
struct net_if_addr **ifaddr,
const char *caller, int line);
#define net_if_addr_unref(iface, family, addr) \
net_if_addr_unref_debug(iface, family, addr, __func__, __LINE__)
#define net_if_addr_unref(iface, family, addr, ifaddr) \
net_if_addr_unref_debug(iface, family, addr, ifaddr, __func__, __LINE__)

extern struct net_if_addr *net_if_addr_ref_debug(struct net_if *iface,
sa_family_t family,
Expand All @@ -1194,7 +1198,8 @@ extern struct net_if_addr *net_if_addr_ref_debug(struct net_if *iface,
#else
extern int net_if_addr_unref(struct net_if *iface,
sa_family_t family,
const void *addr);
const void *addr,
struct net_if_addr **ifaddr);
extern struct net_if_addr *net_if_addr_ref(struct net_if *iface,
sa_family_t family,
const void *addr);
Expand Down
70 changes: 59 additions & 11 deletions subsys/net/ip/net_if.c
Original file line number Diff line number Diff line change
Expand Up @@ -1868,6 +1868,7 @@ static inline void net_if_addr_init(struct net_if_addr *ifaddr,
uint32_t vlifetime)
{
ifaddr->is_used = true;
ifaddr->is_added = true;
ifaddr->is_temporary = false;
ifaddr->address.family = AF_INET6;
ifaddr->addr_type = addr_type;
Expand Down Expand Up @@ -1906,6 +1907,17 @@ struct net_if_addr *net_if_ipv6_addr_add(struct net_if *iface,

ifaddr = ipv6_addr_find(iface, addr);
if (ifaddr) {
/* Address already exists, just return it but update ref count
* if it was not updated. This could happen if the address was
* added and then removed but for example an active connection
* was still using it. In this case we must update the ref count
* so that the address is not removed if the connection is closed.
*/
if (!ifaddr->is_added) {
atomic_inc(&ifaddr->atomic_ref);
ifaddr->is_added = true;
}

goto out;
}

Expand Down Expand Up @@ -1963,28 +1975,37 @@ struct net_if_addr *net_if_ipv6_addr_add(struct net_if *iface,

bool net_if_ipv6_addr_rm(struct net_if *iface, const struct in6_addr *addr)
{
struct net_if_addr *ifaddr;
struct net_if_ipv6 *ipv6;
bool result = true;
int ret;

NET_ASSERT(addr);

net_if_lock(iface);

ipv6 = iface->config.ip.ipv6;
if (!ipv6) {
return false;
result = false;
goto out;
}

ret = net_if_addr_unref(iface, AF_INET6, addr);
ret = net_if_addr_unref(iface, AF_INET6, addr, &ifaddr);
if (ret > 0) {
NET_DBG("Address %s still in use (ref %d)",
net_sprint_ipv6_addr(addr), ret);
return false;

result = false;
ifaddr->is_added = false;
goto out;
} else if (ret < 0) {
NET_DBG("Address %s not found (%d)",
net_sprint_ipv6_addr(addr), ret);
}

return true;
out:
net_if_unlock(iface);

return result;
}

bool z_impl_net_if_ipv6_addr_add_by_index(int index,
Expand Down Expand Up @@ -4258,6 +4279,17 @@ struct net_if_addr *net_if_ipv4_addr_add(struct net_if *iface,
ifaddr = ipv4_addr_find(iface, addr);
if (ifaddr) {
/* TODO: should set addr_type/vlifetime */
/* Address already exists, just return it but update ref count
* if it was not updated. This could happen if the address was
* added and then removed but for example an active connection
* was still using it. In this case we must update the ref count
* so that the address is not removed if the connection is closed.
*/
if (!ifaddr->is_added) {
atomic_inc(&ifaddr->atomic_ref);
ifaddr->is_added = true;
}

goto out;
}

Expand All @@ -4280,6 +4312,7 @@ struct net_if_addr *net_if_ipv4_addr_add(struct net_if *iface,

if (ifaddr) {
ifaddr->is_used = true;
ifaddr->is_added = true;
ifaddr->address.family = AF_INET;
ifaddr->address.in_addr.s4_addr32[0] =
addr->s4_addr32[0];
Expand Down Expand Up @@ -4324,28 +4357,37 @@ struct net_if_addr *net_if_ipv4_addr_add(struct net_if *iface,

bool net_if_ipv4_addr_rm(struct net_if *iface, const struct in_addr *addr)
{
struct net_if_addr *ifaddr;
struct net_if_ipv4 *ipv4;
bool result = true;
int ret;

NET_ASSERT(addr);

net_if_lock(iface);

ipv4 = iface->config.ip.ipv4;
if (!ipv4) {
return false;
result = false;
goto out;
}

ret = net_if_addr_unref(iface, AF_INET, addr);
ret = net_if_addr_unref(iface, AF_INET, addr, &ifaddr);
if (ret > 0) {
NET_DBG("Address %s still in use (ref %d)",
net_sprint_ipv4_addr(addr), ret);
return false;

result = false;
ifaddr->is_added = false;
goto out;
} else if (ret < 0) {
NET_DBG("Address %s not found (%d)",
net_sprint_ipv4_addr(addr), ret);
}

return true;
out:
net_if_unlock(iface);

return result;
}

bool z_impl_net_if_ipv4_addr_add_by_index(int index,
Expand Down Expand Up @@ -4959,11 +5001,13 @@ struct net_if_addr *net_if_addr_ref(struct net_if *iface,
int net_if_addr_unref_debug(struct net_if *iface,
sa_family_t family,
const void *addr,
struct net_if_addr **ret_ifaddr,
const char *caller, int line)
#else
int net_if_addr_unref(struct net_if *iface,
sa_family_t family,
const void *addr)
const void *addr,
struct net_if_addr **ret_ifaddr)
#endif /* NET_LOG_LEVEL >= LOG_LEVEL_DBG */
{
struct net_if_addr *ifaddr;
Expand Down Expand Up @@ -5017,6 +5061,10 @@ int net_if_addr_unref(struct net_if *iface,
#endif

if (ref > 1) {
if (ret_ifaddr) {
*ret_ifaddr = ifaddr;
}

return ref - 1;
}

Expand Down
3 changes: 2 additions & 1 deletion subsys/net/ip/tcp.c
Original file line number Diff line number Diff line change
Expand Up @@ -783,7 +783,8 @@ static void tcp_conn_release(struct k_work *work)
net_if_addr_unref(conn->iface, conn->src.sa.sa_family,
conn->src.sa.sa_family == AF_INET ?
(const void *)&conn->src.sin.sin_addr :
(const void *)&conn->src.sin6.sin6_addr);
(const void *)&conn->src.sin6.sin6_addr,
NULL);
}

conn->context->tcp = NULL;
Expand Down