Skip to content

Commit b40cacb

Browse files
jukkardkalowsk
authored andcommitted
net: Update IP address refcount properly when address already exists
If an IP address already exists when it is tried to be added to the network interface, then 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 and keeping the ref count > 0. In this case we must update the ref count so that the IP address is not removed if the connection is closed. Fixes #85380 Signed-off-by: Jukka Rissanen <[email protected]> (cherry picked from commit ae05221)
1 parent c1f3c44 commit b40cacb

File tree

3 files changed

+48
-8
lines changed

3 files changed

+48
-8
lines changed

include/zephyr/net/net_if.h

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,10 @@ struct net_if_addr {
134134
*/
135135
uint8_t is_temporary : 1;
136136

137-
uint8_t _unused : 4;
137+
/** Was this address added or not */
138+
uint8_t is_added : 1;
139+
140+
uint8_t _unused : 3;
138141
};
139142

140143
/**
@@ -1183,9 +1186,10 @@ int net_if_set_link_addr_locked(struct net_if *iface,
11831186
extern int net_if_addr_unref_debug(struct net_if *iface,
11841187
sa_family_t family,
11851188
const void *addr,
1189+
struct net_if_addr **ifaddr,
11861190
const char *caller, int line);
1187-
#define net_if_addr_unref(iface, family, addr) \
1188-
net_if_addr_unref_debug(iface, family, addr, __func__, __LINE__)
1191+
#define net_if_addr_unref(iface, family, addr, ifaddr) \
1192+
net_if_addr_unref_debug(iface, family, addr, ifaddr, __func__, __LINE__)
11891193

11901194
extern struct net_if_addr *net_if_addr_ref_debug(struct net_if *iface,
11911195
sa_family_t family,
@@ -1197,7 +1201,8 @@ extern struct net_if_addr *net_if_addr_ref_debug(struct net_if *iface,
11971201
#else
11981202
extern int net_if_addr_unref(struct net_if *iface,
11991203
sa_family_t family,
1200-
const void *addr);
1204+
const void *addr,
1205+
struct net_if_addr **ifaddr);
12011206
extern struct net_if_addr *net_if_addr_ref(struct net_if *iface,
12021207
sa_family_t family,
12031208
const void *addr);

subsys/net/ip/net_if.c

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1907,6 +1907,7 @@ static inline void net_if_addr_init(struct net_if_addr *ifaddr,
19071907
uint32_t vlifetime)
19081908
{
19091909
ifaddr->is_used = true;
1910+
ifaddr->is_added = true;
19101911
ifaddr->is_temporary = false;
19111912
ifaddr->address.family = AF_INET6;
19121913
ifaddr->addr_type = addr_type;
@@ -1945,6 +1946,17 @@ struct net_if_addr *net_if_ipv6_addr_add(struct net_if *iface,
19451946

19461947
ifaddr = ipv6_addr_find(iface, addr);
19471948
if (ifaddr) {
1949+
/* Address already exists, just return it but update ref count
1950+
* if it was not updated. This could happen if the address was
1951+
* added and then removed but for example an active connection
1952+
* was still using it. In this case we must update the ref count
1953+
* so that the address is not removed if the connection is closed.
1954+
*/
1955+
if (!ifaddr->is_added) {
1956+
atomic_inc(&ifaddr->atomic_ref);
1957+
ifaddr->is_added = true;
1958+
}
1959+
19481960
goto out;
19491961
}
19501962

