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 <unistd.h> 50 51 #include <netgraph.h> 52 #include <netgraph/netflow/ng_netflow.h> 53 54 #define CISCO_SH_FLOW_HEADER "SrcIf SrcIPaddress DstIf DstIPaddress Pr SrcP DstP Pkts\n" 55 #define CISCO_SH_FLOW "%-13s %-15s %-13s %-15s %2u %4.4x %4.4x %6lu\n" 56 57 #define CISCO_SH_VERB_FLOW_HEADER "SrcIf SrcIPaddress DstIf DstIPaddress Pr TOS Flgs Pkts\n" \ 58 "Port Msk AS Port Msk AS NextHop B/Pk Active\n" 59 60 #define CISCO_SH_VERB_FLOW "%-14s %-15s %-14s %-15s %2u %3x %4x %6lu\n" \ 61 "%4.4x /%-2u %-5u %4.4x /%-2u %-5u %-15s %9u %8u\n\n" 62 63 static int flow_cache_print(struct ngnf_flows *recs); 64 static int flow_cache_print_verbose(struct ngnf_flows *recs); 65 static int ctl_show(int, char **); 66 static void help(void); 67 static void execute_command(int, char **); 68 69 struct ip_ctl_cmd { 70 char *cmd_name; 71 int (*cmd_func)(int argc, char **argv); 72 }; 73 74 struct ip_ctl_cmd cmds[] = { 75 {"show", ctl_show}, 76 {NULL, NULL}, 77 }; 78 79 int cs; 80 char ng_nodename[NG_PATHLEN + 1]; 81 82 int 83 main(int argc, char **argv) 84 { 85 int c; 86 char sname[NG_NODESIZ]; 87 int rcvbuf = SORCVBUF_SIZE; 88 char *ng_name; 89 90 /* parse options */ 91 while ((c = getopt(argc, argv, "d:")) != -1) { 92 switch (c) { 93 case 'd': /* set libnetgraph debug level. */ 94 NgSetDebug(atoi(optarg)); 95 break; 96 } 97 } 98 99 argc -= optind; 100 argv += optind; 101 ng_name = argv[0]; 102 if (ng_name == NULL) 103 help(); 104 argc--; 105 argv++; 106 107 snprintf(ng_nodename, sizeof(ng_nodename), "%s:", ng_name); 108 109 /* create control socket. */ 110 snprintf(sname, sizeof(sname), "flowctl%i", getpid()); 111 112 if (NgMkSockNode(sname, &cs, NULL) == -1) 113 err(1, "NgMkSockNode"); 114 115 /* set receive buffer size */ 116 if (setsockopt(cs, SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(int)) == -1) 117 err(1, "setsockopt(SOL_SOCKET, SO_RCVBUF)"); 118 119 /* parse and execute command */ 120 execute_command(argc, argv); 121 122 close(cs); 123 124 exit(0); 125 } 126 127 static void 128 execute_command(int argc, char **argv) 129 { 130 int cindex = -1; 131 int i; 132 133 if (!argc) 134 help(); 135 for (i = 0; cmds[i].cmd_name != NULL; i++) 136 if (!strncmp(argv[0], cmds[i].cmd_name, strlen(argv[0]))) { 137 if (cindex != -1) 138 errx(1, "ambiguous command: %s", argv[0]); 139 cindex = i; 140 } 141 if (cindex == -1) 142 errx(1, "bad command: %s", argv[0]); 143 argc--; 144 argv++; 145 (*cmds[cindex].cmd_func)(argc, argv); 146 } 147 148 static int 149 ctl_show(int argc, char **argv) 150 { 151 struct ng_mesg *ng_mesg; 152 struct ngnf_flows *data; 153 char path[NG_PATHLEN + 1]; 154 int token, nread, last = 0; 155 int verbose = 0; 156 157 if (argc > 0 && !strncmp(argv[0], "verbose", strlen(argv[0]))) 158 verbose = 1; 159 160 ng_mesg = alloca(SORCVBUF_SIZE); 161 162 if (verbose) 163 printf(CISCO_SH_VERB_FLOW_HEADER); 164 else 165 printf(CISCO_SH_FLOW_HEADER); 166 167 for (;;) { 168 /* request set of accounting records */ 169 token = NgSendMsg(cs, ng_nodename, NGM_NETFLOW_COOKIE, 170 NGM_NETFLOW_SHOW, (void *)&last, sizeof(last)); 171 if (token == -1) 172 err(1, "NgSendMsg(NGM_NETFLOW_SHOW)"); 173 174 /* read reply */ 175 nread = NgRecvMsg(cs, ng_mesg, SORCVBUF_SIZE, path); 176 if (nread == -1) 177 err(1, "NgRecvMsg() failed"); 178 179 if (ng_mesg->header.token != token) 180 err(1, "NgRecvMsg(NGM_NETFLOW_SHOW): token mismatch"); 181 182 data = (struct ngnf_flows*)ng_mesg->data; 183 if ((ng_mesg->header.arglen < (sizeof(*data))) || 184 (ng_mesg->header.arglen < (sizeof(*data) + 185 (data->nentries * sizeof(struct flow_entry_data))))) 186 err(1, "NgRecvMsg(NGM_NETFLOW_SHOW): arglen too small"); 187 188 if (verbose) 189 (void )flow_cache_print_verbose(data); 190 else 191 (void )flow_cache_print(data); 192 193 if (data->last != 0) 194 last = data->last; 195 else 196 break; 197 } 198 199 return (0); 200 } 201 202 static int 203 flow_cache_print(struct ngnf_flows *recs) 204 { 205 struct flow_entry_data *fle; 206 char src[INET_ADDRSTRLEN], dst[INET_ADDRSTRLEN]; 207 char src_if[IFNAMSIZ], dst_if[IFNAMSIZ]; 208 int i; 209 210 /* quick check */ 211 if (recs->nentries == 0) 212 return (0); 213 214 fle = recs->entries; 215 for (i = 0; i < recs->nentries; i++, fle++) { 216 inet_ntop(AF_INET, &fle->r.r_src, src, sizeof(src)); 217 inet_ntop(AF_INET, &fle->r.r_dst, dst, sizeof(dst)); 218 printf(CISCO_SH_FLOW, 219 if_indextoname(fle->fle_i_ifx, src_if), 220 src, 221 if_indextoname(fle->fle_o_ifx, dst_if), 222 dst, 223 fle->r.r_ip_p, 224 ntohs(fle->r.r_sport), 225 ntohs(fle->r.r_dport), 226 fle->packets); 227 228 } 229 230 return (i); 231 } 232 233 static int 234 flow_cache_print_verbose(struct ngnf_flows *recs) 235 { 236 struct flow_entry_data *fle; 237 char src[INET_ADDRSTRLEN], dst[INET_ADDRSTRLEN], next[INET_ADDRSTRLEN]; 238 char src_if[IFNAMSIZ], dst_if[IFNAMSIZ]; 239 int i; 240 241 /* quick check */ 242 if (recs->nentries == 0) 243 return (0); 244 245 fle = recs->entries; 246 for (i = 0; i < recs->nentries; i++, fle++) { 247 inet_ntop(AF_INET, &fle->r.r_src, src, sizeof(src)); 248 inet_ntop(AF_INET, &fle->r.r_dst, dst, sizeof(dst)); 249 inet_ntop(AF_INET, &fle->next_hop, next, sizeof(next)); 250 printf(CISCO_SH_VERB_FLOW, 251 if_indextoname(fle->fle_i_ifx, src_if), 252 src, 253 if_indextoname(fle->fle_o_ifx, dst_if), 254 dst, 255 fle->r.r_ip_p, 256 fle->r.r_tos, 257 fle->tcp_flags, 258 fle->packets, 259 ntohs(fle->r.r_sport), 260 fle->src_mask, 261 0, 262 ntohs(fle->r.r_dport), 263 fle->dst_mask, 264 0, 265 next, 266 (u_int)(fle->bytes / fle->packets), 267 0); 268 269 } 270 271 return (i); 272 } 273 274 static void 275 help(void) 276 { 277 extern char *__progname; 278 279 fprintf(stderr, "usage: %s [-d level] nodename command\n", __progname); 280 exit (0); 281 } 282