• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Original implementation on libmnl:
3  * (C) 2011 by Pablo Neira Ayuso <pablo@netfilter.org>
4  * (C) 2011 by Intra2net AG <http://www.intra2net.com>
5  *
6  * Port to libnl:
7  * (C) 2013 by Mathieu J. Poirier <mathieu.poirier@linaro.org>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published
11  * by the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  */
14 
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <sys/types.h>
19 #include <sys/socket.h>
20 #include <dirent.h>
21 #include <sys/stat.h>
22 #include <fcntl.h>
23 #include <unistd.h>
24 #include <time.h>
25 #include <errno.h>
26 #include <endian.h>
27 
28 #include <netlink-private/object-api.h>
29 #include <netlink-private/types.h>
30 #include <linux/netlink.h>
31 #include <linux/netfilter/nfnetlink.h>
32 #include <linux/netfilter/nfnetlink_acct.h>
33 #include <netlink/netfilter/nfnl.h>
34 #include <netlink/netlink.h>
35 #include <netlink/socket.h>
36 #include <netlink/msg.h>
37 
38 #define VERSION "1.0.1"
39 
40 enum {
41 	NFACCT_CMD_NONE = 0,
42 	NFACCT_CMD_LIST,
43 	NFACCT_CMD_ADD,
44 	NFACCT_CMD_DELETE,
45 	NFACCT_CMD_GET,
46 	NFACCT_CMD_FLUSH,
47 	NFACCT_CMD_VERSION,
48 	NFACCT_CMD_HELP,
49 	NFACCT_CMD_RESTORE,
50 };
51 
52 static int nfacct_cmd_list(int argc, char *argv[]);
53 static int nfacct_cmd_add(int argc, char *argv[]);
54 static int nfacct_cmd_delete(int argc, char *argv[]);
55 static int nfacct_cmd_get(int argc, char *argv[]);
56 static int nfacct_cmd_flush(int argc, char *argv[]);
57 static int nfacct_cmd_version(int argc, char *argv[]);
58 static int nfacct_cmd_help(int argc, char *argv[]);
59 static int nfacct_cmd_restore(int argc, char *argv[]);
60 
61 #ifndef HAVE_LIBNL20
62 #define nl_sock nl_handle
63 #define nl_socket_alloc nl_handle_alloc
64 #define nl_socket_free nl_handle_destroy
65 #endif
66 
67 #define NL_DBG(LVL,FMT,ARG...) do { } while(0)
68 
usage(char * argv[])69 static void usage(char *argv[])
70 {
71 	fprintf(stderr, "Usage: %s command [parameters]...\n", argv[0]);
72 }
73 
nfacct_perror(const char * msg)74 static void nfacct_perror(const char *msg)
75 {
76 	if (errno == 0) {
77 		fprintf(stderr, "nfacct v%s: %s\n", VERSION, msg);
78 	} else {
79 		fprintf(stderr, "nfacct v%s: %s: %s\n",
80 			VERSION, msg, strerror(errno));
81 	}
82 }
83 
main(int argc,char * argv[])84 int main(int argc, char *argv[])
85 {
86 	int cmd = NFACCT_CMD_NONE, ret = 0;
87 
88 	if (argc < 2) {
89 		usage(argv);
90 		exit(EXIT_FAILURE);
91 	}
92 
93 	if (strncmp(argv[1], "list", strlen(argv[1])) == 0)
94 		cmd = NFACCT_CMD_LIST;
95 	else if (strncmp(argv[1], "add", strlen(argv[1])) == 0)
96 		cmd = NFACCT_CMD_ADD;
97 	else if (strncmp(argv[1], "delete", strlen(argv[1])) == 0)
98 		cmd = NFACCT_CMD_DELETE;
99 	else if (strncmp(argv[1], "get", strlen(argv[1])) == 0)
100 		cmd = NFACCT_CMD_GET;
101 	else if (strncmp(argv[1], "flush", strlen(argv[1])) == 0)
102 		cmd = NFACCT_CMD_FLUSH;
103 	else if (strncmp(argv[1], "version", strlen(argv[1])) == 0)
104 		cmd = NFACCT_CMD_VERSION;
105 	else if (strncmp(argv[1], "help", strlen(argv[1])) == 0)
106 		cmd = NFACCT_CMD_HELP;
107 	else if (strncmp(argv[1], "restore", strlen(argv[1])) == 0)
108 		cmd = NFACCT_CMD_RESTORE;
109 	else {
110 		fprintf(stderr, "nfacct v%s: Unknown command: %s\n",
111 			VERSION, argv[1]);
112 		usage(argv);
113 		exit(EXIT_FAILURE);
114 	}
115 
116 	switch(cmd) {
117 	case NFACCT_CMD_LIST:
118 		ret = nfacct_cmd_list(argc, argv);
119 		break;
120 	case NFACCT_CMD_ADD:
121 		ret = nfacct_cmd_add(argc, argv);
122 		break;
123 	case NFACCT_CMD_DELETE:
124 		ret = nfacct_cmd_delete(argc, argv);
125 		break;
126 	case NFACCT_CMD_GET:
127 		ret = nfacct_cmd_get(argc, argv);
128 		break;
129 	case NFACCT_CMD_FLUSH:
130 		ret = nfacct_cmd_flush(argc, argv);
131 		break;
132 	case NFACCT_CMD_VERSION:
133 		ret = nfacct_cmd_version(argc, argv);
134 		break;
135 	case NFACCT_CMD_HELP:
136 		ret = nfacct_cmd_help(argc, argv);
137 		break;
138 	case NFACCT_CMD_RESTORE:
139 		ret = nfacct_cmd_restore(argc, argv);
140 		break;
141 	}
142 	return ret < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
143 }
144 
145 
message_received(struct nl_msg * msg,void * arg)146 static int message_received(struct nl_msg *msg, void *arg)
147 {
148 	struct nlmsghdr *hdr = msg->nm_nlh;
149 
150 	if (hdr->nlmsg_type == NLMSG_ERROR) {
151 		struct nlmsgerr *err = nlmsg_data(hdr);
152 
153 		if (err->error == 0)
154 			return NL_STOP;
155 	}
156 
157 	return NL_OK;
158 }
159 
valid_input(struct nl_msg * msg,void * arg)160 static int valid_input(struct nl_msg *msg, void *arg)
161 {
162 	struct nlattr *nla = nlmsg_attrdata(nlmsg_hdr(msg),
163 					 sizeof(struct nfgenmsg));
164 	struct nlattr *tb[NFACCT_NAME_MAX+1] = {};
165 	char buf[4096];
166 	int ret;
167 
168 	ret = nlmsg_parse(nlmsg_hdr(msg),
169 			 sizeof(struct nfgenmsg), tb, NFACCT_MAX, NULL);
170 
171 	if (ret < 0) {
172 		nfacct_perror("Can't parse message\n");
173 		return ret;
174 	}
175 
176 	ret = snprintf(buf, sizeof(buf),
177 		"{ pkts = %.20llu, bytes = %.20llu } = %s;",
178 		(unsigned long long)be64toh(nla_get_u64(tb[NFACCT_PKTS])),
179 		(unsigned long long)be64toh(nla_get_u64(tb[NFACCT_BYTES])),
180 		nla_get_string(tb[NFACCT_NAME]));
181 
182 	printf("%s\n", buf);
183 
184 	return 0;
185 }
186 
nfacct_cmd_list(int argc,char * argv[])187 static int nfacct_cmd_list(int argc, char *argv[])
188 {
189 	struct nl_msg *msg;
190 	struct nl_sock *handle;
191 	int zeroctr = 0;
192 	int ret, i;
193 
194 	for (i=2; i<argc; i++) {
195 		if (strncmp(argv[i], "reset", strlen(argv[i])) == 0) {
196 			zeroctr = 1;
197 		} else if (strncmp(argv[i], "xml", strlen(argv[i])) == 0) {
198 			nfacct_perror("xml feature not implemented");
199 			return -1;
200 		} else {
201 			nfacct_perror("unknown argument");
202 			return -1;
203 		}
204 	}
205 
206 	msg = nlmsg_alloc();
207 	if (!msg)
208 		return -1;
209 
210 	ret = nfnlmsg_put(msg,
211 			NL_AUTO_PID,
212 			NL_AUTO_SEQ,
213 			NFNL_SUBSYS_ACCT,
214 			zeroctr ?
215 			NFNL_MSG_ACCT_GET_CTRZERO : NFNL_MSG_ACCT_GET,
216 			NLM_F_DUMP | NLM_F_REQUEST,
217 			AF_UNSPEC,
218 			0);
219 
220 	if (ret) {
221 		NL_DBG(2, "Can't append payload to message: %s line: %d\n",
222 							__FUNCTION__, __LINE__);
223 		goto fail;
224 	}
225 
226 	handle = nl_socket_alloc();
227 	if ((ret = nfnl_connect(handle))) {
228 		NL_DBG(2, "Can't connect handle: %s line: %d\n",
229 							__FUNCTION__, __LINE__);
230 		goto fail;
231 	}
232 
233 	if ((ret = nl_send_auto_complete(handle, msg)) < 0) {
234 		NL_DBG(2, "Can't send msg: %s line: %d\n",
235 							__FUNCTION__, __LINE__);
236 		goto fail_send;
237         }
238 
239 	nl_socket_modify_cb(handle, NL_CB_VALID, NL_CB_CUSTOM, valid_input, NULL);
240 	ret = nl_recvmsgs_default(handle);
241 	if (ret < 0) {
242 		NL_DBG(2, "Can't receice msg: %s line: %d\n",
243 							__FUNCTION__, __LINE__);
244 	}
245 
246 fail_send:
247 	nl_close(handle);
248 	nl_socket_free(handle);
249 fail:
250 	nlmsg_free(msg);
251 	return ret;
252 }
253 
_nfacct_cmd_add(char * name,int pkts,int bytes)254 static int _nfacct_cmd_add(char *name, int pkts, int bytes)
255 {
256 	struct nl_msg *msg;
257 	struct nl_sock *handle;
258 	char nfname[NFACCT_NAME_MAX];
259 	int ret;
260 
261 	strncpy(nfname, name, NFACCT_NAME_MAX);
262 	nfname[NFACCT_NAME_MAX-1] = '\0';
263 
264 	msg = nlmsg_alloc();
265 	if (!msg)
266 		return -1;
267 
268 	ret = nfnlmsg_put(msg,
269 			NL_AUTO_PID,
270 			NL_AUTO_SEQ,
271 			NFNL_SUBSYS_ACCT,
272 			NFNL_MSG_ACCT_NEW,
273 			NLM_F_CREATE | NLM_F_ACK | NLM_F_REQUEST,
274 			AF_UNSPEC,
275 			0);
276 
277 	if (ret) {
278 		NL_DBG(2, "Can't append payload to message: %s line: %d\n",
279 							__FUNCTION__, __LINE__);
280 		goto fail;
281 	}
282 
283 	nla_put_string(msg, NFACCT_NAME, nfname);
284 	nla_put_u64(msg, NFACCT_PKTS, htobe64(pkts));
285 	nla_put_u64(msg, NFACCT_BYTES, htobe64(bytes));
286 
287 	handle = nl_socket_alloc();
288 	if ((ret = nfnl_connect(handle))) {
289 		NL_DBG(2, "Can't connect handle: %s line: %d\n",
290 							__FUNCTION__, __LINE__);
291 		goto fail;
292 	}
293 
294 	if ((ret = nl_send_auto_complete(handle, msg)) < 0) {
295 		NL_DBG(2, "Can't send msg: %s line: %d\n",
296 							__FUNCTION__, __LINE__);
297 		goto fail_send;
298         }
299 
300 	ret = nl_recvmsgs_default(handle);
301 	if (ret < 0) {
302 		NL_DBG(2, "Can't receice msg: %s line: %d\n",
303 							__FUNCTION__, __LINE__);
304 	}
305 
306 fail_send:
307 	nl_close(handle);
308 	nl_socket_free(handle);
309 fail:
310 	nlmsg_free(msg);
311 	return ret;
312 }
313 
314 
315 
nfacct_cmd_add(int argc,char * argv[])316 static int nfacct_cmd_add(int argc, char *argv[])
317 {
318 	if (argc < 3) {
319 		nfacct_perror("missing object name");
320 		return -1;
321 	} else if (argc > 3) {
322 		nfacct_perror("too many arguments");
323 		return -1;
324 	}
325 
326 	return _nfacct_cmd_add(argv[2], 0, 0);
327 }
328 
nfacct_cmd_delete(int argc,char * argv[])329 static int nfacct_cmd_delete(int argc, char *argv[])
330 {
331 	struct nl_msg *msg;
332 	struct nl_sock *handle;
333 	char nfname[NFACCT_NAME_MAX];
334 	int ret;
335 
336 	if (argc < 3) {
337 		nfacct_perror("missing object name");
338 		return -1;
339 	} else if (argc > 3) {
340 		nfacct_perror("too many arguments");
341 		return -1;
342 	}
343 
344 	strncpy(nfname, argv[2], NFACCT_NAME_MAX);
345 	nfname[NFACCT_NAME_MAX-1] = '\0';
346 
347 	msg = nlmsg_alloc();
348 	if (!msg)
349 		return -1;
350 
351 	ret = nfnlmsg_put(msg,
352 			NL_AUTO_PID,
353 			NL_AUTO_SEQ,
354 			NFNL_SUBSYS_ACCT,
355 			NFNL_MSG_ACCT_DEL,
356 			NLM_F_ACK | NLM_F_REQUEST,
357 			AF_UNSPEC,
358 			0);
359 
360 	if (ret) {
361 		NL_DBG(2, "Can't append payload to message: %s line: %d\n",
362 							__FUNCTION__, __LINE__);
363 		goto fail;
364 	}
365 
366 	nla_put_string(msg, NFACCT_NAME, nfname);
367 
368 	handle = nl_socket_alloc();
369 	if ((ret = nfnl_connect(handle))) {
370 		NL_DBG(2, "Can't connect handle: %s line: %d\n",
371 							__FUNCTION__, __LINE__);
372 		goto fail;
373 	}
374 
375 	if ((ret = nl_send_auto_complete(handle, msg)) < 0) {
376 		NL_DBG(2, "Can't send msg: %s line: %d\n",
377 							__FUNCTION__, __LINE__);
378 		goto fail_send;
379         }
380 
381 	ret = nl_recvmsgs_default(handle);
382 	if (ret < 0) {
383 		NL_DBG(2, "Can't receice msg: %s line: %d\n",
384 							__FUNCTION__, __LINE__);
385 	}
386 
387 fail_send:
388 	nl_close(handle);
389 	nl_socket_free(handle);
390 fail:
391 	nlmsg_free(msg);
392 	return ret;
393 	return 0;
394 }
395 
396 
nfacct_cmd_get(int argc,char * argv[])397 static int nfacct_cmd_get(int argc, char *argv[])
398 {
399 	struct nl_msg *msg;
400 	struct nl_sock *handle;
401 	struct nl_cb *cb;
402 	char nfname[NFACCT_NAME_MAX];
403 	int zeroctr = 0;
404 	int ret, i;
405 
406 	if (argc < 3) {
407 		nfacct_perror("missing object name");
408 		 return -1;
409 	}
410 
411 	for (i=3; i<argc; i++) {
412 		if (strncmp(argv[i], "reset", strlen(argv[i])) == 0) {
413 			zeroctr = 1;
414 		} else if (strncmp(argv[i], "xml", strlen(argv[i])) == 0) {
415 			nfacct_perror("xml feature not implemented");
416 			return -1;
417 		} else {
418 			nfacct_perror("unknown argument");
419 			return -1;
420 		}
421 	}
422 
423 	strncpy(nfname, argv[2], NFACCT_NAME_MAX);
424 	nfname[NFACCT_NAME_MAX-1] = '\0';
425 
426 	msg = nlmsg_alloc();
427 	if (!msg)
428 		return -1;
429 
430 	ret = nfnlmsg_put(msg,
431 			NL_AUTO_PID,
432 			NL_AUTO_SEQ,
433 			NFNL_SUBSYS_ACCT,
434 			zeroctr ?
435 			NFNL_MSG_ACCT_GET_CTRZERO : NFNL_MSG_ACCT_GET,
436 			NLM_F_ACK | NLM_F_REQUEST,
437 			AF_UNSPEC,
438 			0);
439 
440 	if (ret) {
441 		NL_DBG(2, "Can't append payload to message: %s line: %d\n",
442 							__FUNCTION__, __LINE__);
443 		goto fail;
444 	}
445 
446 	nla_put_string(msg, NFACCT_NAME, nfname);
447 
448 	handle = nl_socket_alloc();
449 
450 	if (handle) {
451 		cb = nl_cb_alloc(NL_CB_DEFAULT);
452 		if (!cb)
453 			goto fail;
454 
455 		if (nl_cb_set(cb, NL_CB_MSG_IN,
456 				 NL_CB_CUSTOM,
457 				 message_received, NULL) < 0)
458 			goto fail;
459 
460 		nl_socket_set_cb(handle,cb);
461 	} else {
462 		goto fail;
463 	}
464 
465 	if ((ret = nfnl_connect(handle))) {
466 		NL_DBG(2, "Can't connect handle: %s line: %d\n",
467 							__FUNCTION__, __LINE__);
468 		goto fail;
469 	}
470 
471 	if ((ret = nl_send_auto_complete(handle, msg)) < 0) {
472 		NL_DBG(2, "Can't send msg: %s line: %d\n",
473 							__FUNCTION__, __LINE__);
474 		goto fail_send;
475         }
476 
477 	nl_socket_modify_cb(handle, NL_CB_VALID, NL_CB_CUSTOM, valid_input, NULL);
478 	ret = nl_recvmsgs_default(handle);
479 	if (ret < 0) {
480 		NL_DBG(2, "Can't receice msg: %s line: %d\n",
481 							__FUNCTION__, __LINE__);
482 	}
483 
484 fail_send:
485 	nl_close(handle);
486 	nl_socket_free(handle);
487 fail:
488 	nlmsg_free(msg);
489 	return ret;
490 }
491 
nfacct_cmd_flush(int argc,char * argv[])492 static int nfacct_cmd_flush(int argc, char *argv[])
493 {
494 	struct nl_msg *msg;
495 	struct nl_sock *handle;
496 	int ret;
497 
498 	if (argc > 2) {
499 		nfacct_perror("too many arguments");
500 		return -1;
501 	}
502 
503 	msg = nlmsg_alloc();
504 	if (!msg)
505 		return -1;
506 
507 	ret = nfnlmsg_put(msg,
508 			NL_AUTO_PID,
509 			NL_AUTO_SEQ,
510 			NFNL_SUBSYS_ACCT,
511 			NFNL_MSG_ACCT_DEL,
512 			NLM_F_ACK | NLM_F_REQUEST,
513 			AF_UNSPEC,
514 			0);
515 
516 	if (ret) {
517 		NL_DBG(2, "Can't append payload to message: %s line: %d\n",
518 							__FUNCTION__, __LINE__);
519 		goto fail;
520 	}
521 
522 	handle = nl_socket_alloc();
523 	if ((ret = nfnl_connect(handle))) {
524 		NL_DBG(2, "Can't connect handle: %s line: %d\n",
525 							__FUNCTION__, __LINE__);
526 		goto fail;
527 	}
528 
529 	if ((ret = nl_send_auto_complete(handle, msg)) < 0) {
530 		NL_DBG(2, "Can't send msg: %s line: %d\n",
531 							__FUNCTION__, __LINE__);
532 		goto fail_send;
533         }
534 
535 	ret = nl_recvmsgs_default(handle);
536 	if (ret < 0) {
537 		NL_DBG(2, "Can't receice msg: %s line: %d\n",
538 							__FUNCTION__, __LINE__);
539 	}
540 
541 fail_send:
542 	nl_close(handle);
543 	nl_socket_free(handle);
544 fail:
545 	nlmsg_free(msg);
546 	return ret;
547 }
548 
549 static const char version_msg[] =
550 	"nfacct v%s: utility for the Netfilter extended accounting "
551 	"infrastructure\n"
552 	"Copyright (C) 2011 Pablo Neira Ayuso <pablo@netfilter.org>\n"
553 	"Copyright (C) 2011 Intra2net AG <http://www.intra2net.com>\n"
554 	"Copyright (C) 2013 Mathieu Poirier <mathieu.poirier@linaro.org>\n"
555 	"This program comes with ABSOLUTELY NO WARRANTY.\n"
556 	"This is free software, and you are welcome to redistribute it under "
557 	"certain \nconditions; see LICENSE file distributed in this package "
558 	"for details.\n";
559 
nfacct_cmd_version(int argc,char * argv[])560 static int nfacct_cmd_version(int argc, char *argv[])
561 {
562 	printf(version_msg, VERSION);
563 	return 0;
564 }
565 
566 static const char help_msg[] =
567 	"nfacct v%s: utility for the Netfilter extended accounting "
568 	"infrastructure\n"
569 	"Usage: %s command [parameters]...\n\n"
570 	"Commands:\n"
571 	"  list [reset]\t\tList the accounting object table (and reset)\n"
572 	"  add object-name\tAdd new accounting object to table\n"
573 	"  delete object-name\tDelete existing accounting object\n"
574 	"  get object-name\tGet existing accounting object\n"
575 	"  flush\t\t\tFlush accounting object table\n"
576 	"  restore\t\tRestore accounting object table reading 'list' output from stdin\n"
577 	"  version\t\tDisplay version and disclaimer\n"
578 	"  help\t\t\tDisplay this help message\n";
579 
nfacct_cmd_help(int argc,char * argv[])580 static int nfacct_cmd_help(int argc, char *argv[])
581 {
582 	printf(help_msg, VERSION, argv[0]);
583 	return 0;
584 }
585 
nfacct_cmd_restore(int argc,char * argv[])586 static int nfacct_cmd_restore(int argc, char *argv[])
587 {
588 	uint64_t pkts, bytes;
589 	char name[512];
590 	char buffer[512];
591 	int ret;
592 	while (fgets(buffer, sizeof(buffer), stdin)) {
593 		char *semicolon = strchr(buffer, ';');
594 		if (semicolon == NULL) {
595 			nfacct_perror("invalid line");
596 			return -1;
597 		}
598 		*semicolon = 0;
599 		ret = sscanf(buffer, "{ pkts = %llu, bytes = %llu } = %s",
600 		       &pkts, &bytes, name);
601 		if (ret != 3) {
602 			nfacct_perror("error reading input");
603 			return -1;
604 		}
605 		if ((ret = _nfacct_cmd_add(name, pkts, bytes)) != 0)
606 			return ret;
607 
608 	}
609 	return 0;
610 }
611