Skip to content

Commit 0ea0868

Browse files
jukkarnashif
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]>
1 parent 764bc21 commit 0ea0868

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
@@ -132,7 +132,10 @@ struct net_if_addr {
132132
*/
133133
uint8_t is_temporary : 1;
134134

135-
uint8_t _unused : 4;
135+
/** Was this address added or not */
136+
uint8_t is_added : 1;
137+
138+
uint8_t _unused : 3;
136139
};
137140

138141
/**
@@ -1180,9 +1183,10 @@ int net_if_set_link_addr_locked(struct net_if *iface,
11801183
extern int net_if_addr_unref_debug(struct net_if *iface,
11811184
sa_family_t family,
11821185
const void *addr,
1186+
struct net_if_addr **ifaddr,
11831187
const char *caller, int line);
1184-
#define net_if_addr_unref(iface, family, addr) \
1185-
net_if_addr_unref_debug(iface, family, addr, __func__, __LINE__)
1188+
#define net_if_addr_unref(iface, family, addr, ifaddr) \
1189+
net_if_addr_unref_debug(iface, family, addr, ifaddr, __func__, __LINE__)
11861190

11871191
extern struct net_if_addr *net_if_addr_ref_debug(struct net_if *iface,
11881192
sa_family_t family,
@@ -1194,7 +1198,8 @@ extern struct net_if_addr *net_if_addr_ref_debug(struct net_if *iface,
11941198
#else
11951199
extern int net_if_addr_unref(struct net_if *iface,
11961200
sa_family_t family,
1197-
const void *addr);
1201+
const void *addr,
1202+
struct net_if_addr **ifaddr);
11981203
extern struct net_if_addr *net_if_addr_ref(struct net_if *iface,
11991204
sa_family_t family,
12001205
const void *addr);

subsys/net/ip/net_if.c

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1868,6 +1868,7 @@ static inline void net_if_addr_init(struct net_if_addr *ifaddr,
18681868
uint32_t vlifetime)
18691869
{
18701870
ifaddr->is_used = true;
1871+
ifaddr->is_added = true;
18711872
ifaddr->is_temporary = false;
18721873
ifaddr->address.family = AF_INET6;
18731874
ifaddr->addr_type = addr_type;
@@ -1906,6 +1907,17 @@ struct net_if_addr *net_if_ipv6_addr_add(struct net_if *iface,
19061907

19071908
ifaddr = ipv6_addr_find(iface, addr);
19081909
if (ifaddr) {
1910+
/* Address already exists, just return it but update ref count
1911+
* if it was not updated. This could happen if the address was
1912+
* added and then removed but for example an active connection
1913+
* was still using it. In this case we must update the ref count
1914+
* so that the address is not removed if the connection is closed.
1915+
*/
1916+
if (!ifaddr->is_added) {
1917+
atomic_inc(&ifaddr->atomic_ref);
1918+
ifaddr->is_added = true;
1919+
}
1920+
19091921
goto out;
19101922
}
19111923

@@ -1963,6 +1975,7 @@ struct net_if_addr *net_if_ipv6_addr_add(struct net_if *iface,
19631975

19641976
bool net_if_ipv6_addr_rm(struct net_if *iface, const struct in6_addr *addr)
19651977
{
1978+
struct net_if_addr *ifaddr;
19661979
struct net_if_ipv6 *ipv6;
19671980
bool result = true;
19681981
int ret;
@@ -1977,11 +1990,12 @@ bool net_if_ipv6_addr_rm(struct net_if *iface, const struct in6_addr *addr)
19771990
goto out;
19781991
}
19791992

1980-
ret = net_if_addr_unref(iface, AF_INET6, addr);
1993+
ret = net_if_addr_unref(iface, AF_INET6, addr, &ifaddr);
19811994
if (ret > 0) {
19821995
NET_DBG("Address %s still in use (ref %d)",
19831996
net_sprint_ipv6_addr(addr), ret);
19841997
result = false;
1998+
ifaddr->is_added = false;
19851999
goto out;
19862000
} else if (ret < 0) {
19872001
NET_DBG("Address %s not found (%d)",
@@ -4265,6 +4279,17 @@ struct net_if_addr *net_if_ipv4_addr_add(struct net_if *iface,
42654279
ifaddr = ipv4_addr_find(iface, addr);
42664280
if (ifaddr) {
42674281
/* TODO: should set addr_type/vlifetime */
4282+
/* Address already exists, just return it but update ref count
4283+
* if it was not updated. This could happen if the address was
4284+
* added and then removed but for example an active connection
4285+
* was still using it. In this case we must update the ref count
4286+
* so that the address is not removed if the connection is closed.
4287+
*/
4288+
if (!ifaddr->is_added) {
4289+
atomic_inc(&ifaddr->atomic_ref);
4290+
ifaddr->is_added = true;
4291+
}
4292+
42684293
goto out;
42694294
}
42704295

@@ -4287,6 +4312,7 @@ struct net_if_addr *net_if_ipv4_addr_add(struct net_if *iface,
42874312

42884313
if (ifaddr) {
42894314
ifaddr->is_used = true;
4315+
ifaddr->is_added = true;
42904316
ifaddr->address.family = AF_INET;
42914317
ifaddr->address.in_addr.s4_addr32[0] =
42924318
addr->s4_addr32[0];
@@ -4331,6 +4357,7 @@ struct net_if_addr *net_if_ipv4_addr_add(struct net_if *iface,
43314357

43324358
bool net_if_ipv4_addr_rm(struct net_if *iface, const struct in_addr *addr)
43334359
{
4360+
struct net_if_addr *ifaddr;
43344361
struct net_if_ipv4 *ipv4;
43354362
bool result = true;
43364363
int ret;
@@ -4345,11 +4372,12 @@ bool net_if_ipv4_addr_rm(struct net_if *iface, const struct in_addr *addr)
43454372
goto out;
43464373
}
43474374

4348-
ret = net_if_addr_unref(iface, AF_INET, addr);
4375+
ret = net_if_addr_unref(iface, AF_INET, addr, &ifaddr);
43494376
if (ret > 0) {
43504377
NET_DBG("Address %s still in use (ref %d)",
43514378
net_sprint_ipv4_addr(addr), ret);
43524379
result = false;
4380+
ifaddr->is_added = false;
43534381
goto out;
43544382
} else if (ret < 0) {
43554383
NET_DBG("Address %s not found (%d)",
@@ -4973,11 +5001,13 @@ struct net_if_addr *net_if_addr_ref(struct net_if *iface,
49735001
int net_if_addr_unref_debug(struct net_if *iface,
49745002
sa_family_t family,
49755003
const void *addr,
5004+
struct net_if_addr **ret_ifaddr,
49765005
const char *caller, int line)
49775006
#else
49785007
int net_if_addr_unref(struct net_if *iface,
49795008
sa_family_t family,
4980-
const void *addr)
5009+
const void *addr,
5010+
struct net_if_addr **ret_ifaddr)
49815011
#endif /* NET_LOG_LEVEL >= LOG_LEVEL_DBG */
49825012
{
49835013
struct net_if_addr *ifaddr;
@@ -5031,6 +5061,10 @@ int net_if_addr_unref(struct net_if *iface,
50315061
#endif
50325062

50335063
if (ref > 1) {
5064+
if (ret_ifaddr) {
5065+
*ret_ifaddr = ifaddr;
5066+
}
5067+
50345068
return ref - 1;
50355069
}
50365070

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)