@@ -2002,6 +2014,7 @@ struct net_if_addr *net_if_ipv6_addr_add(struct net_if *iface,
20022014

20032015
bool net_if_ipv6_addr_rm(struct net_if *iface, const struct in6_addr *addr)
20042016
{
2017+
struct net_if_addr *ifaddr;
20052018
struct net_if_ipv6 *ipv6;
20062019
bool result = true;
20072020
int ret;
@@ -2016,11 +2029,12 @@ bool net_if_ipv6_addr_rm(struct net_if *iface, const struct in6_addr *addr)
20162029
goto out;
20172030
}
20182031

2019-
ret = net_if_addr_unref(iface, AF_INET6, addr);
2032+
ret = net_if_addr_unref(iface, AF_INET6, addr, &ifaddr);
20202033
if (ret > 0) {
20212034
NET_DBG("Address %s still in use (ref %d)",
20222035
net_sprint_ipv6_addr(addr), ret);
20232036
result = false;
2037+
ifaddr->is_added = false;
20242038
goto out;
20252039
} else if (ret < 0) {
20262040
NET_DBG("Address %s not found (%d)",
@@ -4229,6 +4243,17 @@ struct net_if_addr *net_if_ipv4_addr_add(struct net_if *iface,
42294243
ifaddr = ipv4_addr_find(iface, addr);
42304244
if (ifaddr) {
42314245
/* TODO: should set addr_type/vlifetime */
4246+
/* Address already exists, just return it but update ref count
4247+
* if it was not updated. This could happen if the address was
4248+
* added and then removed but for example an active connection
4249+
* was still using it. In this case we must update the ref count
4250+
* so that the address is not removed if the connection is closed.
4251+
*/
4252+
if (!ifaddr->is_added) {
4253+
atomic_inc(&ifaddr->atomic_ref);
4254+
ifaddr->is_added = true;
4255+
}
4256+
42324257
goto out;
42334258
}
42344259

@@ -4251,6 +4276,7 @@ struct net_if_addr *net_if_ipv4_addr_add(struct net_if *iface,
42514276

42524277
if (ifaddr) {
42534278
ifaddr->is_used = true;
4279+
ifaddr->is_added = true;
42544280
ifaddr->address.family = AF_INET;
42554281
ifaddr->address.in_addr.s4_addr32[0] =
42564282
addr->s4_addr32[0];
@@ -4295,6 +4321,7 @@ struct net_if_addr *net_if_ipv4_addr_add(struct net_if *iface,
42954321

42964322
bool net_if_ipv4_addr_rm(struct net_if *iface, const struct in_addr *addr)
42974323
{
4324+
struct net_if_addr *ifaddr;
42984325
struct net_if_ipv4 *ipv4;
42994326
bool result = true;
43004327
int ret;
@@ -4309,11 +4336,12 @@ bool net_if_ipv4_addr_rm(struct net_if *iface, const struct in_addr *addr)
43094336
goto out;
43104337
}
43114338

4312-
ret = net_if_addr_unref(iface, AF_INET, addr);
4339+
ret = net_if_addr_unref(iface, AF_INET, addr, &ifaddr);
43134340
if (ret > 0) {
43144341
NET_DBG("Address %s still in use (ref %d)",
43154342
net_sprint_ipv4_addr(addr), ret);
43164343
result = false;
4344+
ifaddr->is_added = false;
43174345
goto out;
43184346
} else if (ret < 0) {
43194347
NET_DBG("Address %s not found (%d)",
@@ -5048,11 +5076,13 @@ struct net_if_addr *net_if_addr_ref(struct net_if *iface,
50485076
int net_if_addr_unref_debug(struct net_if *iface,
50495077
sa_family_t family,
50505078
const void *addr,
5079+
struct net_if_addr **ret_ifaddr,
50515080
const char *caller, int line)
50525081
#else
50535082
int net_if_addr_unref(struct net_if *iface,
50545083
sa_family_t family,
5055-
const void *addr)
5084+
const void *addr,
5085+
struct net_if_addr **ret_ifaddr)
50565086
#endif /* NET_LOG_LEVEL >= LOG_LEVEL_DBG */
50575087
{
50585088
struct net_if_addr *ifaddr;
@@ -5106,6 +5136,10 @@ int net_if_addr_unref(struct net_if *iface,
51065136
#endif
51075137

51085138
if (ref > 1) {
5139+
if (ret_ifaddr) {
5140+
*ret_ifaddr = ifaddr;
5141+
}
5142+
51095143
return ref - 1;
51105144
}
51115145

subsys/net/ip/tcp.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -783,7 +783,8 @@ static void tcp_conn_release(struct k_work *work)
783783
net_if_addr_unref(conn->iface, conn->src.sa.sa_family,
784784
conn->src.sa.sa_family == AF_INET ?
785785
(const void *)&conn->src.sin.sin_addr :
786-
(const void *)&conn->src.sin6.sin6_addr);
786+
(const void *)&conn->src.sin6.sin6_addr,
787+
NULL);
787788
}
788789

789790
conn->context->tcp = NULL;

0 commit comments

Comments
 (0)