Skip to content

Commit 3a72c20

Browse files
committed
net: sockets: Add socket api to support AF_PACKET
This commit adds packet socket support to socket api. This version supports basic packet socket features. Protocol family is AF_PACKET, type of socket is SOCK_RAW and proto type is ETH_P_ALL. The user will receive every packet (with L2 header) on the wire. For TX, the subsystem expects that the user has set all the protocol headers (L2 and L3) properly. Networking subsystem doesn't verify or alter the headers while sending or receiving the packets. This version supports packet socket over Etherent only. Also combination of other family and protocols doesn't work (i.e. Application can not open packet-socket and non packet-socket together). Signed-off-by: Ravi kumar Veeramally <[email protected]>
1 parent 673c9f1 commit 3a72c20

File tree

6 files changed

+366
-1
lines changed

6 files changed

+366
-1
lines changed

subsys/net/ip/packet_socket.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,5 +38,5 @@ enum net_verdict net_packet_socket_input(struct net_pkt *pkt)
3838

3939
net_pkt_set_family(pkt, AF_PACKET);
4040

41-
return net_conn_input(ETH_P_ALL, pkt);
41+
return net_conn_input(pkt, NULL, ETH_P_ALL, NULL);
4242
}

subsys/net/lib/sockets/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ zephyr_sources(
77
sockets_select.c
88
)
99
zephyr_sources_ifdef(CONFIG_NET_SOCKETS_SOCKOPT_TLS sockets_tls.c)
10+
zephyr_sources_ifdef(CONFIG_NET_SOCKETS_PACKET sockets_packet.c)
1011
endif()
1112
zephyr_sources_ifdef(CONFIG_NET_SOCKETS_OFFLOAD socket_offload.c)
1213

subsys/net/lib/sockets/Kconfig

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,18 @@ config NET_SOCKETS_OFFLOAD
9696
See NET_OFFLOAD for a more deeply integrated approach which offloads
9797
from the net_context() API within the Zephyr IP stack.
9898

99+
config NET_SOCKETS_PACKET
100+
bool "Enable packet socket support"
101+
default n
102+
depends on NET_L2_ETHERNET
103+
help
104+
This is an initial version of packet socket support (special type
105+
raw socket). Packets are passed to and from the device driver
106+
without any changes in the packet headers. It's API caller
107+
responsibility to provide all the headers (e.g L2, L3 and so on)
108+
while sending. While receiving, packets (including all the headers)
109+
will be feed to sockets as it as from the driver.
110+
99111
module = NET_SOCKETS
100112
module-dep = NET_LOG
101113
module-str = Log level for BSD sockets compatible API calls

subsys/net/lib/sockets/sockets.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,12 @@ int _impl_zsock_socket(int family, int type, int proto)
123123
}
124124
#endif
125125

126+
#if defined(CONFIG_NET_SOCKETS_PACKET)
127+
if (family == AF_PACKET) {
128+
return zpacket_socket(family, type, proto);
129+
}
130+
#endif
131+
126132
return zsock_socket_internal(family, type, proto);
127133
}
128134

