1 /*
2  * Copyright (c) 2018 Intel Corporation.
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/logging/log.h>
8 LOG_MODULE_REGISTER(net_promisc_sample, LOG_LEVEL_INF);
9 
10 #include <zephyr/kernel.h>
11 #include <errno.h>
12 #include <stdlib.h>
13 #include <zephyr/shell/shell.h>
14 
15 #include <zephyr/net/net_core.h>
16 #include <zephyr/net/promiscuous.h>
17 #include <zephyr/net/udp.h>
18 
net_pkt_hexdump(struct net_pkt * pkt,const char * str)19 static void net_pkt_hexdump(struct net_pkt *pkt, const char *str)
20 {
21 	struct net_buf *buf = pkt->buffer;
22 
23 	while (buf) {
24 		LOG_HEXDUMP_DBG(buf->data, buf->len, str);
25 		buf = buf->frags;
26 	}
27 }
28 
iface_cb(struct net_if * iface,void * user_data)29 static void iface_cb(struct net_if *iface, void *user_data)
30 {
31 	int ret;
32 
33 	ret = net_promisc_mode_on(iface);
34 	if (ret < 0) {
35 		LOG_INF("Cannot set promiscuous mode for interface %p (%d)",
36 			iface, ret);
37 		return;
38 	}
39 
40 	LOG_INF("Promiscuous mode enabled for interface %p", iface);
41 }
42 
get_ports(struct net_pkt * pkt,uint16_t * src,uint16_t * dst)43 static int get_ports(struct net_pkt *pkt, uint16_t *src, uint16_t *dst)
44 {
45 	struct net_udp_hdr hdr, *udp_hdr;
46 
47 	udp_hdr = net_udp_get_hdr(pkt, &hdr);
48 	if (!udp_hdr) {
49 		return -EINVAL;
50 	}
51 
52 	*src = ntohs(udp_hdr->src_port);
53 	*dst = ntohs(udp_hdr->dst_port);
54 
55 	return 0;
56 }
57 
print_info(struct net_pkt * pkt)58 static void print_info(struct net_pkt *pkt)
59 {
60 	char src_addr_buf[NET_IPV6_ADDR_LEN], *src_addr;
61 	char dst_addr_buf[NET_IPV6_ADDR_LEN], *dst_addr;
62 	uint16_t dst_port = 0U, src_port = 0U;
63 	sa_family_t family = AF_UNSPEC;
64 	void *dst, *src;
65 	uint8_t next_hdr;
66 	const char *proto;
67 	size_t len;
68 	int ret;
69 
70 	/* Enable hexdump by setting the log level to LOG_LEVEL_DBG */
71 	net_pkt_hexdump(pkt, "Network packet");
72 
73 	switch (NET_IPV6_HDR(pkt)->vtc & 0xf0) {
74 	case 0x60:
75 		family = AF_INET6;
76 		net_pkt_set_family(pkt, AF_INET6);
77 		dst = &NET_IPV6_HDR(pkt)->dst;
78 		src = &NET_IPV6_HDR(pkt)->src;
79 		next_hdr = NET_IPV6_HDR(pkt)->nexthdr;
80 		net_pkt_set_ip_hdr_len(pkt, sizeof(struct net_ipv6_hdr));
81 		break;
82 	case 0x40:
83 		family = AF_INET;
84 		net_pkt_set_family(pkt, AF_INET);
85 		dst = &NET_IPV4_HDR(pkt)->dst;
86 		src = &NET_IPV4_HDR(pkt)->src;
87 		next_hdr = NET_IPV4_HDR(pkt)->proto;
88 		net_pkt_set_ip_hdr_len(pkt, sizeof(struct net_ipv4_hdr));
89 		break;
90 	}
91 
92 	if (family == AF_UNSPEC) {
93 		LOG_INF("Recv %p len %zd (unknown address family)",
94 			pkt, net_pkt_get_len(pkt));
95 		return;
96 	}
97 
98 	ret = 0;
99 
100 	switch (next_hdr) {
101 	case IPPROTO_TCP:
102 		proto = "TCP";
103 		ret = get_ports(pkt, &src_port, &dst_port);
104 		break;
105 	case IPPROTO_UDP:
106 		proto = "UDP";
107 		ret = get_ports(pkt, &src_port, &dst_port);
108 		break;
109 	case IPPROTO_ICMPV6:
110 	case IPPROTO_ICMP:
111 		proto = "ICMP";
112 		break;
113 	default:
114 		proto = "<unknown>";
115 		break;
116 	}
117 
118 	if (ret < 0) {
119 		LOG_ERR("Cannot get port numbers for pkt %p", pkt);
120 		return;
121 	}
122 
123 	src_addr = net_addr_ntop(family, src,
124 				 src_addr_buf, sizeof(src_addr_buf));
125 	dst_addr = net_addr_ntop(family, dst,
126 				 dst_addr_buf, sizeof(dst_addr_buf));
127 
128 	len = net_pkt_get_len(pkt);
129 
130 	if (family == AF_INET) {
131 		if (next_hdr == IPPROTO_TCP || next_hdr == IPPROTO_UDP) {
132 			LOG_INF("%s %s (%zd) %s:%u -> %s:%u",
133 				"IPv4", proto, len,
134 				src_addr, src_port,
135 				dst_addr, dst_port);
136 		} else {
137 			LOG_INF("%s %s (%zd) %s -> %s", "IPv4", proto,
138 				len, src_addr,
139 				dst_addr);
140 		}
141 	} else {
142 		if (next_hdr == IPPROTO_TCP || next_hdr == IPPROTO_UDP) {
143 			LOG_INF("%s %s (%zd) [%s]:%u -> [%s]:%u",
144 				"IPv6", proto, len,
145 				src_addr, src_port,
146 				dst_addr, dst_port);
147 		} else {
148 			LOG_INF("%s %s (%zd) %s -> %s", "IPv6", proto,
149 				len, src_addr,
150 				dst_addr);
151 		}
152 	}
153 }
154 
set_promisc_mode(const struct shell * sh,size_t argc,char * argv[],bool enable)155 static int set_promisc_mode(const struct shell *sh,
156 			    size_t argc, char *argv[], bool enable)
157 {
158 	struct net_if *iface;
159 	char *endptr;
160 	int idx, ret;
161 
162 	if (argc < 2) {
163 		shell_fprintf(sh, SHELL_ERROR, "Invalid arguments.\n");
164 		return -ENOEXEC;
165 	}
166 
167 	idx = strtol(argv[1], &endptr, 10);
168 
169 	iface = net_if_get_by_index(idx);
170 	if (!iface) {
171 		shell_fprintf(sh, SHELL_ERROR,
172 			      "Cannot find network interface for index %d\n",
173 			      idx);
174 		return -ENOEXEC;
175 	}
176 
177 	shell_fprintf(sh, SHELL_INFO, "Promiscuous mode %s...\n",
178 		      enable ? "ON" : "OFF");
179 
180 	if (enable) {
181 		ret = net_promisc_mode_on(iface);
182 	} else {
183 		ret = net_promisc_mode_off(iface);
184 	}
185 
186 	if (ret < 0) {
187 		if (ret == -EALREADY) {
188 			shell_fprintf(sh, SHELL_INFO,
189 				      "Promiscuous mode already %s\n",
190 				      enable ? "enabled" : "disabled");
191 		} else {
192 			shell_fprintf(sh, SHELL_ERROR,
193 				      "Cannot %s promiscuous mode for "
194 				      "interface %p (%d)\n",
195 				      enable ? "set" : "unset", iface, ret);
196 		}
197 
198 		return -ENOEXEC;
199 	}
200 
201 	return 0;
202 }
203 
cmd_promisc_on(const struct shell * sh,size_t argc,char * argv[])204 static int cmd_promisc_on(const struct shell *sh,
205 			  size_t argc, char *argv[])
206 {
207 	return set_promisc_mode(sh, argc, argv, true);
208 }
209 
cmd_promisc_off(const struct shell * sh,size_t argc,char * argv[])210 static int cmd_promisc_off(const struct shell *sh,
211 			   size_t argc, char *argv[])
212 {
213 	return set_promisc_mode(sh, argc, argv, false);
214 }
215 
216 SHELL_STATIC_SUBCMD_SET_CREATE(promisc_commands,
217 	SHELL_CMD(on, NULL,
218 		  "Turn promiscuous mode on\n"
219 		  "promisc on  <interface index>  "
220 		      "Turn on promiscuous mode for the interface\n",
221 		  cmd_promisc_on),
222 	SHELL_CMD(off, NULL, "Turn promiscuous mode off\n"
223 		  "promisc off <interface index>  "
224 		      "Turn off promiscuous mode for the interface\n",
225 		  cmd_promisc_off),
226 	SHELL_SUBCMD_SET_END
227 );
228 
229 SHELL_CMD_REGISTER(promisc, &promisc_commands,
230 		   "Promiscuous mode commands", NULL);
231 
main(void)232 int main(void)
233 {
234 	struct net_pkt *pkt;
235 
236 	net_if_foreach(iface_cb, NULL);
237 
238 	while (1) {
239 		pkt = net_promisc_mode_wait_data(K_FOREVER);
240 		if (pkt) {
241 			print_info(pkt);
242 		}
243 
244 		net_pkt_unref(pkt);
245 	}
246 	return 0;
247 }
248