Skip to content

net: sockets: Add SOCK_RAW support for AF_INET/AF_INET6 sockets #88044

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

Merged
merged 7 commits into from
Apr 9, 2025
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
35 changes: 35 additions & 0 deletions doc/connectivity/networking/api/sockets.rst
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,41 @@ there is a table mapping file descriptors to internal object pointers.
The file descriptor table is used by the BSD Sockets API even if the rest
of the POSIX subsystem (filesystem, stdin/stdout) is not enabled.

Zephyr supports multiple types of BSD sockets, the following table summarizes
what socket types are available:

+--------------+-------------+------------------+---------------------------------------------------------------------------+
| Family | Type | Protocol | Description |
+==============+=============+==================+===========================================================================+
| AF_INET |br| | SOCK_DGRAM | IPPROTO_UDP | Enabled if :kconfig:option:`CONFIG_NET_UDP` is set. |br| |
| AF_INET6 | | | Allows to send and receive UDP datagrams. |
| | +------------------+---------------------------------------------------------------------------+
| | | IPPROTO_DTLS_1_x | Enabled if :kconfig:option:`CONFIG_NET_SOCKETS_ENABLE_DTLS` is set. |br| |
| | | | Allows to send and receive DTLS datagrams. |
| +-------------+------------------+---------------------------------------------------------------------------+
| | SOCK_STREAM | IPPROTO_TCP | Enabled if :kconfig:option:`CONFIG_NET_TCP` is set. |br| |
| | | | Allows to send and receive TCP data stream. |
| | +------------------+---------------------------------------------------------------------------+
| | | IPPROTO_TLS_1_x | Enabled if :kconfig:option:`CONFIG_NET_SOCKETS_SOCKOPT_TLS` is set. |br| |
| | | | Allows to send and receive TLS data stream. |
| +-------------+------------------+---------------------------------------------------------------------------+
| | SOCK_RAW | IPPROTO_IP |br| | Enabled if :kconfig:option:`CONFIG_NET_SOCKETS_INET_RAW` is set. |br| |
| | | <proto> | Allows to send and receive IPv4/IPv6 datagrams. |br| |
| | | | Packets are filtered by L4 protocol specified. |
| | | | IPPROTO_IP is a wildcard protocol to receive all IP datagrams. |
+--------------+-------------+------------------+---------------------------------------------------------------------------+
| AF_PACKET | SOCK_DGRAM | ETH_P_ALL |br| | Enabled if :kconfig:option:`CONFIG_NET_SOCKETS_PACKET_DGRAM` is set. |br| |
| | | <proto> | Allows to send and receive packets without L2 header. |br| |
| | | | Packets are filtered by L3 protocol specified. |
| | | | ETH_P_ALL is a wildcard protocol to receive all packets. |
| +-------------+------------------+---------------------------------------------------------------------------+
| | SOCK_RAW | ETH_P_ALL | Enabled if :kconfig:option:`CONFIG_NET_SOCKETS_PACKET` is set. |br| |
| | | | Allows to send and receive packets with L2 header included. |
+--------------+-------------+------------------+---------------------------------------------------------------------------+
| AF_CAN | SOCK_RAW | CAN_RAW | Enabled if :kconfig:option:`CONFIG_NET_SOCKETS_CAN` is set. |br| |
| | | | Allows to send and receive CAN packets. |
+--------------+-------------+------------------+---------------------------------------------------------------------------+

See :zephyr:code-sample:`sockets-echo-server` and :zephyr:code-sample:`sockets-echo-client`
sample applications to learn how to create a simple server or client BSD socket based
application.
Expand Down
5 changes: 5 additions & 0 deletions doc/releases/migration-guide-4.2.rst
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,11 @@ Networking
is optional and not used with older MQTT versions - MQTT 3.1.1 users should pass
NULL as an argument.

* The ``AF_PACKET/SOCK_RAW/IPPROTO_RAW`` socket combination is no longer supported,
as ``AF_PACKET`` sockets should only accept IEEE 802.3 protocol numbers. As an
alternative, ``AF_PACKET/SOCK_DGRAM/ETH_P_ALL`` or ``AF_INET(6)/SOCK_RAW/IPPROTO_IP``
sockets can be used, depending on the actual use case.

SPI
===

Expand Down
4 changes: 4 additions & 0 deletions doc/releases/release-notes-4.2.rst
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,10 @@ New APIs and options

* :kconfig:option:`CONFIG_MQTT_VERSION_5_0`

* Sockets

* :kconfig:option:`CONFIG_NET_SOCKETS_INET_RAW`

* Stepper

* :c:func:`stepper_stop()`
Expand Down
92 changes: 75 additions & 17 deletions subsys/net/ip/connection.c
Original file line number Diff line number Diff line change
Expand Up @@ -322,7 +322,7 @@ static int net_conn_change_remote(struct net_conn *conn,
return 0;
}

