freebsd-dev/usr.sbin/flowctl/flowctl.c
Pedro F. Giffuni 1de7b4b805 various: general adoption of SPDX licensing ID tags.
Mainly focus on files that use BSD 2-Clause license, however the tool I
was using misidentified many licenses so this was mostly a manual - error
prone - task.

The Software Package Data Exchange (SPDX) group provides a specification
to make it easier for automated tools to detect and summarize well known
opensource licenses. We are gradually adopting the specification, noting
that the tags are considered only advisory and do not, in any way,
superceed or replace the license texts.

No functional change intended.
2017-11-27 15:37:16 +00:00

429 lines
11 KiB
C

/*-
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
*
* Copyright (c) 2004-2005 Gleb Smirnoff <glebius@FreeBSD.org>
* Copyright (c) 2001-2003 Roman V. Palagin <romanp@unshadow.net>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $SourceForge: flowctl.c,v 1.15 2004/08/31 20:24:58 glebius Exp $
*/
#ifndef lint
static const char rcs_id[] =
"@(#) $FreeBSD$";
#endif
#include <sys/types.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <sys/queue.h>
#include <net/if.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <err.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sysexits.h>
#include <unistd.h>
#include <netgraph.h>
#include <netgraph/netflow/ng_netflow.h>
#define CISCO_SH_FLOW_HEADER "SrcIf SrcIPaddress " \
"DstIf DstIPaddress Pr SrcP DstP Pkts\n"
#define CISCO_SH_FLOW "%-13s %-15s %-13s %-15s %2u %4.4x %4.4x %6lu\n"
/* human-readable IPv4 header */
#define CISCO_SH_FLOW_HHEADER "SrcIf SrcIPaddress " \
"DstIf DstIPaddress Proto SrcPort DstPort Pkts\n"
#define CISCO_SH_FLOW_H "%-13s %-15s %-13s %-15s %5u %8d %8d %8lu\n"
#define CISCO_SH_FLOW6_HEADER "SrcIf SrcIPaddress " \
"DstIf DstIPaddress Pr SrcP DstP Pkts\n"
#define CISCO_SH_FLOW6 "%-13s %-30s %-13s %-30s %2u %4.4x %4.4x %6lu\n"
/* Human-readable IPv6 headers */
#define CISCO_SH_FLOW6_HHEADER "SrcIf SrcIPaddress " \
"DstIf DstIPaddress Proto SrcPort DstPort Pkts\n"
#define CISCO_SH_FLOW6_H "%-13s %-36s %-13s %-36s %5u %8d %8d %8lu\n"
#define CISCO_SH_VERB_FLOW_HEADER "SrcIf SrcIPaddress " \
"DstIf DstIPaddress Pr TOS Flgs Pkts\n" \
"Port Msk AS Port Msk AS NextHop B/Pk Active\n"
#define CISCO_SH_VERB_FLOW "%-14s %-15s %-14s %-15s %2u %3x %4x %6lu\n" \
"%4.4x /%-2u %-5u %4.4x /%-2u %-5u %-15s %9u %8u\n\n"
#define CISCO_SH_VERB_FLOW6_HEADER "SrcIf SrcIPaddress " \
"DstIf DstIPaddress Pr TOS Flgs Pkts\n" \
"Port Msk AS Port Msk AS NextHop B/Pk Active\n"
#define CISCO_SH_VERB_FLOW6 "%-14s %-30s %-14s %-30s %2u %3x %4x %6lu\n" \
"%4.4x /%-2u %-5u %4.4x /%-2u %-5u %-30s %9u %8u\n\n"
#ifdef INET
static void flow_cache_print(struct ngnf_show_header *resp);
static void flow_cache_print_verbose(struct ngnf_show_header *resp);
#endif
#ifdef INET6
static void flow_cache_print6(struct ngnf_show_header *resp);
static void flow_cache_print6_verbose(struct ngnf_show_header *resp);
#endif
static void ctl_show(int, char **);
#if defined(INET) || defined(INET6)
static void do_show(int, void (*func)(struct ngnf_show_header *));
#endif
static void help(void);
static void execute_command(int, char **);
struct ip_ctl_cmd {
char *cmd_name;
void (*cmd_func)(int argc, char **argv);
};
struct ip_ctl_cmd cmds[] = {
{"show", ctl_show},
{NULL, NULL},
};
int cs, human = 0;
char *ng_path;
int
main(int argc, char **argv)
{
int c;
char sname[NG_NODESIZ];
int rcvbuf = SORCVBUF_SIZE;
/* parse options */
while ((c = getopt(argc, argv, "d:")) != -1) {
switch (c) {
case 'd': /* set libnetgraph debug level. */
NgSetDebug(atoi(optarg));
break;
}
}
argc -= optind;
argv += optind;
ng_path = argv[0];
if (ng_path == NULL || (strlen(ng_path) > NG_PATHSIZ))
help();
argc--;
argv++;
/* create control socket. */
snprintf(sname, sizeof(sname), "flowctl%i", getpid());
if (NgMkSockNode(sname, &cs, NULL) == -1)
err(1, "NgMkSockNode");
/* set receive buffer size */
if (setsockopt(cs, SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(int)) == -1)
err(1, "setsockopt(SOL_SOCKET, SO_RCVBUF)");
/* parse and execute command */
execute_command(argc, argv);
close(cs);
exit(0);
}
static void
execute_command(int argc, char **argv)
{
int cindex = -1;
int i;
if (!argc)
help();
for (i = 0; cmds[i].cmd_name != NULL; i++)
if (!strncmp(argv[0], cmds[i].cmd_name, strlen(argv[0]))) {
if (cindex != -1)
errx(1, "ambiguous command: %s", argv[0]);
cindex = i;
}
if (cindex == -1)
errx(1, "bad command: %s", argv[0]);
argc--;
argv++;
(*cmds[cindex].cmd_func)(argc, argv);
}
static void
ctl_show(int argc, char **argv)
{
int ipv4, ipv6, verbose = 0;
ipv4 = feature_present("inet");
ipv6 = feature_present("inet6");
if (argc > 0 && !strncmp(argv[0], "ipv4", 4)) {
ipv6 = 0;
argc--;
argv++;
}
if (argc > 0 && !strncmp(argv[0], "ipv6", 4)) {
ipv4 = 0;
argc--;
argv++;
}
if (argc > 0 && !strncmp(argv[0], "verbose", strlen(argv[0])))
verbose = 1;
if (argc > 0 && !strncmp(argv[0], "human", strlen(argv[0])))
human = 1;
#ifdef INET
if (ipv4) {
if (verbose)
do_show(4, &flow_cache_print_verbose);
else
do_show(4, &flow_cache_print);
}
#endif
#ifdef INET6
if (ipv6) {
if (verbose)
do_show(6, &flow_cache_print6_verbose);
else
do_show(6, &flow_cache_print6);
}
#endif
}
#if defined(INET) || defined(INET6)
static void
do_show(int version, void (*func)(struct ngnf_show_header *))
{
char buf[SORCVBUF_SIZE];
struct ng_mesg *ng_mesg;
struct ngnf_show_header req, *resp;
int token, nread;
ng_mesg = (struct ng_mesg *)buf;
req.version = version;
req.hash_id = req.list_id = 0;
for (;;) {
/* request set of accounting records */
token = NgSendMsg(cs, ng_path, NGM_NETFLOW_COOKIE,
NGM_NETFLOW_SHOW, (void *)&req, sizeof(req));
if (token == -1)
err(1, "NgSendMsg(NGM_NETFLOW_SHOW)");
/* read reply */
nread = NgRecvMsg(cs, ng_mesg, SORCVBUF_SIZE, NULL);
if (nread == -1)
err(1, "NgRecvMsg() failed");
if (ng_mesg->header.token != token)
err(1, "NgRecvMsg(NGM_NETFLOW_SHOW): token mismatch");
resp = (struct ngnf_show_header *)ng_mesg->data;
if ((ng_mesg->header.arglen < (sizeof(*resp))) ||
(ng_mesg->header.arglen < (sizeof(*resp) +
(resp->nentries * sizeof(struct flow_entry_data)))))
err(1, "NgRecvMsg(NGM_NETFLOW_SHOW): arglen too small");
(*func)(resp);
if (resp->hash_id != 0)
req.hash_id = resp->hash_id;
else
break;
req.list_id = resp->list_id;
}
}
#endif
#ifdef INET
static void
flow_cache_print(struct ngnf_show_header *resp)
{
struct flow_entry_data *fle;
char src[INET_ADDRSTRLEN], dst[INET_ADDRSTRLEN];
char src_if[IFNAMSIZ], dst_if[IFNAMSIZ];
int i;
if (resp->version != 4)
errx(EX_SOFTWARE, "%s: version mismatch: %u",
__func__, resp->version);
if (resp->nentries > 0)
printf(human ? CISCO_SH_FLOW_HHEADER : CISCO_SH_FLOW_HEADER);
fle = (struct flow_entry_data *)(resp + 1);
for (i = 0; i < resp->nentries; i++, fle++) {
inet_ntop(AF_INET, &fle->r.r_src, src, sizeof(src));
inet_ntop(AF_INET, &fle->r.r_dst, dst, sizeof(dst));
printf(human ? CISCO_SH_FLOW_H : CISCO_SH_FLOW,
if_indextoname(fle->fle_i_ifx, src_if),
src,
if_indextoname(fle->fle_o_ifx, dst_if),
dst,
fle->r.r_ip_p,
ntohs(fle->r.r_sport),
ntohs(fle->r.r_dport),
fle->packets);
}
}
#endif
#ifdef INET6
static void
flow_cache_print6(struct ngnf_show_header *resp)
{
struct flow6_entry_data *fle6;
char src6[INET6_ADDRSTRLEN], dst6[INET6_ADDRSTRLEN];
char src_if[IFNAMSIZ], dst_if[IFNAMSIZ];
int i;
if (resp->version != 6)
errx(EX_SOFTWARE, "%s: version mismatch: %u",
__func__, resp->version);
if (resp->nentries > 0)
printf(human ? CISCO_SH_FLOW6_HHEADER : CISCO_SH_FLOW6_HEADER);
fle6 = (struct flow6_entry_data *)(resp + 1);
for (i = 0; i < resp->nentries; i++, fle6++) {
inet_ntop(AF_INET6, &fle6->r.src.r_src6, src6, sizeof(src6));
inet_ntop(AF_INET6, &fle6->r.dst.r_dst6, dst6, sizeof(dst6));
printf(human ? CISCO_SH_FLOW6_H : CISCO_SH_FLOW6,
if_indextoname(fle6->fle_i_ifx, src_if),
src6,
if_indextoname(fle6->fle_o_ifx, dst_if),
dst6,
fle6->r.r_ip_p,
ntohs(fle6->r.r_sport),
ntohs(fle6->r.r_dport),
fle6->packets);
}
}
#endif
#ifdef INET
static void
flow_cache_print_verbose(struct ngnf_show_header *resp)
{
struct flow_entry_data *fle;
char src[INET_ADDRSTRLEN], dst[INET_ADDRSTRLEN], next[INET_ADDRSTRLEN];
char src_if[IFNAMSIZ], dst_if[IFNAMSIZ];
int i;
if (resp->version != 4)
errx(EX_SOFTWARE, "%s: version mismatch: %u",
__func__, resp->version);
printf(CISCO_SH_VERB_FLOW_HEADER);
fle = (struct flow_entry_data *)(resp + 1);
for (i = 0; i < resp->nentries; i++, fle++) {
inet_ntop(AF_INET, &fle->r.r_src, src, sizeof(src));
inet_ntop(AF_INET, &fle->r.r_dst, dst, sizeof(dst));
inet_ntop(AF_INET, &fle->next_hop, next, sizeof(next));
printf(CISCO_SH_VERB_FLOW,
if_indextoname(fle->fle_i_ifx, src_if),
src,
if_indextoname(fle->fle_o_ifx, dst_if),
dst,
fle->r.r_ip_p,
fle->r.r_tos,
fle->tcp_flags,
fle->packets,
ntohs(fle->r.r_sport),
fle->src_mask,
0,
ntohs(fle->r.r_dport),
fle->dst_mask,
0,
next,
(u_int)(fle->bytes / fle->packets),
0);
}
}
#endif
#ifdef INET6
static void
flow_cache_print6_verbose(struct ngnf_show_header *resp)
{
struct flow6_entry_data *fle6;
char src6[INET6_ADDRSTRLEN], dst6[INET6_ADDRSTRLEN], next6[INET6_ADDRSTRLEN];
char src_if[IFNAMSIZ], dst_if[IFNAMSIZ];
int i;
if (resp->version != 6)
errx(EX_SOFTWARE, "%s: version mismatch: %u",
__func__, resp->version);
printf(CISCO_SH_VERB_FLOW6_HEADER);
fle6 = (struct flow6_entry_data *)(resp + 1);
for (i = 0; i < resp->nentries; i++, fle6++) {
inet_ntop(AF_INET6, &fle6->r.src.r_src6, src6, sizeof(src6));
inet_ntop(AF_INET6, &fle6->r.dst.r_dst6, dst6, sizeof(dst6));
inet_ntop(AF_INET6, &fle6->n.next_hop6, next6, sizeof(next6));
printf(CISCO_SH_VERB_FLOW6,
if_indextoname(fle6->fle_i_ifx, src_if),
src6,
if_indextoname(fle6->fle_o_ifx, dst_if),
dst6,
fle6->r.r_ip_p,
fle6->r.r_tos,
fle6->tcp_flags,
fle6->packets,
ntohs(fle6->r.r_sport),
fle6->src_mask,
0,
ntohs(fle6->r.r_dport),
fle6->dst_mask,
0,
next6,
(u_int)(fle6->bytes / fle6->packets),
0);
}
}
#endif
static void
help(void)
{
extern char *__progname;
fprintf(stderr, "usage: %s [-d level] nodename command\n", __progname);
exit (0);
}