flowctl.c revision 135380
1/*- 2 * Copyright (c) 2004 Gleb Smirnoff <glebius@cell.sick.ru> 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 * 3. All advertising materials mentioning features or use of this software 15 * must display the following acknowledgement: 16 * This product includes software developed by Gleb Smirnoff and 17 * contributors. 18 * 4. Neither the name of the author nor the names of its contributors 19 * may be used to endorse or promote products derived from this software 20 * without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 * 34 * $SourceForge: flowctl.c,v 1.15 2004/08/31 20:24:58 glebius Exp $ 35 */ 36 37#ifndef lint 38static const char rcs_id[] = 39 "@(#) $FreeBSD: head/usr.sbin/flowctl/flowctl.c 135380 2004-09-17 12:02:22Z glebius $"; 40#endif 41 42#include <sys/types.h> 43#include <sys/time.h> 44#include <sys/socket.h> 45#include <sys/queue.h> 46 47#include <net/if.h> 48#include <netinet/in.h> 49 50#include <arpa/inet.h> 51 52#include <err.h> 53#include <fcntl.h> 54#include <stdio.h> 55#include <stdlib.h> 56#include <string.h> 57#include <unistd.h> 58 59#include <netgraph.h> 60#include <netgraph/netflow/ng_netflow.h> 61 62#define CISCO_SH_FLOW_HEADER "SrcIf SrcIPaddress DstIf DstIPaddress Pr SrcP DstP Pkts\n" 63#define CISCO_SH_FLOW "%-13s %-15s %-13s %-15s %2u %4.4x %4.4x %6lu\n" 64 65int main(int, char **); 66 67static int flow_cache_print(struct ngnf_flows *recs); 68static int ctl_show(int, int, char **); 69static void help(void); 70static void execute_command(int, char **); 71 72struct ip_ctl_cmd { 73 char *cmd_name; 74 int cmd_code; 75 int (*cmd_func)(int code, int argc, char **argv); 76}; 77 78struct ip_ctl_cmd cmds[] = { 79 {"show", NGM_NETFLOW_SHOW, ctl_show}, 80 {NULL, 0, NULL}, 81}; 82 83int cs; 84char ng_nodename[NG_PATHLEN + 1]; 85 86int 87main(int argc, char **argv) 88{ 89 int flags, c; 90 char sname[NG_NODESIZ]; 91 int rcvbuf = SORCVBUF_SIZE; 92 char *ng_name; 93 94 /* parse options */ 95 while ((c = getopt(argc, argv, "d:")) != -1) { 96 switch (c) { 97 case 'd': /* set libnetgraph debug level. */ 98 NgSetDebug(atoi(optarg)); 99 break; 100 } 101 } 102 103 argc -= optind; 104 argv += optind; 105 ng_name = argv[0]; 106 if (ng_name == NULL) 107 help(); 108 argc--; 109 argv++; 110 111 snprintf(ng_nodename, sizeof(ng_nodename), "%s:", ng_name); 112 113 /* create control socket. */ 114 snprintf(sname, sizeof(sname), "flowctl%i", getpid()); 115 116 if (NgMkSockNode(sname, &cs, NULL) == -1) 117 err(1, "NgMkSockNode"); 118 119 /* set control socket nonblocking */ 120 if ((flags = fcntl(cs, F_GETFL, 0)) == -1) 121 err(1, "fcntl(F_GETFL)"); 122 flags |= O_NONBLOCK; 123 if (fcntl(cs, F_SETFL, flags) == -1) 124 err(1, "fcntl(F_SETFL)"); 125 126 /* set receive buffer size */ 127 if (setsockopt(cs, SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(int)) == -1) 128 err(1, "setsockopt(SOL_SOCKET, SO_RCVBUF)"); 129 130 /* parse and execute command */ 131 execute_command(argc, argv); 132 133 close(cs); 134 135 exit(0); 136} 137 138static void 139execute_command(int argc, char **argv) 140{ 141 int cindex = -1; 142 int i; 143 144 if (!argc) 145 help(); 146 for (i = 0; cmds[i].cmd_name != NULL; i++) 147 if (!strncmp(argv[0], cmds[i].cmd_name, strlen(argv[0]))) { 148 if (cindex != -1) 149 errx(1, "ambiguous command: %s", argv[0]); 150 cindex = i; 151 } 152 if (cindex == -1) 153 errx(1, "bad command: %s", argv[0]); 154 argc--; 155 argv++; 156 (*cmds[cindex].cmd_func)(cmds[cindex].cmd_code, argc, argv); 157} 158 159static int 160ctl_show(int code, int argc, char **argv) 161{ 162 struct ng_mesg *ng_mesg; 163 struct ngnf_flows *data; 164 char path[NG_PATHLEN + 1]; 165 int token, nread, last = 0; 166 167 ng_mesg = alloca(SORCVBUF_SIZE); 168 169 printf(CISCO_SH_FLOW_HEADER); 170 171 for (;;) { 172 /* request set of accounting records */ 173 token = NgSendMsg(cs, ng_nodename, NGM_NETFLOW_COOKIE, 174 NGM_NETFLOW_SHOW, (void *)&last, sizeof(last)); 175 if (token == -1) 176 err(1, "NgSendMsg(NGM_NETFLOW_SHOW)"); 177 178 /* read reply */ 179 nread = NgRecvMsg(cs, ng_mesg, SORCVBUF_SIZE, path); 180 if (nread == -1) 181 err(1, "NgRecvMsg() failed"); 182 183 if (ng_mesg->header.token != token) 184 err(1, "NgRecvMsg(NGM_NETFLOW_SHOW): token mismatch"); 185 186 data = (struct ngnf_flows*)ng_mesg->data; 187 if ((ng_mesg->header.arglen < (sizeof(*data))) || 188 (ng_mesg->header.arglen < (sizeof(*data) + 189 (data->nentries * sizeof(struct flow_entry_data))))) 190 err(1, "NgRecvMsg(NGM_NETFLOW_SHOW): arglen too small"); 191 192 (void )flow_cache_print(data); 193 194 if (data->last != 0) 195 last = data->last; 196 else 197 break; 198 } 199 200 return (0); 201} 202 203static int 204flow_cache_print(struct ngnf_flows *recs) 205{ 206 struct flow_entry_data *fle; 207 char src[INET_ADDRSTRLEN], dst[INET_ADDRSTRLEN]; 208 char src_if[IFNAMSIZ], dst_if[IFNAMSIZ]; 209 int i; 210 211 /* quick check */ 212 if (recs->nentries == 0) 213 return (0); 214 215 fle = recs->entries; 216 for (i = 0; i < recs->nentries; i++, fle++) { 217 inet_ntop(AF_INET, &fle->r.r_src, src, sizeof(src)); 218 inet_ntop(AF_INET, &fle->r.r_dst, dst, sizeof(dst)); 219 printf(CISCO_SH_FLOW, 220 if_indextoname(fle->fle_i_ifx, src_if), 221 src, 222 if_indextoname(fle->fle_o_ifx, dst_if), 223 dst, 224 fle->r.r_ip_p, 225 ntohs(fle->r.r_sport), 226 ntohs(fle->r.r_dport), 227 fle->packets); 228 229 } 230 231 return (i); 232} 233 234static void 235help(void) 236{ 237 extern char *__progname; 238 239 fprintf(stderr, "usage: %s [-d level] nodename command\n", __progname); 240 exit (0); 241} 242