Commit 7e95a8db authored by Arturo Borrero Gonzalez's avatar Arturo Borrero Gonzalez
Browse files

Imported Upstream version 1.4.21

parents
Implements network quotas by decrementing a byte counter with each
packet. The condition matches until the byte counter reaches zero. Behavior
is reversed with negation (i.e. the condition does not match until the
byte counter reaches zero).
.TP
[\fB!\fP] \fB\-\-quota\fP \fIbytes\fP
The quota in bytes.
/*
* Copyright (c) 2008-2013 Patrick McHardy <kaber@trash.net>
*/
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stddef.h>
#include <getopt.h>
#include <xtables.h>
#include <linux/netfilter/xt_rateest.h>
static void rateest_help(void)
{
printf(
"rateest match options:\n"
" --rateest1 name Rate estimator name\n"
" --rateest2 name Rate estimator name\n"
" --rateest-delta Compare difference(s) to given rate(s)\n"
" --rateest-bps1 [bps] Compare bps\n"
" --rateest-pps1 [pps] Compare pps\n"
" --rateest-bps2 [bps] Compare bps\n"
" --rateest-pps2 [pps] Compare pps\n"
" [!] --rateest-lt Match if rate is less than given rate/estimator\n"
" [!] --rateest-gt Match if rate is greater than given rate/estimator\n"
" [!] --rateest-eq Match if rate is equal to given rate/estimator\n");
}
enum rateest_options {
OPT_RATEEST1,
OPT_RATEEST2,
OPT_RATEEST_BPS1,
OPT_RATEEST_PPS1,
OPT_RATEEST_BPS2,
OPT_RATEEST_PPS2,
OPT_RATEEST_DELTA,
OPT_RATEEST_LT,
OPT_RATEEST_GT,
OPT_RATEEST_EQ,
};
static const struct option rateest_opts[] = {
{.name = "rateest1", .has_arg = true, .val = OPT_RATEEST1},
{.name = "rateest", .has_arg = true, .val = OPT_RATEEST1}, /* alias for absolute mode */
{.name = "rateest2", .has_arg = true, .val = OPT_RATEEST2},
{.name = "rateest-bps1", .has_arg = false, .val = OPT_RATEEST_BPS1},
{.name = "rateest-pps1", .has_arg = false, .val = OPT_RATEEST_PPS1},
{.name = "rateest-bps2", .has_arg = false, .val = OPT_RATEEST_BPS2},
{.name = "rateest-pps2", .has_arg = false, .val = OPT_RATEEST_PPS2},
{.name = "rateest-bps", .has_arg = false, .val = OPT_RATEEST_BPS2}, /* alias for absolute mode */
{.name = "rateest-pps", .has_arg = false, .val = OPT_RATEEST_PPS2}, /* alias for absolute mode */
{.name = "rateest-delta", .has_arg = false, .val = OPT_RATEEST_DELTA},
{.name = "rateest-lt", .has_arg = false, .val = OPT_RATEEST_LT},
{.name = "rateest-gt", .has_arg = false, .val = OPT_RATEEST_GT},
{.name = "rateest-eq", .has_arg = false, .val = OPT_RATEEST_EQ},
XT_GETOPT_TABLEEND,
};
/* Copied from iproute. See http://physics.nist.gov/cuu/Units/binary.html */
static const struct rate_suffix {
const char *name;
double scale;
} suffixes[] = {
{ "bit", 1. },
{ "Kibit", 1024. },
{ "kbit", 1000. },
{ "Mibit", 1024.*1024. },
{ "mbit", 1000000. },
{ "Gibit", 1024.*1024.*1024. },
{ "gbit", 1000000000. },
{ "Tibit", 1024.*1024.*1024.*1024. },
{ "tbit", 1000000000000. },
{ "Bps", 8. },
{ "KiBps", 8.*1024. },
{ "KBps", 8000. },
{ "MiBps", 8.*1024*1024. },
{ "MBps", 8000000. },
{ "GiBps", 8.*1024.*1024.*1024. },
{ "GBps", 8000000000. },
{ "TiBps", 8.*1024.*1024.*1024.*1024. },
{ "TBps", 8000000000000. },
{NULL},
};
static int
rateest_get_rate(uint32_t *rate, const char *str)
{
char *p;
double bps = strtod(str, &p);
const struct rate_suffix *s;
if (p == str)
return -1;
if (*p == '\0') {
*rate = bps / 8.; /* assume bytes/sec */
return 0;
}
for (s = suffixes; s->name; ++s) {
if (strcasecmp(s->name, p) == 0) {
*rate = (bps * s->scale) / 8.;
return 0;
}
}
return -1;
}
static int
rateest_parse(int c, char **argv, int invert, unsigned int *flags,
const void *entry, struct xt_entry_match **match)
{
struct xt_rateest_match_info *info = (void *)(*match)->data;
unsigned int val;
switch (c) {
case OPT_RATEEST1:
if (invert)
xtables_error(PARAMETER_PROBLEM,
"rateest: rateest can't be inverted");
if (*flags & (1 << c))
xtables_error(PARAMETER_PROBLEM,
"rateest: can't specify --rateest1 twice");
*flags |= 1 << c;
strncpy(info->name1, optarg, sizeof(info->name1) - 1);
break;
case OPT_RATEEST2:
if (invert)
xtables_error(PARAMETER_PROBLEM,
"rateest: rateest can't be inverted");
if (*flags & (1 << c))
xtables_error(PARAMETER_PROBLEM,
"rateest: can't specify --rateest2 twice");
*flags |= 1 << c;
strncpy(info->name2, optarg, sizeof(info->name2) - 1);
info->flags |= XT_RATEEST_MATCH_REL;
break;
case OPT_RATEEST_BPS1:
if (invert)
xtables_error(PARAMETER_PROBLEM,
"rateest: rateest-bps can't be inverted");
if (*flags & (1 << c))
xtables_error(PARAMETER_PROBLEM,
"rateest: can't specify --rateest-bps1 twice");
*flags |= 1 << c;
info->flags |= XT_RATEEST_MATCH_BPS;
/* The rate is optional and only required in absolute mode */
if (!argv[optind] || *argv[optind] == '-' || *argv[optind] == '!')
break;
if (rateest_get_rate(&info->bps1, argv[optind]) < 0)
xtables_error(PARAMETER_PROBLEM,
"rateest: could not parse rate `%s'",
argv[optind]);
optind++;
break;
case OPT_RATEEST_PPS1:
if (invert)
xtables_error(PARAMETER_PROBLEM,
"rateest: rateest-pps can't be inverted");
if (*flags & (1 << c))
xtables_error(PARAMETER_PROBLEM,
"rateest: can't specify --rateest-pps1 twice");
*flags |= 1 << c;
info->flags |= XT_RATEEST_MATCH_PPS;
/* The rate is optional and only required in absolute mode */
if (!argv[optind] || *argv[optind] == '-' || *argv[optind] == '!')
break;
if (!xtables_strtoui(argv[optind], NULL, &val, 0, UINT32_MAX))
xtables_error(PARAMETER_PROBLEM,
"rateest: could not parse pps `%s'",
argv[optind]);
info->pps1 = val;
optind++;
break;
case OPT_RATEEST_BPS2:
if (invert)
xtables_error(PARAMETER_PROBLEM,
"rateest: rateest-bps can't be inverted");
if (*flags & (1 << c))
xtables_error(PARAMETER_PROBLEM,
"rateest: can't specify --rateest-bps2 twice");
*flags |= 1 << c;
info->flags |= XT_RATEEST_MATCH_BPS;
/* The rate is optional and only required in absolute mode */
if (!argv[optind] || *argv[optind] == '-' || *argv[optind] == '!')
break;
if (rateest_get_rate(&info->bps2, argv[optind]) < 0)
xtables_error(PARAMETER_PROBLEM,
"rateest: could not parse rate `%s'",
argv[optind]);
optind++;
break;
case OPT_RATEEST_PPS2:
if (invert)
xtables_error(PARAMETER_PROBLEM,
"rateest: rateest-pps can't be inverted");
if (*flags & (1 << c))
xtables_error(PARAMETER_PROBLEM,
"rateest: can't specify --rateest-pps2 twice");
*flags |= 1 << c;
info->flags |= XT_RATEEST_MATCH_PPS;
/* The rate is optional and only required in absolute mode */
if (!argv[optind] || *argv[optind] == '-' || *argv[optind] == '!')
break;
if (!xtables_strtoui(argv[optind], NULL, &val, 0, UINT32_MAX))
xtables_error(PARAMETER_PROBLEM,
"rateest: could not parse pps `%s'",
argv[optind]);
info->pps2 = val;
optind++;
break;
case OPT_RATEEST_DELTA:
if (invert)
xtables_error(PARAMETER_PROBLEM,
"rateest: rateest-delta can't be inverted");
if (*flags & (1 << c))
xtables_error(PARAMETER_PROBLEM,
"rateest: can't specify --rateest-delta twice");
*flags |= 1 << c;
info->flags |= XT_RATEEST_MATCH_DELTA;
break;
case OPT_RATEEST_EQ:
if (*flags & (1 << c))
xtables_error(PARAMETER_PROBLEM,
"rateest: can't specify lt/gt/eq twice");
*flags |= 1 << c;
info->mode = XT_RATEEST_MATCH_EQ;
if (invert)
info->flags |= XT_RATEEST_MATCH_INVERT;
break;
case OPT_RATEEST_LT:
if (*flags & (1 << c))
xtables_error(PARAMETER_PROBLEM,
"rateest: can't specify lt/gt/eq twice");
*flags |= 1 << c;
info->mode = XT_RATEEST_MATCH_LT;
if (invert)
info->flags |= XT_RATEEST_MATCH_INVERT;
break;
case OPT_RATEEST_GT:
if (*flags & (1 << c))
xtables_error(PARAMETER_PROBLEM,
"rateest: can't specify lt/gt/eq twice");
*flags |= 1 << c;
info->mode = XT_RATEEST_MATCH_GT;
if (invert)
info->flags |= XT_RATEEST_MATCH_INVERT;
break;
}
return 1;
}
static void rateest_final_check(struct xt_fcheck_call *cb)
{
struct xt_rateest_match_info *info = cb->data;
if (info == NULL)
xtables_error(PARAMETER_PROBLEM, "rateest match: "
"you need to specify some flags");
if (!(info->flags & XT_RATEEST_MATCH_REL))
info->flags |= XT_RATEEST_MATCH_ABS;
}
static void
rateest_print_rate(uint32_t rate, int numeric)
{
double tmp = (double)rate*8;
if (numeric)
printf(" %u", rate);
else if (tmp >= 1000.0*1000000.0)
printf(" %.0fMbit", tmp/1000000.0);
else if (tmp >= 1000.0 * 1000.0)
printf(" %.0fKbit", tmp/1000.0);
else
printf(" %.0fbit", tmp);
}
static void
rateest_print_mode(const struct xt_rateest_match_info *info,
const char *prefix)
{
if (info->flags & XT_RATEEST_MATCH_INVERT)
printf(" !");
switch (info->mode) {
case XT_RATEEST_MATCH_EQ:
printf(" %seq", prefix);
break;
case XT_RATEEST_MATCH_LT:
printf(" %slt", prefix);
break;
case XT_RATEEST_MATCH_GT:
printf(" %sgt", prefix);
break;
default:
exit(1);
}
}
static void
rateest_print(const void *ip, const struct xt_entry_match *match, int numeric)
{
const struct xt_rateest_match_info *info = (const void *)match->data;
printf(" rateest match ");
printf("%s", info->name1);
if (info->flags & XT_RATEEST_MATCH_DELTA)
printf(" delta");
if (info->flags & XT_RATEEST_MATCH_BPS) {
printf(" bps");
if (info->flags & XT_RATEEST_MATCH_DELTA)
rateest_print_rate(info->bps1, numeric);
if (info->flags & XT_RATEEST_MATCH_ABS) {
rateest_print_rate(info->bps2, numeric);
rateest_print_mode(info, "");
}
}
if (info->flags & XT_RATEEST_MATCH_PPS) {
printf(" pps");
if (info->flags & XT_RATEEST_MATCH_DELTA)
printf(" %u", info->pps1);
if (info->flags & XT_RATEEST_MATCH_ABS) {
rateest_print_mode(info, "");
printf(" %u", info->pps2);
}
}
if (info->flags & XT_RATEEST_MATCH_REL) {
rateest_print_mode(info, "");
printf(" %s", info->name2);
if (info->flags & XT_RATEEST_MATCH_BPS) {
printf(" bps");
if (info->flags & XT_RATEEST_MATCH_DELTA)
rateest_print_rate(info->bps2, numeric);
}
if (info->flags & XT_RATEEST_MATCH_PPS) {
printf(" pps");
if (info->flags & XT_RATEEST_MATCH_DELTA)
printf(" %u", info->pps2);
}
}
}
static void __rateest_save_rate(const struct xt_rateest_match_info *info,
const char *name, uint32_t r1, uint32_t r2,
int numeric)
{
if (info->flags & XT_RATEEST_MATCH_DELTA) {
printf(" --rateest-%s1", name);
rateest_print_rate(r1, numeric);
rateest_print_mode(info, "--rateest-");
printf(" --rateest-%s2", name);
} else {
rateest_print_mode(info, "--rateest-");
printf(" --rateest-%s", name);
}
if (info->flags & (XT_RATEEST_MATCH_ABS|XT_RATEEST_MATCH_DELTA))
rateest_print_rate(r2, numeric);
}
static void rateest_save_rates(const struct xt_rateest_match_info *info)
{
if (info->flags & XT_RATEEST_MATCH_BPS)
__rateest_save_rate(info, "bps", info->bps1, info->bps2, 0);
if (info->flags & XT_RATEEST_MATCH_PPS)
__rateest_save_rate(info, "pps", info->pps1, info->pps2, 1);
}
static void
rateest_save(const void *ip, const struct xt_entry_match *match)
{
const struct xt_rateest_match_info *info = (const void *)match->data;
if (info->flags & XT_RATEEST_MATCH_DELTA)
printf(" --rateest-delta");
if (info->flags & XT_RATEEST_MATCH_REL) {
printf(" --rateest1 %s", info->name1);
rateest_save_rates(info);
printf(" --rateest2 %s", info->name2);
} else { /* XT_RATEEST_MATCH_ABS */
printf(" --rateest %s", info->name1);
rateest_save_rates(info);
}
}
static struct xtables_match rateest_mt_reg = {
.family = NFPROTO_UNSPEC,
.name = "rateest",
.version = XTABLES_VERSION,
.size = XT_ALIGN(sizeof(struct xt_rateest_match_info)),
.userspacesize = XT_ALIGN(offsetof(struct xt_rateest_match_info, est1)),
.help = rateest_help,
.parse = rateest_parse,
.x6_fcheck = rateest_final_check,
.print = rateest_print,
.save = rateest_save,
.extra_opts = rateest_opts,
};
void _init(void)
{
xtables_register_match(&rateest_mt_reg);
}
The rate estimator can match on estimated rates as collected by the RATEEST
target. It supports matching on absolute bps/pps values, comparing two rate
estimators and matching on the difference between two rate estimators.
.PP
For a better understanding of the available options, these are all possible
combinations:
.\" * Absolute:
.IP \(bu 4
\fBrateest\fP \fIoperator\fP \fBrateest-bps\fP
.IP \(bu 4
\fBrateest\fP \fIoperator\fP \fBrateest-pps\fP
.\" * Absolute + Delta:
.IP \(bu 4
(\fBrateest\fP minus \fBrateest-bps1\fP) \fIoperator\fP \fBrateest-bps2\fP
.IP \(bu 4
(\fBrateest\fP minus \fBrateest-pps1\fP) \fIoperator\fP \fBrateest-pps2\fP
.\" * Relative:
.IP \(bu 4
\fBrateest1\fP \fIoperator\fP \fBrateest2\fP \fBrateest-bps\fP(without rate!)
.IP \(bu 4
\fBrateest1\fP \fIoperator\fP \fBrateest2\fP \fBrateest-pps\fP(without rate!)
.\" * Relative + Delta:
.IP \(bu 4
(\fBrateest1\fP minus \fBrateest-bps1\fP) \fIoperator\fP
(\fBrateest2\fP minus \fBrateest-bps2\fP)
.IP \(bu 4
(\fBrateest1\fP minus \fBrateest-pps1\fP) \fIoperator\fP
(\fBrateest2\fP minus \fBrateest-pps2\fP)
.TP
\fB\-\-rateest\-delta\fP
For each estimator (either absolute or relative mode), calculate the difference
between the estimator-determined flow rate and the static value chosen with the
BPS/PPS options. If the flow rate is higher than the specified BPS/PPS, 0 will
be used instead of a negative value. In other words, "max(0, rateest#_rate -
rateest#_bps)" is used.
.TP
[\fB!\fP] \fB\-\-rateest\-lt\fP
Match if rate is less than given rate/estimator.
.TP
[\fB!\fP] \fB\-\-rateest\-gt\fP
Match if rate is greater than given rate/estimator.
.TP
[\fB!\fP] \fB\-\-rateest\-eq\fP
Match if rate is equal to given rate/estimator.
.PP
In the so-called "absolute mode", only one rate estimator is used and compared
against a static value, while in "relative mode", two rate estimators are
compared against another.
.TP
\fB\-\-rateest\fP \fIname\fP
Name of the one rate estimator for absolute mode.
.TP
\fB\-\-rateest1\fP \fIname\fP
.TP
\fB\-\-rateest2\fP \fIname\fP
The names of the two rate estimators for relative mode.
.TP
\fB\-\-rateest\-bps\fP [\fIvalue\fP]
.TP
\fB\-\-rateest\-pps\fP [\fIvalue\fP]
.TP
\fB\-\-rateest\-bps1\fP [\fIvalue\fP]
.TP
\fB\-\-rateest\-bps2\fP [\fIvalue\fP]
.TP
\fB\-\-rateest\-pps1\fP [\fIvalue\fP]
.TP
\fB\-\-rateest\-pps2\fP [\fIvalue\fP]
Compare the estimator(s) by bytes or packets per second, and compare against
the chosen value. See the above bullet list for which option is to be used in
which case. A unit suffix may be used - available ones are: bit, [kmgt]bit,
[KMGT]ibit, Bps, [KMGT]Bps, [KMGT]iBps.
.PP
Example: This is what can be used to route outgoing data connections from an
FTP server over two lines based on the available bandwidth at the time the data
connection was started:
.PP
# Estimate outgoing rates
.PP
iptables \-t mangle \-A POSTROUTING \-o eth0 \-j RATEEST \-\-rateest\-name eth0
\-\-rateest\-interval 250ms \-\-rateest\-ewma 0.5s
.PP
iptables \-t mangle \-A POSTROUTING \-o ppp0 \-j RATEEST \-\-rateest\-name ppp0
\-\-rateest\-interval 250ms \-\-rateest\-ewma 0.5s
.PP
# Mark based on available bandwidth
.PP
iptables \-t mangle \-A balance \-m conntrack \-\-ctstate NEW \-m helper \-\-helper ftp
\-m rateest \-\-rateest\-delta \-\-rateest1 eth0 \-\-rateest\-bps1 2.5mbit \-\-rateest\-gt
\-\-rateest2 ppp0 \-\-rateest\-bps2 2mbit \-j CONNMARK \-\-set\-mark 1
.PP
iptables \-t mangle \-A balance \-m conntrack \-\-ctstate NEW \-m helper \-\-helper ftp
\-m rateest \-\-rateest\-delta \-\-rateest1 ppp0 \-\-rateest\-bps1 2mbit \-\-rateest\-gt
\-\-rateest2 eth0 \-\-rateest\-bps2 2.5mbit \-j CONNMARK \-\-set\-mark 2
.PP
iptables \-t mangle \-A balance \-j CONNMARK \-\-restore\-mark
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <xtables.h>
#include <linux/netfilter/xt_recent.h>
enum {
O_SET = 0,
O_RCHECK,
O_UPDATE,
O_REMOVE,
O_SECONDS,
O_REAP,
O_HITCOUNT,
O_RTTL,
O_NAME,
O_RSOURCE,
O_RDEST,
O_MASK,
F_SET = 1 << O_SET,
F_RCHECK = 1 << O_RCHECK,
F_UPDATE = 1 << O_UPDATE,
F_REMOVE = 1 << O_REMOVE,
F_SECONDS = 1 << O_SECONDS,
F_ANY_OP = F_SET | F_RCHECK | F_UPDATE | F_REMOVE,
};
#define s struct xt_recent_mtinfo
static const struct xt_option_entry recent_opts_v0[] = {
{.name = "set", .id = O_SET, .type = XTTYPE_NONE,
.excl = F_ANY_OP, .flags = XTOPT_INVERT},
{.name = "rcheck", .id = O_RCHECK, .type = XTTYPE_NONE,
.excl = F_ANY_OP, .flags = XTOPT_INVERT},
{.name = "update", .id = O_UPDATE, .type = XTTYPE_NONE,
.excl = F_ANY_OP, .flags = XTOPT_INVERT},
{.name = "remove", .id = O_REMOVE, .type = XTTYPE_NONE,
.excl = F_ANY_OP, .flags = XTOPT_INVERT},
{.name = "seconds", .id = O_SECONDS, .type = XTTYPE_UINT32,
.flags = XTOPT_PUT, XTOPT_POINTER(s, seconds), .min = 1},
{.name = "reap", .id = O_REAP, .type = XTTYPE_NONE,
.also = F_SECONDS },
{.name = "hitcount", .id = O_HITCOUNT, .type = XTTYPE_UINT32,
.flags = XTOPT_PUT, XTOPT_POINTER(s, hit_count)},
{.name = "rttl", .id = O_RTTL, .type = XTTYPE_NONE,
.excl = F_SET | F_REMOVE},
{.name = "name", .id = O_NAME, .type = XTTYPE_STRING,
.flags = XTOPT_PUT, XTOPT_POINTER(s, name)},
{.name = "rsource", .id = O_RSOURCE, .type = XTTYPE_NONE},
{.name = "rdest", .id = O_RDEST, .type = XTTYPE_NONE},
XTOPT_TABLEEND,
};
#undef s
#define s struct xt_recent_mtinfo_v1
static const struct xt_option_entry recent_opts_v1[] = {
{.name = "set", .id = O_SET, .type = XTTYPE_NONE,
.excl = F_ANY_OP, .flags = XTOPT_INVERT},
{.name = "rcheck", .id = O_RCHECK, .type = XTTYPE_NONE,
.excl = F_ANY_OP, .flags = XTOPT_INVERT},
{.name = "update", .id = O_UPDATE, .type = XTTYPE_NONE,
.excl = F_ANY_OP, .flags = XTOPT_INVERT},
{.name = "remove", .id = O_REMOVE, .type = XTTYPE_NONE,
.excl = F_ANY_OP, .flags = XTOPT_INVERT},
{.name = "seconds", .id = O_SECONDS, .type = XTTYPE_UINT32,
.flags = XTOPT_PUT, XTOPT_POINTER(s, seconds), .min = 1},
{.name = "reap", .id = O_REAP, .type = XTTYPE_NONE,
.also = F_SECONDS },
{.name = "hitcount", .id = O_HITCOUNT, .type = XTTYPE_UINT32,
.flags = XTOPT_PUT, XTOPT_POINTER(s, hit_count)},
{.name = "rttl", .id = O_RTTL, .type = XTTYPE_NONE,
.excl = F_SET | F_REMOVE},
{.name = "name", .id = O_NAME, .type = XTTYPE_STRING,
.flags = XTOPT_PUT, XTOPT_POINTER(s, name)},
{.name = "rsource", .id = O_RSOURCE, .type = XTTYPE_NONE},
{.name = "rdest", .id = O_RDEST, .type = XTTYPE_NONE},
{.name = "mask", .id = O_MASK, .type = XTTYPE_HOST,
.flags = XTOPT_PUT, XTOPT_POINTER(s, mask)},
XTOPT_TABLEEND,
};
#undef s
static void recent_help(void)
{
printf(
"recent match options:\n"
"[!] --set Add source address to list, always matches.\n"
"[!] --rcheck Match if source address in list.\n"
"[!] --update Match if source address in list, also update last-seen time.\n"
"[!] --remove Match if source address in list, also removes that address from list.\n"
" --seconds seconds For check and update commands above.\n"
" Specifies that the match will only occur if source address last seen within\n"
" the last 'seconds' seconds.\n"
" --reap Purge entries older then 'seconds'.\n"
" Can only be used in conjunction with the seconds option.\n"
" --hitcount hits For check and update commands above.\n"
" Specifies that the match will only occur if source address seen hits times.\n"
" May be used in conjunction with the seconds option.\n"
" --rttl For check and update commands above.\n"
" Specifies that the match will only occur if the source address and the TTL\n"
" match between this packet and the one which was set.\n"
" Useful if you have problems with people spoofing their source address in order\n"
" to DoS you via this module.\n"
" --name name Name of the recent list to be used. DEFAULT used if none given.\n"
" --rsource Match/Save the source address of each packet in the recent list table (default).\n"
" --rdest Match/Save the destination address of each packet in the recent list table.\n"
" --mask netmask Netmask that will be applied to this recent list.\n"
"xt_recent by: Stephen Frost <sfrost@snowman.net>.\n");
}
enum {
XT_RECENT_REV_0 = 0,
XT_RECENT_REV_1,
};
static void recent_init(struct xt_entry_match *match, unsigned int rev)
{
struct xt_recent_mtinfo *info = (struct xt_recent_mtinfo *)match->data;
struct xt_recent_mtinfo_v1 *info_v1 =
(struct xt_recent_mtinfo_v1 *)match->data;
strncpy(info->name,"DEFAULT", XT_RECENT_NAME_LEN);
/* even though XT_RECENT_NAME_LEN is currently defined as 200,
* better be safe, than sorry */
info->name[XT_RECENT_NAME_LEN-1] = '\0';
info->side = XT_RECENT_SOURCE;
if (rev == XT_RECENT_REV_1)
memset(&info_v1->mask, 0xFF, sizeof(info_v1->mask));
}
static void recent_parse(struct xt_option_call *cb)
{
struct xt_recent_mtinfo *info = cb->data;
xtables_option_parse(cb);
switch (cb->entry->id) {
case O_SET:
info->check_set |= XT_RECENT_SET;
if (cb->invert)
info->invert = true;
break;
case O_RCHECK:
info->check_set |= XT_RECENT_CHECK;
if (cb->invert)
info->invert = true;
break;
case O_UPDATE:
info->check_set |= XT_RECENT_UPDATE;
if (cb->invert)
info->invert = true;
break;
case O_REMOVE:
info->check_set |= XT_RECENT_REMOVE;
if (cb->invert)
info->invert = true;
break;
case O_RTTL:
info->check_set |= XT_RECENT_TTL;
break;
case O_RSOURCE:
info->side = XT_RECENT_SOURCE;
break;
case O_RDEST:
info->side = XT_RECENT_DEST;
break;
case O_REAP:
info->check_set |= XT_RECENT_REAP;
break;
}
}
static void recent_check(struct xt_fcheck_call *cb)
{
if (!(cb->xflags & F_ANY_OP))
xtables_error(PARAMETER_PROBLEM,
"recent: you must specify one of `--set', `--rcheck' "
"`--update' or `--remove'");
}
static void recent_print(const void *ip, const struct xt_entry_match *match,
unsigned int family)
{
const struct xt_recent_mtinfo_v1 *info = (const void *)match->data;
if (info->invert)
printf(" !");
printf(" recent:");
if (info->check_set & XT_RECENT_SET)
printf(" SET");
if (info->check_set & XT_RECENT_CHECK)
printf(" CHECK");
if (info->check_set & XT_RECENT_UPDATE)
printf(" UPDATE");
if (info->check_set & XT_RECENT_REMOVE)
printf(" REMOVE");
if(info->seconds) printf(" seconds: %d", info->seconds);
if (info->check_set & XT_RECENT_REAP)
printf(" reap");
if(info->hit_count) printf(" hit_count: %d", info->hit_count);
if (info->check_set & XT_RECENT_TTL)
printf(" TTL-Match");
if(info->name) printf(" name: %s", info->name);
if (info->side == XT_RECENT_SOURCE)
printf(" side: source");
if (info->side == XT_RECENT_DEST)
printf(" side: dest");
switch(family) {
case NFPROTO_IPV4:
printf(" mask: %s",
xtables_ipaddr_to_numeric(&info->mask.in));
break;
case NFPROTO_IPV6:
printf(" mask: %s",
xtables_ip6addr_to_numeric(&info->mask.in6));
break;
}
}
static void recent_save(const void *ip, const struct xt_entry_match *match,
unsigned int family)
{
const struct xt_recent_mtinfo_v1 *info = (const void *)match->data;
if (info->invert)
printf(" !");
if (info->check_set & XT_RECENT_SET)
printf(" --set");
if (info->check_set & XT_RECENT_CHECK)
printf(" --rcheck");
if (info->check_set & XT_RECENT_UPDATE)
printf(" --update");
if (info->check_set & XT_RECENT_REMOVE)
printf(" --remove");
if(info->seconds) printf(" --seconds %d", info->seconds);
if (info->check_set & XT_RECENT_REAP)
printf(" --reap");
if(info->hit_count) printf(" --hitcount %d", info->hit_count);
if (info->check_set & XT_RECENT_TTL)
printf(" --rttl");
if(info->name) printf(" --name %s",info->name);
switch(family) {
case NFPROTO_IPV4:
printf(" --mask %s",
xtables_ipaddr_to_numeric(&info->mask.in));
break;
case NFPROTO_IPV6:
printf(" --mask %s",
xtables_ip6addr_to_numeric(&info->mask.in6));
break;
}
if (info->side == XT_RECENT_SOURCE)
printf(" --rsource");
if (info->side == XT_RECENT_DEST)
printf(" --rdest");
}
static void recent_init_v0(struct xt_entry_match *match)
{
recent_init(match, XT_RECENT_REV_0);
}
static void recent_init_v1(struct xt_entry_match *match)
{
recent_init(match, XT_RECENT_REV_1);
}
static void recent_save_v0(const void *ip, const struct xt_entry_match *match)
{
recent_save(ip, match, NFPROTO_UNSPEC);
}
static void recent_save_v4(const void *ip, const struct xt_entry_match *match)
{
recent_save(ip, match, NFPROTO_IPV4);
}
static void recent_save_v6(const void *ip, const struct xt_entry_match *match)
{
recent_save(ip, match, NFPROTO_IPV6);
}
static void recent_print_v0(const void *ip, const struct xt_entry_match *match,
int numeric)
{
recent_print(ip, match, NFPROTO_UNSPEC);
}
static void recent_print_v4(const void *ip, const struct xt_entry_match *match,
int numeric)
{
recent_print(ip, match, NFPROTO_IPV4);
}
static void recent_print_v6(const void *ip, const struct xt_entry_match *match,
int numeric)
{
recent_print(ip, match, NFPROTO_IPV6);
}
static struct xtables_match recent_mt_reg[] = {
{
.name = "recent",
.version = XTABLES_VERSION,
.revision = 0,
.family = NFPROTO_UNSPEC,
.size = XT_ALIGN(sizeof(struct xt_recent_mtinfo)),
.userspacesize = XT_ALIGN(sizeof(struct xt_recent_mtinfo)),
.help = recent_help,
.init = recent_init_v0,
.x6_parse = recent_parse,
.x6_fcheck = recent_check,
.print = recent_print_v0,
.save = recent_save_v0,
.x6_options = recent_opts_v0,
},
{
.name = "recent",
.version = XTABLES_VERSION,
.revision = 1,
.family = NFPROTO_IPV4,
.size = XT_ALIGN(sizeof(struct xt_recent_mtinfo_v1)),
.userspacesize = XT_ALIGN(sizeof(struct xt_recent_mtinfo_v1)),
.help = recent_help,
.init = recent_init_v1,
.x6_parse = recent_parse,
.x6_fcheck = recent_check,
.print = recent_print_v4,
.save = recent_save_v4,
.x6_options = recent_opts_v1,
},
{
.name = "recent",
.version = XTABLES_VERSION,
.revision = 1,
.family = NFPROTO_IPV6,
.size = XT_ALIGN(sizeof(struct xt_recent_mtinfo_v1)),
.userspacesize = XT_ALIGN(sizeof(struct xt_recent_mtinfo_v1)),
.help = recent_help,
.init = recent_init_v1,
.x6_parse = recent_parse,
.x6_fcheck = recent_check,
.print = recent_print_v6,
.save = recent_save_v6,
.x6_options = recent_opts_v1,
},
};
void _init(void)
{
xtables_register_matches(recent_mt_reg, ARRAY_SIZE(recent_mt_reg));
}
Allows you to dynamically create a list of IP addresses and then match against
that list in a few different ways.
.PP
For example, you can create a "badguy" list out of people attempting to connect
to port 139 on your firewall and then DROP all future packets from them without
considering them.
.PP
\fB\-\-set\fP, \fB\-\-rcheck\fP, \fB\-\-update\fP and \fB\-\-remove\fP are
mutually exclusive.
.TP
\fB\-\-name\fP \fIname\fP
Specify the list to use for the commands. If no name is given then
\fBDEFAULT\fP will be used.
.TP
[\fB!\fP] \fB\-\-set\fP
This will add the source address of the packet to the list. If the source
address is already in the list, this will update the existing entry. This will
always return success (or failure if \fB!\fP is passed in).
.TP
\fB\-\-rsource\fP
Match/save the source address of each packet in the recent list table. This
is the default.
.TP
\fB\-\-rdest\fP
Match/save the destination address of each packet in the recent list table.
.TP
\fB\-\-mask\fP \fInetmask\fP
Netmask that will be applied to this recent list.
.TP
[\fB!\fP] \fB\-\-rcheck\fP
Check if the source address of the packet is currently in the list.
.TP
[\fB!\fP] \fB\-\-update\fP
Like \fB\-\-rcheck\fP, except it will update the "last seen" timestamp if it
matches.
.TP
[\fB!\fP] \fB\-\-remove\fP
Check if the source address of the packet is currently in the list and if so
that address will be removed from the list and the rule will return true. If
the address is not found, false is returned.
.TP
\fB\-\-seconds\fP \fIseconds\fP
This option must be used in conjunction with one of \fB\-\-rcheck\fP or
\fB\-\-update\fP. When used, this will narrow the match to only happen when the
address is in the list and was seen within the last given number of seconds.
.TP
\fB\-\-reap\fP
This option can only be used in conjunction with \fB\-\-seconds\fP.
When used, this will cause entries older than the last given number of seconds
to be purged.
.TP
\fB\-\-hitcount\fP \fIhits\fP
This option must be used in conjunction with one of \fB\-\-rcheck\fP or
\fB\-\-update\fP. When used, this will narrow the match to only happen when the
address is in the list and packets had been received greater than or equal to
the given value. This option may be used along with \fB\-\-seconds\fP to create
an even narrower match requiring a certain number of hits within a specific
time frame. The maximum value for the hitcount parameter is given by the
"ip_pkt_list_tot" parameter of the xt_recent kernel module. Exceeding this
value on the command line will cause the rule to be rejected.
.TP
\fB\-\-rttl\fP
This option may only be used in conjunction with one of \fB\-\-rcheck\fP or
\fB\-\-update\fP. When used, this will narrow the match to only happen when the
address is in the list and the TTL of the current packet matches that of the
packet which hit the \fB\-\-set\fP rule. This may be useful if you have problems
with people faking their source address in order to DoS you via this module by
disallowing others access to your site by sending bogus packets to you.
.PP
Examples:
.IP
iptables \-A FORWARD \-m recent \-\-name badguy \-\-rcheck \-\-seconds 60 \-j DROP
.IP
iptables \-A FORWARD \-p tcp \-i eth0 \-\-dport 139 \-m recent \-\-name badguy \-\-set \-j DROP
.PP
\fB/proc/net/xt_recent/*\fP are the current lists of addresses and information
about each entry of each list.
.PP
Each file in \fB/proc/net/xt_recent/\fP can be read from to see the current
list or written two using the following commands to modify the list:
.TP
\fBecho +\fP\fIaddr\fP\fB >/proc/net/xt_recent/DEFAULT\fP
to add \fIaddr\fP to the DEFAULT list
.TP
\fBecho \-\fP\fIaddr\fP\fB >/proc/net/xt_recent/DEFAULT\fP
to remove \fIaddr\fP from the DEFAULT list
.TP
\fBecho / >/proc/net/xt_recent/DEFAULT\fP
to flush the DEFAULT list (remove all entries).
.PP
The module itself accepts parameters, defaults shown:
.TP
\fBip_list_tot\fP=\fI100\fP
Number of addresses remembered per table.
.TP
\fBip_pkt_list_tot\fP=\fI20\fP
Number of packets per address remembered.
.TP
\fBip_list_hash_size\fP=\fI0\fP
Hash table size. 0 means to calculate it based on ip_list_tot, default: 512.
.TP
\fBip_list_perms\fP=\fI0644\fP
Permissions for /proc/net/xt_recent/* files.
.TP
\fBip_list_uid\fP=\fI0\fP
Numerical UID for ownership of /proc/net/xt_recent/* files.
.TP
\fBip_list_gid\fP=\fI0\fP
Numerical GID for ownership of /proc/net/xt_recent/* files.
#include <stdio.h>
#include <xtables.h>
#include <linux/netfilter/xt_rpfilter.h>
enum {
O_RPF_LOOSE = 0,
O_RPF_VMARK = 1,
O_RPF_ACCEPT_LOCAL = 2,
O_RPF_INVERT = 3,
};
static void rpfilter_help(void)
{
printf(
"rpfilter match options:\n"
" --loose permit reverse path via any interface\n"
" --validmark use skb nfmark when performing route lookup\n"
" --accept-local do not reject packets with a local source address\n"
" --invert match packets that failed the reverse path test\n"
);
}
static const struct xt_option_entry rpfilter_opts[] = {
{.name = "loose", .id = O_RPF_LOOSE, .type = XTTYPE_NONE, },
{.name = "validmark", .id = O_RPF_VMARK, .type = XTTYPE_NONE, },
{.name = "accept-local", .id = O_RPF_ACCEPT_LOCAL, .type = XTTYPE_NONE, },
{.name = "invert", .id = O_RPF_INVERT, .type = XTTYPE_NONE, },
XTOPT_TABLEEND,
};
static void rpfilter_parse(struct xt_option_call *cb)
{
struct xt_rpfilter_info *rpfinfo = cb->data;
xtables_option_parse(cb);
switch (cb->entry->id) {
case O_RPF_LOOSE:
rpfinfo->flags |= XT_RPFILTER_LOOSE;
break;
case O_RPF_VMARK:
rpfinfo->flags |= XT_RPFILTER_VALID_MARK;
break;
case O_RPF_ACCEPT_LOCAL:
rpfinfo->flags |= XT_RPFILTER_ACCEPT_LOCAL;
break;
case O_RPF_INVERT:
rpfinfo->flags |= XT_RPFILTER_INVERT;
break;
}
}
static void
rpfilter_print_prefix(const void *ip, const void *matchinfo,
const char *prefix)
{
const struct xt_rpfilter_info *info = matchinfo;
if (info->flags & XT_RPFILTER_LOOSE)
printf(" %s%s", prefix, rpfilter_opts[O_RPF_LOOSE].name);
if (info->flags & XT_RPFILTER_VALID_MARK)
printf(" %s%s", prefix, rpfilter_opts[O_RPF_VMARK].name);
if (info->flags & XT_RPFILTER_ACCEPT_LOCAL)
printf(" %s%s", prefix, rpfilter_opts[O_RPF_ACCEPT_LOCAL].name);
if (info->flags & XT_RPFILTER_INVERT)
printf(" %s%s", prefix, rpfilter_opts[O_RPF_INVERT].name);
}
static void
rpfilter_print(const void *ip, const struct xt_entry_match *match, int numeric)
{
printf(" rpfilter");
return rpfilter_print_prefix(ip, match->data, "");
}
static void rpfilter_save(const void *ip, const struct xt_entry_match *match)
{
return rpfilter_print_prefix(ip, match->data, "--");
}
static struct xtables_match rpfilter_match = {
.family = NFPROTO_UNSPEC,
.name = "rpfilter",
.version = XTABLES_VERSION,
.size = XT_ALIGN(sizeof(struct xt_rpfilter_info)),
.userspacesize = XT_ALIGN(sizeof(struct xt_rpfilter_info)),
.help = rpfilter_help,
.print = rpfilter_print,
.save = rpfilter_save,
.x6_parse = rpfilter_parse,
.x6_options = rpfilter_opts,
};
void _init(void)
{
xtables_register_match(&rpfilter_match);
}
Performs a reverse path filter test on a packet.
If a reply to the packet would be sent via the same interface
that the packet arrived on, the packet will match.
Note that, unlike the in-kernel rp_filter, packets protected
by IPSec are not treated specially. Combine this match with
the policy match if you want this.
Also, packets arriving via the loopback interface are always permitted.
This match can only be used in the PREROUTING chain of the raw or mangle table.
.TP
\fB\-\-loose\fP
Used to specifiy that the reverse path filter test should match
even if the selected output device is not the expected one.
.TP
\fB\-\-validmark\fP
Also use the packets' nfmark value when performing the reverse path route lookup.
.TP
\fB\-\-accept\-local\fP
This will permit packets arriving from the network with a source address that is also
assigned to the local machine.
.TP
\fB\-\-invert\fP
This will invert the sense of the match. Instead of matching packets that passed the
reverse path filter test, match those that have failed it.
.PP
Example to log and drop packets failing the reverse path filter test:
iptables \-t raw \-N RPFILTER
iptables \-t raw \-A RPFILTER \-m rpfilter \-j RETURN
iptables \-t raw \-A RPFILTER \-m limit \-\-limit 10/minute \-j NFLOG \-\-nflog\-prefix "rpfilter drop"
iptables \-t raw \-A RPFILTER \-j DROP
iptables \-t raw \-A PREROUTING \-j RPFILTER
Example to drop failed packets, without logging:
iptables \-t raw \-A RPFILTER \-m rpfilter \-\-invert \-j DROP
/* Shared library add-on to iptables for SCTP matching
*
* (C) 2003 by Harald Welte <laforge@gnumonks.org>
*
* This program is distributed under the terms of GNU GPL v2, 1991
*
* libipt_ecn.c borrowed heavily from libipt_dscp.c
*
*/
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <getopt.h>
#include <netdb.h>
#include <ctype.h>
#include <netinet/in.h>
#include <xtables.h>
#include <linux/netfilter/xt_sctp.h>
#if 0
#define DEBUGP(format, first...) printf(format, ##first)
#define static
#else
#define DEBUGP(format, fist...)
#endif
static void
print_chunk(uint32_t chunknum, int numeric);
static void sctp_init(struct xt_entry_match *m)
{
int i;
struct xt_sctp_info *einfo = (struct xt_sctp_info *)m->data;
for (i = 0; i < XT_NUM_SCTP_FLAGS; i++) {
einfo->flag_info[i].chunktype = -1;
}
}
static void sctp_help(void)
{
printf(
"sctp match options\n"
"[!] --source-port port[:port] match source port(s)\n"
" --sport ...\n"
"[!] --destination-port port[:port] match destination port(s)\n"
" --dport ...\n"
"[!] --chunk-types (all|any|none) (chunktype[:flags])+ match if all, any or none of\n"
" chunktypes are present\n"
"chunktypes - DATA INIT INIT_ACK SACK HEARTBEAT HEARTBEAT_ACK ABORT SHUTDOWN SHUTDOWN_ACK ERROR COOKIE_ECHO COOKIE_ACK ECN_ECNE ECN_CWR SHUTDOWN_COMPLETE ASCONF ASCONF_ACK FORWARD_TSN ALL NONE\n");
}
static const struct option sctp_opts[] = {
{.name = "source-port", .has_arg = true, .val = '1'},
{.name = "sport", .has_arg = true, .val = '1'},
{.name = "destination-port", .has_arg = true, .val = '2'},
{.name = "dport", .has_arg = true, .val = '2'},
{.name = "chunk-types", .has_arg = true, .val = '3'},
XT_GETOPT_TABLEEND,
};
static void
parse_sctp_ports(const char *portstring,
uint16_t *ports)
{
char *buffer;
char *cp;
buffer = strdup(portstring);
DEBUGP("%s\n", portstring);
if ((cp = strchr(buffer, ':')) == NULL) {
ports[0] = ports[1] = xtables_parse_port(buffer, "sctp");
}
else {
*cp = '\0';
cp++;
ports[0] = buffer[0] ? xtables_parse_port(buffer, "sctp") : 0;
ports[1] = cp[0] ? xtables_parse_port(cp, "sctp") : 0xFFFF;
if (ports[0] > ports[1])
xtables_error(PARAMETER_PROBLEM,
"invalid portrange (min > max)");
}
free(buffer);
}
struct sctp_chunk_names {
const char *name;
unsigned int chunk_type;
const char *valid_flags;
};
/*'ALL' and 'NONE' will be treated specially. */
static const struct sctp_chunk_names sctp_chunk_names[]
= { { .name = "DATA", .chunk_type = 0, .valid_flags = "----IUBE"},
{ .name = "INIT", .chunk_type = 1, .valid_flags = "--------"},
{ .name = "INIT_ACK", .chunk_type = 2, .valid_flags = "--------"},
{ .name = "SACK", .chunk_type = 3, .valid_flags = "--------"},
{ .name = "HEARTBEAT", .chunk_type = 4, .valid_flags = "--------"},
{ .name = "HEARTBEAT_ACK", .chunk_type = 5, .valid_flags = "--------"},
{ .name = "ABORT", .chunk_type = 6, .valid_flags = "-------T"},
{ .name = "SHUTDOWN", .chunk_type = 7, .valid_flags = "--------"},
{ .name = "SHUTDOWN_ACK", .chunk_type = 8, .valid_flags = "--------"},
{ .name = "ERROR", .chunk_type = 9, .valid_flags = "--------"},
{ .name = "COOKIE_ECHO", .chunk_type = 10, .valid_flags = "--------"},
{ .name = "COOKIE_ACK", .chunk_type = 11, .valid_flags = "--------"},
{ .name = "ECN_ECNE", .chunk_type = 12, .valid_flags = "--------"},
{ .name = "ECN_CWR", .chunk_type = 13, .valid_flags = "--------"},
{ .name = "SHUTDOWN_COMPLETE", .chunk_type = 14, .valid_flags = "-------T"},
{ .name = "ASCONF", .chunk_type = 193, .valid_flags = "--------"},
{ .name = "ASCONF_ACK", .chunk_type = 128, .valid_flags = "--------"},
{ .name = "FORWARD_TSN", .chunk_type = 192, .valid_flags = "--------"},
};
static void
save_chunk_flag_info(struct xt_sctp_flag_info *flag_info,
int *flag_count,
int chunktype,
int bit,
int set)
{
int i;
for (i = 0; i < *flag_count; i++) {
if (flag_info[i].chunktype == chunktype) {
DEBUGP("Previous match found\n");
flag_info[i].chunktype = chunktype;
flag_info[i].flag_mask |= (1 << bit);
if (set) {
flag_info[i].flag |= (1 << bit);
}
return;
}
}
if (*flag_count == XT_NUM_SCTP_FLAGS) {
xtables_error (PARAMETER_PROBLEM,
"Number of chunk types with flags exceeds currently allowed limit."
"Increasing this limit involves changing IPT_NUM_SCTP_FLAGS and"
"recompiling both the kernel space and user space modules\n");
}
flag_info[*flag_count].chunktype = chunktype;
flag_info[*flag_count].flag_mask |= (1 << bit);
if (set) {
flag_info[*flag_count].flag |= (1 << bit);
}
(*flag_count)++;
}
static void
parse_sctp_chunk(struct xt_sctp_info *einfo,
const char *chunks)
{
char *ptr;
char *buffer;
unsigned int i, j;
int found = 0;
char *chunk_flags;
buffer = strdup(chunks);
DEBUGP("Buffer: %s\n", buffer);
SCTP_CHUNKMAP_RESET(einfo->chunkmap);
if (!strcasecmp(buffer, "ALL")) {
SCTP_CHUNKMAP_SET_ALL(einfo->chunkmap);
goto out;
}
if (!strcasecmp(buffer, "NONE")) {
SCTP_CHUNKMAP_RESET(einfo->chunkmap);
goto out;
}
for (ptr = strtok(buffer, ","); ptr; ptr = strtok(NULL, ",")) {
found = 0;
DEBUGP("Next Chunk type %s\n", ptr);
if ((chunk_flags = strchr(ptr, ':')) != NULL) {
*chunk_flags++ = 0;
}
for (i = 0; i < ARRAY_SIZE(sctp_chunk_names); ++i)
if (strcasecmp(sctp_chunk_names[i].name, ptr) == 0) {
DEBUGP("Chunk num %d\n", sctp_chunk_names[i].chunk_type);
SCTP_CHUNKMAP_SET(einfo->chunkmap,
sctp_chunk_names[i].chunk_type);
found = 1;
break;
}
if (!found)
xtables_error(PARAMETER_PROBLEM,
"Unknown sctp chunk `%s'", ptr);
if (chunk_flags) {
DEBUGP("Chunk flags %s\n", chunk_flags);
for (j = 0; j < strlen(chunk_flags); j++) {
char *p;
int bit;
if ((p = strchr(sctp_chunk_names[i].valid_flags,
toupper(chunk_flags[j]))) != NULL) {
bit = p - sctp_chunk_names[i].valid_flags;
bit = 7 - bit;
save_chunk_flag_info(einfo->flag_info,
&(einfo->flag_count), i, bit,
isupper(chunk_flags[j]));
} else {
xtables_error(PARAMETER_PROBLEM,
"Invalid flags for chunk type %d\n", i);
}
}
}
}
out:
free(buffer);
}
static void
parse_sctp_chunks(struct xt_sctp_info *einfo,
const char *match_type,
const char *chunks)
{
DEBUGP("Match type: %s Chunks: %s\n", match_type, chunks);
if (!strcasecmp(match_type, "ANY")) {
einfo->chunk_match_type = SCTP_CHUNK_MATCH_ANY;
} else if (!strcasecmp(match_type, "ALL")) {
einfo->chunk_match_type = SCTP_CHUNK_MATCH_ALL;
} else if (!strcasecmp(match_type, "ONLY")) {
einfo->chunk_match_type = SCTP_CHUNK_MATCH_ONLY;
} else {
xtables_error (PARAMETER_PROBLEM,
"Match type has to be one of \"ALL\", \"ANY\" or \"ONLY\"");
}
SCTP_CHUNKMAP_RESET(einfo->chunkmap);
parse_sctp_chunk(einfo, chunks);
}
static int
sctp_parse(int c, char **argv, int invert, unsigned int *flags,
const void *entry, struct xt_entry_match **match)
{
struct xt_sctp_info *einfo
= (struct xt_sctp_info *)(*match)->data;
switch (c) {
case '1':
if (*flags & XT_SCTP_SRC_PORTS)
xtables_error(PARAMETER_PROBLEM,
"Only one `--source-port' allowed");
einfo->flags |= XT_SCTP_SRC_PORTS;
parse_sctp_ports(optarg, einfo->spts);
if (invert)
einfo->invflags |= XT_SCTP_SRC_PORTS;
*flags |= XT_SCTP_SRC_PORTS;
break;
case '2':
if (*flags & XT_SCTP_DEST_PORTS)
xtables_error(PARAMETER_PROBLEM,
"Only one `--destination-port' allowed");
einfo->flags |= XT_SCTP_DEST_PORTS;
parse_sctp_ports(optarg, einfo->dpts);
if (invert)
einfo->invflags |= XT_SCTP_DEST_PORTS;
*flags |= XT_SCTP_DEST_PORTS;
break;
case '3':
if (*flags & XT_SCTP_CHUNK_TYPES)
xtables_error(PARAMETER_PROBLEM,
"Only one `--chunk-types' allowed");
if (!argv[optind]
|| argv[optind][0] == '-' || argv[optind][0] == '!')
xtables_error(PARAMETER_PROBLEM,
"--chunk-types requires two args");
einfo->flags |= XT_SCTP_CHUNK_TYPES;
parse_sctp_chunks(einfo, optarg, argv[optind]);
if (invert)
einfo->invflags |= XT_SCTP_CHUNK_TYPES;
optind++;
*flags |= XT_SCTP_CHUNK_TYPES;
break;
}
return 1;
}
static const char *
port_to_service(int port)
{
const struct servent *service;
if ((service = getservbyport(htons(port), "sctp")))
return service->s_name;
return NULL;
}
static void
print_port(uint16_t port, int numeric)
{
const char *service;
if (numeric || (service = port_to_service(port)) == NULL)
printf("%u", port);
else
printf("%s", service);
}
static void
print_ports(const char *name, uint16_t min, uint16_t max,
int invert, int numeric)
{
const char *inv = invert ? "!" : "";
if (min != 0 || max != 0xFFFF || invert) {
printf(" %s", name);
if (min == max) {
printf(":%s", inv);
print_port(min, numeric);
} else {
printf("s:%s", inv);
print_port(min, numeric);
printf(":");
print_port(max, numeric);
}
}
}
static void
print_chunk_flags(uint32_t chunknum, uint8_t chunk_flags, uint8_t chunk_flags_mask)
{
int i;
DEBUGP("type: %d\tflags: %x\tflag mask: %x\n", chunknum, chunk_flags,
chunk_flags_mask);
if (chunk_flags_mask) {
printf(":");
}
for (i = 7; i >= 0; i--) {
if (chunk_flags_mask & (1 << i)) {
if (chunk_flags & (1 << i)) {
printf("%c", sctp_chunk_names[chunknum].valid_flags[7-i]);
} else {
printf("%c", tolower(sctp_chunk_names[chunknum].valid_flags[7-i]));
}
}
}
}
static void
print_chunk(uint32_t chunknum, int numeric)
{
if (numeric) {
printf("0x%04X", chunknum);
}
else {
int i;
for (i = 0; i < ARRAY_SIZE(sctp_chunk_names); ++i)
if (sctp_chunk_names[i].chunk_type == chunknum)
printf("%s", sctp_chunk_names[chunknum].name);
}
}
static void
print_chunks(const struct xt_sctp_info *einfo, int numeric)
{
uint32_t chunk_match_type = einfo->chunk_match_type;
const struct xt_sctp_flag_info *flag_info = einfo->flag_info;
int flag_count = einfo->flag_count;
int i, j;
int flag;
switch (chunk_match_type) {
case SCTP_CHUNK_MATCH_ANY: printf(" any"); break;
case SCTP_CHUNK_MATCH_ALL: printf(" all"); break;
case SCTP_CHUNK_MATCH_ONLY: printf(" only"); break;
default: printf("Never reach here\n"); break;
}
if (SCTP_CHUNKMAP_IS_CLEAR(einfo->chunkmap)) {
printf(" NONE");
goto out;
}
if (SCTP_CHUNKMAP_IS_ALL_SET(einfo->chunkmap)) {
printf(" ALL");
goto out;
}
flag = 0;
for (i = 0; i < 256; i++) {
if (SCTP_CHUNKMAP_IS_SET(einfo->chunkmap, i)) {
if (flag)
printf(",");
else
putchar(' ');
flag = 1;
print_chunk(i, numeric);
for (j = 0; j < flag_count; j++) {
if (flag_info[j].chunktype == i) {
print_chunk_flags(i, flag_info[j].flag,
flag_info[j].flag_mask);
}
}
}
}
out:
return;
}
static void
sctp_print(const void *ip, const struct xt_entry_match *match, int numeric)
{
const struct xt_sctp_info *einfo =
(const struct xt_sctp_info *)match->data;
printf(" sctp");
if (einfo->flags & XT_SCTP_SRC_PORTS) {
print_ports("spt", einfo->spts[0], einfo->spts[1],
einfo->invflags & XT_SCTP_SRC_PORTS,
numeric);
}
if (einfo->flags & XT_SCTP_DEST_PORTS) {
print_ports("dpt", einfo->dpts[0], einfo->dpts[1],
einfo->invflags & XT_SCTP_DEST_PORTS,
numeric);
}
if (einfo->flags & XT_SCTP_CHUNK_TYPES) {
/* FIXME: print_chunks() is used in save() where the printing of '!'
s taken care of, so we need to do that here as well */
if (einfo->invflags & XT_SCTP_CHUNK_TYPES) {
printf(" !");
}
print_chunks(einfo, numeric);
}
}
static void sctp_save(const void *ip, const struct xt_entry_match *match)
{
const struct xt_sctp_info *einfo =
(const struct xt_sctp_info *)match->data;
if (einfo->flags & XT_SCTP_SRC_PORTS) {
if (einfo->invflags & XT_SCTP_SRC_PORTS)
printf(" !");
if (einfo->spts[0] != einfo->spts[1])
printf(" --sport %u:%u",
einfo->spts[0], einfo->spts[1]);
else
printf(" --sport %u", einfo->spts[0]);
}
if (einfo->flags & XT_SCTP_DEST_PORTS) {
if (einfo->invflags & XT_SCTP_DEST_PORTS)
printf(" !");
if (einfo->dpts[0] != einfo->dpts[1])
printf(" --dport %u:%u",
einfo->dpts[0], einfo->dpts[1]);
else
printf(" --dport %u", einfo->dpts[0]);
}
if (einfo->flags & XT_SCTP_CHUNK_TYPES) {
if (einfo->invflags & XT_SCTP_CHUNK_TYPES)
printf(" !");
printf(" --chunk-types");
print_chunks(einfo, 0);
}
}
static struct xtables_match sctp_match = {
.name = "sctp",
.family = NFPROTO_UNSPEC,
.version = XTABLES_VERSION,
.size = XT_ALIGN(sizeof(struct xt_sctp_info)),
.userspacesize = XT_ALIGN(sizeof(struct xt_sctp_info)),
.help = sctp_help,
.init = sctp_init,
.parse = sctp_parse,
.print = sctp_print,
.save = sctp_save,
.extra_opts = sctp_opts,
};
void _init(void)
{
xtables_register_match(&sctp_match);
}
.TP
[\fB!\fP] \fB\-\-source\-port\fP,\fB\-\-sport\fP \fIport\fP[\fB:\fP\fIport\fP]
.TP
[\fB!\fP] \fB\-\-destination\-port\fP,\fB\-\-dport\fP \fIport\fP[\fB:\fP\fIport\fP]
.TP
[\fB!\fP] \fB\-\-chunk\-types\fP {\fBall\fP|\fBany\fP|\fBonly\fP} \fIchunktype\fP[\fB:\fP\fIflags\fP] [...]
The flag letter in upper case indicates that the flag is to match if set,
in the lower case indicates to match if unset.
Chunk types: DATA INIT INIT_ACK SACK HEARTBEAT HEARTBEAT_ACK ABORT SHUTDOWN SHUTDOWN_ACK ERROR COOKIE_ECHO COOKIE_ACK ECN_ECNE ECN_CWR SHUTDOWN_COMPLETE ASCONF ASCONF_ACK FORWARD_TSN
chunk type available flags
.br
DATA I U B E i u b e
.br
ABORT T t
.br
SHUTDOWN_COMPLETE T t
(lowercase means flag should be "off", uppercase means "on")
.P
Examples:
iptables \-A INPUT \-p sctp \-\-dport 80 \-j DROP
iptables \-A INPUT \-p sctp \-\-chunk\-types any DATA,INIT \-j DROP
iptables \-A INPUT \-p sctp \-\-chunk\-types any DATA:Be \-j ACCEPT
/* Copyright (C) 2000-2002 Joakim Axelsson <gozem@linux.nu>
* Patrick Schaaf <bof@bof.de>
* Martin Josefsson <gandalf@wlug.westbo.se>
* Copyright (C) 2003-2010 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
/* Shared library add-on to iptables to add IP set matching. */
#include <stdbool.h>
#include <stdio.h>
#include <netdb.h>
#include <string.h>
#include <stdlib.h>
#include <getopt.h>
#include <ctype.h>
#include <errno.h>
#include <xtables.h>
#include <linux/netfilter/xt_set.h>
#include "libxt_set.h"
/* Revision 0 */
static void
set_help_v0(void)
{
printf("set match options:\n"
" [!] --match-set name flags\n"
" 'name' is the set name from to match,\n"
" 'flags' are the comma separated list of\n"
" 'src' and 'dst' specifications.\n");
}
static const struct option set_opts_v0[] = {
{.name = "match-set", .has_arg = true, .val = '1'},
{.name = "set", .has_arg = true, .val = '2'},
XT_GETOPT_TABLEEND,
};
static void
set_check_v0(unsigned int flags)
{
if (!flags)
xtables_error(PARAMETER_PROBLEM,
"You must specify `--match-set' with proper arguments");
}
static int
set_parse_v0(int c, char **argv, int invert, unsigned int *flags,
const void *entry, struct xt_entry_match **match)
{
struct xt_set_info_match_v0 *myinfo =
(struct xt_set_info_match_v0 *) (*match)->data;
struct xt_set_info_v0 *info = &myinfo->match_set;
switch (c) {
case '2':
fprintf(stderr,
"--set option deprecated, please use --match-set\n");
case '1': /* --match-set <set> <flag>[,<flag> */
if (info->u.flags[0])
xtables_error(PARAMETER_PROBLEM,
"--match-set can be specified only once");
if (invert)
info->u.flags[0] |= IPSET_MATCH_INV;
if (!argv[optind]
|| argv[optind][0] == '-'
|| argv[optind][0] == '!')
xtables_error(PARAMETER_PROBLEM,
"--match-set requires two args.");
if (strlen(optarg) > IPSET_MAXNAMELEN - 1)
xtables_error(PARAMETER_PROBLEM,
"setname `%s' too long, max %d characters.",
optarg, IPSET_MAXNAMELEN - 1);
get_set_byname(optarg, (struct xt_set_info *)info);
parse_dirs_v0(argv[optind], info);
DEBUGP("parse: set index %u\n", info->index);
optind++;
*flags = 1;
break;
}
return 1;
}
static void
print_match_v0(const char *prefix, const struct xt_set_info_v0 *info)
{
int i;
char setname[IPSET_MAXNAMELEN];
get_set_byid(setname, info->index);
printf("%s %s %s",
(info->u.flags[0] & IPSET_MATCH_INV) ? " !" : "",
prefix,
setname);
for (i = 0; i < IPSET_DIM_MAX; i++) {
if (!info->u.flags[i])
break;
printf("%s%s",
i == 0 ? " " : ",",
info->u.flags[i] & IPSET_SRC ? "src" : "dst");
}
}
/* Prints out the matchinfo. */
static void
set_print_v0(const void *ip, const struct xt_entry_match *match, int numeric)
{
const struct xt_set_info_match_v0 *info = (const void *)match->data;
print_match_v0("match-set", &info->match_set);
}
static void
set_save_v0(const void *ip, const struct xt_entry_match *match)
{
const struct xt_set_info_match_v0 *info = (const void *)match->data;
print_match_v0("--match-set", &info->match_set);
}
/* Revision 1 */
static int
set_parse_v1(int c, char **argv, int invert, unsigned int *flags,
const void *entry, struct xt_entry_match **match)
{
struct xt_set_info_match_v1 *myinfo =
(struct xt_set_info_match_v1 *) (*match)->data;
struct xt_set_info *info = &myinfo->match_set;
switch (c) {
case '2':
fprintf(stderr,
"--set option deprecated, please use --match-set\n");
case '1': /* --match-set <set> <flag>[,<flag> */
if (info->dim)
xtables_error(PARAMETER_PROBLEM,
"--match-set can be specified only once");
if (invert)
info->flags |= IPSET_INV_MATCH;
if (!argv[optind]
|| argv[optind][0] == '-'
|| argv[optind][0] == '!')
xtables_error(PARAMETER_PROBLEM,
"--match-set requires two args.");
if (strlen(optarg) > IPSET_MAXNAMELEN - 1)
xtables_error(PARAMETER_PROBLEM,
"setname `%s' too long, max %d characters.",
optarg, IPSET_MAXNAMELEN - 1);
get_set_byname(optarg, info);
parse_dirs(argv[optind], info);
DEBUGP("parse: set index %u\n", info->index);
optind++;
*flags = 1;
break;
}
return 1;
}
static void
print_match(const char *prefix, const struct xt_set_info *info)
{
int i;
char setname[IPSET_MAXNAMELEN];
get_set_byid(setname, info->index);
printf("%s %s %s",
(info->flags & IPSET_INV_MATCH) ? " !" : "",
prefix,
setname);
for (i = 1; i <= info->dim; i++) {
printf("%s%s",
i == 1 ? " " : ",",
info->flags & (1 << i) ? "src" : "dst");
}
}
/* Prints out the matchinfo. */
static void
set_print_v1(const void *ip, const struct xt_entry_match *match, int numeric)
{
const struct xt_set_info_match_v1 *info = (const void *)match->data;
print_match("match-set", &info->match_set);
}
static void
set_save_v1(const void *ip, const struct xt_entry_match *match)
{
const struct xt_set_info_match_v1 *info = (const void *)match->data;
print_match("--match-set", &info->match_set);
}
/* Revision 2 */
static void
set_help_v2(void)
{
printf("set match options:\n"
" [!] --match-set name flags [--return-nomatch]\n"
" 'name' is the set name from to match,\n"
" 'flags' are the comma separated list of\n"
" 'src' and 'dst' specifications.\n");
}
static const struct option set_opts_v2[] = {
{.name = "match-set", .has_arg = true, .val = '1'},
{.name = "set", .has_arg = true, .val = '2'},
{.name = "return-nomatch", .has_arg = false, .val = '3'},
XT_GETOPT_TABLEEND,
};
static int
set_parse_v2(int c, char **argv, int invert, unsigned int *flags,
const void *entry, struct xt_entry_match **match)
{
struct xt_set_info_match_v1 *myinfo =
(struct xt_set_info_match_v1 *) (*match)->data;
struct xt_set_info *info = &myinfo->match_set;
switch (c) {
case '3':
info->flags |= IPSET_RETURN_NOMATCH;
break;
case '2':
fprintf(stderr,
"--set option deprecated, please use --match-set\n");
case '1': /* --match-set <set> <flag>[,<flag> */
if (info->dim)
xtables_error(PARAMETER_PROBLEM,
"--match-set can be specified only once");
if (invert)
info->flags |= IPSET_INV_MATCH;
if (!argv[optind]
|| argv[optind][0] == '-'
|| argv[optind][0] == '!')
xtables_error(PARAMETER_PROBLEM,
"--match-set requires two args.");
if (strlen(optarg) > IPSET_MAXNAMELEN - 1)
xtables_error(PARAMETER_PROBLEM,
"setname `%s' too long, max %d characters.",
optarg, IPSET_MAXNAMELEN - 1);
get_set_byname(optarg, info);
parse_dirs(argv[optind], info);
DEBUGP("parse: set index %u\n", info->index);
optind++;
*flags = 1;
break;
}
return 1;
}
/* Prints out the matchinfo. */
static void
set_print_v2(const void *ip, const struct xt_entry_match *match, int numeric)
{
const struct xt_set_info_match_v1 *info = (const void *)match->data;
print_match("match-set", &info->match_set);
if (info->match_set.flags & IPSET_RETURN_NOMATCH)
printf(" return-nomatch");
}
static void
set_save_v2(const void *ip, const struct xt_entry_match *match)
{
const struct xt_set_info_match_v1 *info = (const void *)match->data;
print_match("--match-set", &info->match_set);
if (info->match_set.flags & IPSET_RETURN_NOMATCH)
printf(" --return-nomatch");
}
/* Revision 3 */
static void
set_help_v3(void)
{
printf("set match options:\n"
" [!] --match-set name flags [--return-nomatch]\n"
" [! --update-counters] [! --update-subcounters]\n"
" [[!] --packets-eq value | --packets-lt value | --packets-gt value\n"
" [[!] --bytes-eq value | --bytes-lt value | --bytes-gt value\n"
" 'name' is the set name from to match,\n"
" 'flags' are the comma separated list of\n"
" 'src' and 'dst' specifications.\n");
}
static const struct option set_opts_v3[] = {
{.name = "match-set", .has_arg = true, .val = '1'},
{.name = "set", .has_arg = true, .val = '2'},
{.name = "return-nomatch", .has_arg = false, .val = '3'},
{.name = "update-counters", .has_arg = false, .val = '4'},
{.name = "packets-eq", .has_arg = true, .val = '5'},
{.name = "packets-lt", .has_arg = true, .val = '6'},
{.name = "packets-gt", .has_arg = true, .val = '7'},
{.name = "bytes-eq", .has_arg = true, .val = '8'},
{.name = "bytes-lt", .has_arg = true, .val = '9'},
{.name = "bytes-gt", .has_arg = true, .val = '0'},
{.name = "update-subcounters", .has_arg = false, .val = 'a'},
XT_GETOPT_TABLEEND,
};
static uint64_t
parse_counter(const char *opt)
{
uintmax_t value;
if (!xtables_strtoul(opt, NULL, &value, 0, UINT64_MAX))
xtables_error(PARAMETER_PROBLEM,
"Cannot parse %s as a counter value\n",
opt);
return (uint64_t)value;
}
static int
set_parse_v3(int c, char **argv, int invert, unsigned int *flags,
const void *entry, struct xt_entry_match **match)
{
struct xt_set_info_match_v3 *info =
(struct xt_set_info_match_v3 *) (*match)->data;
switch (c) {
case 'a':
if (invert)
info->flags |= IPSET_FLAG_SKIP_SUBCOUNTER_UPDATE;
break;
case '0':
if (info->bytes.op != IPSET_COUNTER_NONE)
xtables_error(PARAMETER_PROBLEM,
"only one of the --bytes-[eq|lt|gt]"
" is allowed\n");
if (invert)
xtables_error(PARAMETER_PROBLEM,
"--bytes-gt option cannot be inverted\n");
info->bytes.op = IPSET_COUNTER_GT;
info->bytes.value = parse_counter(optarg);
break;
case '9':
if (info->bytes.op != IPSET_COUNTER_NONE)
xtables_error(PARAMETER_PROBLEM,
"only one of the --bytes-[eq|lt|gt]"
" is allowed\n");
if (invert)
xtables_error(PARAMETER_PROBLEM,
"--bytes-lt option cannot be inverted\n");
info->bytes.op = IPSET_COUNTER_LT;
info->bytes.value = parse_counter(optarg);
break;
case '8':
if (info->bytes.op != IPSET_COUNTER_NONE)
xtables_error(PARAMETER_PROBLEM,
"only one of the --bytes-[eq|lt|gt]"
" is allowed\n");
info->bytes.op = invert ? IPSET_COUNTER_NE : IPSET_COUNTER_EQ;
info->bytes.value = parse_counter(optarg);
break;
case '7':
if (info->packets.op != IPSET_COUNTER_NONE)
xtables_error(PARAMETER_PROBLEM,
"only one of the --packets-[eq|lt|gt]"
" is allowed\n");
if (invert)
xtables_error(PARAMETER_PROBLEM,
"--packets-gt option cannot be inverted\n");
info->packets.op = IPSET_COUNTER_GT;
info->packets.value = parse_counter(optarg);
break;
case '6':
if (info->packets.op != IPSET_COUNTER_NONE)
xtables_error(PARAMETER_PROBLEM,
"only one of the --packets-[eq|lt|gt]"
" is allowed\n");
if (invert)
xtables_error(PARAMETER_PROBLEM,
"--packets-lt option cannot be inverted\n");
info->packets.op = IPSET_COUNTER_LT;
info->packets.value = parse_counter(optarg);
break;
case '5':
if (info->packets.op != IPSET_COUNTER_NONE)
xtables_error(PARAMETER_PROBLEM,
"only one of the --packets-[eq|lt|gt]"
" is allowed\n");
info->packets.op = invert ? IPSET_COUNTER_NE : IPSET_COUNTER_EQ;
info->packets.value = parse_counter(optarg);
break;
case '4':
if (invert)
info->flags |= IPSET_FLAG_SKIP_COUNTER_UPDATE;
break;
case '3':
if (invert)
xtables_error(PARAMETER_PROBLEM,
"--return-nomatch flag cannot be inverted\n");
info->flags |= IPSET_FLAG_RETURN_NOMATCH;
break;
case '2':
fprintf(stderr,
"--set option deprecated, please use --match-set\n");
case '1': /* --match-set <set> <flag>[,<flag> */
if (info->match_set.dim)
xtables_error(PARAMETER_PROBLEM,
"--match-set can be specified only once");
if (invert)
info->match_set.flags |= IPSET_INV_MATCH;
if (!argv[optind]
|| argv[optind][0] == '-'
|| argv[optind][0] == '!')
xtables_error(PARAMETER_PROBLEM,
"--match-set requires two args.");
if (strlen(optarg) > IPSET_MAXNAMELEN - 1)
xtables_error(PARAMETER_PROBLEM,
"setname `%s' too long, max %d characters.",
optarg, IPSET_MAXNAMELEN - 1);
get_set_byname(optarg, &info->match_set);
parse_dirs(argv[optind], &info->match_set);
DEBUGP("parse: set index %u\n", info->match_set.index);
optind++;
*flags = 1;
break;
}
return 1;
}
static void
set_printv3_counter(const struct ip_set_counter_match *c, const char *name,
const char *sep)
{
switch (c->op) {
case IPSET_COUNTER_EQ:
printf(" %s%s-eq %llu", sep, name, c->value);
break;
case IPSET_COUNTER_NE:
printf(" ! %s%s-eq %llu", sep, name, c->value);
break;
case IPSET_COUNTER_LT:
printf(" %s%s-lt %llu", sep, name, c->value);
break;
case IPSET_COUNTER_GT:
printf(" %s%s-gt %llu", sep, name, c->value);
break;
}
}
static void
set_print_v3_matchinfo(const struct xt_set_info_match_v3 *info,
const char *opt, const char *sep)
{
print_match(opt, &info->match_set);
if (info->flags & IPSET_FLAG_RETURN_NOMATCH)
printf(" %sreturn-nomatch", sep);
if ((info->flags & IPSET_FLAG_SKIP_COUNTER_UPDATE))
printf(" ! %supdate-counters", sep);
if ((info->flags & IPSET_FLAG_SKIP_SUBCOUNTER_UPDATE))
printf(" ! %supdate-subcounters", sep);
set_printv3_counter(&info->packets, "packets", sep);
set_printv3_counter(&info->bytes, "bytes", sep);
}
/* Prints out the matchinfo. */
static void
set_print_v3(const void *ip, const struct xt_entry_match *match, int numeric)
{
const struct xt_set_info_match_v3 *info = (const void *)match->data;
set_print_v3_matchinfo(info, "match-set", "");
}
static void
set_save_v3(const void *ip, const struct xt_entry_match *match)
{
const struct xt_set_info_match_v3 *info = (const void *)match->data;
set_print_v3_matchinfo(info, "--match-set", "--");
}
static struct xtables_match set_mt_reg[] = {
{
.name = "set",
.revision = 0,
.version = XTABLES_VERSION,
.family = NFPROTO_IPV4,
.size = XT_ALIGN(sizeof(struct xt_set_info_match_v0)),
.userspacesize = XT_ALIGN(sizeof(struct xt_set_info_match_v0)),
.help = set_help_v0,
.parse = set_parse_v0,
.final_check = set_check_v0,
.print = set_print_v0,
.save = set_save_v0,
.extra_opts = set_opts_v0,
},
{
.name = "set",
.revision = 1,
.version = XTABLES_VERSION,
.family = NFPROTO_UNSPEC,
.size = XT_ALIGN(sizeof(struct xt_set_info_match_v1)),
.userspacesize = XT_ALIGN(sizeof(struct xt_set_info_match_v1)),
.help = set_help_v0,
.parse = set_parse_v1,
.final_check = set_check_v0,
.print = set_print_v1,
.save = set_save_v1,
.extra_opts = set_opts_v0,
},
{
.name = "set",
.revision = 2,
.version = XTABLES_VERSION,
.family = NFPROTO_UNSPEC,
.size = XT_ALIGN(sizeof(struct xt_set_info_match_v1)),
.userspacesize = XT_ALIGN(sizeof(struct xt_set_info_match_v1)),
.help = set_help_v2,
.parse = set_parse_v2,
.final_check = set_check_v0,
.print = set_print_v2,
.save = set_save_v2,
.extra_opts = set_opts_v2,
},
{
.name = "set",
.revision = 3,
.version = XTABLES_VERSION,
.family = NFPROTO_UNSPEC,
.size = XT_ALIGN(sizeof(struct xt_set_info_match_v3)),
.userspacesize = XT_ALIGN(sizeof(struct xt_set_info_match_v3)),
.help = set_help_v3,
.parse = set_parse_v3,
.final_check = set_check_v0,
.print = set_print_v3,
.save = set_save_v3,
.extra_opts = set_opts_v3,
},
};
void _init(void)
{
xtables_register_matches(set_mt_reg, ARRAY_SIZE(set_mt_reg));
}
#ifndef _LIBXT_SET_H
#define _LIBXT_SET_H
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <errno.h>
#include "../iptables/xshared.h"
#ifdef DEBUG
#define DEBUGP(x, args...) fprintf(stderr, x , ## args)
#else
#define DEBUGP(x, args...)
#endif
static int
get_version(unsigned *version)
{
int res, sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
struct ip_set_req_version req_version;
socklen_t size = sizeof(req_version);
if (sockfd < 0)
xtables_error(OTHER_PROBLEM,
"Can't open socket to ipset.\n");
if (fcntl(sockfd, F_SETFD, FD_CLOEXEC) == -1) {
xtables_error(OTHER_PROBLEM,
"Could not set close on exec: %s\n",
strerror(errno));
}
req_version.op = IP_SET_OP_VERSION;
res = getsockopt(sockfd, SOL_IP, SO_IP_SET, &req_version, &size);
if (res != 0)
xtables_error(OTHER_PROBLEM,
"Kernel module xt_set is not loaded in.\n");
*version = req_version.version;
return sockfd;
}
static void
get_set_byid(char *setname, ip_set_id_t idx)
{
struct ip_set_req_get_set req;
socklen_t size = sizeof(struct ip_set_req_get_set);
int res, sockfd;
sockfd = get_version(&req.version);
req.op = IP_SET_OP_GET_BYINDEX;
req.set.index = idx;
res = getsockopt(sockfd, SOL_IP, SO_IP_SET, &req, &size);
close(sockfd);
if (res != 0)
xtables_error(OTHER_PROBLEM,
"Problem when communicating with ipset, errno=%d.\n",
errno);
if (size != sizeof(struct ip_set_req_get_set))
xtables_error(OTHER_PROBLEM,
"Incorrect return size from kernel during ipset lookup, "
"(want %zu, got %zu)\n",
sizeof(struct ip_set_req_get_set), (size_t)size);
if (req.set.name[0] == '\0')
xtables_error(PARAMETER_PROBLEM,
"Set with index %i in kernel doesn't exist.\n", idx);
strncpy(setname, req.set.name, IPSET_MAXNAMELEN);
}
static void
get_set_byname_only(const char *setname, struct xt_set_info *info,
int sockfd, unsigned int version)
{
struct ip_set_req_get_set req = { .version = version };
socklen_t size = sizeof(struct ip_set_req_get_set);
int res;
req.op = IP_SET_OP_GET_BYNAME;
strncpy(req.set.name, setname, IPSET_MAXNAMELEN);
req.set.name[IPSET_MAXNAMELEN - 1] = '\0';
res = getsockopt(sockfd, SOL_IP, SO_IP_SET, &req, &size);
close(sockfd);
if (res != 0)
xtables_error(OTHER_PROBLEM,
"Problem when communicating with ipset, errno=%d.\n",
errno);
if (size != sizeof(struct ip_set_req_get_set))
xtables_error(OTHER_PROBLEM,
"Incorrect return size from kernel during ipset lookup, "
"(want %zu, got %zu)\n",
sizeof(struct ip_set_req_get_set), (size_t)size);
if (req.set.index == IPSET_INVALID_ID)
xtables_error(PARAMETER_PROBLEM,
"Set %s doesn't exist.\n", setname);
info->index = req.set.index;
}
static void
get_set_byname(const char *setname, struct xt_set_info *info)
{
struct ip_set_req_get_set_family req;
socklen_t size = sizeof(struct ip_set_req_get_set_family);
int res, sockfd, version;
sockfd = get_version(&req.version);
version = req.version;
req.op = IP_SET_OP_GET_FNAME;
strncpy(req.set.name, setname, IPSET_MAXNAMELEN);
req.set.name[IPSET_MAXNAMELEN - 1] = '\0';
res = getsockopt(sockfd, SOL_IP, SO_IP_SET, &req, &size);
if (res != 0 && errno == EBADMSG)
/* Backward compatibility */
return get_set_byname_only(setname, info, sockfd, version);
close(sockfd);
if (res != 0)
xtables_error(OTHER_PROBLEM,
"Problem when communicating with ipset, errno=%d.\n",
errno);
if (size != sizeof(struct ip_set_req_get_set_family))
xtables_error(OTHER_PROBLEM,
"Incorrect return size from kernel during ipset lookup, "
"(want %zu, got %zu)\n",
sizeof(struct ip_set_req_get_set_family),
(size_t)size);
if (req.set.index == IPSET_INVALID_ID)
xtables_error(PARAMETER_PROBLEM,
"Set %s doesn't exist.\n", setname);
if (!(req.family == afinfo->family ||
req.family == NFPROTO_UNSPEC))
xtables_error(PARAMETER_PROBLEM,
"The protocol family of set %s is %s, "
"which is not applicable.\n",
setname,
req.family == NFPROTO_IPV4 ? "IPv4" : "IPv6");
info->index = req.set.index;
}
static void
parse_dirs_v0(const char *opt_arg, struct xt_set_info_v0 *info)
{
char *saved = strdup(opt_arg);
char *ptr, *tmp = saved;
int i = 0;
while (i < (IPSET_DIM_MAX - 1) && tmp != NULL) {
ptr = strsep(&tmp, ",");
if (strncmp(ptr, "src", 3) == 0)
info->u.flags[i++] |= IPSET_SRC;
else if (strncmp(ptr, "dst", 3) == 0)
info->u.flags[i++] |= IPSET_DST;
else
xtables_error(PARAMETER_PROBLEM,
"You must spefify (the comma separated list of) 'src' or 'dst'.");
}
if (tmp)
xtables_error(PARAMETER_PROBLEM,
"Can't be more src/dst options than %i.",
IPSET_DIM_MAX);
free(saved);
}
static void
parse_dirs(const char *opt_arg, struct xt_set_info *info)
{
char *saved = strdup(opt_arg);
char *ptr, *tmp = saved;
while (info->dim < IPSET_DIM_MAX && tmp != NULL) {
info->dim++;
ptr = strsep(&tmp, ",");
if (strncmp(ptr, "src", 3) == 0)
info->flags |= (1 << info->dim);
else if (strncmp(ptr, "dst", 3) != 0)
xtables_error(PARAMETER_PROBLEM,
"You must spefify (the comma separated list of) 'src' or 'dst'.");
}
if (tmp)
xtables_error(PARAMETER_PROBLEM,
"Can't be more src/dst options than %i.",
IPSET_DIM_MAX);
free(saved);
}
#endif /*_LIBXT_SET_H*/
This module matches IP sets which can be defined by ipset(8).
.TP
[\fB!\fP] \fB\-\-match\-set\fP \fIsetname\fP \fIflag\fP[\fB,\fP\fIflag\fP]...
where flags are the comma separated list of
.BR "src"
and/or
.BR "dst"
specifications and there can be no more than six of them. Hence the command
.IP
iptables \-A FORWARD \-m set \-\-match\-set test src,dst
.IP
will match packets, for which (if the set type is ipportmap) the source
address and destination port pair can be found in the specified set. If
the set type of the specified set is single dimension (for example ipmap),
then the command will match packets for which the source address can be
found in the specified set.
.TP
\fB\-\-return\-nomatch\fP
If the \fB\-\-return\-nomatch\fP option is specified and the set type
supports the \fBnomatch\fP flag, then the matching is reversed: a match
with an element flagged with \fBnomatch\fP returns \fBtrue\fP, while a
match with a plain element returns \fBfalse\fP.
.TP
\fB!\fP \fB\-\-update\-counters\fP
If the \fB\-\-update\-counters\fP flag is negated, then the packet and
byte counters of the matching element in the set won't be updated. Default
the packet and byte counters are updated.
.TP
\fB!\fP \fB\-\-update\-subcounters\fP
If the \fB\-\-update\-subcounters\fP flag is negated, then the packet and
byte counters of the matching element in the member set of a list type of
set won't be updated. Default the packet and byte counters are updated.
.TP
[\fB!\fP] \fB\-\-packets\-eq\fP \fIvalue\fP
If the packet is matched an element in the set, match only if the
packet counter of the element matches the given value too.
.TP
\fB\-\-packets\-lt\fP \fIvalue\fP
If the packet is matched an element in the set, match only if the
packet counter of the element is less than the given value as well.
.TP
\fB\-\-packets\-gt\fP \fIvalue\fP
If the packet is matched an element in the set, match only if the
packet counter of the element is greater than the given value as well.
.TP
[\fB!\fP] \fB\-bytes\-eq\fP \fIvalue\fP
If the packet is matched an element in the set, match only if the
byte counter of the element matches the given value too.
.TP
\fB\-\-bytes\-lt\fP \fIvalue\fP
If the packet is matched an element in the set, match only if the
byte counter of the element is less than the given value as well.
.TP
\fB\-\-bytes\-gt\fP \fIvalue\fP
If the packet is matched an element in the set, match only if the
byte counter of the element is greater than the given value as well.
.PP
The packet and byte counters related options and flags are ignored
when the set was defined without counter support.
.PP
The option \fB\-\-match\-set\fP can be replaced by \fB\-\-set\fP if that does
not clash with an option of other extensions.
.PP
Use of -m set requires that ipset kernel support is provided, which, for
standard kernels, is the case since Linux 2.6.39.
/*
* Shared library add-on to iptables to add early socket matching support.
*
* Copyright (C) 2007 BalaBit IT Ltd.
*/
#include <stdio.h>
#include <xtables.h>
#include <linux/netfilter/xt_socket.h>
enum {
O_TRANSPARENT = 0,
O_NOWILDCARD = 1,
};
static const struct xt_option_entry socket_mt_opts[] = {
{.name = "transparent", .id = O_TRANSPARENT, .type = XTTYPE_NONE},
XTOPT_TABLEEND,
};
static const struct xt_option_entry socket_mt_opts_v2[] = {
{.name = "transparent", .id = O_TRANSPARENT, .type = XTTYPE_NONE},
{.name = "nowildcard", .id = O_NOWILDCARD, .type = XTTYPE_NONE},
XTOPT_TABLEEND,
};
static void socket_mt_help(void)
{
printf(
"socket match options:\n"
" --transparent Ignore non-transparent sockets\n\n");
}
static void socket_mt_help_v2(void)
{
printf(
"socket match options:\n"
" --nowildcard Do not ignore LISTEN sockets bound on INADDR_ANY\n"
" --transparent Ignore non-transparent sockets\n\n");
}
static void socket_mt_parse(struct xt_option_call *cb)
{
struct xt_socket_mtinfo1 *info = cb->data;
xtables_option_parse(cb);
switch (cb->entry->id) {
case O_TRANSPARENT:
info->flags |= XT_SOCKET_TRANSPARENT;
break;
}
}
static void socket_mt_parse_v2(struct xt_option_call *cb)
{
struct xt_socket_mtinfo2 *info = cb->data;
xtables_option_parse(cb);
switch (cb->entry->id) {
case O_TRANSPARENT:
info->flags |= XT_SOCKET_TRANSPARENT;
break;
case O_NOWILDCARD:
info->flags |= XT_SOCKET_NOWILDCARD;
break;
}
}
static void
socket_mt_save(const void *ip, const struct xt_entry_match *match)
{
const struct xt_socket_mtinfo1 *info = (const void *)match->data;
if (info->flags & XT_SOCKET_TRANSPARENT)
printf(" --transparent");
}
static void
socket_mt_print(const void *ip, const struct xt_entry_match *match,
int numeric)
{
printf(" socket");
socket_mt_save(ip, match);
}
static void
socket_mt_save_v2(const void *ip, const struct xt_entry_match *match)
{
const struct xt_socket_mtinfo2 *info = (const void *)match->data;
if (info->flags & XT_SOCKET_TRANSPARENT)
printf(" --transparent");
if (info->flags & XT_SOCKET_NOWILDCARD)
printf(" --nowildcard");
}
static void
socket_mt_print_v2(const void *ip, const struct xt_entry_match *match,
int numeric)
{
printf(" socket");
socket_mt_save_v2(ip, match);
}
static struct xtables_match socket_mt_reg[] = {
{
.name = "socket",
.revision = 0,
.family = NFPROTO_IPV4,
.version = XTABLES_VERSION,
.size = XT_ALIGN(0),
.userspacesize = XT_ALIGN(0),
},
{
.name = "socket",
.revision = 1,
.family = NFPROTO_UNSPEC,
.version = XTABLES_VERSION,
.size = XT_ALIGN(sizeof(struct xt_socket_mtinfo1)),
.userspacesize = XT_ALIGN(sizeof(struct xt_socket_mtinfo1)),
.help = socket_mt_help,
.print = socket_mt_print,
.save = socket_mt_save,
.x6_parse = socket_mt_parse,
.x6_options = socket_mt_opts,
},
{
.name = "socket",
.revision = 2,
.family = NFPROTO_UNSPEC,
.version = XTABLES_VERSION,
.size = XT_ALIGN(sizeof(struct xt_socket_mtinfo2)),
.userspacesize = XT_ALIGN(sizeof(struct xt_socket_mtinfo2)),
.help = socket_mt_help_v2,
.print = socket_mt_print_v2,
.save = socket_mt_save_v2,
.x6_parse = socket_mt_parse_v2,
.x6_options = socket_mt_opts_v2,
},
};
void _init(void)
{
xtables_register_matches(socket_mt_reg, ARRAY_SIZE(socket_mt_reg));
}
This matches if an open TCP/UDP socket can be found by doing a socket lookup on the
packet. It matches if there is an established or non\-zero bound listening
socket (possibly with a non\-local address). The lookup is performed using
the \fBpacket\fP tuple of TCP/UDP packets, or the original TCP/UDP header
\fBembedded\fP in an ICMP/ICPMv6 error packet.
.TP
\fB\-\-transparent\fP
Ignore non-transparent sockets.
.TP
\fB\-\-nowildcard\fP
Do not ignore sockets bound to 'any' address.
The socket match won't accept zero\-bound listeners by default, since
then local services could intercept traffic that would otherwise be forwarded.
This option therefore has security implications when used to match traffic being
forwarded to redirect such packets to local machine with policy routing.
When using the socket match to implement fully transparent
proxies bound to non\-local addresses it is recommended to use the \-\-transparent
option instead.
.PP
Example (assuming packets with mark 1 are delivered locally):
.IP
\-t mangle \-A PREROUTING \-m socket \-\-transparent \-j MARK \-\-set\-mark 1
/* Shared library add-on to iptables for standard target support. */
#include <stdio.h>
#include <xtables.h>
static void standard_help(void)
{
printf(
"standard match options:\n"
"(If target is DROP, ACCEPT, RETURN or nothing)\n");
}
static struct xtables_target standard_target = {
.family = NFPROTO_UNSPEC,
.name = "standard",
.version = XTABLES_VERSION,
.size = XT_ALIGN(sizeof(int)),
.userspacesize = XT_ALIGN(sizeof(int)),
.help = standard_help,
};
void _init(void)
{
xtables_register_target(&standard_target);
}
The "state" extension is a subset of the "conntrack" module.
"state" allows access to the connection tracking state for this packet.
.TP
[\fB!\fP] \fB\-\-state\fP \fIstate\fP
Where state is a comma separated list of the connection states to match. Only a
subset of the states unterstood by "conntrack" are recognized: \fBINVALID\fP,
\fBESTABLISHED\fP, \fBNEW\fP, \fBRELATED\fP or \fBUNTRACKED\fP. For their
description, see the "conntrack" heading in this manpage.
/*
* Copyright (c) 2006-2013 Patrick McHardy <kaber@trash.net>
*/
#include <math.h>
#include <stdio.h>
#include <string.h>
#include <xtables.h>
#include <linux/netfilter/xt_statistic.h>
enum {
O_MODE = 0,
O_PROBABILITY,
O_EVERY,
O_PACKET,
F_PROBABILITY = 1 << O_PROBABILITY,
F_EVERY = 1 << O_EVERY,
F_PACKET = 1 << O_PACKET,
};
static void statistic_help(void)
{
printf(
"statistic match options:\n"
" --mode mode Match mode (random, nth)\n"
" random mode:\n"
"[!] --probability p Probability\n"
" nth mode:\n"
"[!] --every n Match every nth packet\n"
" --packet p Initial counter value (0 <= p <= n-1, default 0)\n");
}
#define s struct xt_statistic_info
static const struct xt_option_entry statistic_opts[] = {
{.name = "mode", .id = O_MODE, .type = XTTYPE_STRING,
.flags = XTOPT_MAND},
{.name = "probability", .id = O_PROBABILITY, .type = XTTYPE_DOUBLE,
.flags = XTOPT_INVERT, .min = 0, .max = 1,
.excl = F_EVERY | F_PACKET},
{.name = "every", .id = O_EVERY, .type = XTTYPE_UINT32, .min = 1,
.flags = XTOPT_INVERT | XTOPT_PUT, XTOPT_POINTER(s, u.nth.every),
.excl = F_PROBABILITY, .also = F_PACKET},
{.name = "packet", .id = O_PACKET, .type = XTTYPE_UINT32,
.flags = XTOPT_PUT, XTOPT_POINTER(s, u.nth.packet),
.excl = F_PROBABILITY, .also = F_EVERY},
XTOPT_TABLEEND,
};
#undef s
static void statistic_parse(struct xt_option_call *cb)
{
struct xt_statistic_info *info = cb->data;
if (cb->invert)
info->flags |= XT_STATISTIC_INVERT;
xtables_option_parse(cb);
switch (cb->entry->id) {
case O_MODE:
if (strcmp(cb->arg, "random") == 0)
info->mode = XT_STATISTIC_MODE_RANDOM;
else if (strcmp(cb->arg, "nth") == 0)
info->mode = XT_STATISTIC_MODE_NTH;
else
xtables_error(PARAMETER_PROBLEM, "Bad mode \"%s\"",
cb->arg);
break;
case O_PROBABILITY:
info->u.random.probability = lround(0x80000000 * cb->val.dbl);
break;
case O_EVERY:
--info->u.nth.every;
break;
}
}
static void statistic_check(struct xt_fcheck_call *cb)
{
struct xt_statistic_info *info = cb->data;
if (info->mode == XT_STATISTIC_MODE_RANDOM &&
!(cb->xflags & F_PROBABILITY))
xtables_error(PARAMETER_PROBLEM,
"--probability must be specified when using "
"random mode");
if (info->mode == XT_STATISTIC_MODE_NTH &&
!(cb->xflags & (F_EVERY | F_PACKET)))
xtables_error(PARAMETER_PROBLEM,
"--every and --packet must be specified when "
"using nth mode");
/* at this point, info->u.nth.every have been decreased. */
if (info->u.nth.packet > info->u.nth.every)
xtables_error(PARAMETER_PROBLEM,
"the --packet p must be 0 <= p <= n-1");
info->u.nth.count = info->u.nth.every - info->u.nth.packet;
}
static void print_match(const struct xt_statistic_info *info, char *prefix)
{
switch (info->mode) {
case XT_STATISTIC_MODE_RANDOM:
printf(" %smode random%s %sprobability %.11f", prefix,
(info->flags & XT_STATISTIC_INVERT) ? " !" : "",
prefix,
1.0 * info->u.random.probability / 0x80000000);
break;
case XT_STATISTIC_MODE_NTH:
printf(" %smode nth%s %severy %u", prefix,
(info->flags & XT_STATISTIC_INVERT) ? " !" : "",
prefix,
info->u.nth.every + 1);
if (info->u.nth.packet || *prefix)
printf(" %spacket %u", prefix, info->u.nth.packet);
break;
}
}
static void
statistic_print(const void *ip, const struct xt_entry_match *match, int numeric)
{
const struct xt_statistic_info *info = (const void *)match->data;
printf(" statistic");
print_match(info, "");
}
static void statistic_save(const void *ip, const struct xt_entry_match *match)
{
const struct xt_statistic_info *info = (const void *)match->data;
print_match(info, "--");
}
static struct xtables_match statistic_match = {
.family = NFPROTO_UNSPEC,
.name = "statistic",
.version = XTABLES_VERSION,
.size = XT_ALIGN(sizeof(struct xt_statistic_info)),
.userspacesize = offsetof(struct xt_statistic_info, u.nth.count),
.help = statistic_help,
.x6_parse = statistic_parse,
.x6_fcheck = statistic_check,
.print = statistic_print,
.save = statistic_save,
.x6_options = statistic_opts,
};
void _init(void)
{
xtables_register_match(&statistic_match);
}
This module matches packets based on some statistic condition.
It supports two distinct modes settable with the
\fB\-\-mode\fP
option.
.PP
Supported options:
.TP
\fB\-\-mode\fP \fImode\fP
Set the matching mode of the matching rule, supported modes are
.B random
and
.B nth.
.TP
[\fB!\fP] \fB\-\-probability\fP \fIp\fP
Set the probability for a packet to be randomly matched. It only works with the
\fBrandom\fP mode. \fIp\fP must be within 0.0 and 1.0. The supported
granularity is in 1/2147483648th increments.
.TP
[\fB!\fP] \fB\-\-every\fP \fIn\fP
Match one packet every nth packet. It works only with the
.B nth
mode (see also the
\fB\-\-packet\fP
option).
.TP
\fB\-\-packet\fP \fIp\fP
Set the initial counter value (0 <= p <= n\-1, default 0) for the
.B nth
mode.
/* Shared library add-on to iptables to add string matching support.
*
* Copyright (C) 2000 Emmanuel Roger <winfield@freegates.be>
*
* 2005-08-05 Pablo Neira Ayuso <pablo@eurodev.net>
* - reimplemented to use new string matching iptables match
* - add functionality to match packets by using window offsets
* - add functionality to select the string matching algorithm
*
* ChangeLog
* 29.12.2003: Michael Rash <mbr@cipherdyne.org>
* Fixed iptables save/restore for ascii strings
* that contain space chars, and hex strings that
* contain embedded NULL chars. Updated to print
* strings in hex mode if any non-printable char
* is contained within the string.
*
* 27.01.2001: Gianni Tedesco <gianni@ecsc.co.uk>
* Changed --tos to --string in save(). Also
* updated to work with slightly modified
* ipt_string_info.
*/
#define _GNU_SOURCE 1 /* strnlen for older glibcs */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <xtables.h>
#include <linux/netfilter/xt_string.h>
enum {
O_FROM = 0,
O_TO,
O_ALGO,
O_ICASE,
O_STRING,
O_HEX_STRING,
F_STRING = 1 << O_STRING,
F_HEX_STRING = 1 << O_HEX_STRING,
F_OP_ANY = F_STRING | F_HEX_STRING,
};
static void string_help(void)
{
printf(
"string match options:\n"
"--from Offset to start searching from\n"
"--to Offset to stop searching\n"
"--algo Algorithm\n"
"--icase Ignore case (default: 0)\n"
"[!] --string string Match a string in a packet\n"
"[!] --hex-string string Match a hex string in a packet\n");
}
#define s struct xt_string_info
static const struct xt_option_entry string_opts[] = {
{.name = "from", .id = O_FROM, .type = XTTYPE_UINT16,
.flags = XTOPT_PUT, XTOPT_POINTER(s, from_offset)},
{.name = "to", .id = O_TO, .type = XTTYPE_UINT16,
.flags = XTOPT_PUT, XTOPT_POINTER(s, to_offset)},
{.name = "algo", .id = O_ALGO, .type = XTTYPE_STRING,
.flags = XTOPT_MAND | XTOPT_PUT, XTOPT_POINTER(s, algo)},
{.name = "string", .id = O_STRING, .type = XTTYPE_STRING,
.flags = XTOPT_INVERT, .excl = F_HEX_STRING},
{.name = "hex-string", .id = O_HEX_STRING, .type = XTTYPE_STRING,
.flags = XTOPT_INVERT, .excl = F_STRING},
{.name = "icase", .id = O_ICASE, .type = XTTYPE_NONE},
XTOPT_TABLEEND,
};
#undef s
static void string_init(struct xt_entry_match *m)
{
struct xt_string_info *i = (struct xt_string_info *) m->data;
i->to_offset = UINT16_MAX;
}
static void
parse_string(const char *s, struct xt_string_info *info)
{
/* xt_string does not need \0 at the end of the pattern */
if (strlen(s) <= XT_STRING_MAX_PATTERN_SIZE) {
strncpy(info->pattern, s, XT_STRING_MAX_PATTERN_SIZE);
info->patlen = strnlen(s, XT_STRING_MAX_PATTERN_SIZE);
return;
}
xtables_error(PARAMETER_PROBLEM, "STRING too long \"%s\"", s);
}
static void
parse_hex_string(const char *s, struct xt_string_info *info)
{
int i=0, slen, sindex=0, schar;
short hex_f = 0, literal_f = 0;
char hextmp[3];
slen = strlen(s);
if (slen == 0) {
xtables_error(PARAMETER_PROBLEM,
"STRING must contain at least one char");
}
while (i < slen) {
if (s[i] == '\\' && !hex_f) {
literal_f = 1;
} else if (s[i] == '\\') {
xtables_error(PARAMETER_PROBLEM,
"Cannot include literals in hex data");
} else if (s[i] == '|') {
if (hex_f)
hex_f = 0;
else {
hex_f = 1;
/* get past any initial whitespace just after the '|' */
while (s[i+1] == ' ')
i++;
}
if (i+1 >= slen)
break;
else
i++; /* advance to the next character */
}
if (literal_f) {
if (i+1 >= slen) {
xtables_error(PARAMETER_PROBLEM,
"Bad literal placement at end of string");
}
info->pattern[sindex] = s[i+1];
i += 2; /* skip over literal char */
literal_f = 0;
} else if (hex_f) {
if (i+1 >= slen) {
xtables_error(PARAMETER_PROBLEM,
"Odd number of hex digits");
}
if (i+2 >= slen) {
/* must end with a "|" */
xtables_error(PARAMETER_PROBLEM, "Invalid hex block");
}
if (! isxdigit(s[i])) /* check for valid hex char */
xtables_error(PARAMETER_PROBLEM, "Invalid hex char '%c'", s[i]);
if (! isxdigit(s[i+1])) /* check for valid hex char */
xtables_error(PARAMETER_PROBLEM, "Invalid hex char '%c'", s[i+1]);
hextmp[0] = s[i];
hextmp[1] = s[i+1];
hextmp[2] = '\0';
if (! sscanf(hextmp, "%x", &schar))
xtables_error(PARAMETER_PROBLEM,
"Invalid hex char `%c'", s[i]);
info->pattern[sindex] = (char) schar;
if (s[i+2] == ' ')
i += 3; /* spaces included in the hex block */
else
i += 2;
} else { /* the char is not part of hex data, so just copy */
info->pattern[sindex] = s[i];
i++;
}
if (sindex > XT_STRING_MAX_PATTERN_SIZE)
xtables_error(PARAMETER_PROBLEM, "STRING too long \"%s\"", s);
sindex++;
}
info->patlen = sindex;
}
static void string_parse(struct xt_option_call *cb)
{
struct xt_string_info *stringinfo = cb->data;
const unsigned int revision = (*cb->match)->u.user.revision;
xtables_option_parse(cb);
switch (cb->entry->id) {
case O_STRING:
parse_string(cb->arg, stringinfo);
if (cb->invert) {
if (revision == 0)
stringinfo->u.v0.invert = 1;
else
stringinfo->u.v1.flags |= XT_STRING_FLAG_INVERT;
}
break;
case O_HEX_STRING:
parse_hex_string(cb->arg, stringinfo); /* sets length */
if (cb->invert) {
if (revision == 0)
stringinfo->u.v0.invert = 1;
else
stringinfo->u.v1.flags |= XT_STRING_FLAG_INVERT;
}
break;
case O_ICASE:
if (revision == 0)
xtables_error(VERSION_PROBLEM,
"Kernel doesn't support --icase");
stringinfo->u.v1.flags |= XT_STRING_FLAG_IGNORECASE;
break;
}
}
static void string_check(struct xt_fcheck_call *cb)
{
if (!(cb->xflags & F_OP_ANY))
xtables_error(PARAMETER_PROBLEM,
"STRING match: You must specify `--string' or "
"`--hex-string'");
}
/* Test to see if the string contains non-printable chars or quotes */
static unsigned short int
is_hex_string(const char *str, const unsigned short int len)
{
unsigned int i;
for (i=0; i < len; i++)
if (! isprint(str[i]))
return 1; /* string contains at least one non-printable char */
/* use hex output if the last char is a "\" */
if (str[len-1] == '\\')
return 1;
return 0;
}
/* Print string with "|" chars included as one would pass to --hex-string */
static void
print_hex_string(const char *str, const unsigned short int len)
{
unsigned int i;
/* start hex block */
printf(" \"|");
for (i=0; i < len; i++)
printf("%02x", (unsigned char)str[i]);
/* close hex block */
printf("|\"");
}
static void
print_string(const char *str, const unsigned short int len)
{
unsigned int i;
printf(" \"");
for (i=0; i < len; i++) {
if (str[i] == '\"' || str[i] == '\\')
putchar('\\');
printf("%c", (unsigned char) str[i]);
}
printf("\""); /* closing quote */
}
static void
string_print(const void *ip, const struct xt_entry_match *match, int numeric)
{
const struct xt_string_info *info =
(const struct xt_string_info*) match->data;
const int revision = match->u.user.revision;
int invert = (revision == 0 ? info->u.v0.invert :
info->u.v1.flags & XT_STRING_FLAG_INVERT);
if (is_hex_string(info->pattern, info->patlen)) {
printf(" STRING match %s", invert ? "!" : "");
print_hex_string(info->pattern, info->patlen);
} else {
printf(" STRING match %s", invert ? "!" : "");
print_string(info->pattern, info->patlen);
}
printf(" ALGO name %s", info->algo);
if (info->from_offset != 0)
printf(" FROM %u", info->from_offset);
if (info->to_offset != 0)
printf(" TO %u", info->to_offset);
if (revision > 0 && info->u.v1.flags & XT_STRING_FLAG_IGNORECASE)
printf(" ICASE");
}
static void string_save(const void *ip, const struct xt_entry_match *match)
{
const struct xt_string_info *info =
(const struct xt_string_info*) match->data;
const int revision = match->u.user.revision;
int invert = (revision == 0 ? info->u.v0.invert :
info->u.v1.flags & XT_STRING_FLAG_INVERT);
if (is_hex_string(info->pattern, info->patlen)) {
printf("%s --hex-string", (invert) ? " !" : "");
print_hex_string(info->pattern, info->patlen);
} else {
printf("%s --string", (invert) ? " !": "");
print_string(info->pattern, info->patlen);
}
printf(" --algo %s", info->algo);
if (info->from_offset != 0)
printf(" --from %u", info->from_offset);
if (info->to_offset != 0)
printf(" --to %u", info->to_offset);
if (revision > 0 && info->u.v1.flags & XT_STRING_FLAG_IGNORECASE)
printf(" --icase");
}
static struct xtables_match string_mt_reg[] = {
{
.name = "string",
.revision = 0,
.family = NFPROTO_UNSPEC,
.version = XTABLES_VERSION,
.size = XT_ALIGN(sizeof(struct xt_string_info)),
.userspacesize = offsetof(struct xt_string_info, config),
.help = string_help,
.init = string_init,
.print = string_print,
.save = string_save,
.x6_parse = string_parse,
.x6_fcheck = string_check,
.x6_options = string_opts,
},
{
.name = "string",
.revision = 1,
.family = NFPROTO_UNSPEC,
.version = XTABLES_VERSION,
.size = XT_ALIGN(sizeof(struct xt_string_info)),
.userspacesize = offsetof(struct xt_string_info, config),
.help = string_help,
.init = string_init,
.print = string_print,
.save = string_save,
.x6_parse = string_parse,
.x6_fcheck = string_check,
.x6_options = string_opts,
},
};
void _init(void)
{
xtables_register_matches(string_mt_reg, ARRAY_SIZE(string_mt_reg));
}
This modules matches a given string by using some pattern matching strategy. It requires a linux kernel >= 2.6.14.
.TP
\fB\-\-algo\fP {\fBbm\fP|\fBkmp\fP}
Select the pattern matching strategy. (bm = Boyer-Moore, kmp = Knuth-Pratt-Morris)
.TP
\fB\-\-from\fP \fIoffset\fP
Set the offset from which it starts looking for any matching. If not passed, default is 0.
.TP
\fB\-\-to\fP \fIoffset\fP
Set the offset up to which should be scanned. That is, byte \fIoffset\fP-1
(counting from 0) is the last one that is scanned.
If not passed, default is the packet size.
.TP
[\fB!\fP] \fB\-\-string\fP \fIpattern\fP
Matches the given pattern.
.TP
[\fB!\fP] \fB\-\-hex\-string\fP \fIpattern\fP
Matches the given pattern in hex notation.
.TP
Examples:
.IP
# The string pattern can be used for simple text characters.
.br
iptables \-A INPUT \-p tcp \-\-dport 80 \-m string \-\-algo bm \-\-string 'GET /index.html' \-j LOG
.IP
# The hex string pattern can be used for non-printable characters, like |0D 0A| or |0D0A|.
.br
iptables \-p udp \-\-dport 53 \-m string \-\-algo bm \-\-from 40 \-\-to 57 \-\-hex\-string '|03|www|09|netfilter|03|org|00|'
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment