apps/system: Add conntrack command

A simple tool like Linux's 'conntrack', only supports printing NAT connections now.

nuttx> conntrack -E
    [NEW] icmp  src=192.168.21.66 dst=1.2.3.4 type=8 code=0 id=26739 src=1.2.3.4 dst=192.168.11.1 type=0 code=0 id=26739
    [NEW] tcp   src=192.168.21.66 dst=1.2.3.4 sport=38446 dport=80 src=1.2.3.4 dst=192.168.11.1 sport=80 dport=38446
[DESTROY] icmp  src=192.168.21.66 dst=1.2.3.4 type=8 code=0 id=26739 src=1.2.3.4 dst=192.168.11.1 type=0 code=0 id=26739

nuttx> conntrack -L
tcp   src=192.168.21.66 dst=1.2.3.4 sport=38446 dport=80 src=1.2.3.4 dst=192.168.11.1 sport=80 dport=38446
icmp  src=192.168.21.66 dst=1.2.3.4 type=8 code=0 id=26739 src=1.2.3.4 dst=192.168.11.1 type=0 code=0 id=26739
conntrack: 2 flow entries have been shown.

Signed-off-by: Zhe Weng <wengzhe@xiaomi.com>
This commit is contained in:
Zhe Weng 2024-03-21 21:11:52 +08:00 committed by Xiang Xiao
parent 7e7b108ef9
commit 43a8e862e0
4 changed files with 374 additions and 0 deletions

32
system/conntrack/Kconfig Normal file
View file

@ -0,0 +1,32 @@
#
# For a description of the syntax of this configuration file,
# see the file kconfig-language.txt in the NuttX tools repository.
#
config SYSTEM_CONNTRACK
tristate "conntrack command"
default n
depends on NETLINK_NETFILTER
select SYSTEM_ARGTABLE3
---help---
Enable support for the 'conntrack' command, a simple tool like
Linux's 'conntrack', only supports printing NAT connections now.
if SYSTEM_CONNTRACK
config SYSTEM_CONNTRACK_PROGNAME
string "conntrack program name"
default "conntrack"
---help---
This is the name of the program that will be used when the NSH ELF
program is installed.
config SYSTEM_CONNTRACK_PRIORITY
int "conntrack task priority"
default 100
config SYSTEM_CONNTRACK_STACKSIZE
int "conntrack stack size"
default DEFAULT_TASK_STACKSIZE
endif

View file

@ -0,0 +1,23 @@
############################################################################
# apps/system/conntrack/Make.defs
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership. The
# ASF licenses this file to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance with the
# License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
############################################################################
ifneq ($(CONFIG_SYSTEM_CONNTRACK),)
CONFIGURED_APPS += $(APPDIR)/system/conntrack
endif

30
system/conntrack/Makefile Normal file
View file

@ -0,0 +1,30 @@
############################################################################
# apps/system/conntrack/Makefile
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership. The
# ASF licenses this file to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance with the
# License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
############################################################################
include $(APPDIR)/Make.defs
PROGNAME = $(CONFIG_SYSTEM_CONNTRACK_PROGNAME)
PRIORITY = $(CONFIG_SYSTEM_CONNTRACK_PRIORITY)
STACKSIZE = $(CONFIG_SYSTEM_CONNTRACK_STACKSIZE)
MODULE = $(CONFIG_SYSTEM_CONNTRACK)
MAINSRC = conntrack.c
include $(APPDIR)/Application.mk

View file