subsys/net/lib/sockets/sockets_internal.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,4 +49,6 @@ struct socket_op_vtable {
4949

5050
int ztls_socket(int family, int type, int proto);
5151

52+
int zpacket_socket(int family, int type, int proto);
53+
5254
#endif /* _SOCKETS_INTERNAL_H_ */
Lines changed: 344 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,344 @@
1+
/*
2+
* Copyright (c) 2019 Intel Corporation
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#include <stdbool.h>
8+
#include <fcntl.h>
9+
10+
#include <logging/log.h>
11+
LOG_MODULE_REGISTER(net_sock_packet, CONFIG_NET_SOCKETS_LOG_LEVEL);
12+
13+
#include <kernel.h>
14+
#include <entropy.h>
15+
#include <misc/util.h>
16+
#include <net/net_context.h>
17+
#include <net/net_pkt.h>
18+
#include <net/socket.h>
19+
#include <net/ethernet.h>
20+
#include <syscall_handler.h>
21+
#include <misc/fdtable.h>
22+
23+
#include "sockets_internal.h"
24+
25+
extern const struct socket_op_vtable sock_fd_op_vtable;
26+
27+
static const struct socket_op_vtable packet_sock_fd_op_vtable;
28+
29+
static inline int _k_fifo_wait_non_empty(struct k_fifo *fifo, int32_t timeout)
30+
{
31+
struct k_poll_event events[] = {
32+
K_POLL_EVENT_INITIALIZER(K_POLL_TYPE_FIFO_DATA_AVAILABLE,
33+
K_POLL_MODE_NOTIFY_ONLY, fifo),
34+
};
35+
36+
return k_poll(events, ARRAY_SIZE(events), timeout);
37+
}
38+
39+
int zpacket_socket(int family, int type, int proto)
40+
{
41+
struct net_context *ctx;
42+
int fd;
43+
int ret;
44+
45+
if (type != SOCK_RAW || proto != ETH_P_ALL) {
46+
errno = -EOPNOTSUPP;
47+
return -1;
48+
}
49+
50+
fd = z_reserve_fd();
51+
if (fd < 0) {
52+
return -1;
53+
}
54+
55+
ret = net_context_get(family, type, proto, &ctx);
56+
if (ret < 0) {
57+
z_free_fd(fd);
58+
errno = -ret;
59+
return -1;
60+
}
61+
62+
/* Initialize user_data, all other calls will preserve it */
63+
ctx->user_data = NULL;
64+
65+
/* recv_q and accept_q are in union */
66+
k_fifo_init(&ctx->recv_q);
67+
68+
#ifdef CONFIG_USERSPACE
69+
/* Set net context object as initialized and grant access to the
70+
* calling thread (and only the calling thread)
71+
*/
72+
_k_object_recycle(ctx);
73+
#endif
74+
75+
z_finalize_fd(fd, ctx,
76+
(const struct fd_op_vtable *)&packet_sock_fd_op_vtable);
77+
78+
return fd;
79+
}
80+
81+
static void zpacket_received_cb(struct net_context *ctx,
82+
struct net_pkt *pkt,
83+
union net_ip_header *ip_hdr,
84+
union net_proto_header *proto_hdr,
85+
int status,
86+
void *user_data)
87+
{
88+
NET_DBG("ctx=%p, pkt=%p, st=%d, user_data=%p", ctx, pkt, status,
89+
user_data);
90+
91+
/* if pkt is NULL, EOF */
92+
if (!pkt) {
93+
struct net_pkt *last_pkt = k_fifo_peek_tail(&ctx->recv_q);
94+
95+
if (!last_pkt) {
96+
/* If there're no packets in the queue, recv() may
97+
* be blocked waiting on it to become non-empty,
98+
* so cancel that wait.
99+
*/
100+
sock_set_eof(ctx);
101+
k_fifo_cancel_wait(&ctx->recv_q);
102+
NET_DBG("Marked socket %p as peer-closed", ctx);
103+
} else {
104+
net_pkt_set_eof(last_pkt, true);
105+
NET_DBG("Set EOF flag on pkt %p", ctx);
106+
}
107+
108+
return;
109+
}
110+
111+
/* Normal packet */
112+
net_pkt_set_eof(pkt, false);
113+
114+
k_fifo_put(&ctx->recv_q, pkt);
115+
}
116+
117+
static int zpacket_bind_ctx(struct net_context *ctx,
118+
const struct sockaddr *addr,
119+
socklen_t addrlen)
120+
{
121+
int ret = 0;
122+
123+
ret = net_context_bind(ctx, addr, addrlen);
124+
if (ret < 0) {
125+
errno = -ret;
126+
return -1;
127+
}
128+
129+
/* For packet socket, we expect to receive packets after call
130+
* to bind().
131+
*/
132+
ret = net_context_recv(ctx, zpacket_received_cb, K_NO_WAIT,
133+
ctx->user_data);
134+
if (ret < 0) {
135+
errno = -ret;
136+
return -1;
137+
}
138+
139+
return 0;
140+
}
141+
142+
ssize_t zpacket_sendto_ctx(struct net_context *ctx, const void *buf, size_t len,
143+
int flags, const struct sockaddr *dest_addr,
144+
socklen_t addrlen)
145+
{
146+
s32_t timeout = K_FOREVER;
147+
int status;
148+
149+
if (!dest_addr) {
150+
errno = EDESTADDRREQ;
151+
return -1;
152+
}
153+
154+
if ((flags & ZSOCK_MSG_DONTWAIT) || sock_is_nonblock(ctx)) {
155+
timeout = K_NO_WAIT;
156+
}
157+
158+
/* Register the callback before sending in order to receive the response
159+
* from the peer.
160+
*/
161+
162+
status = net_context_recv(ctx, zpacket_received_cb, K_NO_WAIT,
163+
ctx->user_data);
164+
if (status < 0) {
165+
errno = -status;
166+
return -1;
167+
}
168+
169+
status = net_context_sendto_new(ctx, buf, len, dest_addr,
170+
addrlen, NULL, timeout, NULL,
171+
ctx->user_data);
172+
if (status < 0) {
173+
errno = -status;
174+
return -1;
175+
}
176+
177+
return status;
178+
}
179+
180+
ssize_t zpacket_recvfrom_ctx(struct net_context *ctx, void *buf, size_t max_len,
181+
int flags, struct sockaddr *src_addr,
182+
socklen_t *addrlen)
183+
{
184+
size_t recv_len = 0;
185+
s32_t timeout = K_FOREVER;
186+
struct net_pkt *pkt;
187+
188+
if ((flags & ZSOCK_MSG_DONTWAIT) || sock_is_nonblock(ctx)) {
189+
timeout = K_NO_WAIT;
190+
}
191+
192+
if (flags & ZSOCK_MSG_PEEK) {
193+
int res;
194+
195+
res = _k_fifo_wait_non_empty(&ctx->recv_q, timeout);
196+
/* EAGAIN when timeout expired, EINTR when cancelled */
197+
if (res && res != -EAGAIN && res != -EINTR) {
198+
errno = -res;
199+
return -1;
200+
}
201+
202+
pkt = k_fifo_peek_head(&ctx->recv_q);
203+
} else {
204+
pkt = k_fifo_get(&ctx->recv_q, timeout);
205+
}
206+
207+
if (!pkt) {
208+
errno = EAGAIN;
209+
return -1;
210+
}
211+
212+
/* We do not handle any headers here,
213+
* just pass the whole packet to caller.
214+
*/
215+
recv_len = net_pkt_get_len(pkt);
216+
if (recv_len > max_len) {
217+
recv_len = max_len;
218+
}
219+
220+
if (net_pkt_read_new(pkt, buf, recv_len)) {
221+
errno = ENOBUFS;
222+
return -1;
223+
}
224+
225+
if (!(flags & ZSOCK_MSG_PEEK)) {
226+
net_pkt_unref(pkt);
227+
} else {
228+
net_pkt_cursor_init(pkt);
229+
}
230+
231+
return recv_len;
232+
}
233+
234+
int zpacket_getsockopt_ctx(struct net_context *ctx, int level, int optname,
235+
void *optval, socklen_t *optlen)
236+
{
237+
if (!optval || !optlen) {
238+
errno = EINVAL;
239+
return -1;
240+
}
241+
242+
return sock_fd_op_vtable.getsockopt(ctx, level, optname,
243+
optval, optlen);
244+
}
245+
246+
int zpacket_setsockopt_ctx(struct net_context *ctx, int level, int optname,
247+
const void *optval, socklen_t optlen)
248+
{
249+
return sock_fd_op_vtable.setsockopt(ctx, level, optname,
250+
optval, optlen);
251+
}
252+
253+
static ssize_t packet_sock_read_vmeth(void *obj, void *buffer, size_t count)
254+
{
255+
return zpacket_recvfrom_ctx(obj, buffer, count, 0, NULL, 0);
256+
}
257+
258+
static ssize_t packet_sock_write_vmeth(void *obj, const void *buffer,
259+
size_t count)
260+
{
261+
return zpacket_sendto_ctx(obj, buffer, count, 0, NULL, 0);
262+
}
263+
264+
static int packet_sock_ioctl_vmeth(void *obj, unsigned int request,
265+
va_list args)
266+
{
267+
return sock_fd_op_vtable.fd_vtable.ioctl(obj, request, args);
268+
}
269+
270+
/*
271+
* TODO: A packet socket can be bound to a network device using SO_BINDTODEVICE.
272+
*/
273+
static int packet_sock_bind_vmeth(void *obj, const struct sockaddr *addr,
274+
socklen_t addrlen)
275+
{
276+
return zpacket_bind_ctx(obj, addr, addrlen);
277+
}
278+
279+
/* The connect() function is no longer necessary. */
280+
static int packet_sock_connect_vmeth(void *obj, const struct sockaddr *addr,
281+
socklen_t addrlen)
282+
{
283+
return -EOPNOTSUPP;
284+
}
285+
286+
/*
287+
* The listen() and accept() functions are without any functionality,
288+
* since the client-Server-Semantic is no longer present.
289+
* When we use packet sockets we are sending unconnected packets.
290+
*/
291+
static int packet_sock_listen_vmeth(void *obj, int backlog)
292+
{
293+
return -EOPNOTSUPP;
294+
}
295+
296+
static int packet_sock_accept_vmeth(void *obj, struct sockaddr *addr,
297+
socklen_t *addrlen)
298+
{
299+
return -EOPNOTSUPP;
300+
}
301+
302+
static ssize_t packet_sock_sendto_vmeth(void *obj, const void *buf, size_t len,
303+
int flags,
304+
const struct sockaddr *dest_addr,
305+
socklen_t addrlen)
306+
{
307+
return zpacket_sendto_ctx(obj, buf, len, flags, dest_addr, addrlen);
308+
}
309+
310+
static ssize_t packet_sock_recvfrom_vmeth(void *obj, void *buf, size_t max_len,
311+
int flags, struct sockaddr *src_addr,
312+
socklen_t *addrlen)
313+
{
314+
return zpacket_recvfrom_ctx(obj, buf, max_len, flags,
315+
src_addr, addrlen);
316+
}
317+
318+
static int packet_sock_getsockopt_vmeth(void *obj, int level, int optname,
319+
void *optval, socklen_t *optlen)
320+
{
321+
return zpacket_getsockopt_ctx(obj, level, optname, optval, optlen);
322+
}
323+
324+
static int packet_sock_setsockopt_vmeth(void *obj, int level, int optname,
325+
const void *optval, socklen_t optlen)
326+
{
327+
return zpacket_setsockopt_ctx(obj, level, optname, optval, optlen);
328+
}
329+
330+
static const struct socket_op_vtable packet_sock_fd_op_vtable = {
331+
.fd_vtable = {
332+
.read = packet_sock_read_vmeth,
333+
.write = packet_sock_write_vmeth,
334+
.ioctl = packet_sock_ioctl_vmeth,
335+
},
336+
.bind = packet_sock_bind_vmeth,
337+
.connect = packet_sock_connect_vmeth,
338+
.listen = packet_sock_listen_vmeth,
339+
.accept = packet_sock_accept_vmeth,
340+
.sendto = packet_sock_sendto_vmeth,
341+
.recvfrom = packet_sock_recvfrom_vmeth,
342+
.getsockopt = packet_sock_getsockopt_vmeth,
343+
.setsockopt = packet_sock_setsockopt_vmeth,
344+
};

0 commit comments

Comments
 (0)