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

net: conn: Deliver multicast pkt to all interested parties #18797

Merged
merged 2 commits into from
Sep 12, 2019
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
78 changes: 69 additions & 9 deletions subsys/net/ip/connection.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ LOG_MODULE_REGISTER(net_conn, CONFIG_NET_CONN_LOG_LEVEL);
#include "connection.h"
#include "net_stats.h"

/** How long to wait for when cloning multicast packet */
#define CLONE_TIMEOUT K_MSEC(100)

/** Is this connection used or not */
#define NET_CONN_IN_USE BIT(0)

Expand Down Expand Up @@ -504,6 +507,7 @@ enum net_verdict net_conn_input(struct net_pkt *pkt,
{
struct net_if *pkt_iface = net_pkt_iface(pkt);
struct net_conn *best_match = NULL;
bool is_mcast_pkt = false, mcast_pkt_delivered = false;
s16_t best_rank = -1;
struct net_conn *conn;
u16_t src_port;
Expand Down Expand Up @@ -546,6 +550,20 @@ 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));

/* 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) && net_pkt_family(pkt) == AF_INET) {
if (net_ipv4_is_addr_mcast(&ip_hdr->ipv4->dst)) {
is_mcast_pkt = true;
}
} else if (IS_ENABLED(CONFIG_NET_IPV6) &&
net_pkt_family(pkt) == AF_INET6) {
if (net_ipv6_is_addr_mcast(&ip_hdr->ipv6->dst)) {
is_mcast_pkt = true;
}
}

SYS_SLIST_FOR_EACH_CONTAINER(&conn_used, conn, node) {
if (conn->proto != proto) {
continue;
Expand Down Expand Up @@ -598,8 +616,42 @@ enum net_verdict net_conn_input(struct net_pkt *pkt,
}

if (best_rank < NET_CONN_RANK(conn->flags)) {
best_rank = NET_CONN_RANK(conn->flags);
best_match = conn;
struct net_pkt *mcast_pkt;

if (!is_mcast_pkt) {
best_rank = NET_CONN_RANK(conn->flags);
best_match = conn;

continue;
}

/* If we have a multicast packet, and we found
* a match, then deliver the packet immediately
* to the handler. As there might be several
* sockets interested about these, we need to
* clone the received pkt.
*/

NET_DBG("[%p] mcast match found cb %p ud %p",
conn, conn->cb, conn->user_data);

mcast_pkt = net_pkt_clone(pkt, CLONE_TIMEOUT);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think we could do a shallow clone here?
It would save some memory but it's also risky that a receiver would change something in the pkt.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As we cannot guarantee that the apps won't modify the data, the net_buf needs to be cloned too.

if (!mcast_pkt) {
goto drop;
}

if (conn->cb(conn, mcast_pkt, ip_hdr,
proto_hdr, conn->user_data) ==
NET_DROP) {
net_stats_update_per_proto_drop(
pkt_iface, proto);
net_pkt_unref(mcast_pkt);
} else {
net_stats_update_per_proto_recv(
pkt_iface, proto);
}

mcast_pkt_delivered = true;
}
} else if (IS_ENABLED(CONFIG_NET_SOCKETS_PACKET) ||
IS_ENABLED(CONFIG_NET_SOCKETS_CAN)) {
Expand All @@ -608,6 +660,16 @@ enum net_verdict net_conn_input(struct net_pkt *pkt,
}
}

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
*/

net_pkt_unref(pkt);

return NET_OK;
}

conn = best_match;
if (conn) {
NET_DBG("[%p] match found cb %p ud %p rank 0x%02x",
Expand All @@ -625,16 +687,14 @@ enum net_verdict net_conn_input(struct net_pkt *pkt,

NET_DBG("No match found.");

/* If the destination address is multicast address,
* we will not send an ICMP error as that makes no sense.
/* Do not send ICMP error for Packet socket as that makes no
* sense here.
*/
if (IS_ENABLED(CONFIG_NET_IPV6) &&
net_pkt_family(pkt) == AF_INET6 &&
net_ipv6_is_addr_mcast(&ip_hdr->ipv6->dst)) {
net_pkt_family(pkt) == AF_INET6 && is_mcast_pkt) {
;
} else if (IS_ENABLED(CONFIG_NET_IPV4) &&
net_pkt_family(pkt) == AF_INET &&
net_ipv4_is_addr_mcast(&ip_hdr->ipv4->dst)) {
net_pkt_family(pkt) == AF_INET && is_mcast_pkt) {
;
} else if (IS_ENABLED(CONFIG_NET_SOCKETS_PACKET) &&
net_pkt_family(pkt) == AF_PACKET) {
Expand All @@ -643,7 +703,7 @@ enum net_verdict net_conn_input(struct net_pkt *pkt,
conn_send_icmp_error(pkt);

if (IS_ENABLED(CONFIG_NET_TCP) && proto == IPPROTO_TCP) {
net_stats_update_tcp_seg_connrst(net_pkt_iface(pkt));
net_stats_update_tcp_seg_connrst(pkt_iface);
}
}

Expand Down
8 changes: 4 additions & 4 deletions tests/net/dhcpv4/prj.conf
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ CONFIG_NET_L2_DUMMY=y
CONFIG_NET_UDP=y
CONFIG_NET_DHCPV4=y
CONFIG_NET_BUF=y
CONFIG_NET_PKT_RX_COUNT=4
CONFIG_NET_PKT_TX_COUNT=4
CONFIG_NET_BUF_RX_COUNT=7
CONFIG_NET_BUF_TX_COUNT=7
CONFIG_NET_PKT_RX_COUNT=5
CONFIG_NET_PKT_TX_COUNT=5
CONFIG_NET_BUF_RX_COUNT=8
CONFIG_NET_BUF_TX_COUNT=8
CONFIG_NET_LOG=y
CONFIG_ENTROPY_GENERATOR=y
CONFIG_TEST_RANDOM_GENERATOR=y
Expand Down