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 31 static 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 " \ 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 87 static void flow_cache_print(struct ngnf_show_header *resp); 88 static void flow_cache_print_verbose(struct ngnf_show_header *resp); 89 #endif 90 #ifdef INET6 91 static void flow_cache_print6(struct ngnf_show_header *resp); 92 static void flow_cache_print6_verbose(struct ngnf_show_header *resp); 93 #endif 94 static void ctl_show(int, char **); 95 #if defined(INET) || defined(INET6) 96 static void do_show(int, void (*func)(struct ngnf_show_header *)); 97 #endif 98 static void help(void); 99 static void execute_command(int, char **); 100 101 struct ip_ctl_cmd { 102 char *cmd_name; 103 void (*cmd_func)(int argc, char **argv); 104 }; 105 106 struct ip_ctl_cmd cmds[] = { 107 {"show", ctl_show}, 108 {NULL, NULL}, 109 }; 110 111 int cs, human = 0; 112 char *ng_path; 113 114 int 115 main(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 156 static void 157 execute_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 177 static void 178 ctl_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) 222 static void 223 do_show(int version, void (*func)(struct ngnf_show_header *)) 224 { 225 char buf[SORCVBUF_SIZE]; 226 struct ng_mesg *ng_mesg; 227 struct ngnf_show_header req, *resp; 228 int token, nread; 229 230 ng_mesg = (struct ng_mesg *)buf; 231 req.version = version; 232 req.hash_id = req.list_id = 0; 233 234 for (;;) { 235 /* request set of accounting records */ 236 token = NgSendMsg(cs, ng_path, NGM_NETFLOW_COOKIE, 237 NGM_NETFLOW_SHOW, (void *)&req, sizeof(req)); 238 if (token == -1) 239 err(1, "NgSendMsg(NGM_NETFLOW_SHOW)"); 240 241 /* read reply */ 242 nread = NgRecvMsg(cs, ng_mesg, SORCVBUF_SIZE, NULL); 243 if (nread == -1) 244 err(1, "NgRecvMsg() failed"); 245 246 if (ng_mesg->header.token != token) 247 err(1, "NgRecvMsg(NGM_NETFLOW_SHOW): token mismatch"); 248 249 resp = (struct ngnf_show_header *)ng_mesg->data; 250 if ((ng_mesg->header.arglen < (sizeof(*resp))) || 251 (ng_mesg->header.arglen < (sizeof(*resp) + 252 (resp->nentries * sizeof(struct flow_entry_data))))) 253 err(1, "NgRecvMsg(NGM_NETFLOW_SHOW): arglen too small"); 254 255 (*func)(resp); 256 257 if (resp->hash_id != 0) 258 req.hash_id = resp->hash_id; 259 else 260 break; 261 req.list_id = resp->list_id; 262 } 263 } 264 #endif 265 266 #ifdef INET 267 static void 268 flow_cache_print(struct ngnf_show_header *resp) 269 { 270 struct flow_entry_data *fle; 271 char src[INET_ADDRSTRLEN], dst[INET_ADDRSTRLEN]; 272 char src_if[IFNAMSIZ], dst_if[IFNAMSIZ]; 273 int i; 274 275 if (resp->version != 4) 276 errx(EX_SOFTWARE, "%s: version mismatch: %u", 277 __func__, resp->version); 278 279 if (resp->nentries > 0) 280 printf(human ? CISCO_SH_FLOW_HHEADER : CISCO_SH_FLOW_HEADER); 281 282 fle = (struct flow_entry_data *)(resp + 1); 283 for (i = 0; i < resp->nentries; i++, fle++) { 284 inet_ntop(AF_INET, &fle->r.r_src, src, sizeof(src)); 285 inet_ntop(AF_INET, &fle->r.r_dst, dst, sizeof(dst)); 286 printf(human ? CISCO_SH_FLOW_H : CISCO_SH_FLOW, 287 if_indextoname(fle->fle_i_ifx, src_if), 288 src, 289 if_indextoname(fle->fle_o_ifx, dst_if), 290 dst, 291 fle->r.r_ip_p, 292 ntohs(fle->r.r_sport), 293 ntohs(fle->r.r_dport), 294 fle->packets); 295 296 } 297 } 298 #endif 299 300 #ifdef INET6 301 static void 302 flow_cache_print6(struct ngnf_show_header *resp) 303 { 304 struct flow6_entry_data *fle6; 305 char src6[INET6_ADDRSTRLEN], dst6[INET6_ADDRSTRLEN]; 306 char src_if[IFNAMSIZ], dst_if[IFNAMSIZ]; 307 int i; 308 309 if (resp->version != 6) 310 errx(EX_SOFTWARE, "%s: version mismatch: %u", 311 __func__, resp->version); 312 313 if (resp->nentries > 0) 314 printf(human ? CISCO_SH_FLOW6_HHEADER : CISCO_SH_FLOW6_HEADER); 315 316 fle6 = (struct flow6_entry_data *)(resp + 1); 317 for (i = 0; i < resp->nentries; i++, fle6++) { 318 inet_ntop(AF_INET6, &fle6->r.src.r_src6, src6, sizeof(src6)); 319 inet_ntop(AF_INET6, &fle6->r.dst.r_dst6, dst6, sizeof(dst6)); 320 printf(human ? CISCO_SH_FLOW6_H : CISCO_SH_FLOW6, 321 if_indextoname(fle6->fle_i_ifx, src_if), 322 src6, 323 if_indextoname(fle6->fle_o_ifx, dst_if), 324 dst6, 325 fle6->r.r_ip_p, 326 ntohs(fle6->r.r_sport), 327 ntohs(fle6->r.r_dport), 328 fle6->packets); 329 330 } 331 } 332 #endif 333 334 #ifdef INET 335 static void 336 flow_cache_print_verbose(struct ngnf_show_header *resp) 337 { 338 struct flow_entry_data *fle; 339 char src[INET_ADDRSTRLEN], dst[INET_ADDRSTRLEN], next[INET_ADDRSTRLEN]; 340 char src_if[IFNAMSIZ], dst_if[IFNAMSIZ]; 341 int i; 342 343 if (resp->version != 4) 344 errx(EX_SOFTWARE, "%s: version mismatch: %u", 345 __func__, resp->version); 346 347 printf(CISCO_SH_VERB_FLOW_HEADER); 348 349 fle = (struct flow_entry_data *)(resp + 1); 350 for (i = 0; i < resp->nentries; i++, fle++) { 351 inet_ntop(AF_INET, &fle->r.r_src, src, sizeof(src)); 352 inet_ntop(AF_INET, &fle->r.r_dst, dst, sizeof(dst)); 353 inet_ntop(AF_INET, &fle->next_hop, next, sizeof(next)); 354 printf(CISCO_SH_VERB_FLOW, 355 if_indextoname(fle->fle_i_ifx, src_if), 356 src, 357 if_indextoname(fle->fle_o_ifx, dst_if), 358 dst, 359 fle->r.r_ip_p, 360 fle->r.r_tos, 361 fle->tcp_flags, 362 fle->packets, 363 ntohs(fle->r.r_sport), 364 fle->src_mask, 365 0, 366 ntohs(fle->r.r_dport), 367 fle->dst_mask, 368 0, 369 next, 370 (u_int)(fle->bytes / fle->packets), 371 0); 372 373 } 374 } 375 #endif 376 377 #ifdef INET6 378 static void 379 flow_cache_print6_verbose(struct ngnf_show_header *resp) 380 { 381 struct flow6_entry_data *fle6; 382 char src6[INET6_ADDRSTRLEN], dst6[INET6_ADDRSTRLEN], next6[INET6_ADDRSTRLEN]; 383 char src_if[IFNAMSIZ], dst_if[IFNAMSIZ]; 384 int i; 385 386 if (resp->version != 6) 387 errx(EX_SOFTWARE, "%s: version mismatch: %u", 388 __func__, resp->version); 389 390 printf(CISCO_SH_VERB_FLOW6_HEADER); 391 392 fle6 = (struct flow6_entry_data *)(resp + 1); 393 for (i = 0; i < resp->nentries; i++, fle6++) { 394 inet_ntop(AF_INET6, &fle6->r.src.r_src6, src6, sizeof(src6)); 395 inet_ntop(AF_INET6, &fle6->r.dst.r_dst6, dst6, sizeof(dst6)); 396 inet_ntop(AF_INET6, &fle6->n.next_hop6, next6, sizeof(next6)); 397 printf(CISCO_SH_VERB_FLOW6, 398 if_indextoname(fle6->fle_i_ifx, src_if), 399 src6, 400 if_indextoname(fle6->fle_o_ifx, dst_if), 401 dst6, 402 fle6->r.r_ip_p, 403 fle6->r.r_tos, 404 fle6->tcp_flags, 405 fle6->packets, 406 ntohs(fle6->r.r_sport), 407 fle6->src_mask, 408 0, 409 ntohs(fle6->r.r_dport), 410 fle6->dst_mask, 411 0, 412 next6, 413 (u_int)(fle6->bytes / fle6->packets), 414 0); 415 } 416 } 417 #endif 418 419 static void 420 help(void) 421 { 422 extern char *__progname; 423 424 fprintf(stderr, "usage: %s [-d level] nodename command\n", __progname); 425 exit (0); 426 } 427