@ -0,0 +1,289 @@
/****************************************************************************
* apps/system/conntrack/conntrack.c
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <netpacket/netlink.h>
#include <nuttx/net/ip.h>
#include "argtable3.h"
#include "netutils/netlib.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#define RXBUFFER_SIZE 256
/****************************************************************************
* Private Types
****************************************************************************/
struct conntrack_args_s
{
FAR struct arg_lit *dump;
FAR struct arg_lit *event;
FAR struct arg_str *family;
FAR struct arg_end *end;
};
/****************************************************************************
* Private Data
****************************************************************************/
static volatile bool g_exiting;
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: sigexit
****************************************************************************/
static void sigexit(int signo)
{
g_exiting = true;
}
/****************************************************************************
* Name: proto2str
****************************************************************************/
static FAR const char *proto2str(uint8_t proto)
{
switch (proto)
{
case IPPROTO_TCP:
return "tcp";
case IPPROTO_UDP:
return "udp";
case IPPROTO_ICMP:
return "icmp";
case IPPROTO_ICMP6:
return "icmp6";
default:
return "";
}
}
/****************************************************************************
* Name: conntrack_print_tuple
****************************************************************************/
static void conntrack_print_tuple(sa_family_t family,
FAR const struct netlib_conntrack_tuple_s *tuple)
{
char ipstrbuf[INET6_ADDRSTRLEN];
/* src=10.88.0.88 dst=192.168.66.66 sport=45065 dport=5001 */
inet_ntop(family, &tuple->src, ipstrbuf, INET6_ADDRSTRLEN);
printf("src=%s ", ipstrbuf);
inet_ntop(family, &tuple->dst, ipstrbuf, INET6_ADDRSTRLEN);
printf("dst=%s ", ipstrbuf);
switch (tuple->l4proto)
{
case IPPROTO_TCP:
case IPPROTO_UDP:
printf("sport=%" PRIu16 " dport=%" PRIu16 " ",
tuple->l4.tcp.sport, tuple->l4.tcp.dport);
break;
case IPPROTO_ICMP:
case IPPROTO_ICMP6:
printf("type=%" PRIu8 " code=%" PRIu8 " id=%" PRIu16 " ",
tuple->l4.icmp.type, tuple->l4.icmp.code, tuple->l4.icmp.id);
break;
}
}
/****************************************************************************
* Name: conntrack_print
****************************************************************************/
static int conntrack_print(FAR struct netlib_conntrack_s *ct)
{
/* tcp <orig> <reply> */
printf("%-5s ", proto2str(ct->orig.l4proto));
conntrack_print_tuple(ct->family, &ct->orig);
conntrack_print_tuple(ct->family, &ct->reply);
printf("\n");
return 0;
}
/****************************************************************************
* Name: conntrack_dump
****************************************************************************/
static void conntrack_dump(sa_family_t family)
{
ssize_t ndumped = netlib_get_conntrack(family, conntrack_print);
printf("conntrack: %zd flow entries have been shown.\n", ndumped);
}
/****************************************************************************
* Name: conntrack_monitor_socket
****************************************************************************/
static int conntrack_monitor_socket(uint32_t groups)
{
struct sockaddr_nl addr;
int fd;
/* Create a NetLink socket with NETLINK_NETFILTER protocol */
fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_NETFILTER);
if (fd < 0)
{
perror("ERROR: failed to create netlink socket");
return -errno;
}
addr.nl_family = AF_NETLINK;
addr.nl_pad = 0;
addr.nl_pid = getpid();
addr.nl_groups = groups;
if (bind(fd, (FAR const struct sockaddr *)&addr, sizeof(addr)) < 0)
{
perror("ERROR: failed to bind netlink socket");
close(fd);
return -errno;
}
return fd;
}
/****************************************************************************
* Name: conntrack_monitor_event
****************************************************************************/
static void conntrack_monitor_event(void)
{
FAR const struct nlmsghdr *nlh;
struct netlib_conntrack_s ct;
uint8_t buf[RXBUFFER_SIZE];
ssize_t len;
int fd;
/* Setup exit signal handler */
g_exiting = false;
signal(SIGINT, sigexit);
/* Create a NetLink socket with NETLINK_NETFILTER protocol */
fd = conntrack_monitor_socket(NF_NETLINK_CONNTRACK_NEW |
NF_NETLINK_CONNTRACK_DESTROY);
if (fd < 0)
{
return;
}
while ((len = read(fd, buf, sizeof(buf))) >= 0 && !g_exiting)
{
nlh = (FAR struct nlmsghdr *)buf;
if (netlib_parse_conntrack(nlh, len, &ct) < 0)
{
fprintf(stderr, "Failed to parse conntrack message\n");
continue;
}
/* Only event log needs to print type prefix */
switch (ct.type)
{
case IPCTNL_MSG_CT_NEW:
printf(" [NEW] ");
break;
case IPCTNL_MSG_CT_DELETE:
printf("[DESTROY] ");
break;
default:
printf("[UNKNOWN] ");
break;
}
/* Print the remaining line */
conntrack_print(&ct);
}
close(fd);
}
/****************************************************************************
* Public Functions
****************************************************************************/
int main(int argc, FAR char *argv[])
{
struct conntrack_args_s args;
sa_family_t family = AF_INET;
int nerrors;
args.dump = arg_lit0("L", "dump", "List connection tracking");
args.event = arg_lit0("E", "event", "Display a real-time event log");
args.family = arg_str0("f", "family", "PROTO", "Specify L3 (ipv4, ipv6) "
"protocol, only for dump option (default ipv4)");
args.end = arg_end(3);
nerrors = arg_parse(argc, argv, (FAR void **)&args);
if (nerrors != 0 || args.dump->count + args.event->count == 0)
{
arg_print_errors(stdout, args.end, argv[0]);
printf("Usage:\n");
arg_print_glossary(stdout, (FAR void **)&args, NULL);
goto out;
}
if (args.family->count != 0)
{
if (strncmp(args.family->sval[0], "ipv6", 4) == 0)
{
family = AF_INET6;
}
}
if (args.dump->count != 0)
{
conntrack_dump(family);
}
if (args.event->count != 0)
{
conntrack_monitor_event();
}
out:
arg_freetable((FAR void **)&args, 1);
return 0;
}