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$"; 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 DstIf DstIPaddress Pr SrcP DstP Pkts\n" 56#define CISCO_SH_FLOW "%-13s %-15s %-13s %-15s %2u %4.4x %4.4x %6lu\n" 57 58#define CISCO_SH_FLOW6_HEADER "SrcIf SrcIPaddress DstIf DstIPaddress Pr SrcP DstP Pkts\n" 59#define CISCO_SH_FLOW6 "%-13s %-30s %-13s %-30s %2u %4.4x %4.4x %6lu\n" 60 61#define CISCO_SH_VERB_FLOW_HEADER "SrcIf SrcIPaddress DstIf DstIPaddress Pr TOS Flgs Pkts\n" \ 62"Port Msk AS Port Msk AS NextHop B/Pk Active\n" 63 64#define CISCO_SH_VERB_FLOW "%-14s %-15s %-14s %-15s %2u %3x %4x %6lu\n" \ 65 "%4.4x /%-2u %-5u %4.4x /%-2u %-5u %-15s %9u %8u\n\n" 66 67#define CISCO_SH_VERB_FLOW6_HEADER "SrcIf SrcIPaddress DstIf DstIPaddress Pr TOS Flgs Pkts\n" \ 68"Port Msk AS Port Msk AS NextHop B/Pk Active\n" 69 70#define CISCO_SH_VERB_FLOW6 "%-14s %-30s %-14s %-30s %2u %3x %4x %6lu\n" \ 71 "%4.4x /%-2u %-5u %4.4x /%-2u %-5u %-30s %9u %8u\n\n" 72#ifdef INET 73static void flow_cache_print(struct ngnf_show_header *resp); 74static void flow_cache_print_verbose(struct ngnf_show_header *resp); 75#endif 76#ifdef INET6 77static void flow_cache_print6(struct ngnf_show_header *resp); 78static void flow_cache_print6_verbose(struct ngnf_show_header *resp); 79#endif 80static void ctl_show(int, char **); 81#if defined(INET) || defined(INET6) 82static void do_show(int, void (*func)(struct ngnf_show_header *)); 83#endif 84static void help(void); 85static void execute_command(int, char **); 86 87struct ip_ctl_cmd { 88 char *cmd_name; 89 void (*cmd_func)(int argc, char **argv); 90}; 91 92struct ip_ctl_cmd cmds[] = { 93 {"show", ctl_show}, 94 {NULL, NULL}, 95}; 96 97int cs; 98char *ng_path; 99 100int 101main(int argc, char **argv) 102{ 103 int c; 104 char sname[NG_NODESIZ]; 105 int rcvbuf = SORCVBUF_SIZE; 106 107 /* parse options */ 108 while ((c = getopt(argc, argv, "d:")) != -1) { 109 switch (c) { 110 case 'd': /* set libnetgraph debug level. */ 111 NgSetDebug(atoi(optarg)); 112 break; 113 } 114 } 115 116 argc -= optind; 117 argv += optind; 118 ng_path = argv[0]; 119 if (ng_path == NULL || (strlen(ng_path) > NG_PATHSIZ)) 120 help(); 121 argc--; 122 argv++; 123 124 /* create control socket. */ 125 snprintf(sname, sizeof(sname), "flowctl%i", getpid()); 126 127 if (NgMkSockNode(sname, &cs, NULL) == -1) 128 err(1, "NgMkSockNode"); 129 130 /* set receive buffer size */ 131 if (setsockopt(cs, SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(int)) == -1) 132 err(1, "setsockopt(SOL_SOCKET, SO_RCVBUF)"); 133 134 /* parse and execute command */ 135 execute_command(argc, argv); 136 137 close(cs); 138 139 exit(0); 140} 141 142static void 143execute_command(int argc, char **argv) 144{ 145 int cindex = -1; 146 int i; 147 148 if (!argc) 149 help(); 150 for (i = 0; cmds[i].cmd_name != NULL; i++) 151 if (!strncmp(argv[0], cmds[i].cmd_name, strlen(argv[0]))) { 152 if (cindex != -1) 153 errx(1, "ambiguous command: %s", argv[0]); 154 cindex = i; 155 } 156 if (cindex == -1) 157 errx(1, "bad command: %s", argv[0]); 158 argc--; 159 argv++; 160 (*cmds[cindex].cmd_func)(argc, argv); 161} 162 163static void 164ctl_show(int argc, char **argv) 165{ 166 int ipv4, ipv6, verbose = 0; 167 168 ipv4 = feature_present("inet"); 169 ipv6 = feature_present("inet6"); 170 171 if (argc > 0 && !strncmp(argv[0], "ipv4", 4)) { 172 ipv6 = 0; 173 argc--; 174 argv++; 175 } 176 if (argc > 0 && !strncmp(argv[0], "ipv6", 4)) { 177 ipv4 = 0; 178 argc--; 179 argv++; 180 } 181 182 if (argc > 0 && !strncmp(argv[0], "verbose", strlen(argv[0]))) 183 verbose = 1; 184 185#ifdef INET 186 if (ipv4) { 187 if (verbose) 188 do_show(4, &flow_cache_print_verbose); 189 else 190 do_show(4, &flow_cache_print); 191 } 192#endif 193 194#ifdef INET6 195 if (ipv6) { 196 if (verbose) 197 do_show(6, &flow_cache_print6_verbose); 198 else 199 do_show(6, &flow_cache_print6); 200 } 201#endif 202} 203 204#if defined(INET) || defined(INET6) 205static void 206do_show(int version, void (*func)(struct ngnf_show_header *)) 207{ 208 struct ng_mesg *ng_mesg; 209 struct ngnf_show_header req, *resp; 210 int token, nread; 211 212 ng_mesg = alloca(SORCVBUF_SIZE); 213 214 req.version = version; 215 req.hash_id = req.list_id = 0; 216 217 for (;;) { 218 /* request set of accounting records */ 219 token = NgSendMsg(cs, ng_path, NGM_NETFLOW_COOKIE, 220 NGM_NETFLOW_SHOW, (void *)&req, sizeof(req)); 221 if (token == -1) 222 err(1, "NgSendMsg(NGM_NETFLOW_SHOW)"); 223 224 /* read reply */ 225 nread = NgRecvMsg(cs, ng_mesg, SORCVBUF_SIZE, NULL); 226 if (nread == -1) 227 err(1, "NgRecvMsg() failed"); 228 229 if (ng_mesg->header.token != token) 230 err(1, "NgRecvMsg(NGM_NETFLOW_SHOW): token mismatch"); 231 232 resp = (struct ngnf_show_header *)ng_mesg->data; 233 if ((ng_mesg->header.arglen < (sizeof(*resp))) || 234 (ng_mesg->header.arglen < (sizeof(*resp) + 235 (resp->nentries * sizeof(struct flow_entry_data))))) 236 err(1, "NgRecvMsg(NGM_NETFLOW_SHOW): arglen too small"); 237 238 (*func)(resp); 239 240 if (resp->hash_id != 0) 241 req.hash_id = resp->hash_id; 242 else 243 break; 244 req.list_id = resp->list_id; 245 } 246} 247#endif 248 249#ifdef INET 250static void 251flow_cache_print(struct ngnf_show_header *resp) 252{ 253 struct flow_entry_data *fle; 254 char src[INET_ADDRSTRLEN], dst[INET_ADDRSTRLEN]; 255 char src_if[IFNAMSIZ], dst_if[IFNAMSIZ]; 256 int i; 257 258 if (resp->version != 4) 259 errx(EX_SOFTWARE, "%s: version mismatch: %u", 260 __func__, resp->version); 261 262 printf(CISCO_SH_FLOW_HEADER); 263 264 fle = (struct flow_entry_data *)(resp + 1); 265 for (i = 0; i < resp->nentries; i++, fle++) { 266 inet_ntop(AF_INET, &fle->r.r_src, src, sizeof(src)); 267 inet_ntop(AF_INET, &fle->r.r_dst, dst, sizeof(dst)); 268 printf(CISCO_SH_FLOW, 269 if_indextoname(fle->fle_i_ifx, src_if), 270 src, 271 if_indextoname(fle->fle_o_ifx, dst_if), 272 dst, 273 fle->r.r_ip_p, 274 ntohs(fle->r.r_sport), 275 ntohs(fle->r.r_dport), 276 fle->packets); 277 278 } 279} 280#endif 281 282#ifdef INET6 283static void 284flow_cache_print6(struct ngnf_show_header *resp) 285{ 286 struct flow6_entry_data *fle6; 287 char src6[INET6_ADDRSTRLEN], dst6[INET6_ADDRSTRLEN]; 288 char src_if[IFNAMSIZ], dst_if[IFNAMSIZ]; 289 int i; 290 291 if (resp->version != 6) 292 errx(EX_SOFTWARE, "%s: version mismatch: %u", 293 __func__, resp->version); 294 295 printf(CISCO_SH_FLOW6_HEADER); 296 297 fle6 = (struct flow6_entry_data *)(resp + 1); 298 for (i = 0; i < resp->nentries; i++, fle6++) { 299 inet_ntop(AF_INET6, &fle6->r.src.r_src6, src6, sizeof(src6)); 300 inet_ntop(AF_INET6, &fle6->r.dst.r_dst6, dst6, sizeof(dst6)); 301 printf(CISCO_SH_FLOW6, 302 if_indextoname(fle6->fle_i_ifx, src_if), 303 src6, 304 if_indextoname(fle6->fle_o_ifx, dst_if), 305 dst6, 306 fle6->r.r_ip_p, 307 ntohs(fle6->r.r_sport), 308 ntohs(fle6->r.r_dport), 309 fle6->packets); 310 311 } 312} 313#endif 314 315#ifdef INET 316static void 317flow_cache_print_verbose(struct ngnf_show_header *resp) 318{ 319 struct flow_entry_data *fle; 320 char src[INET_ADDRSTRLEN], dst[INET_ADDRSTRLEN], next[INET_ADDRSTRLEN]; 321 char src_if[IFNAMSIZ], dst_if[IFNAMSIZ]; 322 int i; 323 324 if (resp->version != 4) 325 errx(EX_SOFTWARE, "%s: version mismatch: %u", 326 __func__, resp->version); 327 328 printf(CISCO_SH_VERB_FLOW_HEADER); 329 330 fle = (struct flow_entry_data *)(resp + 1); 331 for (i = 0; i < resp->nentries; i++, fle++) { 332 inet_ntop(AF_INET, &fle->r.r_src, src, sizeof(src)); 333 inet_ntop(AF_INET, &fle->r.r_dst, dst, sizeof(dst)); 334 inet_ntop(AF_INET, &fle->next_hop, next, sizeof(next)); 335 printf(CISCO_SH_VERB_FLOW, 336 if_indextoname(fle->fle_i_ifx, src_if), 337 src, 338 if_indextoname(fle->fle_o_ifx, dst_if), 339 dst, 340 fle->r.r_ip_p, 341 fle->r.r_tos, 342 fle->tcp_flags, 343 fle->packets, 344 ntohs(fle->r.r_sport), 345 fle->src_mask, 346 0, 347 ntohs(fle->r.r_dport), 348 fle->dst_mask, 349 0, 350 next, 351 (u_int)(fle->bytes / fle->packets), 352 0); 353 354 } 355} 356#endif 357 358#ifdef INET6 359static void 360flow_cache_print6_verbose(struct ngnf_show_header *resp) 361{ 362 struct flow6_entry_data *fle6; 363 char src6[INET6_ADDRSTRLEN], dst6[INET6_ADDRSTRLEN], next6[INET6_ADDRSTRLEN]; 364 char src_if[IFNAMSIZ], dst_if[IFNAMSIZ]; 365 int i; 366 367 if (resp->version != 6) 368 errx(EX_SOFTWARE, "%s: version mismatch: %u", 369 __func__, resp->version); 370 371 printf(CISCO_SH_VERB_FLOW6_HEADER); 372 373 fle6 = (struct flow6_entry_data *)(resp + 1); 374 for (i = 0; i < resp->nentries; i++, fle6++) { 375 inet_ntop(AF_INET6, &fle6->r.src.r_src6, src6, sizeof(src6)); 376 inet_ntop(AF_INET6, &fle6->r.dst.r_dst6, dst6, sizeof(dst6)); 377 inet_ntop(AF_INET6, &fle6->n.next_hop6, next6, sizeof(next6)); 378 printf(CISCO_SH_VERB_FLOW6, 379 if_indextoname(fle6->fle_i_ifx, src_if), 380 src6, 381 if_indextoname(fle6->fle_o_ifx, dst_if), 382 dst6, 383 fle6->r.r_ip_p, 384 fle6->r.r_tos, 385 fle6->tcp_flags, 386 fle6->packets, 387 ntohs(fle6->r.r_sport), 388 fle6->src_mask, 389 0, 390 ntohs(fle6->r.r_dport), 391 fle6->dst_mask, 392 0, 393 next6, 394 (u_int)(fle6->bytes / fle6->packets), 395 0); 396 } 397} 398#endif 399 400static void 401help(void) 402{ 403 extern char *__progname; 404 405 fprintf(stderr, "usage: %s [-d level] nodename command\n", __progname); 406 exit (0); 407} 408