net: connection: Split net_conn_input()

The current implementation of net_conn_input() can accept different
packet types, with completely different processing code, resulting in a
function which is pretty bloated, sliced with conditionally enabled code
and hard to understand and therefore maintain.

This commit splits that function into smaller ones, specialized for
different packet types (and entry levels). The following functions have
been extracted from the original one:
  - net_conn_packet_input() for early packet processing (covering
    AF_PACKET family sockets)
  - net_conn_raw_ip_input() for raw IP packets processing (covering
    AF_INET(6)/SOCK_RAW sockets)
  - net_conn_can_input() for CAN packets processing (covering AF_CAN
    family sockets)

The net_conn_input() function stripped from above cases now only takes
care of packets that have been processed by respective L4 and are
intended for regular TCP/UDP sockets.

Signed-off-by: Robert Lubos <robert.lubos@nordicsemi.no>
This commit is contained in:
Robert Lubos 2025-04-15 13:24:32 +02:00 committed by Benjamin Cabé
commit 32f3ce396f
6 changed files with 311 additions and 143 deletions

View file

@ -23,7 +23,7 @@ enum net_verdict net_canbus_socket_input(struct net_pkt *pkt)
__ASSERT_NO_MSG(net_pkt_family(pkt) == AF_CAN);
if (net_if_l2(net_pkt_iface(pkt)) == &NET_L2_GET_NAME(CANBUS_RAW)) {
return net_conn_input(pkt, NULL, CAN_RAW, NULL);
return net_conn_can_input(pkt, CAN_RAW);
}
return NET_CONTINUE;

View file

