1 /*
2 * Copyright (c) 2021 Intel Corporation.
3 * Copyright (c) 2024 Nordic Semiconductor
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 */
7
8 #include <zephyr/logging/log.h>
9 LOG_MODULE_REGISTER(net_capture_sample, LOG_LEVEL_DBG);
10
11 #include <zephyr/posix/net/if_arp.h>
12 #include <zephyr/kernel.h>
13 #include <zephyr/shell/shell.h>
14 #include <zephyr/net/capture.h>
15 #include <zephyr/net/ethernet.h>
16 #include <zephyr/net/virtual_mgmt.h>
17
18 #if defined(CONFIG_NET_CAPTURE_COOKED_MODE)
19 #define COOKED_MODE_INTERFACE_NAME CONFIG_NET_CAPTURE_COOKED_MODE_INTERFACE_NAME
20 #else
21 #define COOKED_MODE_INTERFACE_NAME ""
22 #endif
23
24 static bool started;
25
26 uint16_t link_types_to_monitor[] = {
27 NET_ETH_PTYPE_CAN,
28 NET_ETH_PTYPE_HDLC,
29 };
30
31 struct data_to_send {
32 struct net_capture_cooked *ctx;
33 size_t len;
34 const char *data;
35 enum net_capture_packet_type type;
36 uint16_t eth_p_type;
37 };
38
39 #define DATA(_ctx, _data, _type, _ptype) { \
40 .ctx = _ctx, \
41 .len = sizeof(_data), \
42 .data = _data, \
43 .type = _type, \
44 .eth_p_type = _ptype \
45 }
46
47 static const char can_data[] = {
48 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
49 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
50 };
51
52 static struct net_capture_cooked ctx_can = {
53 .hatype = ARPHRD_CAN,
54 .halen = 0,
55 .addr = { 0 }
56 };
57
58 static const char ppp_send_lcp_conf_req_data[] = {
59 0xff, 0x7d, 0x23, 0xc0, 0x21, 0x7d, 0x21, 0x7d,
60 0x21, 0x7d, 0x20, 0x7d, 0x24, 0xd1, 0xb5,
61 };
62
63 static const char ppp_recv_lcp_conf_req_data[] = {
64 0xff, 0x03, 0xc0, 0x21, 0x01, 0x01, 0x00, 0x16,
65 0x02, 0x06, 0x00, 0x00, 0x00, 0x00, 0x03, 0x04,
66 0xc0, 0x23, 0x05, 0x06, 0xe4, 0xdd, 0x30, 0x57,
67 0x07, 0x02, 0x0c, 0x57,
68 };
69
70 static const char ppp_send_lcp_conf_rej_data[] = {
71 0xff, 0x7d, 0x23, 0xc0, 0x21, 0x7d, 0x24, 0x7d,
72 0x21, 0x7d, 0x20, 0x7d, 0x32, 0x7d, 0x22, 0x7d,
73 0x26, 0x7d, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d,
74 0x20, 0x7d, 0x25, 0x7d, 0x26, 0x7d, 0x20, 0x38,
75 0xea, 0x74, 0x7d, 0x27, 0x7d, 0x22, 0x8c, 0xa3,
76 };
77
78 static struct net_capture_cooked ctx_ppp = {
79 .hatype = ARPHRD_PPP,
80 .halen = 6,
81 .addr = { 0x1, 0x2, 0x3, 0x4, 0x5, 0x6 }
82 };
83
84 /* We just construct some demo packets and send them out */
85 static const struct data_to_send data[] = {
86 DATA(&ctx_can, can_data, NET_CAPTURE_OUTGOING, NET_ETH_PTYPE_CAN),
87 DATA(&ctx_ppp, ppp_send_lcp_conf_req_data, NET_CAPTURE_OUTGOING, NET_ETH_PTYPE_HDLC),
88 DATA(&ctx_ppp, ppp_recv_lcp_conf_req_data, NET_CAPTURE_HOST, NET_ETH_PTYPE_HDLC),
89 DATA(&ctx_ppp, ppp_send_lcp_conf_rej_data, NET_CAPTURE_OUTGOING, NET_ETH_PTYPE_HDLC),
90 };
91
cmd_sample_send(const struct shell * sh,size_t argc,char * argv[])92 static int cmd_sample_send(const struct shell *sh,
93 size_t argc, char *argv[])
94 {
95 if (!IS_ENABLED(CONFIG_NET_CAPTURE_COOKED_MODE)) {
96 shell_fprintf(sh, SHELL_NORMAL,
97 "Enable %s to use the sample shell.\n",
98 "CONFIG_NET_CAPTURE_COOKED_MODE");
99 return 0;
100 }
101
102 if (!started) {
103 if (sh != NULL) {
104 shell_fprintf(sh, SHELL_WARNING, "%s",
105 "Capturing not enabled, cannot send data.\n");
106 }
107
108 return 0;
109 }
110
111 ARRAY_FOR_EACH_PTR(data, ptr) {
112 net_capture_data(ptr->ctx, ptr->data, ptr->len,
113 ptr->type, ptr->eth_p_type);
114 }
115
116 return 0;
117 }
118
119 SHELL_STATIC_SUBCMD_SET_CREATE(sample_commands,
120 SHELL_CMD(send, NULL,
121 "Send example data\n",
122 cmd_sample_send),
123 SHELL_SUBCMD_SET_END
124 );
125
126 SHELL_CMD_REGISTER(sample, &sample_commands,
127 "Sample application commands", NULL);
128
init_app(void)129 static int init_app(void)
130 {
131 /* What this sample does:
132 * - Create a tunnel that runs on top of the Ethernet interface
133 * - Start to capture data from cooked mode capture interface
134 * - Take the cooked mode interface up
135 *
136 * All of the above could be done manually from net-shell with
137 * these commands:
138 *
139 * net capture setup 192.0.2.2 2001:db8:200::1 2001:db8:200::2
140 * net capture enable 2
141 * net iface up 2
142 *
143 * Explanation what those commands do:
144 *
145 * The "net capture setup" creates a tunnel. The tunnel is IPv6
146 * tunnel and our inner end point address is 2001:db8:200::1
147 * and the inner peer end point address is 2001:db8:200::2. In the
148 * tests, the tunnel can be created in Linux host with this command
149 *
150 * net-setup.sh -c zeth-tunnel.conf
151 *
152 * The net-setup.sh command is found in net-tools Zephyr project.
153 * In host side, the tunnel interface is called zeth-ip6ip where IPv6
154 * packets are run in a IPv4 tunnel.
155 *
156 * The network interfaces in this sample application are:
157 *
158 * Interface any (0x808ab3c) (Dummy) [1]
159 * ================================
160 * Virtual interfaces attached to this : 2
161 * Device : NET_ANY (0x80849a4)
162 *
163 * Interface cooked (0x808ac94) (Virtual) [2]
164 * ==================================
165 * Virtual name : Cooked mode capture
166 * Attached : 1 (Dummy / 0x808ab3c)
167 * Device : NET_COOKED (0x808497c)
168 *
169 * Interface eth0 (0x808adec) (Ethernet) [3]
170 * ===================================
171 * Virtual interfaces attached to this : 4
172 * Device : zeth0 (0x80849b8)
173 * IPv6 unicast addresses (max 4):
174 * fe80::5eff:fe00:53e6 autoconf preferred infinite
175 * 2001:db8::1 manual preferred infinite
176 * IPv4 unicast addresses (max 2):
177 * 192.0.2.1/255.255.255.0 overridable preferred infinite
178 *
179 * Interface net0 (0x808af44) (Virtual) [4]
180 * ==================================
181 * Virtual name : Capture tunnel
182 * Attached : 3 (Ethernet / 0x808adec)
183 * Device : IP_TUNNEL0 (0x8084990)
184 * IPv6 unicast addresses (max 4):
185 * 2001:db8:200::1 manual preferred infinite
186 * fe80::efed:6dff:fef2:b1df autoconf preferred infinite
187 * fe80::56da:1eff:fe5e:bc02 autoconf preferred infinite
188 *
189 * The 192.0.2.2 is the address of the outer end point of the host that
190 * terminates the tunnel. Zephyr uses this address to select the internal
191 * interface to use for the tunnel. In this example it is interface 3.
192 *
193 * The interface 2 is the interface that runs on top of interface 1. The
194 * cooked capture packets are written by the capture API to sink interface 1.
195 * The packets propagate to interface 2 because it is linked to first interface.
196 * The "net capture enable 2" command will cause the packets sent to interface 2
197 * to be written to capture interface 4, which in turn capsulates the packets and
198 * tunnels them to peer via the Ethernet interface 3.
199 *
200 * The above IP addresses might change if you change the addresses in
201 * overlay-tunnel.conf file.
202 *
203 * If the cooked mode capture is enabled (CONFIG_NET_CAPTURE_COOKED_MODE=y),
204 * then we setup the capture automatically to the correct network interface.
205 * User is then able to send sample network packets in cooked mode and monitor
206 * the captured packets in the host zeth-ip6ip network interface. Use "sample send"
207 * command to do that.
208 *
209 * You can use "net-capture.py -i zeth-ip6ip -c" command to capture the cooked
210 * packets in host side. The net-capture.py tool is found in net-tools package.
211 */
212
213 const char remote[] = CONFIG_NET_CONFIG_PEER_IPV4_ADDR;
214 const char local[] = CONFIG_NET_SAMPLE_TUNNEL_MY_ADDR;
215 const char peer[] = CONFIG_NET_SAMPLE_TUNNEL_PEER_ADDR;
216 const struct device *capture_dev;
217 int ret;
218
219 ret = net_capture_setup(remote, local, peer, &capture_dev);
220 if (ret < 0) {
221 LOG_ERR("Capture cannot be setup (%d)", ret);
222 return -ENOEXEC;
223 }
224
225 if (IS_ENABLED(CONFIG_NET_CAPTURE_COOKED_MODE)) {
226 /* If we are running in cooked mode, start to capture the packets
227 * from cooked mode interface.
228 */
229 struct virtual_interface_req_params params = { 0 };
230 struct virtual_interface_link_types link_types;
231 int ifindex;
232
233 ifindex = net_if_get_by_name(COOKED_MODE_INTERFACE_NAME);
234 if (ifindex < 0) {
235 LOG_ERR("Interface \"%s\" not found.", COOKED_MODE_INTERFACE_NAME);
236 return -ENOENT;
237 }
238
239 ret = net_capture_enable(capture_dev, net_if_get_by_index(ifindex));
240 if (ret < 0) {
241 LOG_ERR("Cannot enable capture to interface %d (%d)",
242 ifindex, ret);
243 return ret;
244 }
245
246 /* Setup the cooked interface to capture these types of packets */
247 memcpy(&link_types.type, &link_types_to_monitor,
248 MIN(sizeof(link_types.type), sizeof(link_types_to_monitor)));
249 link_types.count = MIN(ARRAY_SIZE(link_types.type),
250 ARRAY_SIZE(link_types_to_monitor));
251
252 params.family = AF_UNSPEC;
253 memcpy(¶ms.link_types, &link_types,
254 sizeof(struct virtual_interface_link_types));
255
256 ret = net_mgmt(NET_REQUEST_VIRTUAL_INTERFACE_SET_LINK_TYPE,
257 net_if_get_by_index(ifindex), ¶ms,
258 sizeof(struct virtual_interface_req_params));
259 if (ret < 0 && ret != -ENOTSUP) {
260 LOG_ERR("Cannot set interface %d link types (%d)",
261 ifindex, ret);
262 return ret;
263 }
264
265 /* Now the capture device and the tunnel interface is setup,
266 * we just need to bring up the actual interface we want to capture
267 * as it is down by default.
268 */
269 ret = net_if_up(net_if_get_by_index(ifindex));
270 if (ret < 0) {
271 LOG_ERR("Cannot take up interface %d (%d)", ifindex, ret);
272 return ret;
273 }
274
275 LOG_INF("Type \"sample send\" to send dummy capture data to tunnel.");
276 } else {
277 LOG_INF("Please enable capture manually from net-shell");
278 LOG_INF("Use \"net capture enable <ifindex>\" command to start "
279 "capturing desired network interface.");
280 }
281
282 return 0;
283 }
284
285 #define EVENT_MASK (NET_EVENT_CAPTURE_STARTED | NET_EVENT_CAPTURE_STOPPED)
286
event_handler(struct net_mgmt_event_callback * cb,uint32_t mgmt_event,struct net_if * iface)287 static void event_handler(struct net_mgmt_event_callback *cb,
288 uint32_t mgmt_event, struct net_if *iface)
289 {
290 ARG_UNUSED(iface);
291 ARG_UNUSED(cb);
292
293 if ((mgmt_event & EVENT_MASK) != mgmt_event) {
294 return;
295 }
296
297 if (mgmt_event == NET_EVENT_CAPTURE_STARTED) {
298 started = true;
299 }
300
301 if (mgmt_event == NET_EVENT_CAPTURE_STOPPED) {
302 started = false;
303 }
304 }
305
main(void)306 int main(void)
307 {
308 static struct net_mgmt_event_callback mgmt_cb;
309 struct net_if *iface;
310 uint32_t event;
311 int ret;
312
313 LOG_INF("Starting network capture sample");
314
315 net_mgmt_init_event_callback(&mgmt_cb, event_handler, EVENT_MASK);
316 net_mgmt_add_event_callback(&mgmt_cb);
317
318 ret = init_app();
319 if (ret < 0) {
320 LOG_ERR("Cannot start the application.");
321 return ret;
322 }
323
324 while (1) {
325 ret = net_mgmt_event_wait(EVENT_MASK,
326 &event,
327 &iface,
328 NULL,
329 NULL,
330 K_FOREVER);
331 if (ret < 0) {
332 continue;
333 }
334
335 LOG_INF("Capturing %s on interface %d",
336 event == NET_EVENT_CAPTURE_STARTED ? "started" : "stopped",
337 net_if_get_by_iface(iface));
338 }
339
340 return 0;
341 }
342