1135380Sglebius/*- 2144017Sglebius * Copyright (c) 2004-2005 Gleb Smirnoff <glebius@FreeBSD.org> 3135380Sglebius * Copyright (c) 2001-2003 Roman V. Palagin <romanp@unshadow.net> 4135380Sglebius * All rights reserved. 5135380Sglebius * 6135380Sglebius * Redistribution and use in source and binary forms, with or without 7135380Sglebius * modification, are permitted provided that the following conditions 8135380Sglebius * are met: 9135380Sglebius * 1. Redistributions of source code must retain the above copyright 10135380Sglebius * notice, this list of conditions and the following disclaimer. 11135380Sglebius * 2. Redistributions in binary form must reproduce the above copyright 12135380Sglebius * notice, this list of conditions and the following disclaimer in the 13135380Sglebius * documentation and/or other materials provided with the distribution. 14135380Sglebius * 15135380Sglebius * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16135380Sglebius * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17135380Sglebius * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18135380Sglebius * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19135380Sglebius * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20135380Sglebius * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21135380Sglebius * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22135380Sglebius * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23135380Sglebius * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24135380Sglebius * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25135380Sglebius * SUCH DAMAGE. 26135380Sglebius * 27135380Sglebius * $SourceForge: flowctl.c,v 1.15 2004/08/31 20:24:58 glebius Exp $ 28135380Sglebius */ 29135380Sglebius 30135380Sglebius#ifndef lint 31135380Sglebiusstatic const char rcs_id[] = 32135380Sglebius "@(#) $FreeBSD$"; 33135380Sglebius#endif 34135380Sglebius 35135380Sglebius#include <sys/types.h> 36135380Sglebius#include <sys/time.h> 37135380Sglebius#include <sys/socket.h> 38135380Sglebius#include <sys/queue.h> 39135380Sglebius 40135380Sglebius#include <net/if.h> 41135380Sglebius#include <netinet/in.h> 42135380Sglebius 43135380Sglebius#include <arpa/inet.h> 44135380Sglebius 45135380Sglebius#include <err.h> 46135380Sglebius#include <stdio.h> 47135380Sglebius#include <stdlib.h> 48135380Sglebius#include <string.h> 49223788Sglebius#include <sysexits.h> 50135380Sglebius#include <unistd.h> 51135380Sglebius 52135380Sglebius#include <netgraph.h> 53135380Sglebius#include <netgraph/netflow/ng_netflow.h> 54135380Sglebius 55236808Smelifaro#define CISCO_SH_FLOW_HEADER "SrcIf SrcIPaddress " \ 56236808Smelifaro"DstIf DstIPaddress Pr SrcP DstP Pkts\n" 57135380Sglebius#define CISCO_SH_FLOW "%-13s %-15s %-13s %-15s %2u %4.4x %4.4x %6lu\n" 58135380Sglebius 59236808Smelifaro/* human-readable IPv4 header */ 60236808Smelifaro#define CISCO_SH_FLOW_HHEADER "SrcIf SrcIPaddress " \ 61236808Smelifaro"DstIf DstIPaddress Proto SrcPort DstPort Pkts\n" 62236808Smelifaro#define CISCO_SH_FLOW_H "%-13s %-15s %-13s %-15s %5u %8d %8d %8lu\n" 63223788Sglebius 64236808Smelifaro#define CISCO_SH_FLOW6_HEADER "SrcIf SrcIPaddress " \ 65236808Smelifaro"DstIf DstIPaddress Pr SrcP DstP Pkts\n" 66236808Smelifaro#define CISCO_SH_FLOW6 "%-13s %-30s %-13s %-30s %2u %4.4x %4.4x %6lu\n" 67236808Smelifaro 68236808Smelifaro/* Human-readable IPv6 headers */ 69236808Smelifaro#define CISCO_SH_FLOW6_HHEADER "SrcIf SrcIPaddress " \ 70236808Smelifaro"DstIf DstIPaddress Proto SrcPort DstPort Pkts\n" 71236808Smelifaro#define CISCO_SH_FLOW6_H "%-13s %-36s %-13s %-36s %5u %8d %8d %8lu\n" 72236808Smelifaro 73236808Smelifaro#define CISCO_SH_VERB_FLOW_HEADER "SrcIf SrcIPaddress " \ 74236808Smelifaro"DstIf DstIPaddress Pr TOS Flgs Pkts\n" \ 75144017Sglebius"Port Msk AS Port Msk AS NextHop B/Pk Active\n" 76135380Sglebius 77144017Sglebius#define CISCO_SH_VERB_FLOW "%-14s %-15s %-14s %-15s %2u %3x %4x %6lu\n" \ 78144017Sglebius "%4.4x /%-2u %-5u %4.4x /%-2u %-5u %-15s %9u %8u\n\n" 79144017Sglebius 80236808Smelifaro#define CISCO_SH_VERB_FLOW6_HEADER "SrcIf SrcIPaddress " \ 81236808Smelifaro"DstIf DstIPaddress Pr TOS Flgs Pkts\n" \ 82223788Sglebius"Port Msk AS Port Msk AS NextHop B/Pk Active\n" 83223788Sglebius 84223788Sglebius#define CISCO_SH_VERB_FLOW6 "%-14s %-30s %-14s %-30s %2u %3x %4x %6lu\n" \ 85223788Sglebius "%4.4x /%-2u %-5u %4.4x /%-2u %-5u %-30s %9u %8u\n\n" 86223824Spluknet#ifdef INET 87223788Sglebiusstatic void flow_cache_print(struct ngnf_show_header *resp); 88223824Spluknetstatic void flow_cache_print_verbose(struct ngnf_show_header *resp); 89223824Spluknet#endif 90223824Spluknet#ifdef INET6 91223788Sglebiusstatic void flow_cache_print6(struct ngnf_show_header *resp); 92223788Sglebiusstatic void flow_cache_print6_verbose(struct ngnf_show_header *resp); 93223824Spluknet#endif 94223788Sglebiusstatic void ctl_show(int, char **); 95223824Spluknet#if defined(INET) || defined(INET6) 96223788Sglebiusstatic void do_show(int, void (*func)(struct ngnf_show_header *)); 97223824Spluknet#endif 98135380Sglebiusstatic void help(void); 99135380Sglebiusstatic void execute_command(int, char **); 100135380Sglebius 101135380Sglebiusstruct ip_ctl_cmd { 102135380Sglebius char *cmd_name; 103223788Sglebius void (*cmd_func)(int argc, char **argv); 104135380Sglebius}; 105135380Sglebius 106135380Sglebiusstruct ip_ctl_cmd cmds[] = { 107140413Sglebius {"show", ctl_show}, 108140413Sglebius {NULL, NULL}, 109135380Sglebius}; 110135380Sglebius 111236808Smelifaroint cs, human = 0; 112223788Sglebiuschar *ng_path; 113135380Sglebius 114135380Sglebiusint 115135380Sglebiusmain(int argc, char **argv) 116135380Sglebius{ 117141393Sglebius int c; 118135380Sglebius char sname[NG_NODESIZ]; 119135380Sglebius int rcvbuf = SORCVBUF_SIZE; 120135380Sglebius 121135380Sglebius /* parse options */ 122135380Sglebius while ((c = getopt(argc, argv, "d:")) != -1) { 123135380Sglebius switch (c) { 124135380Sglebius case 'd': /* set libnetgraph debug level. */ 125135380Sglebius NgSetDebug(atoi(optarg)); 126135380Sglebius break; 127135380Sglebius } 128135380Sglebius } 129135380Sglebius 130135380Sglebius argc -= optind; 131135380Sglebius argv += optind; 132223788Sglebius ng_path = argv[0]; 133223788Sglebius if (ng_path == NULL || (strlen(ng_path) > NG_PATHSIZ)) 134135380Sglebius help(); 135135380Sglebius argc--; 136135380Sglebius argv++; 137135380Sglebius 138135380Sglebius /* create control socket. */ 139135380Sglebius snprintf(sname, sizeof(sname), "flowctl%i", getpid()); 140135380Sglebius 141135380Sglebius if (NgMkSockNode(sname, &cs, NULL) == -1) 142135380Sglebius err(1, "NgMkSockNode"); 143135380Sglebius 144135380Sglebius /* set receive buffer size */ 145135380Sglebius if (setsockopt(cs, SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(int)) == -1) 146135380Sglebius err(1, "setsockopt(SOL_SOCKET, SO_RCVBUF)"); 147135380Sglebius 148135380Sglebius /* parse and execute command */ 149135380Sglebius execute_command(argc, argv); 150135380Sglebius 151135380Sglebius close(cs); 152135380Sglebius 153135380Sglebius exit(0); 154135380Sglebius} 155135380Sglebius 156135380Sglebiusstatic void 157135380Sglebiusexecute_command(int argc, char **argv) 158135380Sglebius{ 159135380Sglebius int cindex = -1; 160135380Sglebius int i; 161135380Sglebius 162135380Sglebius if (!argc) 163135380Sglebius help(); 164135380Sglebius for (i = 0; cmds[i].cmd_name != NULL; i++) 165135380Sglebius if (!strncmp(argv[0], cmds[i].cmd_name, strlen(argv[0]))) { 166135380Sglebius if (cindex != -1) 167135380Sglebius errx(1, "ambiguous command: %s", argv[0]); 168135380Sglebius cindex = i; 169135380Sglebius } 170135380Sglebius if (cindex == -1) 171135380Sglebius errx(1, "bad command: %s", argv[0]); 172135380Sglebius argc--; 173135380Sglebius argv++; 174140413Sglebius (*cmds[cindex].cmd_func)(argc, argv); 175135380Sglebius} 176135380Sglebius 177223788Sglebiusstatic void 178140413Sglebiusctl_show(int argc, char **argv) 179135380Sglebius{ 180223824Spluknet int ipv4, ipv6, verbose = 0; 181135380Sglebius 182223824Spluknet ipv4 = feature_present("inet"); 183223824Spluknet ipv6 = feature_present("inet6"); 184223824Spluknet 185223788Sglebius if (argc > 0 && !strncmp(argv[0], "ipv4", 4)) { 186223788Sglebius ipv6 = 0; 187223788Sglebius argc--; 188223788Sglebius argv++; 189223788Sglebius } 190223788Sglebius if (argc > 0 && !strncmp(argv[0], "ipv6", 4)) { 191223788Sglebius ipv4 = 0; 192223788Sglebius argc--; 193223788Sglebius argv++; 194223788Sglebius } 195223788Sglebius 196144017Sglebius if (argc > 0 && !strncmp(argv[0], "verbose", strlen(argv[0]))) 197144017Sglebius verbose = 1; 198144017Sglebius 199236808Smelifaro if (argc > 0 && !strncmp(argv[0], "human", strlen(argv[0]))) 200236808Smelifaro human = 1; 201236808Smelifaro 202223824Spluknet#ifdef INET 203223788Sglebius if (ipv4) { 204223788Sglebius if (verbose) 205223788Sglebius do_show(4, &flow_cache_print_verbose); 206223788Sglebius else 207223788Sglebius do_show(4, &flow_cache_print); 208223788Sglebius } 209223824Spluknet#endif 210223788Sglebius 211223824Spluknet#ifdef INET6 212223788Sglebius if (ipv6) { 213223788Sglebius if (verbose) 214223788Sglebius do_show(6, &flow_cache_print6_verbose); 215223788Sglebius else 216223788Sglebius do_show(6, &flow_cache_print6); 217223788Sglebius } 218223824Spluknet#endif 219223788Sglebius} 220223788Sglebius 221223824Spluknet#if defined(INET) || defined(INET6) 222223788Sglebiusstatic void 223223788Sglebiusdo_show(int version, void (*func)(struct ngnf_show_header *)) 224223788Sglebius{ 225223788Sglebius struct ng_mesg *ng_mesg; 226223788Sglebius struct ngnf_show_header req, *resp; 227223788Sglebius int token, nread; 228223788Sglebius 229135380Sglebius ng_mesg = alloca(SORCVBUF_SIZE); 230135380Sglebius 231223788Sglebius req.version = version; 232223788Sglebius req.hash_id = req.list_id = 0; 233135380Sglebius 234135380Sglebius for (;;) { 235135380Sglebius /* request set of accounting records */ 236223788Sglebius token = NgSendMsg(cs, ng_path, NGM_NETFLOW_COOKIE, 237223788Sglebius NGM_NETFLOW_SHOW, (void *)&req, sizeof(req)); 238135380Sglebius if (token == -1) 239135380Sglebius err(1, "NgSendMsg(NGM_NETFLOW_SHOW)"); 240135380Sglebius 241135380Sglebius /* read reply */ 242223788Sglebius nread = NgRecvMsg(cs, ng_mesg, SORCVBUF_SIZE, NULL); 243135380Sglebius if (nread == -1) 244135380Sglebius err(1, "NgRecvMsg() failed"); 245135380Sglebius 246135380Sglebius if (ng_mesg->header.token != token) 247135380Sglebius err(1, "NgRecvMsg(NGM_NETFLOW_SHOW): token mismatch"); 248135380Sglebius 249223788Sglebius resp = (struct ngnf_show_header *)ng_mesg->data; 250223788Sglebius if ((ng_mesg->header.arglen < (sizeof(*resp))) || 251223788Sglebius (ng_mesg->header.arglen < (sizeof(*resp) + 252223788Sglebius (resp->nentries * sizeof(struct flow_entry_data))))) 253135380Sglebius err(1, "NgRecvMsg(NGM_NETFLOW_SHOW): arglen too small"); 254135380Sglebius 255223788Sglebius (*func)(resp); 256135380Sglebius 257223788Sglebius if (resp->hash_id != 0) 258223788Sglebius req.hash_id = resp->hash_id; 259135380Sglebius else 260135380Sglebius break; 261223788Sglebius req.list_id = resp->list_id; 262135380Sglebius } 263135380Sglebius} 264223824Spluknet#endif 265135380Sglebius 266223824Spluknet#ifdef INET 267223788Sglebiusstatic void 268223788Sglebiusflow_cache_print(struct ngnf_show_header *resp) 269135380Sglebius{ 270135380Sglebius struct flow_entry_data *fle; 271135380Sglebius char src[INET_ADDRSTRLEN], dst[INET_ADDRSTRLEN]; 272135380Sglebius char src_if[IFNAMSIZ], dst_if[IFNAMSIZ]; 273135380Sglebius int i; 274135380Sglebius 275223788Sglebius if (resp->version != 4) 276223788Sglebius errx(EX_SOFTWARE, "%s: version mismatch: %u", 277223788Sglebius __func__, resp->version); 278135380Sglebius 279236808Smelifaro if (resp->nentries > 0) 280236808Smelifaro printf(human ? CISCO_SH_FLOW_HHEADER : CISCO_SH_FLOW_HEADER); 281223788Sglebius 282223788Sglebius fle = (struct flow_entry_data *)(resp + 1); 283223788Sglebius for (i = 0; i < resp->nentries; i++, fle++) { 284135380Sglebius inet_ntop(AF_INET, &fle->r.r_src, src, sizeof(src)); 285135380Sglebius inet_ntop(AF_INET, &fle->r.r_dst, dst, sizeof(dst)); 286236808Smelifaro printf(human ? CISCO_SH_FLOW_H : CISCO_SH_FLOW, 287135380Sglebius if_indextoname(fle->fle_i_ifx, src_if), 288135380Sglebius src, 289135380Sglebius if_indextoname(fle->fle_o_ifx, dst_if), 290135380Sglebius dst, 291135380Sglebius fle->r.r_ip_p, 292135380Sglebius ntohs(fle->r.r_sport), 293135380Sglebius ntohs(fle->r.r_dport), 294135380Sglebius fle->packets); 295135380Sglebius 296135380Sglebius } 297135380Sglebius} 298223824Spluknet#endif 299135380Sglebius 300223788Sglebius#ifdef INET6 301223788Sglebiusstatic void 302223788Sglebiusflow_cache_print6(struct ngnf_show_header *resp) 303144017Sglebius{ 304223788Sglebius struct flow6_entry_data *fle6; 305223788Sglebius char src6[INET6_ADDRSTRLEN], dst6[INET6_ADDRSTRLEN]; 306223788Sglebius char src_if[IFNAMSIZ], dst_if[IFNAMSIZ]; 307223788Sglebius int i; 308223788Sglebius 309223788Sglebius if (resp->version != 6) 310223788Sglebius errx(EX_SOFTWARE, "%s: version mismatch: %u", 311223788Sglebius __func__, resp->version); 312223788Sglebius 313236808Smelifaro if (resp->nentries > 0) 314236808Smelifaro printf(human ? CISCO_SH_FLOW6_HHEADER : CISCO_SH_FLOW6_HEADER); 315223788Sglebius 316223788Sglebius fle6 = (struct flow6_entry_data *)(resp + 1); 317223788Sglebius for (i = 0; i < resp->nentries; i++, fle6++) { 318223788Sglebius inet_ntop(AF_INET6, &fle6->r.src.r_src6, src6, sizeof(src6)); 319223788Sglebius inet_ntop(AF_INET6, &fle6->r.dst.r_dst6, dst6, sizeof(dst6)); 320236808Smelifaro printf(human ? CISCO_SH_FLOW6_H : CISCO_SH_FLOW6, 321223788Sglebius if_indextoname(fle6->fle_i_ifx, src_if), 322223788Sglebius src6, 323223788Sglebius if_indextoname(fle6->fle_o_ifx, dst_if), 324223788Sglebius dst6, 325223788Sglebius fle6->r.r_ip_p, 326223788Sglebius ntohs(fle6->r.r_sport), 327223788Sglebius ntohs(fle6->r.r_dport), 328223788Sglebius fle6->packets); 329223788Sglebius 330223788Sglebius } 331223788Sglebius} 332223788Sglebius#endif 333223788Sglebius 334223824Spluknet#ifdef INET 335223788Sglebiusstatic void 336223788Sglebiusflow_cache_print_verbose(struct ngnf_show_header *resp) 337223788Sglebius{ 338144017Sglebius struct flow_entry_data *fle; 339144017Sglebius char src[INET_ADDRSTRLEN], dst[INET_ADDRSTRLEN], next[INET_ADDRSTRLEN]; 340144017Sglebius char src_if[IFNAMSIZ], dst_if[IFNAMSIZ]; 341144017Sglebius int i; 342144017Sglebius 343223788Sglebius if (resp->version != 4) 344223788Sglebius errx(EX_SOFTWARE, "%s: version mismatch: %u", 345223788Sglebius __func__, resp->version); 346144017Sglebius 347223788Sglebius printf(CISCO_SH_VERB_FLOW_HEADER); 348223788Sglebius 349223788Sglebius fle = (struct flow_entry_data *)(resp + 1); 350223788Sglebius for (i = 0; i < resp->nentries; i++, fle++) { 351144017Sglebius inet_ntop(AF_INET, &fle->r.r_src, src, sizeof(src)); 352144017Sglebius inet_ntop(AF_INET, &fle->r.r_dst, dst, sizeof(dst)); 353144017Sglebius inet_ntop(AF_INET, &fle->next_hop, next, sizeof(next)); 354144017Sglebius printf(CISCO_SH_VERB_FLOW, 355144017Sglebius if_indextoname(fle->fle_i_ifx, src_if), 356144017Sglebius src, 357144017Sglebius if_indextoname(fle->fle_o_ifx, dst_if), 358144017Sglebius dst, 359144017Sglebius fle->r.r_ip_p, 360144017Sglebius fle->r.r_tos, 361144017Sglebius fle->tcp_flags, 362144017Sglebius fle->packets, 363144017Sglebius ntohs(fle->r.r_sport), 364144017Sglebius fle->src_mask, 365144017Sglebius 0, 366144017Sglebius ntohs(fle->r.r_dport), 367144017Sglebius fle->dst_mask, 368144017Sglebius 0, 369144017Sglebius next, 370144017Sglebius (u_int)(fle->bytes / fle->packets), 371144017Sglebius 0); 372144017Sglebius 373144017Sglebius } 374144017Sglebius} 375223824Spluknet#endif 376144017Sglebius 377223788Sglebius#ifdef INET6 378135380Sglebiusstatic void 379223788Sglebiusflow_cache_print6_verbose(struct ngnf_show_header *resp) 380223788Sglebius{ 381223788Sglebius struct flow6_entry_data *fle6; 382223788Sglebius char src6[INET6_ADDRSTRLEN], dst6[INET6_ADDRSTRLEN], next6[INET6_ADDRSTRLEN]; 383223788Sglebius char src_if[IFNAMSIZ], dst_if[IFNAMSIZ]; 384223788Sglebius int i; 385223788Sglebius 386223788Sglebius if (resp->version != 6) 387223788Sglebius errx(EX_SOFTWARE, "%s: version mismatch: %u", 388223788Sglebius __func__, resp->version); 389223788Sglebius 390223788Sglebius printf(CISCO_SH_VERB_FLOW6_HEADER); 391223788Sglebius 392223788Sglebius fle6 = (struct flow6_entry_data *)(resp + 1); 393223788Sglebius for (i = 0; i < resp->nentries; i++, fle6++) { 394223788Sglebius inet_ntop(AF_INET6, &fle6->r.src.r_src6, src6, sizeof(src6)); 395223788Sglebius inet_ntop(AF_INET6, &fle6->r.dst.r_dst6, dst6, sizeof(dst6)); 396223788Sglebius inet_ntop(AF_INET6, &fle6->n.next_hop6, next6, sizeof(next6)); 397223788Sglebius printf(CISCO_SH_VERB_FLOW6, 398223788Sglebius if_indextoname(fle6->fle_i_ifx, src_if), 399223788Sglebius src6, 400223788Sglebius if_indextoname(fle6->fle_o_ifx, dst_if), 401223788Sglebius dst6, 402223788Sglebius fle6->r.r_ip_p, 403223788Sglebius fle6->r.r_tos, 404223788Sglebius fle6->tcp_flags, 405223788Sglebius fle6->packets, 406223788Sglebius ntohs(fle6->r.r_sport), 407223788Sglebius fle6->src_mask, 408223788Sglebius 0, 409223788Sglebius ntohs(fle6->r.r_dport), 410223788Sglebius fle6->dst_mask, 411223788Sglebius 0, 412223788Sglebius next6, 413223788Sglebius (u_int)(fle6->bytes / fle6->packets), 414223788Sglebius 0); 415223788Sglebius } 416223788Sglebius} 417223788Sglebius#endif 418223788Sglebius 419223788Sglebiusstatic void 420135380Sglebiushelp(void) 421135380Sglebius{ 422135380Sglebius extern char *__progname; 423135380Sglebius 424135380Sglebius fprintf(stderr, "usage: %s [-d level] nodename command\n", __progname); 425135380Sglebius exit (0); 426135380Sglebius} 427