@ -576,6 +576,21 @@ static bool conn_are_endpoints_valid(struct net_pkt *pkt, uint8_t family,
return !are_invalid_endpoints;
}
/* Is the candidate connection matching the packet's interface? */
static bool is_iface_matching(struct net_conn *conn, struct net_pkt *pkt)
{
if (conn->context == NULL) {
return true;
}
if (!net_context_is_bound_to_iface(conn->context)) {
return true;
}
return (net_pkt_iface(pkt) == net_context_get_iface(conn->context));
}
#if defined(CONFIG_NET_SOCKETS_PACKET)
static enum net_verdict conn_raw_socket(struct net_pkt *pkt,
struct net_conn *conn, uint8_t proto)
{
@ -632,6 +647,103 @@ static enum net_verdict conn_raw_socket(struct net_pkt *pkt,
return NET_OK;
}
enum net_verdict net_conn_packet_input(struct net_pkt *pkt, uint16_t proto)
{
bool raw_pkt_delivered = false;
bool raw_pkt_continue = false;
struct net_conn *conn;
enum net_verdict ret;
/* Only accept input with AF_PACKET family. */
if (net_pkt_family(pkt) != AF_PACKET) {
return NET_CONTINUE;
}
NET_DBG("Check proto 0x%04x listener for pkt %p family %d",
proto, pkt, net_pkt_family(pkt));
k_mutex_lock(&conn_lock, K_FOREVER);
SYS_SLIST_FOR_EACH_CONTAINER(&conn_used, conn, node) {
if (!is_iface_matching(conn, pkt)) {
continue; /* wrong interface */
}
/* If there are other listening connections than
* AF_PACKET, the packet shall be also passed back to
* net_conn_input() in upper layer processing in order to
* re-check if there is any listening socket interested
* in this packet.
*/
if (conn->family != AF_PACKET) {
raw_pkt_continue = true;
continue; /* wrong family */
}
if (conn->type == SOCK_DGRAM && !net_pkt_is_l2_processed(pkt)) {
/* If DGRAM packet sockets are present, we shall continue
* with this packet regardless the result.
*/
raw_pkt_continue = true;
continue;
}
if (conn->proto == 0) {
continue; /* Local proto 0 doesn't forward any packets */
}
if (conn->proto != proto) {
/* Allow proto mismatch if socket was created with ETH_P_ALL, or it's raw
* packet socket input (proto ETH_P_ALL).
*/
if (conn->proto != ETH_P_ALL && proto != ETH_P_ALL) {
continue; /* wrong protocol */
}
}
/* Apply protocol-specific matching criteria... */
ret = conn_raw_socket(pkt, conn, proto);
if (ret == NET_DROP) {
k_mutex_unlock(&conn_lock);
goto drop;
} else if (ret == NET_OK) {
raw_pkt_delivered = true;
}
}
k_mutex_unlock(&conn_lock);
if (raw_pkt_continue) {
/* When there is open connection different than AF_PACKET this
* packet shall be also handled in the upper net stack layers.
*/
return NET_CONTINUE;
}
if (raw_pkt_delivered) {
/* As one or more raw socket packets have already been delivered
* in the loop above, report NET_OK.
*/
net_pkt_unref(pkt);
return NET_OK;
}
drop:
net_stats_update_per_proto_drop(net_pkt_iface(pkt), proto);
return NET_DROP;
}
#else
enum net_verdict net_conn_packet_input(struct net_pkt *pkt, uint16_t proto)
{
ARG_UNUSED(pkt);
ARG_UNUSED(proto);
return NET_CONTINUE;
}
#endif /* defined(CONFIG_NET_SOCKETS_PACKET) */
#if defined(CONFIG_NET_SOCKETS_INET_RAW)
static void conn_raw_ip_socket(struct net_pkt *pkt, struct net_conn *conn)
{
@ -656,14 +768,141 @@ static void conn_raw_ip_socket(struct net_pkt *pkt, struct net_conn *conn)
out:
net_pkt_cursor_restore(pkt, &cur);
}
enum net_verdict net_conn_raw_ip_input(struct net_pkt *pkt,
union net_ip_header *ip_hdr,
uint8_t proto)
{
uint8_t pkt_family = net_pkt_family(pkt);
struct net_conn *conn;
if (pkt_family != AF_INET && pkt_family != AF_INET6) {
return NET_CONTINUE;
}
NET_DBG("Check %s listener for pkt %p family %d",
net_proto2str(net_pkt_family(pkt), proto), pkt,
net_pkt_family(pkt));
k_mutex_lock(&conn_lock, K_FOREVER);
SYS_SLIST_FOR_EACH_CONTAINER(&conn_used, conn, node) {
if (!is_iface_matching(conn, pkt)) {
continue; /* wrong interface */
}
if (conn->family != pkt_family) {
continue; /* wrong family */
}
if (conn->type != SOCK_RAW) {
continue; /* wrong type */
}
if (conn->proto != proto && conn->proto != IPPROTO_IP) {
continue; /* wrong protocol */
}
/* Apply protocol-specific matching criteria... */
if ((conn->flags & NET_CONN_LOCAL_ADDR_SET) &&
!conn_addr_cmp(pkt, ip_hdr, &conn->local_addr, false)) {
continue; /* wrong local address */
}
conn_raw_ip_socket(pkt, conn);
}
k_mutex_unlock(&conn_lock);
/* Raw IP packets are passed further in the stack regardless. */
return NET_CONTINUE;
}
#else
static void conn_raw_ip_socket(struct net_pkt *pkt, struct net_conn *conn)
enum net_verdict net_conn_raw_ip_input(struct net_pkt *pkt,
union net_ip_header *ip_hdr,
uint8_t proto)
{
ARG_UNUSED(pkt);
ARG_UNUSED(conn);
ARG_UNUSED(ip_hdr);
ARG_UNUSED(proto);
return NET_CONTINUE;
}
#endif /* defined(CONFIG_NET_SOCKETS_INET_RAW) */
#if defined(CONFIG_NET_SOCKETS_CAN)
enum net_verdict net_conn_can_input(struct net_pkt *pkt, uint8_t proto)
{
struct net_conn *best_match = NULL;
struct net_conn *conn;
net_conn_cb_t cb = NULL;
void *user_data = NULL;
/* Only accept input with AF_CAN family and CAN_RAW protocol. */
if (net_pkt_family(pkt) != AF_CAN || proto != CAN_RAW) {
return NET_DROP;
}
NET_DBG("Check %s listener for pkt %p family %d",
net_proto2str(net_pkt_family(pkt), proto), pkt,
net_pkt_family(pkt));
k_mutex_lock(&conn_lock, K_FOREVER);
SYS_SLIST_FOR_EACH_CONTAINER(&conn_used, conn, node) {
if (!is_iface_matching(conn, pkt)) {
continue; /* wrong interface */
}
if (conn->family != AF_CAN) {
continue; /* wrong family */
}
if (conn->proto != CAN_RAW) {
continue; /* wrong protocol */
}
best_match = conn;
}
if (best_match != NULL) {
cb = best_match->cb;
user_data = best_match->user_data;
}
k_mutex_unlock(&conn_lock);
if (cb != NULL) {
NET_DBG("[%p] match found cb %p ud %p rank 0x%02x", best_match, cb,
user_data, NET_CONN_RANK(best_match->flags));
if (cb(best_match, pkt, NULL, NULL, user_data) == NET_DROP) {
goto drop;
}
net_stats_update_per_proto_recv(net_pkt_iface(pkt), proto);
return NET_OK;
}
NET_DBG("No match found.");
drop:
net_stats_update_per_proto_drop(net_pkt_iface(pkt), proto);
return NET_DROP;
}
#else
enum net_verdict net_conn_can_input(struct net_pkt *pkt, uint8_t proto)
{
ARG_UNUSED(pkt);
ARG_UNUSED(proto);
return NET_DROP;
}
#endif /* defined(CONFIG_NET_SOCKETS_CAN) */
enum net_verdict net_conn_input(struct net_pkt *pkt,
union net_ip_header *ip_hdr,
uint8_t proto,
@ -672,7 +911,6 @@ enum net_verdict net_conn_input(struct net_pkt *pkt,
struct net_if *pkt_iface = net_pkt_iface(pkt);
uint8_t pkt_family = net_pkt_family(pkt);
uint16_t src_port = 0U, dst_port = 0U;
bool raw_ip_pkt = false;
if (!net_pkt_filter_local_in_recv_ok(pkt)) {
/* drop the packet */
@ -680,10 +918,8 @@ enum net_verdict net_conn_input(struct net_pkt *pkt,
return NET_DROP;
}
if (IS_ENABLED(CONFIG_NET_IP) && (pkt_family == AF_INET || pkt_family == AF_INET6)) {
if (IS_ENABLED(CONFIG_NET_SOCKETS_INET_RAW) && proto_hdr == NULL) {
raw_ip_pkt = true;
} else if (IS_ENABLED(CONFIG_NET_UDP) && proto == IPPROTO_UDP) {
if (pkt_family == AF_INET || pkt_family == AF_INET6) {
if (IS_ENABLED(CONFIG_NET_UDP) && proto == IPPROTO_UDP) {
src_port = proto_hdr->udp->src_port;
dst_port = proto_hdr->udp->dst_port;
} else if (IS_ENABLED(CONFIG_NET_TCP) && proto == IPPROTO_TCP) {
@ -693,19 +929,11 @@ enum net_verdict net_conn_input(struct net_pkt *pkt,
src_port = proto_hdr->tcp->src_port;
dst_port = proto_hdr->tcp->dst_port;
}
if (!raw_ip_pkt && !conn_are_endpoints_valid(pkt, pkt_family, ip_hdr,
src_port, dst_port)) {
if (!conn_are_endpoints_valid(pkt, pkt_family, ip_hdr,
src_port, dst_port)) {
NET_DBG("Dropping invalid src/dst end-points packet");
return NET_DROP;
}
} else if (IS_ENABLED(CONFIG_NET_SOCKETS_PACKET) && pkt_family == AF_PACKET) {
if (proto != ETH_P_ALL) {
return NET_DROP;
}
} else if (IS_ENABLED(CONFIG_NET_SOCKETS_CAN) && pkt_family == AF_CAN) {
if (proto != CAN_RAW) {
return NET_DROP;
}
} else {
NET_DBG("No suitable protocol handler configured");
return NET_DROP;
@ -715,59 +943,39 @@ enum net_verdict net_conn_input(struct net_pkt *pkt,
" family %d", net_proto2str(net_pkt_family(pkt), proto), pkt,
ntohs(src_port), ntohs(dst_port), net_pkt_family(pkt));
struct net_conn *best_match = NULL;
int16_t best_rank = -1;
bool is_mcast_pkt = false;
bool mcast_pkt_delivered = false;
bool is_bcast_pkt = false;
bool raw_pkt_delivered = false;
bool raw_pkt_continue = false;
struct net_conn *conn;
net_conn_cb_t cb = NULL;
void *user_data = NULL;
if (IS_ENABLED(CONFIG_NET_IP) && !raw_ip_pkt) {
/* If we receive a packet with multicast destination address, we might
* need to deliver the packet to multiple recipients.
*/
if (IS_ENABLED(CONFIG_NET_IPV4) && pkt_family == AF_INET) {
if (net_ipv4_is_addr_mcast((struct in_addr *)ip_hdr->ipv4->dst)) {
is_mcast_pkt = true;
} else if (net_if_ipv4_is_addr_bcast(pkt_iface,
(struct in_addr *)ip_hdr->ipv4->dst)) {
is_bcast_pkt = true;
}
} else if (IS_ENABLED(CONFIG_NET_IPV6) && pkt_family == AF_INET6) {
is_mcast_pkt = net_ipv6_is_addr_mcast((struct in6_addr *)ip_hdr->ipv6->dst);
/* If we receive a packet with multicast destination address, we might
* need to deliver the packet to multiple recipients.
*/
if (IS_ENABLED(CONFIG_NET_IPV4) && pkt_family == AF_INET) {
if (net_ipv4_is_addr_mcast((struct in_addr *)ip_hdr->ipv4->dst)) {
is_mcast_pkt = true;
} else if (net_if_ipv4_is_addr_bcast(pkt_iface,
(struct in_addr *)ip_hdr->ipv4->dst)) {
is_bcast_pkt = true;
}
} else if (IS_ENABLED(CONFIG_NET_IPV6) && pkt_family == AF_INET6) {
is_mcast_pkt = net_ipv6_is_addr_mcast((struct in6_addr *)ip_hdr->ipv6->dst);
}
k_mutex_lock(&conn_lock, K_FOREVER);
SYS_SLIST_FOR_EACH_CONTAINER(&conn_used, conn, node) {
/* Is the candidate connection matching the packet's interface? */
if (conn->context != NULL &&
net_context_is_bound_to_iface(conn->context) &&
net_pkt_iface(pkt) != net_context_get_iface(conn->context)) {
if (!is_iface_matching(conn, pkt)) {
continue; /* wrong interface */
}
/* Is the candidate connection matching the packet's protocol family? */
if (conn->family != AF_UNSPEC &&
conn->family != pkt_family) {
if (IS_ENABLED(CONFIG_NET_SOCKETS_PACKET)) {
/* If there are other listening connections than
* AF_PACKET, the packet shall be also passed back to
* net_conn_input() in upper layer processing in order to
* re-check if there is any listening socket interested
* in this packet.
*/
if (conn->family != AF_PACKET) {
raw_pkt_continue = true;
}
}
if (conn->family != AF_UNSPEC && conn->family != pkt_family) {
if (IS_ENABLED(CONFIG_NET_IPV4_MAPPING_TO_IPV6)) {
if (!(conn->family == AF_INET6 && pkt_family == AF_INET &&
!conn->v6only && conn->type != SOCK_RAW)) {
@ -780,72 +988,17 @@ enum net_verdict net_conn_input(struct net_pkt *pkt,
/* We might have a match for v4-to-v6 mapping, check more */
}
if (IS_ENABLED(CONFIG_NET_SOCKETS_INET_RAW) && raw_ip_pkt &&
conn->type != SOCK_RAW) {
continue;
}
/* Is the candidate connection matching the packet's protocol within the family? */
if (conn->proto != proto) {
/* For packet socket data, the proto is set to ETH_P_ALL
* but the listener might have a specific protocol set.
* This is ok and let the packet pass this check in this
* case.
*/
if (IS_ENABLED(CONFIG_NET_SOCKETS_PACKET) && pkt_family == AF_PACKET) {
if (conn->proto == 0 || proto != ETH_P_ALL) {
continue; /* wrong protocol */
}
} else if (IS_ENABLED(CONFIG_NET_SOCKETS_INET_RAW) && raw_ip_pkt) {
if (conn->proto != IPPROTO_IP) {
continue;
}
} else {
continue; /* wrong protocol */
}
continue; /* wrong protocol */
}
/* Apply protocol-specific matching criteria... */
uint8_t conn_family = conn->family;
if (IS_ENABLED(CONFIG_NET_SOCKETS_PACKET) && conn_family == AF_PACKET) {
/* This code shall be only executed when one enters
* the net_conn_input() from net_packet_socket() which
* targets AF_PACKET sockets.
*
* All AF_PACKET connections will receive the packet if
* their socket type and protocol also matches.
*/
if (IS_ENABLED(CONFIG_NET_SOCKETS_PACKET_DGRAM) &&
(proto == ETH_P_ALL) && !net_pkt_is_l2_processed(pkt)) {
/* We shall continue with ETH_P_ALL to AF_PACKET/SOCK_DGRAM: */
raw_pkt_continue = true;
}
if (proto == ETH_P_ALL) {
enum net_verdict ret = conn_raw_socket(pkt, conn, proto);
if (ret == NET_DROP) {
k_mutex_unlock(&conn_lock);
goto drop;
} else if (ret == NET_OK) {
raw_pkt_delivered = true;
}
continue; /* packet was consumed */
}
} else if (IS_ENABLED(CONFIG_NET_SOCKETS_INET_RAW) && raw_ip_pkt) {
if ((conn->flags & NET_CONN_LOCAL_ADDR_SET) &&
!conn_addr_cmp(pkt, ip_hdr, &conn->local_addr, false)) {
continue; /* wrong local address */
}
conn_raw_ip_socket(pkt, conn);
continue;
} else if ((IS_ENABLED(CONFIG_NET_UDP) || IS_ENABLED(CONFIG_NET_TCP)) &&
(conn_family == AF_INET || conn_family == AF_INET6 ||
conn_family == AF_UNSPEC)) {
if ((IS_ENABLED(CONFIG_NET_UDP) || IS_ENABLED(CONFIG_NET_TCP)) &&
(conn_family == AF_INET || conn_family == AF_INET6 ||
conn_family == AF_UNSPEC)) {
/* Is the candidate connection matching the packet's TCP/UDP
* address and port?
*/
@ -925,42 +1078,17 @@ enum net_verdict net_conn_input(struct net_pkt *pkt,
mcast_pkt_delivered = true;
}
} else if (IS_ENABLED(CONFIG_NET_SOCKETS_CAN) && conn_family == AF_CAN) {
best_match = conn;
}
} /* loop end */
if (best_match) {
if (best_match != NULL) {
cb = best_match->cb;
user_data = best_match->user_data;
}
k_mutex_unlock(&conn_lock);
if (IS_ENABLED(CONFIG_NET_SOCKETS_PACKET) && pkt_family == AF_PACKET) {
if (raw_pkt_continue) {
/* When there is open connection different than
* AF_PACKET this packet shall be also handled in
* the upper net stack layers.
*/
return NET_CONTINUE;
}
if (raw_pkt_delivered) {
/* As one or more raw socket packets
* have already been delivered in the loop above,
* we shall not call the callback again here.
*/
net_pkt_unref(pkt);
return NET_OK;
}
}
if (IS_ENABLED(CONFIG_NET_SOCKETS_INET_RAW) && raw_ip_pkt) {
/* Raw IP packets are passed further in the stack regardless. */
return NET_CONTINUE;
}
if (IS_ENABLED(CONFIG_NET_IP) && is_mcast_pkt && mcast_pkt_delivered) {
if (is_mcast_pkt && mcast_pkt_delivered) {
/* As one or more multicast packets
* have already been delivered in the loop above,
* we shall not call the callback again here.
@ -969,7 +1097,7 @@ enum net_verdict net_conn_input(struct net_pkt *pkt,
return NET_OK;
}
if (cb) {
if (cb != NULL) {
NET_DBG("[%p] match found cb %p ud %p rank 0x%02x", best_match, cb,
user_data, NET_CONN_RANK(best_match->flags));
@ -985,7 +1113,7 @@ enum net_verdict net_conn_input(struct net_pkt *pkt,
NET_DBG("No match found.");
if (IS_ENABLED(CONFIG_NET_IP) && (pkt_family == AF_INET || pkt_family == AF_INET6) &&
if ((pkt_family == AF_INET || pkt_family == AF_INET6) &&
!(is_mcast_pkt || is_bcast_pkt)) {
if (IS_ENABLED(CONFIG_NET_TCP) && proto == IPPROTO_TCP &&
IS_ENABLED(CONFIG_NET_TCP_REJECT_CONN_WITH_RST)) {

View file

@ -180,11 +180,51 @@ int net_conn_update(struct net_conn_handle *handle,
uint16_t remote_port);
/**
* @brief Called by net_core.c when a network packet is received.
* @brief Called by net_core.c when a network packet is received
* (before L3 processing).
*
* @param pkt Network packet holding received data
* @param proto LL protocol for the connection
*
* @return NET_OK if the packet was consumed, NET_CONTINUE if the packet should
* be processed further in the stack.
*/
enum net_verdict net_conn_packet_input(struct net_pkt *pkt, uint16_t proto);
/**
* @brief Called by net_core.c when an IP packet is received
* (before L4 processing).
*
* @param pkt Network packet holding received data
* @param ip_hdr A pointer to the IP header within the packet
* @param proto L4 protocol for the connection
*
* @return NET_CONTINUE if the packet should be further processed in the stack.
*/
enum net_verdict net_conn_raw_ip_input(struct net_pkt *pkt,
union net_ip_header *ip_hdr,
uint8_t proto);
/**
* @brief Called by net_core.c when a CAN packet is received.
*
* @param pkt Network packet holding received data
* @param proto Protocol for the connection
*
* @return NET_OK if the packet was consumed, NET_DROP if the packet parsing
* failed and the packet should be discarded.
*/
enum net_verdict net_conn_can_input(struct net_pkt *pkt, uint8_t proto);
/**
* @brief Called by net_core.c when a network packet is received (after L4
* processing).
*
* @param pkt Network packet holding received data
* @param ip_hdr A pointer to the IP header within the packet
* @param proto Protocol for the connection
* @param proto_hdr A pointer to the L4 protocol header within the packet
*
* @return NET_OK if the packet was consumed, NET_DROP if
* the packet parsing failed and the caller should handle
* the received packet. If corresponding IP protocol support is

View file

@ -387,7 +387,7 @@ enum net_verdict net_ipv4_input(struct net_pkt *pkt, bool is_loopback)
ip.ipv4 = hdr;
if (IS_ENABLED(CONFIG_NET_SOCKETS_INET_RAW)) {
if (net_conn_input(pkt, &ip, hdr->proto, NULL) == NET_DROP) {
if (net_conn_raw_ip_input(pkt, &ip, hdr->proto) == NET_DROP) {
goto drop;
}
}

View file

@ -766,7 +766,7 @@ enum net_verdict net_ipv6_input(struct net_pkt *pkt, bool is_loopback)
ip.ipv6 = hdr;
if (IS_ENABLED(CONFIG_NET_SOCKETS_INET_RAW)) {
if (net_conn_input(pkt, &ip, current_hdr, NULL) == NET_DROP) {
if (net_conn_raw_ip_input(pkt, &ip, current_hdr) == NET_DROP) {
goto drop;
}
}

View file

@ -39,7 +39,7 @@ enum net_verdict net_packet_socket_input(struct net_pkt *pkt, uint16_t proto)
net_pkt_set_family(pkt, AF_PACKET);
net_verdict = net_conn_input(pkt, NULL, proto, NULL);
net_verdict = net_conn_packet_input(pkt, proto);
net_pkt_set_family(pkt, orig_family);