int net_conn_register(uint16_t proto, uint8_t family,
int net_conn_register(uint16_t proto, enum net_sock_type type, uint8_t family,
const struct sockaddr *remote_addr,
const struct sockaddr *local_addr,
uint16_t remote_port,
Expand Down Expand Up @@ -405,6 +405,7 @@ int net_conn_register(uint16_t proto, uint8_t family,

conn->flags = flags;
conn->proto = proto;
conn->type = type;
conn->family = family;
conn->context = context;

Expand Down Expand Up @@ -631,6 +632,38 @@ static enum net_verdict conn_raw_socket(struct net_pkt *pkt,
return NET_OK;
}

#if defined(CONFIG_NET_SOCKETS_INET_RAW)
static void conn_raw_ip_socket(struct net_pkt *pkt, struct net_conn *conn)
{
struct net_pkt *raw_pkt;
struct net_pkt_cursor cur;

net_pkt_cursor_backup(pkt, &cur);
net_pkt_cursor_init(pkt);

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

raw_pkt = net_pkt_clone(pkt, K_MSEC(CONFIG_NET_CONN_PACKET_CLONE_TIMEOUT));
if (raw_pkt == NULL) {
goto out;
}

if (conn->cb(conn, raw_pkt, NULL, NULL, conn->user_data) == NET_DROP) {
net_pkt_unref(raw_pkt);
}

out:
net_pkt_cursor_restore(pkt, &cur);
}
#else
static void conn_raw_ip_socket(struct net_pkt *pkt, struct net_conn *conn)
{
ARG_UNUSED(pkt);
ARG_UNUSED(conn);
}
#endif /* defined(CONFIG_NET_SOCKETS_INET_RAW) */

enum net_verdict net_conn_input(struct net_pkt *pkt,
union net_ip_header *ip_hdr,
uint8_t proto,
Expand All @@ -639,14 +672,17 @@ 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 */
return NET_DROP;
}

if (IS_ENABLED(CONFIG_NET_IP) && (pkt_family == AF_INET || pkt_family == AF_INET6)) {
if (IS_ENABLED(CONFIG_NET_UDP) && proto == IPPROTO_UDP) {
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) {
src_port = proto_hdr->udp->src_port;
dst_port = proto_hdr->udp->dst_port;
} else if (IS_ENABLED(CONFIG_NET_TCP) && proto == IPPROTO_TCP) {
Expand All @@ -656,12 +692,13 @@ 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 (!conn_are_endpoints_valid(pkt, pkt_family, ip_hdr, src_port, dst_port)) {
if (!raw_ip_pkt && !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 && proto != IPPROTO_RAW) {
if (proto != ETH_P_ALL) {
return NET_DROP;
}
} else if (IS_ENABLED(CONFIG_NET_SOCKETS_CAN) && pkt_family == AF_CAN) {
Expand Down Expand Up @@ -689,7 +726,7 @@ enum net_verdict net_conn_input(struct net_pkt *pkt,
net_conn_cb_t cb = NULL;
void *user_data = NULL;

if (IS_ENABLED(CONFIG_NET_IP)) {
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.
*/
Expand Down Expand Up @@ -732,7 +769,7 @@ enum net_verdict net_conn_input(struct net_pkt *pkt,

if (IS_ENABLED(CONFIG_NET_IPV4_MAPPING_TO_IPV6)) {
if (!(conn->family == AF_INET6 && pkt_family == AF_INET &&
!conn->v6only)) {
!conn->v6only && conn->type != SOCK_RAW)) {
continue;
}
} else {
Expand All @@ -742,17 +779,26 @@ 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
* or IPPROTO_RAW but the listener might have a specific
* protocol set. This is ok and let the packet pass this
* check in this case.
* 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 (proto != ETH_P_ALL && proto != IPPROTO_RAW) {
if (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 */
}
Expand All @@ -767,17 +813,15 @@ enum net_verdict net_conn_input(struct net_pkt *pkt,
* targets AF_PACKET sockets.
*
* All AF_PACKET connections will receive the packet if
* their socket type and - in case of IPPROTO - protocol
* also matches.
* their socket type and protocol also matches.
*/
if (proto == ETH_P_ALL) {
/* We shall continue with ETH_P_ALL to IPPROTO_RAW: */
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;
}

/* With IPPROTO_RAW deliver only if protocol match: */
if ((proto == ETH_P_ALL && conn->proto != IPPROTO_RAW) ||
conn->proto == proto) {
if (proto == ETH_P_ALL) {
enum net_verdict ret = conn_raw_socket(pkt, conn, proto);

if (ret == NET_DROP) {
Expand All @@ -789,6 +833,15 @@ enum net_verdict net_conn_input(struct net_pkt *pkt,

continue; /* packet was consumed */
}
} else if (IS_ENABLED(CONFIG_NET_SOCKETS_INET_RAW) && raw_ip_pkt) {
Copy link
Member

Choose a reason for hiding this comment

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

Note that few lines above, the code is still checking IPPROTO_RAW for packet socket, we need to remove/change those checks from packet socket

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yes, do you want me to extend this PR with AF_PACKET/IPPROTO_RAW removal?

Copy link
Member

@jukkar jukkar Apr 3, 2025

Choose a reason for hiding this comment

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

I think we need to remove the IPPROTO_RAW from AF_PACKET, that was actually the point of the original issue.

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)) {
Expand Down Expand Up @@ -901,6 +954,11 @@ enum net_verdict net_conn_input(struct net_pkt *pkt,
}
}

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) {
/* As one or more multicast packets
* have already been delivered in the loop above,
Expand Down
9 changes: 7 additions & 2 deletions subsys/net/ip/connection.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,9 @@ struct net_conn {
/** Connection protocol */
uint16_t proto;

/** Connection type */
enum net_sock_type type;

/** Protocol family */
uint8_t family;

Expand All @@ -90,6 +93,7 @@ struct net_conn {
*
* @param proto Protocol for the connection (depends on the protocol
* family, e.g. UDP/TCP in the case of AF_INET/AF_INET6)
* @param type Connection type (SOCK_STREAM/DGRAM/RAW)
* @param family Protocol family (AF_*)
* @param remote_addr Remote address of the connection end point.
* @param local_addr Local address of the connection end point.
Expand All @@ -103,7 +107,7 @@ struct net_conn {
* @return Return 0 if the registration succeed, <0 otherwise.
*/
#if defined(CONFIG_NET_NATIVE)
int net_conn_register(uint16_t proto, uint8_t family,
int net_conn_register(uint16_t proto, enum net_sock_type type, uint8_t family,
const struct sockaddr *remote_addr,
const struct sockaddr *local_addr,
uint16_t remote_port,
Expand All @@ -113,7 +117,8 @@ int net_conn_register(uint16_t proto, uint8_t family,
void *user_data,
struct net_conn_handle **handle);
#else
static inline int net_conn_register(uint16_t proto, uint8_t family,
static inline int net_conn_register(uint16_t proto, enum net_sock_type type,
uint8_t family,
const struct sockaddr *remote_addr,
const struct sockaddr *local_addr,
uint16_t remote_port,
Expand Down
10 changes: 8 additions & 2 deletions subsys/net/ip/ipv4.c
Original file line number Diff line number Diff line change
Expand Up @@ -383,6 +383,14 @@ enum net_verdict net_ipv4_input(struct net_pkt *pkt, bool is_loopback)
net_sprint_ipv4_addr(&hdr->src),
net_sprint_ipv4_addr(&hdr->dst));

ip.ipv4 = hdr;

if (IS_ENABLED(CONFIG_NET_SOCKETS_INET_RAW)) {
if (net_conn_input(pkt, &ip, hdr->proto, NULL) == NET_DROP) {
goto drop;
}
}

switch (hdr->proto) {
case IPPROTO_ICMP:
verdict = net_icmpv4_input(pkt, hdr);
Expand Down Expand Up @@ -440,8 +448,6 @@ enum net_verdict net_ipv4_input(struct net_pkt *pkt, bool is_loopback)
goto drop;
}

ip.ipv4 = hdr;

verdict = net_conn_input(pkt, &ip, hdr->proto, &proto_hdr);
if (verdict != NET_DROP) {
return verdict;
Expand Down
10 changes: 8 additions & 2 deletions subsys/net/ip/ipv6.c
Original file line number Diff line number Diff line change
Expand Up @@ -762,6 +762,14 @@ enum net_verdict net_ipv6_input(struct net_pkt *pkt, bool is_loopback)

net_pkt_set_ipv6_ext_len(pkt, ext_len);

ip.ipv6 = hdr;

if (IS_ENABLED(CONFIG_NET_SOCKETS_INET_RAW)) {
if (net_conn_input(pkt, &ip, current_hdr, NULL) == NET_DROP) {
goto drop;
}
}

switch (current_hdr) {
case IPPROTO_ICMPV6:
verdict = net_icmpv6_input(pkt, hdr);
Expand Down Expand Up @@ -816,8 +824,6 @@ enum net_verdict net_ipv6_input(struct net_pkt *pkt, bool is_loopback)
return verdict;
}

ip.ipv6 = hdr;

verdict = net_conn_input(pkt, &ip, current_hdr, &proto_hdr);

NET_DBG("%s verdict %s", "Connection", net_verdict2str(verdict));
Expand Down
Loading