flowctl.c revision 278589
1/*- 2 * Copyright (c) 2004-2005 Gleb Smirnoff <glebius@FreeBSD.org> 3 * Copyright (c) 2001-2003 Roman V. Palagin <romanp@unshadow.net> 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 * 27 * $SourceForge: flowctl.c,v 1.15 2004/08/31 20:24:58 glebius Exp $ 28 */ 29 30#ifndef lint 31static const char rcs_id[] = 32 "@(#) $FreeBSD: head/usr.sbin/flowctl/flowctl.c 278589 2015-02-11 17:46:35Z pfg $"; 33#endif 34 35#include <sys/types.h> 36#include <sys/time.h> 37#include <sys/socket.h> 38#include <sys/queue.h> 39 40#include <net/if.h> 41#include <netinet/in.h> 42 43#include <arpa/inet.h> 44 45#include <err.h> 46#include <stdio.h> 47#include <stdlib.h> 48#include <string.h> 49#include <sysexits.h> 50#include <unistd.h> 51 52#include <netgraph.h> 53#include <netgraph/netflow/ng_netflow.h> 54 55#define CISCO_SH_FLOW_HEADER "SrcIf SrcIPaddress " \ 56"DstIf DstIPaddress Pr SrcP DstP Pkts\n" 57#define CISCO_SH_FLOW "%-13s %-15s %-13s %-15s %2u %4.4x %4.4x %6lu\n" 58 59/* human-readable IPv4 header */ 60#define CISCO_SH_FLOW_HHEADER "SrcIf SrcIPaddress " \ 61"DstIf DstIPaddress Proto SrcPort DstPort Pkts\n" 62#define CISCO_SH_FLOW_H "%-13s %-15s %-13s %-15s %5u %8d %8d %8lu\n" 63 64#define CISCO_SH_FLOW6_HEADER "SrcIf SrcIPaddress " \ 65"DstIf DstIPaddress Pr SrcP DstP Pkts\n" 66#define CISCO_SH_FLOW6 "%-13s %-30s %-13s %-30s %2u %4.4x %4.4x %6lu\n" 67 68/* Human-readable IPv6 headers */ 69#define CISCO_SH_FLOW6_HHEADER "SrcIf SrcIPaddress " \ 70"DstIf DstIPaddress Proto SrcPort DstPort Pkts\n" 71#define CISCO_SH_FLOW6_H "%-13s %-36s %-13s %-36s %5u %8d %8d %8lu\n" 72 73#define CISCO_SH_VERB_FLOW_HEADER "SrcIf SrcIPaddress " \ 74"DstIf DstIPaddress Pr TOS Flgs Pkts\n" \ 75"Port Msk AS Port Msk AS NextHop B/Pk Active\n" 76 77#define CISCO_SH_VERB_FLOW "%-14s %-15s %-14s %-15s %2u %3x %4x %6lu\n" \ 78 "%4.4x /%-2u %-5u %4.4x /%-2u %-5u %-15s %9u %8u\n\n" 79 80#define CISCO_SH_VERB_FLOW6_HEADER "SrcIf SrcIPaddress " \ 81"DstIf DstIPaddress Pr TOS Flgs Pkts\n" \ 82"Port Msk AS Port Msk AS NextHop B/Pk Active\n" 83 84#define CISCO_SH_VERB_FLOW6 "%-14s %-30s %-14s %-30s %2u %3x %4x %6lu\n" \ 85 "%4.4x /%-2u %-5u %4.4x /%-2u %-5u %-30s %9u %8u\n\n" 86#ifdef INET 87static void flow_cache_print(struct ngnf_show_header *resp); 88static void flow_cache_print_verbose(struct ngnf_show_header *resp); 89#endif 90#ifdef INET6 91static void flow_cache_print6(struct ngnf_show_header *resp); 92static void flow_cache_print6_verbose(struct ngnf_show_header *resp); 93#endif 94static void ctl_show(int, char **); 95#if defined(INET) || defined(INET6) 96static void do_show(int, void (*func)(struct ngnf_show_header *)); 97#endif 98static void help(void); 99static void execute_command(int, char **); 100 101struct ip_ctl_cmd { 102 char *cmd_name; 103 void (*cmd_func)(int argc, char **argv); 104}; 105 106struct ip_ctl_cmd cmds[] = { 107 {"show", ctl_show}, 108 {NULL, NULL}, 109}; 110 111int cs, human = 0; 112char *ng_path; 113 114int 115main(int argc, char **argv) 116{ 117 int c; 118 char sname[NG_NODESIZ]; 119 int rcvbuf = SORCVBUF_SIZE; 120 121 /* parse options */ 122 while ((c = getopt(argc, argv, "d:")) != -1) { 123 switch (c) { 124 case 'd': /* set libnetgraph debug level. */ 125 NgSetDebug(atoi(optarg)); 126 break; 127 } 128 } 129 130 argc -= optind; 131 argv += optind; 132 ng_path = argv[0]; 133 if (ng_path == NULL || (strlen(ng_path) > NG_PATHSIZ)) 134 help(); 135 argc--; 136 argv++; 137 138 /* create control socket. */ 139 snprintf(sname, sizeof(sname), "flowctl%i", getpid()); 140 141 if (NgMkSockNode(sname, &cs, NULL) == -1) 142 err(1, "NgMkSockNode"); 143 144 /* set receive buffer size */ 145 if (setsockopt(cs, SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(int)) == -1) 146 err(1, "setsockopt(SOL_SOCKET, SO_RCVBUF)"); 147 148 /* parse and execute command */ 149 execute_command(argc, argv); 150 151 close(cs); 152 153 exit(0); 154} 155 156static void 157execute_command(int argc, char **argv) 158{ 159 int cindex = -1; 160 int i; 161 162 if (!argc) 163 help(); 164 for (i = 0; cmds[i].cmd_name != NULL; i++) 165 if (!strncmp(argv[0], cmds[i].cmd_name, strlen(argv[0]))) { 166 if (cindex != -1) 167 errx(1, "ambiguous command: %s", argv[0]); 168 cindex = i; 169 } 170 if (cindex == -1) 171 errx(1, "bad command: %s", argv[0]); 172 argc--; 173 argv++; 174 (*cmds[cindex].cmd_func)(argc, argv); 175} 176 177static void 178ctl_show(int argc, char **argv) 179{ 180 int ipv4, ipv6, verbose = 0; 181 182 ipv4 = feature_present("inet"); 183 ipv6 = feature_present("inet6"); 184 185 if (argc > 0 && !strncmp(argv[0], "ipv4", 4)) { 186 ipv6 = 0; 187 argc--; 188 argv++; 189 } 190 if (argc > 0 && !strncmp(argv[0], "ipv6", 4)) { 191 ipv4 = 0; 192 argc--; 193 argv++; 194 } 195 196 if (argc > 0 && !strncmp(argv[0], "verbose", strlen(argv[0]))) 197 verbose = 1; 198 199 if (argc > 0 && !strncmp(argv[0], "human", strlen(argv[0]))) 200 human = 1; 201 202#ifdef INET 203 if (ipv4) { 204 if (verbose) 205 do_show(4, &flow_cache_print_verbose); 206 else 207 do_show(4, &flow_cache_print); 208 } 209#endif 210 211#ifdef INET6 212 if (ipv6) { 213 if (verbose) 214 do_show(6, &flow_cache_print6_verbose); 215 else 216 do_show(6, &flow_cache_print6); 217 } 218#endif 219} 220 221#if defined(INET) || defined(INET6) 222static void 223do_show(int version, void (*func)(struct ngnf_show_header *)) 224{ 225 struct ng_mesg ng_mesg[SORCVBUF_SIZE]; 226 struct ngnf_show_header req, *resp; 227 int token, nread; 228 229 req.version = version; 230 req.hash_id = req.list_id = 0; 231 232 for (;;) { 233 /* request set of accounting records */ 234 token = NgSendMsg(cs, ng_path, NGM_NETFLOW_COOKIE, 235 NGM_NETFLOW_SHOW, (void *)&req, sizeof(req)); 236 if (token == -1) 237 err(1, "NgSendMsg(NGM_NETFLOW_SHOW)"); 238 239 /* read reply */ 240 nread = NgRecvMsg(cs, ng_mesg, SORCVBUF_SIZE, NULL); 241 if (nread == -1) 242 err(1, "NgRecvMsg() failed"); 243 244 if (ng_mesg->header.token != token) 245 err(1, "NgRecvMsg(NGM_NETFLOW_SHOW): token mismatch"); 246 247 resp = (struct ngnf_show_header *)ng_mesg->data; 248 if ((ng_mesg->header.arglen < (sizeof(*resp))) || 249 (ng_mesg->header.arglen < (sizeof(*resp) + 250 (resp->nentries * sizeof(struct flow_entry_data))))) 251 err(1, "NgRecvMsg(NGM_NETFLOW_SHOW): arglen too small"); 252 253 (*func)(resp); 254 255 if (resp->hash_id != 0) 256 req.hash_id = resp->hash_id; 257 else 258 break; 259 req.list_id = resp->list_id; 260 } 261} 262#endif 263 264#ifdef INET 265static void 266flow_cache_print(struct ngnf_show_header *resp) 267{ 268 struct flow_entry_data *fle; 269 char src[INET_ADDRSTRLEN], dst[INET_ADDRSTRLEN]; 270 char src_if[IFNAMSIZ], dst_if[IFNAMSIZ]; 271 int i; 272 273 if (resp->version != 4) 274 errx(EX_SOFTWARE, "%s: version mismatch: %u", 275 __func__, resp->version); 276 277 if (resp->nentries > 0) 278 printf(human ? CISCO_SH_FLOW_HHEADER : CISCO_SH_FLOW_HEADER); 279 280 fle = (struct flow_entry_data *)(resp + 1); 281 for (i = 0; i < resp->nentries; i++, fle++) { 282 inet_ntop(AF_INET, &fle->r.r_src, src, sizeof(src)); 283 inet_ntop(AF_INET, &fle->r.r_dst, dst, sizeof(dst)); 284 printf(human ? CISCO_SH_FLOW_H : CISCO_SH_FLOW, 285 if_indextoname(fle->fle_i_ifx, src_if), 286 src, 287 if_indextoname(fle->fle_o_ifx, dst_if), 288 dst, 289 fle->r.r_ip_p, 290 ntohs(fle->r.r_sport), 291 ntohs(fle->r.r_dport), 292 fle->packets); 293 294 } 295} 296#endif 297 298#ifdef INET6 299static void 300flow_cache_print6(struct ngnf_show_header *resp) 301{ 302 struct flow6_entry_data *fle6; 303 char src6[INET6_ADDRSTRLEN], dst6[INET6_ADDRSTRLEN]; 304 char src_if[IFNAMSIZ], dst_if[IFNAMSIZ]; 305 int i; 306 307 if (resp->version != 6) 308 errx(EX_SOFTWARE, "%s: version mismatch: %u", 309 __func__, resp->version); 310 311 if (resp->nentries > 0) 312 printf(human ? CISCO_SH_FLOW6_HHEADER : CISCO_SH_FLOW6_HEADER); 313 314 fle6 = (struct flow6_entry_data *)(resp + 1); 315 for (i = 0; i < resp->nentries; i++, fle6++) { 316 inet_ntop(AF_INET6, &fle6->r.src.r_src6, src6, sizeof(src6)); 317 inet_ntop(AF_INET6, &fle6->r.dst.r_dst6, dst6, sizeof(dst6)); 318 printf(human ? CISCO_SH_FLOW6_H : CISCO_SH_FLOW6, 319 if_indextoname(fle6->fle_i_ifx, src_if), 320 src6, 321 if_indextoname(fle6->fle_o_ifx, dst_if), 322 dst6, 323 fle6->r.r_ip_p, 324 ntohs(fle6->r.r_sport), 325 ntohs(fle6->r.r_dport), 326 fle6->packets); 327 328 } 329} 330#endif 331 332#ifdef INET 333static void 334flow_cache_print_verbose(struct ngnf_show_header *resp) 335{ 336 struct flow_entry_data *fle; 337 char src[INET_ADDRSTRLEN], dst[INET_ADDRSTRLEN], next[INET_ADDRSTRLEN]; 338 char src_if[IFNAMSIZ], dst_if[IFNAMSIZ]; 339 int i; 340 341 if (resp->version != 4) 342 errx(EX_SOFTWARE, "%s: version mismatch: %u", 343 __func__, resp->version); 344 345 printf(CISCO_SH_VERB_FLOW_HEADER); 346 347 fle = (struct flow_entry_data *)(resp + 1); 348 for (i = 0; i < resp->nentries; i++, fle++) { 349 inet_ntop(AF_INET, &fle->r.r_src, src, sizeof(src)); 350 inet_ntop(AF_INET, &fle->r.r_dst, dst, sizeof(dst)); 351 inet_ntop(AF_INET, &fle->next_hop, next, sizeof(next)); 352 printf(CISCO_SH_VERB_FLOW, 353 if_indextoname(fle->fle_i_ifx, src_if), 354 src, 355 if_indextoname(fle->fle_o_ifx, dst_if), 356 dst, 357 fle->r.r_ip_p, 358 fle->r.r_tos, 359 fle->tcp_flags, 360 fle->packets, 361 ntohs(fle->r.r_sport), 362 fle->src_mask, 363 0, 364 ntohs(fle->r.r_dport), 365 fle->dst_mask, 366 0, 367 next, 368 (u_int)(fle->bytes / fle->packets), 369 0); 370 371 } 372} 373#endif 374 375#ifdef INET6 376static void 377flow_cache_print6_verbose(struct ngnf_show_header *resp) 378{ 379 struct flow6_entry_data *fle6; 380 char src6[INET6_ADDRSTRLEN], dst6[INET6_ADDRSTRLEN], next6[INET6_ADDRSTRLEN]; 381 char src_if[IFNAMSIZ], dst_if[IFNAMSIZ]; 382 int i; 383 384 if (resp->version != 6) 385 errx(EX_SOFTWARE, "%s: version mismatch: %u", 386 __func__, resp->version); 387 388 printf(CISCO_SH_VERB_FLOW6_HEADER); 389 390 fle6 = (struct flow6_entry_data *)(resp + 1); 391 for (i = 0; i < resp->nentries; i++, fle6++) { 392 inet_ntop(AF_INET6, &fle6->r.src.r_src6, src6, sizeof(src6)); 393 inet_ntop(AF_INET6, &fle6->r.dst.r_dst6, dst6, sizeof(dst6)); 394 inet_ntop(AF_INET6, &fle6->n.next_hop6, next6, sizeof(next6)); 395 printf(CISCO_SH_VERB_FLOW6, 396 if_indextoname(fle6->fle_i_ifx, src_if), 397 src6, 398 if_indextoname(fle6->fle_o_ifx, dst_if), 399 dst6, 400 fle6->r.r_ip_p, 401 fle6->r.r_tos, 402 fle6->tcp_flags, 403 fle6->packets, 404 ntohs(fle6->r.r_sport), 405 fle6->src_mask, 406 0, 407 ntohs(fle6->r.r_dport), 408 fle6->dst_mask, 409 0, 410 next6, 411 (u_int)(fle6->bytes / fle6->packets), 412 0); 413 } 414} 415#endif 416 417static void 418help(void) 419{ 420 extern char *__progname; 421 422 fprintf(stderr, "usage: %s [-d level] nodename command\n", __progname); 423 exit (0); 424} 425