|
|
9c64ad |
Adapted version of
|
|
|
9c64ad |
|
|
|
9c64ad |
commit e8f857a5a1514c3e7d0d8ea0f7d2d571f0e37bd1
|
|
|
9c64ad |
Author: Subash Abhinov Kasiviswanathan <subashab@codeaurora.org>
|
|
|
9c64ad |
Date: Thu Jun 23 18:44:06 2016 -0600
|
|
|
9c64ad |
|
|
|
9c64ad |
xtables: Add an interval option for xtables lock wait
|
|
|
9c64ad |
|
|
|
9c64ad |
ip[6]tables currently waits for 1 second for the xtables lock to be
|
|
|
9c64ad |
freed if the -w option is used. We have seen that the lock is held
|
|
|
9c64ad |
much less than that resulting in unnecessary delay when trying to
|
|
|
9c64ad |
acquire the lock. This problem is even severe in case of latency
|
|
|
9c64ad |
sensitive applications.
|
|
|
9c64ad |
|
|
|
9c64ad |
Introduce a new option 'W' to specify the wait interval in microseconds.
|
|
|
9c64ad |
If this option is not specified, the command sleeps for 1 second by
|
|
|
9c64ad |
default.
|
|
|
9c64ad |
|
|
|
9c64ad |
v1->v2: Change behavior to take millisecond sleep as an argument to
|
|
|
9c64ad |
-w as suggested by Pablo. Also maintain current behavior for -w to
|
|
|
9c64ad |
sleep for 1 second as mentioned by Liping.
|
|
|
9c64ad |
|
|
|
9c64ad |
v2->v3: Move the millisecond behavior to a new option as suggested
|
|
|
9c64ad |
by Pablo.
|
|
|
9c64ad |
|
|
|
9c64ad |
v3->v4: Use select instead of usleep. Sleep every iteration for
|
|
|
9c64ad |
the time specified in the "-W" argument. Update man page.
|
|
|
9c64ad |
|
|
|
9c64ad |
v4->v5: Fix compilation error when enabling nftables
|
|
|
9c64ad |
|
|
|
9c64ad |
v5->v6: Simplify -W so it only takes the interval wait in microseconds.
|
|
|
9c64ad |
Bail out if -W is specific but -w is not.
|
|
|
9c64ad |
|
|
|
9c64ad |
Joint work with Pablo Neira.
|
|
|
9c64ad |
|
|
|
9c64ad |
Signed-off-by: Subash Abhinov Kasiviswanathan <subashab@codeaurora.org>
|
|
|
9c64ad |
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
|
|
|
9c64ad |
|
|
|
9c64ad |
diff -up iptables-1.4.21/iptables/ip6tables.c.wait-interval iptables-1.4.21/iptables/ip6tables.c
|
|
|
9c64ad |
--- iptables-1.4.21/iptables/ip6tables.c.wait-interval 2017-04-05 14:04:04.560346651 +0200
|
|
|
9c64ad |
+++ iptables-1.4.21/iptables/ip6tables.c 2017-04-05 14:04:04.562346670 +0200
|
|
|
9c64ad |
@@ -103,6 +103,7 @@ static struct option original_opts[] = {
|
|
|
9c64ad |
{.name = "out-interface", .has_arg = 1, .val = 'o'},
|
|
|
9c64ad |
{.name = "verbose", .has_arg = 0, .val = 'v'},
|
|
|
9c64ad |
{.name = "wait", .has_arg = 2, .val = 'w'},
|
|
|
9c64ad |
+ {.name = "wait-interval", .has_arg = 2, .val = 'W'},
|
|
|
9c64ad |
{.name = "exact", .has_arg = 0, .val = 'x'},
|
|
|
9c64ad |
{.name = "version", .has_arg = 0, .val = 'V'},
|
|
|
9c64ad |
{.name = "help", .has_arg = 2, .val = 'h'},
|
|
|
9c64ad |
@@ -258,7 +259,10 @@ exit_printhelp(const struct xtables_rule
|
|
|
9c64ad |
" network interface name ([+] for wildcard)\n"
|
|
|
9c64ad |
" --table -t table table to manipulate (default: `filter')\n"
|
|
|
9c64ad |
" --verbose -v verbose mode\n"
|
|
|
9c64ad |
-" --wait -w [seconds] wait for the xtables lock\n"
|
|
|
9c64ad |
+" --wait -w [seconds] maximum wait to acquire xtables lock before give up\n"
|
|
|
9c64ad |
+" --wait-interval -W [usecs] wait time to try to acquire xtables lock\n"
|
|
|
9c64ad |
+" interval to wait for xtables lock\n"
|
|
|
9c64ad |
+" default is 1 second\n"
|
|
|
9c64ad |
" --line-numbers print line numbers when listing\n"
|
|
|
9c64ad |
" --exact -x expand numbers (display exact values)\n"
|
|
|
9c64ad |
/*"[!] --fragment -f match second or further fragments only\n"*/
|
|
|
9c64ad |
@@ -1323,6 +1327,10 @@ int do_command6(int argc, char *argv[],
|
|
|
9c64ad |
|
|
|
9c64ad |
int verbose = 0;
|
|
|
9c64ad |
int wait = 0;
|
|
|
9c64ad |
+ struct timeval wait_interval = {
|
|
|
9c64ad |
+ .tv_sec = 1,
|
|
|
9c64ad |
+ };
|
|
|
9c64ad |
+ bool wait_interval_set = false;
|
|
|
9c64ad |
const char *chain = NULL;
|
|
|
9c64ad |
const char *shostnetworkmask = NULL, *dhostnetworkmask = NULL;
|
|
|
9c64ad |
const char *policy = NULL, *newname = NULL;
|
|
|
9c64ad |
@@ -1358,7 +1366,7 @@ int do_command6(int argc, char *argv[],
|
|
|
9c64ad |
|
|
|
9c64ad |
opts = xt_params->orig_opts;
|
|
|
9c64ad |
while ((cs.c = getopt_long(argc, argv,
|
|
|
9c64ad |
- "-:A:C:D:R:I:L::S::M:F::Z::N:X::E:P:Vh::o:p:s:d:j:i:bvw::nt:m:xc:g:46",
|
|
|
9c64ad |
+ "-:A:C:D:R:I:L::S::M:F::Z::N:X::E:P:Vh::o:p:s:d:j:i:bvw::W::nt:m:xc:g:46",
|
|
|
9c64ad |
opts, NULL)) != -1) {
|
|
|
9c64ad |
switch (cs.c) {
|
|
|
9c64ad |
/*
|
|
|
9c64ad |
@@ -1614,6 +1622,23 @@ int do_command6(int argc, char *argv[],
|
|
|
9c64ad |
"wait seconds not numeric");
|
|
|
9c64ad |
break;
|
|
|
9c64ad |
|
|
|
9c64ad |
+ case 'W':
|
|
|
9c64ad |
+ if (restore) {
|
|
|
9c64ad |
+ xtables_error(PARAMETER_PROBLEM,
|
|
|
9c64ad |
+ "You cannot use `-W' from "
|
|
|
9c64ad |
+ "ip6tables-restore");
|
|
|
9c64ad |
+ }
|
|
|
9c64ad |
+ if (optarg)
|
|
|
9c64ad |
+ parse_wait_interval(optarg, &wait_interval);
|
|
|
9c64ad |
+ else if (optind < argc &&
|
|
|
9c64ad |
+ argv[optind][0] != '-' &&
|
|
|
9c64ad |
+ argv[optind][0] != '!')
|
|
|
9c64ad |
+ parse_wait_interval(argv[optind++],
|
|
|
9c64ad |
+ &wait_interval);
|
|
|
9c64ad |
+
|
|
|
9c64ad |
+ wait_interval_set = true;
|
|
|
9c64ad |
+ break;
|
|
|
9c64ad |
+
|
|
|
9c64ad |
case 'm':
|
|
|
9c64ad |
command_match(&cs);
|
|
|
9c64ad |
break;
|
|
|
9c64ad |
@@ -1718,6 +1743,10 @@ int do_command6(int argc, char *argv[],
|
|
|
9c64ad |
cs.invert = FALSE;
|
|
|
9c64ad |
}
|
|
|
9c64ad |
|
|
|
9c64ad |
+ if (!wait && wait_interval_set)
|
|
|
9c64ad |
+ xtables_error(PARAMETER_PROBLEM,
|
|
|
9c64ad |
+ "--wait-interval only makes sense with --wait\n");
|
|
|
9c64ad |
+
|
|
|
9c64ad |
if (strcmp(*table, "nat") == 0 &&
|
|
|
9c64ad |
((policy != NULL && strcmp(policy, "DROP") == 0) ||
|
|
|
9c64ad |
(cs.jumpto != NULL && strcmp(cs.jumpto, "DROP") == 0)))
|
|
|
9c64ad |
@@ -1768,7 +1797,7 @@ int do_command6(int argc, char *argv[],
|
|
|
9c64ad |
generic_opt_check(command, cs.options);
|
|
|
9c64ad |
|
|
|
9c64ad |
/* Attempt to acquire the xtables lock */
|
|
|
9c64ad |
- if (!restore && !xtables_lock(wait)) {
|
|
|
9c64ad |
+ if (!restore && !xtables_lock(wait, &wait_interval)) {
|
|
|
9c64ad |
fprintf(stderr, "Another app is currently holding the xtables lock. ");
|
|
|
9c64ad |
if (wait == 0)
|
|
|
9c64ad |
fprintf(stderr, "Perhaps you want to use the -w option?\n");
|
|
|
9c64ad |
diff -up iptables-1.4.21/iptables/iptables.8.in.wait-interval iptables-1.4.21/iptables/iptables.8.in
|
|
|
9c64ad |
--- iptables-1.4.21/iptables/iptables.8.in.wait-interval 2017-04-05 14:04:04.555346605 +0200
|
|
|
9c64ad |
+++ iptables-1.4.21/iptables/iptables.8.in 2017-04-05 14:04:04.562346670 +0200
|
|
|
9c64ad |
@@ -369,6 +369,13 @@ the program will exit if the lock cannot
|
|
|
9c64ad |
make the program wait (indefinitely or for optional \fIseconds\fP) until
|
|
|
9c64ad |
the exclusive lock can be obtained.
|
|
|
9c64ad |
.TP
|
|
|
9c64ad |
+\fB\-W\fP, \fB\-\-wait-interval\fP \fImicroseconds\fP
|
|
|
9c64ad |
+Interval to wait per each iteration.
|
|
|
9c64ad |
+When running latency sensitive applications, waiting for the xtables lock
|
|
|
9c64ad |
+for extended durations may not be acceptable. This option will make each
|
|
|
9c64ad |
+iteration take the amount of time specified. The default interval is
|
|
|
9c64ad |
+1 second. This option only works with \fB\-w\fP.
|
|
|
9c64ad |
+.TP
|
|
|
9c64ad |
\fB\-n\fP, \fB\-\-numeric\fP
|
|
|
9c64ad |
Numeric output.
|
|
|
9c64ad |
IP addresses and port numbers will be printed in numeric format.
|
|
|
9c64ad |
diff -up iptables-1.4.21/iptables/iptables.c.wait-interval iptables-1.4.21/iptables/iptables.c
|
|
|
9c64ad |
--- iptables-1.4.21/iptables/iptables.c.wait-interval 2017-04-05 14:04:04.555346605 +0200
|
|
|
9c64ad |
+++ iptables-1.4.21/iptables/iptables.c 2017-04-05 14:04:04.563346679 +0200
|
|
|
9c64ad |
@@ -100,6 +100,7 @@ static struct option original_opts[] = {
|
|
|
9c64ad |
{.name = "out-interface", .has_arg = 1, .val = 'o'},
|
|
|
9c64ad |
{.name = "verbose", .has_arg = 0, .val = 'v'},
|
|
|
9c64ad |
{.name = "wait", .has_arg = 2, .val = 'w'},
|
|
|
9c64ad |
+ {.name = "wait-interval", .has_arg = 2, .val = 'W'},
|
|
|
9c64ad |
{.name = "exact", .has_arg = 0, .val = 'x'},
|
|
|
9c64ad |
{.name = "fragments", .has_arg = 0, .val = 'f'},
|
|
|
9c64ad |
{.name = "version", .has_arg = 0, .val = 'V'},
|
|
|
9c64ad |
@@ -252,7 +253,9 @@ exit_printhelp(const struct xtables_rule
|
|
|
9c64ad |
" network interface name ([+] for wildcard)\n"
|
|
|
9c64ad |
" --table -t table table to manipulate (default: `filter')\n"
|
|
|
9c64ad |
" --verbose -v verbose mode\n"
|
|
|
9c64ad |
-" --wait -w [seconds] wait for the xtables lock\n"
|
|
|
9c64ad |
+" --wait -w [seconds] maximum wait to acquire xtables lock before give up\n"
|
|
|
9c64ad |
+" --wait-interval -W [usecs] wait time to try to acquire xtables lock\n"
|
|
|
9c64ad |
+" default is 1 second\n"
|
|
|
9c64ad |
" --line-numbers print line numbers when listing\n"
|
|
|
9c64ad |
" --exact -x expand numbers (display exact values)\n"
|
|
|
9c64ad |
"[!] --fragment -f match second or further fragments only\n"
|
|
|
9c64ad |
@@ -1316,7 +1319,10 @@ int do_command4(int argc, char *argv[],
|
|
|
9c64ad |
unsigned int nsaddrs = 0, ndaddrs = 0;
|
|
|
9c64ad |
struct in_addr *saddrs = NULL, *smasks = NULL;
|
|
|
9c64ad |
struct in_addr *daddrs = NULL, *dmasks = NULL;
|
|
|
9c64ad |
-
|
|
|
9c64ad |
+ struct timeval wait_interval = {
|
|
|
9c64ad |
+ .tv_sec = 1,
|
|
|
9c64ad |
+ };
|
|
|
9c64ad |
+ bool wait_interval_set = false;
|
|
|
9c64ad |
int verbose = 0;
|
|
|
9c64ad |
int wait = 0;
|
|
|
9c64ad |
const char *chain = NULL;
|
|
|
9c64ad |
@@ -1353,7 +1359,7 @@ int do_command4(int argc, char *argv[],
|
|
|
9c64ad |
opterr = 0;
|
|
|
9c64ad |
opts = xt_params->orig_opts;
|
|
|
9c64ad |
while ((cs.c = getopt_long(argc, argv,
|
|
|
9c64ad |
- "-:A:C:D:R:I:L::S::M:F::Z::N:X::E:P:Vh::o:p:s:d:j:i:fbvw::nt:m:xc:g:46",
|
|
|
9c64ad |
+ "-:A:C:D:R:I:L::S::M:F::Z::N:X::E:P:Vh::o:p:s:d:j:i:fbvw::W::nt:m:xc:g:46",
|
|
|
9c64ad |
opts, NULL)) != -1) {
|
|
|
9c64ad |
switch (cs.c) {
|
|
|
9c64ad |
/*
|
|
|
9c64ad |
@@ -1607,6 +1613,23 @@ int do_command4(int argc, char *argv[],
|
|
|
9c64ad |
"wait seconds not numeric");
|
|
|
9c64ad |
break;
|
|
|
9c64ad |
|
|
|
9c64ad |
+ case 'W':
|
|
|
9c64ad |
+ if (restore) {
|
|
|
9c64ad |
+ xtables_error(PARAMETER_PROBLEM,
|
|
|
9c64ad |
+ "You cannot use `-W' from "
|
|
|
9c64ad |
+ "iptables-restore");
|
|
|
9c64ad |
+ }
|
|
|
9c64ad |
+ if (optarg)
|
|
|
9c64ad |
+ parse_wait_interval(optarg, &wait_interval);
|
|
|
9c64ad |
+ else if (optind < argc &&
|
|
|
9c64ad |
+ argv[optind][0] != '-' &&
|
|
|
9c64ad |
+ argv[optind][0] != '!')
|
|
|
9c64ad |
+ parse_wait_interval(argv[optind++],
|
|
|
9c64ad |
+ &wait_interval);
|
|
|
9c64ad |
+
|
|
|
9c64ad |
+ wait_interval_set = true;
|
|
|
9c64ad |
+ break;
|
|
|
9c64ad |
+
|
|
|
9c64ad |
case 'm':
|
|
|
9c64ad |
command_match(&cs);
|
|
|
9c64ad |
break;
|
|
|
9c64ad |
@@ -1707,6 +1730,10 @@ int do_command4(int argc, char *argv[],
|
|
|
9c64ad |
cs.invert = FALSE;
|
|
|
9c64ad |
}
|
|
|
9c64ad |
|
|
|
9c64ad |
+ if (!wait && wait_interval_set)
|
|
|
9c64ad |
+ xtables_error(PARAMETER_PROBLEM,
|
|
|
9c64ad |
+ "--wait-interval only makes sense with --wait\n");
|
|
|
9c64ad |
+
|
|
|
9c64ad |
if (strcmp(*table, "nat") == 0 &&
|
|
|
9c64ad |
((policy != NULL && strcmp(policy, "DROP") == 0) ||
|
|
|
9c64ad |
(cs.jumpto != NULL && strcmp(cs.jumpto, "DROP") == 0)))
|
|
|
9c64ad |
@@ -1757,7 +1784,7 @@ int do_command4(int argc, char *argv[],
|
|
|
9c64ad |
generic_opt_check(command, cs.options);
|
|
|
9c64ad |
|
|
|
9c64ad |
/* Attempt to acquire the xtables lock */
|
|
|
9c64ad |
- if (!restore && !xtables_lock(wait)) {
|
|
|
9c64ad |
+ if (!restore && !xtables_lock(wait, &wait_interval)) {
|
|
|
9c64ad |
fprintf(stderr, "Another app is currently holding the xtables lock. ");
|
|
|
9c64ad |
if (wait == 0)
|
|
|
9c64ad |
fprintf(stderr, "Perhaps you want to use the -w option?\n");
|
|
|
9c64ad |
diff -up iptables-1.4.21/iptables/xshared.c.wait-interval iptables-1.4.21/iptables/xshared.c
|
|
|
9c64ad |
--- iptables-1.4.21/iptables/xshared.c.wait-interval 2017-04-05 14:04:04.557346624 +0200
|
|
|
9c64ad |
+++ iptables-1.4.21/iptables/xshared.c 2017-04-05 14:04:04.563346679 +0200
|
|
|
9c64ad |
@@ -9,12 +9,15 @@
|
|
|
9c64ad |
#include <sys/file.h>
|
|
|
9c64ad |
#include <sys/socket.h>
|
|
|
9c64ad |
#include <sys/un.h>
|
|
|
9c64ad |
+#include <sys/time.h>
|
|
|
9c64ad |
#include <unistd.h>
|
|
|
9c64ad |
#include <fcntl.h>
|
|
|
9c64ad |
#include <xtables.h>
|
|
|
9c64ad |
+#include <math.h>
|
|
|
9c64ad |
#include "xshared.h"
|
|
|
9c64ad |
|
|
|
9c64ad |
#define XT_LOCK_NAME "/run/xtables.lock"
|
|
|
9c64ad |
+#define BASE_MICROSECONDS 100000
|
|
|
9c64ad |
|
|
|
9c64ad |
/*
|
|
|
9c64ad |
* Print out any special helps. A user might like to be able to add a --help
|
|
|
9c64ad |
@@ -244,9 +247,15 @@ void xs_init_match(struct xtables_match
|
|
|
9c64ad |
match->init(match->m);
|
|
|
9c64ad |
}
|
|
|
9c64ad |
|
|
|
9c64ad |
-bool xtables_lock(int wait)
|
|
|
9c64ad |
+bool xtables_lock(int wait, struct timeval *wait_interval)
|
|
|
9c64ad |
{
|
|
|
9c64ad |
- int fd, waited = 0, i = 0;
|
|
|
9c64ad |
+ struct timeval time_left, wait_time, waited_time;
|
|
|
9c64ad |
+ int fd, i = 0;
|
|
|
9c64ad |
+
|
|
|
9c64ad |
+ time_left.tv_sec = wait;
|
|
|
9c64ad |
+ time_left.tv_usec = 0;
|
|
|
9c64ad |
+ waited_time.tv_sec = 0;
|
|
|
9c64ad |
+ waited_time.tv_usec = 0;
|
|
|
9c64ad |
|
|
|
9c64ad |
fd = open(XT_LOCK_NAME, O_CREAT, 0600);
|
|
|
9c64ad |
if (fd < 0)
|
|
|
9c64ad |
@@ -255,12 +264,43 @@ bool xtables_lock(int wait)
|
|
|
9c64ad |
while (1) {
|
|
|
9c64ad |
if (flock(fd, LOCK_EX | LOCK_NB) == 0)
|
|
|
9c64ad |
return true;
|
|
|
9c64ad |
- else if (wait >= 0 && waited >= wait)
|
|
|
9c64ad |
+ if (++i % 10 == 0) {
|
|
|
9c64ad |
+ if (wait != -1)
|
|
|
9c64ad |
+ fprintf(stderr, "Another app is currently holding the xtables lock; "
|
|
|
9c64ad |
+ "still %lds %ldus time ahead to have a chance to grab the lock...\n",
|
|
|
9c64ad |
+ time_left.tv_sec, time_left.tv_usec);
|
|
|
9c64ad |
+ else
|
|
|
9c64ad |
+ fprintf(stderr, "Another app is currently holding the xtables lock; "
|
|
|
9c64ad |
+ "waiting for it to exit...\n");
|
|
|
9c64ad |
+ }
|
|
|
9c64ad |
+
|
|
|
9c64ad |
+ wait_time = *wait_interval;
|
|
|
9c64ad |
+ select(0, NULL, NULL, NULL, &wait_time);
|
|
|
9c64ad |
+ if (wait == -1)
|
|
|
9c64ad |
+ continue;
|
|
|
9c64ad |
+
|
|
|
9c64ad |
+ timeradd(&waited_time, wait_interval, &waited_time);
|
|
|
9c64ad |
+ timersub(&time_left, wait_interval, &time_left);
|
|
|
9c64ad |
+ if (!timerisset(&time_left))
|
|
|
9c64ad |
return false;
|
|
|
9c64ad |
- if (++i % 2 == 0)
|
|
|
9c64ad |
- fprintf(stderr, "Another app is currently holding the xtables lock; "
|
|
|
9c64ad |
- "waiting (%ds) for it to exit...\n", waited);
|
|
|
9c64ad |
- waited++;
|
|
|
9c64ad |
- sleep(1);
|
|
|
9c64ad |
}
|
|
|
9c64ad |
}
|
|
|
9c64ad |
+
|
|
|
9c64ad |
+void parse_wait_interval(const char *str, struct timeval *wait_interval)
|
|
|
9c64ad |
+{
|
|
|
9c64ad |
+ unsigned int usec;
|
|
|
9c64ad |
+ int ret;
|
|
|
9c64ad |
+
|
|
|
9c64ad |
+ ret = sscanf(str, "%u", &usec);
|
|
|
9c64ad |
+ if (ret == 1) {
|
|
|
9c64ad |
+ if (usec > 999999)
|
|
|
9c64ad |
+ xtables_error(PARAMETER_PROBLEM,
|
|
|
9c64ad |
+ "too long usec wait %u > 999999 usec",
|
|
|
9c64ad |
+ usec);
|
|
|
9c64ad |
+
|
|
|
9c64ad |
+ wait_interval->tv_sec = 0;
|
|
|
9c64ad |
+ wait_interval->tv_usec = usec;
|
|
|
9c64ad |
+ return;
|
|
|
9c64ad |
+ }
|
|
|
9c64ad |
+ xtables_error(PARAMETER_PROBLEM, "wait interval not numeric");
|
|
|
9c64ad |
+}
|
|
|
9c64ad |
diff -up iptables-1.4.21/iptables/xshared.h.wait-interval iptables-1.4.21/iptables/xshared.h
|
|
|
9c64ad |
--- iptables-1.4.21/iptables/xshared.h.wait-interval 2017-04-05 14:04:04.555346605 +0200
|
|
|
9c64ad |
+++ iptables-1.4.21/iptables/xshared.h 2017-04-05 14:04:04.563346679 +0200
|
|
|
9c64ad |
@@ -84,7 +84,9 @@ extern struct xtables_match *load_proto(
|
|
|
9c64ad |
extern int subcmd_main(int, char **, const struct subcommand *);
|
|
|
9c64ad |
extern void xs_init_target(struct xtables_target *);
|
|
|
9c64ad |
extern void xs_init_match(struct xtables_match *);
|
|
|
9c64ad |
-extern bool xtables_lock(int wait);
|
|
|
9c64ad |
+bool xtables_lock(int wait, struct timeval *wait_interval);
|
|
|
9c64ad |
+
|
|
|
9c64ad |
+void parse_wait_interval(const char *str, struct timeval *wait_interval);
|
|
|
9c64ad |
|
|
|
9c64ad |
extern const struct xtables_afinfo *afinfo;
|
|
|
9c64ad |
|