nuttx-apps/system/conntrack/conntrack.c
Zhe Weng 43a8e862e0 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>
2024-04-18 09:57:01 +08:00

290 lines
8.1 KiB
C

/****************************************************************************
* 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;
}