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 int main(int, char **); 58 59 static int flow_cache_print(struct ngnf_flows *recs); 60 static int ctl_show(int, char **); 61 static void help(void); 62 static void execute_command(int, char **); 63 64 struct ip_ctl_cmd { 65 char *cmd_name; 66 int (*cmd_func)(int argc, char **argv); 67 }; 68 69 struct ip_ctl_cmd cmds[] = { 70 {"show", ctl_show}, 71 {NULL, NULL}, 72 }; 73 74 int cs; 75 char ng_nodename[NG_PATHLEN + 1]; 76 77 int 78 main(int argc, char **argv) 79 { 80 int c; 81 char sname[NG_NODESIZ]; 82 int rcvbuf = SORCVBUF_SIZE; 83 char *ng_name; 84 85 /* parse options */ 86 while ((c = getopt(argc, argv, "d:")) != -1) { 87 switch (c) { 88 case 'd': /* set libnetgraph debug level. */ 89 NgSetDebug(atoi(optarg)); 90 break; 91 } 92 } 93 94 argc -= optind; 95 argv += optind; 96 ng_name = argv[0]; 97 if (ng_name == NULL) 98 help(); 99 argc--; 100 argv++; 101 102 snprintf(ng_nodename, sizeof(ng_nodename), "%s:", ng_name); 103 104 /* create control socket. */ 105 snprintf(sname, sizeof(sname), "flowctl%i", getpid()); 106 107 if (NgMkSockNode(sname, &cs, NULL) == -1) 108 err(1, "NgMkSockNode"); 109 110 /* set receive buffer size */ 111 if (setsockopt(cs, SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(int)) == -1) 112 err(1, "setsockopt(SOL_SOCKET, SO_RCVBUF)"); 113 114 /* parse and execute command */ 115 execute_command(argc, argv); 116 117 close(cs); 118 119 exit(0); 120 } 121 122 static void 123 execute_command(int argc, char **argv) 124 { 125 int cindex = -1; 126 int i; 127 128 if (!argc) 129 help(); 130 for (i = 0; cmds[i].cmd_name != NULL; i++) 131 if (!strncmp(argv[0], cmds[i].cmd_name, strlen(argv[0]))) { 132 if (cindex != -1) 133 errx(1, "ambiguous command: %s", argv[0]); 134 cindex = i; 135 } 136 if (cindex == -1) 137 errx(1, "bad command: %s", argv[0]); 138 argc--; 139 argv++; 140 (*cmds[cindex].cmd_func)(argc, argv); 141 } 142 143 static int 144 ctl_show(int argc, char **argv) 145 { 146 struct ng_mesg *ng_mesg; 147 struct ngnf_flows *data; 148 char path[NG_PATHLEN + 1]; 149 int token, nread, last = 0; 150 151 ng_mesg = alloca(SORCVBUF_SIZE); 152 153 printf(CISCO_SH_FLOW_HEADER); 154 155 for (;;) { 156 /* request set of accounting records */ 157 token = NgSendMsg(cs, ng_nodename, NGM_NETFLOW_COOKIE, 158 NGM_NETFLOW_SHOW, (void *)&last, sizeof(last)); 159 if (token == -1) 160 err(1, "NgSendMsg(NGM_NETFLOW_SHOW)"); 161 162 /* read reply */ 163 nread = NgRecvMsg(cs, ng_mesg, SORCVBUF_SIZE, path); 164 if (nread == -1) 165 err(1, "NgRecvMsg() failed"); 166 167 if (ng_mesg->header.token != token) 168 err(1, "NgRecvMsg(NGM_NETFLOW_SHOW): token mismatch"); 169 170 data = (struct ngnf_flows*)ng_mesg->data; 171 if ((ng_mesg->header.arglen < (sizeof(*data))) || 172 (ng_mesg->header.arglen < (sizeof(*data) + 173 (data->nentries * sizeof(struct flow_entry_data))))) 174 err(1, "NgRecvMsg(NGM_NETFLOW_SHOW): arglen too small"); 175 176 (void )flow_cache_print(data); 177 178 if (data->last != 0) 179 last = data->last; 180 else 181 break; 182 } 183 184 return (0); 185 } 186 187 static int 188 flow_cache_print(struct ngnf_flows *recs) 189 { 190 struct flow_entry_data *fle; 191 char src[INET_ADDRSTRLEN], dst[INET_ADDRSTRLEN]; 192 char src_if[IFNAMSIZ], dst_if[IFNAMSIZ]; 193 int i; 194 195 /* quick check */ 196 if (recs->nentries == 0) 197 return (0); 198 199 fle = recs->entries; 200 for (i = 0; i < recs->nentries; i++, fle++) { 201 inet_ntop(AF_INET, &fle->r.r_src, src, sizeof(src)); 202 inet_ntop(AF_INET, &fle->r.r_dst, dst, sizeof(dst)); 203 printf(CISCO_SH_FLOW, 204 if_indextoname(fle->fle_i_ifx, src_if), 205 src, 206 if_indextoname(fle->fle_o_ifx, dst_if), 207 dst, 208 fle->r.r_ip_p, 209 ntohs(fle->r.r_sport), 210 ntohs(fle->r.r_dport), 211 fle->packets); 212 213 } 214 215 return (i); 216 } 217 218 static void 219 help(void) 220 { 221 extern char *__progname; 222 223 fprintf(stderr, "usage: %s [-d level] nodename command\n", __progname); 224 exit (0); 225 } 226