blob: 481c290ffb8d030e7078858b9e6bfc2ccbe848fc [file] [log] [blame]
/*
* Copyright (c) 2018 Intel Corporation.
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(net_promisc_sample, LOG_LEVEL_INF);
#include <zephyr/zephyr.h>
#include <errno.h>
#include <stdlib.h>
#include <zephyr/shell/shell.h>
#include <zephyr/net/net_core.h>
#include <zephyr/net/promiscuous.h>
#include <zephyr/net/udp.h>
static void net_pkt_hexdump(struct net_pkt *pkt, const char *str)
{
struct net_buf *buf = pkt->buffer;
while (buf) {
LOG_HEXDUMP_DBG(buf->data, buf->len, str);
buf = buf->frags;
}
}
static void iface_cb(struct net_if *iface, void *user_data)
{
int ret;
ret = net_promisc_mode_on(iface);
if (ret < 0) {
LOG_INF("Cannot set promiscuous mode for interface %p (%d)",
iface, ret);
return;
}
LOG_INF("Promiscuous mode enabled for interface %p", iface);
}
static int get_ports(struct net_pkt *pkt, uint16_t *src, uint16_t *dst)
{
struct net_udp_hdr hdr, *udp_hdr;
udp_hdr = net_udp_get_hdr(pkt, &hdr);
if (!udp_hdr) {
return -EINVAL;
}
*src = ntohs(udp_hdr->src_port);
*dst = ntohs(udp_hdr->dst_port);
return 0;
}
static void print_info(struct net_pkt *pkt)
{
char src_addr_buf[NET_IPV6_ADDR_LEN], *src_addr;
char dst_addr_buf[NET_IPV6_ADDR_LEN], *dst_addr;
uint16_t dst_port = 0U, src_port = 0U;
sa_family_t family = AF_UNSPEC;
void *dst, *src;
uint8_t next_hdr;
const char *proto;
size_t len;
int ret;
/* Enable hexdump by setting the log level to LOG_LEVEL_DBG */
net_pkt_hexdump(pkt, "Network packet");
switch (NET_IPV6_HDR(pkt)->vtc & 0xf0) {
case 0x60:
family = AF_INET6;
net_pkt_set_family(pkt, AF_INET6);
dst = &NET_IPV6_HDR(pkt)->dst;
src = &NET_IPV6_HDR(pkt)->src;
next_hdr = NET_IPV6_HDR(pkt)->nexthdr;
net_pkt_set_ip_hdr_len(pkt, sizeof(struct net_ipv6_hdr));
break;
case 0x40:
family = AF_INET;
net_pkt_set_family(pkt, AF_INET);
dst = &NET_IPV4_HDR(pkt)->dst;
src = &NET_IPV4_HDR(pkt)->src;
next_hdr = NET_IPV4_HDR(pkt)->proto;
net_pkt_set_ip_hdr_len(pkt, sizeof(struct net_ipv4_hdr));
break;
}
if (family == AF_UNSPEC) {
LOG_INF("Recv %p len %zd (unknown address family)",
pkt, net_pkt_get_len(pkt));
return;
}
ret = 0;
switch (next_hdr) {
case IPPROTO_TCP:
proto = "TCP";
ret = get_ports(pkt, &src_port, &dst_port);
break;
case IPPROTO_UDP:
proto = "UDP";
ret = get_ports(pkt, &src_port, &dst_port);
break;
case IPPROTO_ICMPV6:
case IPPROTO_ICMP:
proto = "ICMP";
break;
default:
proto = "<unknown>";
break;
}
if (ret < 0) {
LOG_ERR("Cannot get port numbers for pkt %p", pkt);
return;
}
src_addr = net_addr_ntop(family, src,
src_addr_buf, sizeof(src_addr_buf));
dst_addr = net_addr_ntop(family, dst,
dst_addr_buf, sizeof(dst_addr_buf));
len = net_pkt_get_len(pkt);
if (family == AF_INET) {
if (next_hdr == IPPROTO_TCP || next_hdr == IPPROTO_UDP) {
LOG_INF("%s %s (%zd) %s:%u -> %s:%u",
"IPv4", proto, len,
src_addr, src_port,
dst_addr, dst_port);
} else {
LOG_INF("%s %s (%zd) %s -> %s", "IPv4", proto,
len, src_addr,
dst_addr);
}
} else {
if (next_hdr == IPPROTO_TCP || next_hdr == IPPROTO_UDP) {
LOG_INF("%s %s (%zd) [%s]:%u -> [%s]:%u",
"IPv6", proto, len,
src_addr, src_port,
dst_addr, dst_port);
} else {
LOG_INF("%s %s (%zd) %s -> %s", "IPv6", proto,
len, src_addr,
dst_addr);
}
}
}
static int set_promisc_mode(const struct shell *shell,
size_t argc, char *argv[], bool enable)
{
struct net_if *iface;
char *endptr;
int idx, ret;
if (argc < 2) {
shell_fprintf(shell, SHELL_ERROR, "Invalid arguments.\n");
return -ENOEXEC;
}
idx = strtol(argv[1], &endptr, 10);
iface = net_if_get_by_index(idx);
if (!iface) {
shell_fprintf(shell, SHELL_ERROR,
"Cannot find network interface for index %d\n",
idx);
return -ENOEXEC;
}
shell_fprintf(shell, SHELL_INFO, "Promiscuous mode %s...\n",
enable ? "ON" : "OFF");
if (enable) {
ret = net_promisc_mode_on(iface);
} else {
ret = net_promisc_mode_off(iface);
}
if (ret < 0) {
if (ret == -EALREADY) {
shell_fprintf(shell, SHELL_INFO,
"Promiscuous mode already %s\n",
enable ? "enabled" : "disabled");
} else {
shell_fprintf(shell, SHELL_ERROR,
"Cannot %s promiscuous mode for "
"interface %p (%d)\n",
enable ? "set" : "unset", iface, ret);
}
return -ENOEXEC;
}
return 0;
}
static int cmd_promisc_on(const struct shell *shell,
size_t argc, char *argv[])
{
return set_promisc_mode(shell, argc, argv, true);
}
static int cmd_promisc_off(const struct shell *shell,
size_t argc, char *argv[])
{
return set_promisc_mode(shell, argc, argv, false);
}
SHELL_STATIC_SUBCMD_SET_CREATE(promisc_commands,
SHELL_CMD(on, NULL,
"Turn promiscuous mode on\n"
"promisc on <interface index> "
"Turn on promiscuous mode for the interface\n",
cmd_promisc_on),
SHELL_CMD(off, NULL, "Turn promiscuous mode off\n"
"promisc off <interface index> "
"Turn off promiscuous mode for the interface\n",
cmd_promisc_off),
SHELL_SUBCMD_SET_END
);
SHELL_CMD_REGISTER(promisc, &promisc_commands,
"Promiscuous mode commands", NULL);
void main(void)
{
struct net_pkt *pkt;
net_if_foreach(iface_cb, NULL);
while (1) {
pkt = net_promisc_mode_wait_data(K_FOREVER);
if (pkt) {
print_info(pkt);
}
net_pkt_unref(pkt);
}
}