Commit ae052217 authored by Jukka Rissanen's avatar Jukka Rissanen Committed by Benjamin Cabé
Browse files

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: default avatarJukka Rissanen <jukka.rissanen@nordicsemi.no>
parent 8ac19edc
Loading
Loading
Loading
Loading
+9 −4
Changes for include/zephyr/net/net_if.h: 9 added lines, 4 removed lines.
Original line number Diff line number Diff line
@@ -136,7 +136,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;
};

/**
@@ -1217,9 +1220,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,
@@ -1231,7 +1235,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);
+37 −3
Changes for subsys/net/ip/net_if.c: 37 added lines, 3 removed lines.
Original line number Diff line number Diff line
@@ -1929,6 +1929,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;
@@ -1967,6 +1968,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;
	}

@@ -2024,6 +2036,7 @@ out:

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;
@@ -2040,11 +2053,12 @@ bool net_if_ipv6_addr_rm(struct net_if *iface, const struct in6_addr *addr)
		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);
		result = false;
		ifaddr->is_added = false;
		goto out;
	} else if (ret < 0) {
		NET_DBG("Address %s not found (%d)",
@@ -4316,6 +4330,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;
	}

@@ -4338,6 +4363,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];
@@ -4384,6 +4410,7 @@ out:

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;
@@ -4400,11 +4427,12 @@ bool net_if_ipv4_addr_rm(struct net_if *iface, const struct in_addr *addr)
		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);
		result = false;
		ifaddr->is_added = false;
		goto out;
	} else if (ret < 0) {
		NET_DBG("Address %s not found (%d)",
@@ -5215,11 +5243,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;
@@ -5273,6 +5303,10 @@ int net_if_addr_unref(struct net_if *iface,
#endif

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

		return ref - 1;
	}

+2 −1
Changes for subsys/net/ip/tcp.c: 2 added lines, 1 removed line.
Original line number Diff line number Diff line
@@ -784,7 +784,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;