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