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

Imported Upstream version 1.4.21

parents
This module matches packets based on their
.B address type.
Address types are used within the kernel networking stack and categorize
addresses into various groups. The exact definition of that group depends on the specific layer three protocol.
.PP
The following address types are possible:
.TP
.BI "UNSPEC"
an unspecified address (i.e. 0.0.0.0)
.TP
.BI "UNICAST"
an unicast address
.TP
.BI "LOCAL"
a local address
.TP
.BI "BROADCAST"
a broadcast address
.TP
.BI "ANYCAST"
an anycast packet
.TP
.BI "MULTICAST"
a multicast address
.TP
.BI "BLACKHOLE"
a blackhole address
.TP
.BI "UNREACHABLE"
an unreachable address
.TP
.BI "PROHIBIT"
a prohibited address
.TP
.BI "THROW"
FIXME
.TP
.BI "NAT"
FIXME
.TP
.BI "XRESOLVE"
.TP
[\fB!\fP] \fB\-\-src\-type\fP \fItype\fP
Matches if the source address is of given type
.TP
[\fB!\fP] \fB\-\-dst\-type\fP \fItype\fP
Matches if the destination address is of given type
.TP
.BI "\-\-limit\-iface\-in"
The address type checking can be limited to the interface the packet is coming
in. This option is only valid in the
.BR PREROUTING ,
.B INPUT
and
.B FORWARD
chains. It cannot be specified with the
\fB\-\-limit\-iface\-out\fP
option.
.TP
\fB\-\-limit\-iface\-out\fP
The address type checking can be limited to the interface the packet is going
out. This option is only valid in the
.BR POSTROUTING ,
.B OUTPUT
and
.B FORWARD
chains. It cannot be specified with the
\fB\-\-limit\-iface\-in\fP
option.
/*
* Xtables BPF extension
*
* Written by Willem de Bruijn (willemb@google.com)
* Copyright Google, Inc. 2013
* Licensed under the GNU General Public License version 2 (GPLv2)
*/
#include <linux/netfilter/xt_bpf.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <xtables.h>
#define BCODE_FILE_MAX_LEN_B 1024
enum {
O_BCODE_STDIN = 0,
};
static void bpf_help(void)
{
printf(
"bpf match options:\n"
"--bytecode <program> : a bpf program as generated by\n"
" `nfbpf_compiler RAW <filter>`\n");
}
static const struct xt_option_entry bpf_opts[] = {
{.name = "bytecode", .id = O_BCODE_STDIN, .type = XTTYPE_STRING},
XTOPT_TABLEEND,
};
static void bpf_parse_string(struct xt_option_call *cb, const char *bpf_program,
const char separator)
{
struct xt_bpf_info *bi = (void *) cb->data;
const char *token;
char sp;
int i;
/* parse head: length. */
if (sscanf(bpf_program, "%hu%c", &bi->bpf_program_num_elem, &sp) != 2 ||
sp != separator)
xtables_error(PARAMETER_PROBLEM,
"bpf: error parsing program length");
if (!bi->bpf_program_num_elem)
xtables_error(PARAMETER_PROBLEM,
"bpf: illegal zero length program");
if (bi->bpf_program_num_elem > XT_BPF_MAX_NUM_INSTR)
xtables_error(PARAMETER_PROBLEM,
"bpf: number of instructions exceeds maximum");
/* parse instructions. */
i = 0;
token = bpf_program;
while ((token = strchr(token, separator)) && (++token)[0]) {
if (i >= bi->bpf_program_num_elem)
xtables_error(PARAMETER_PROBLEM,
"bpf: real program length exceeds"
" the encoded length parameter");
if (sscanf(token, "%hu %hhu %hhu %u,",
&bi->bpf_program[i].code,
&bi->bpf_program[i].jt,
&bi->bpf_program[i].jf,
&bi->bpf_program[i].k) != 4)
xtables_error(PARAMETER_PROBLEM,
"bpf: error at instr %d", i);
i++;
}
if (i != bi->bpf_program_num_elem)
xtables_error(PARAMETER_PROBLEM,
"bpf: parsed program length is less than the"
" encoded length parameter");
}
static void bpf_parse(struct xt_option_call *cb)
{
xtables_option_parse(cb);
switch (cb->entry->id) {
case O_BCODE_STDIN:
bpf_parse_string(cb, cb->arg, ',');
break;
default:
xtables_error(PARAMETER_PROBLEM, "bpf: unknown option");
}
}
static void bpf_print_code(const void *ip, const struct xt_entry_match *match)
{
const struct xt_bpf_info *info = (void *) match->data;
int i;
for (i = 0; i < info->bpf_program_num_elem-1; i++)
printf("%hu %hhu %hhu %u,", info->bpf_program[i].code,
info->bpf_program[i].jt,
info->bpf_program[i].jf,
info->bpf_program[i].k);
printf("%hu %hhu %hhu %u", info->bpf_program[i].code,
info->bpf_program[i].jt,
info->bpf_program[i].jf,
info->bpf_program[i].k);
}
static void bpf_save(const void *ip, const struct xt_entry_match *match)
{
const struct xt_bpf_info *info = (void *) match->data;
printf(" --bytecode \"%hu,", info->bpf_program_num_elem);
bpf_print_code(ip, match);
printf("\"");
}
static void bpf_fcheck(struct xt_fcheck_call *cb)
{
if (!(cb->xflags & (1 << O_BCODE_STDIN)))
xtables_error(PARAMETER_PROBLEM,
"bpf: missing --bytecode parameter");
}
static void bpf_print(const void *ip, const struct xt_entry_match *match,
int numeric)
{
printf("match bpf ");
return bpf_print_code(ip, match);
}
static struct xtables_match bpf_match = {
.family = NFPROTO_UNSPEC,
.name = "bpf",
.version = XTABLES_VERSION,
.size = XT_ALIGN(sizeof(struct xt_bpf_info)),
.userspacesize = XT_ALIGN(offsetof(struct xt_bpf_info, filter)),
.help = bpf_help,
.print = bpf_print,
.save = bpf_save,
.x6_parse = bpf_parse,
.x6_fcheck = bpf_fcheck,
.x6_options = bpf_opts,
};
void _init(void)
{
xtables_register_match(&bpf_match);
}
Match using Linux Socket Filter. Expects a BPF program in decimal format. This
is the format generated by the \fBnfbpf_compile\fP utility.
.TP
\fB\-\-bytecode\fP \fIcode\fP
Pass the BPF byte code format (described in the example below).
.PP
The code format is similar to the output of the tcpdump -ddd command: one line
that stores the number of instructions, followed by one line for each
instruction. Instruction lines follow the pattern 'u16 u8 u8 u32' in decimal
notation. Fields encode the operation, jump offset if true, jump offset if
false and generic multiuse field 'K'. Comments are not supported.
.PP
For example, to read only packets matching 'ip proto 6', insert the following,
without the comments or trailing whitespace:
.IP
4 # number of instructions
.br
48 0 0 9 # load byte ip->proto
.br
21 0 1 6 # jump equal IPPROTO_TCP
.br
6 0 0 1 # return pass (non-zero)
.br
6 0 0 0 # return fail (zero)
.PP
You can pass this filter to the bpf match with the following command:
.IP
iptables \-A OUTPUT \-m bpf \-\-bytecode '4,48 0 0 9,21 0 1 6,6 0 0 1,6 0 0 0' \-j ACCEPT
.PP
Or instead, you can invoke the nfbpf_compile utility.
.IP
iptables \-A OUTPUT \-m bpf \-\-bytecode "`nfbpf_compile RAW 'ip proto 6'`" \-j ACCEPT
.PP
You may want to learn more about BPF from FreeBSD's bpf(4) manpage.
/*
* (C) 2009 by Pablo Neira Ayuso <pablo@netfilter.org>
*
* 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.
*/
#include <stdio.h>
#include <xtables.h>
#include <linux/netfilter/xt_cluster.h>
static void
cluster_help(void)
{
printf(
"cluster match options:\n"
" --cluster-total-nodes <num> Set number of total nodes in cluster\n"
" [!] --cluster-local-node <num> Set the local node number\n"
" [!] --cluster-local-nodemask <num> Set the local node mask\n"
" --cluster-hash-seed <num> Set seed value of the Jenkins hash\n");
}
enum {
O_CL_TOTAL_NODES = 0,
O_CL_LOCAL_NODE,
O_CL_LOCAL_NODEMASK,
O_CL_HASH_SEED,
F_CL_TOTAL_NODES = 1 << O_CL_TOTAL_NODES,
F_CL_LOCAL_NODE = 1 << O_CL_LOCAL_NODE,
F_CL_LOCAL_NODEMASK = 1 << O_CL_LOCAL_NODEMASK,
F_CL_HASH_SEED = 1 << O_CL_HASH_SEED,
};
#define s struct xt_cluster_match_info
static const struct xt_option_entry cluster_opts[] = {
{.name = "cluster-total-nodes", .id = O_CL_TOTAL_NODES,
.type = XTTYPE_UINT32, .min = 1, .max = XT_CLUSTER_NODES_MAX,
.flags = XTOPT_MAND | XTOPT_PUT, XTOPT_POINTER(s, total_nodes)},
{.name = "cluster-local-node", .id = O_CL_LOCAL_NODE,
.excl = F_CL_LOCAL_NODEMASK, .flags = XTOPT_INVERT,
.type = XTTYPE_UINT32, .min = 1, .max = XT_CLUSTER_NODES_MAX},
{.name = "cluster-local-nodemask", .id = O_CL_LOCAL_NODEMASK,
.excl = F_CL_LOCAL_NODE, .type = XTTYPE_UINT32,
.min = 1, .max = XT_CLUSTER_NODES_MAX,
.flags = XTOPT_INVERT | XTOPT_PUT, XTOPT_POINTER(s, node_mask)},
{.name = "cluster-hash-seed", .id = O_CL_HASH_SEED,
.type = XTTYPE_UINT32, .flags = XTOPT_MAND | XTOPT_PUT,
XTOPT_POINTER(s, hash_seed)},
XTOPT_TABLEEND,
};
static void cluster_parse(struct xt_option_call *cb)
{
struct xt_cluster_match_info *info = cb->data;
xtables_option_parse(cb);
switch (cb->entry->id) {
case O_CL_LOCAL_NODE:
if (cb->invert)
info->flags |= XT_CLUSTER_F_INV;
info->node_mask = 1 << (cb->val.u32 - 1);
break;
case O_CL_LOCAL_NODEMASK:
if (cb->invert)
info->flags |= XT_CLUSTER_F_INV;
break;
}
}
static void cluster_check(struct xt_fcheck_call *cb)
{
const struct xt_cluster_match_info *info = cb->data;
unsigned int test;
test = F_CL_TOTAL_NODES | F_CL_LOCAL_NODE | F_CL_HASH_SEED;
if ((cb->xflags & test) == test) {
if (info->node_mask >= (1ULL << info->total_nodes))
xtables_error(PARAMETER_PROBLEM,
"cluster match: "
"`--cluster-local-node' "
"must be <= `--cluster-total-nodes'");
return;
}
test = F_CL_TOTAL_NODES | F_CL_LOCAL_NODEMASK | F_CL_HASH_SEED;
if ((cb->xflags & test) == test) {
if (info->node_mask >= (1ULL << info->total_nodes))
xtables_error(PARAMETER_PROBLEM,
"cluster match: "
"`--cluster-local-nodemask' too big "
"for `--cluster-total-nodes'");
return;
}
if (!(cb->xflags & (F_CL_LOCAL_NODE | F_CL_LOCAL_NODEMASK)))
xtables_error(PARAMETER_PROBLEM,
"cluster match: `--cluster-local-node' or"
"`--cluster-local-nodemask' is missing");
}
static void
cluster_print(const void *ip, const struct xt_entry_match *match, int numeric)
{
const struct xt_cluster_match_info *info = (void *)match->data;
printf(" cluster ");
if (info->flags & XT_CLUSTER_F_INV)
printf("!node_mask=0x%08x", info->node_mask);
else
printf("node_mask=0x%08x", info->node_mask);
printf(" total_nodes=%u hash_seed=0x%08x",
info->total_nodes, info->hash_seed);
}
static void
cluster_save(const void *ip, const struct xt_entry_match *match)
{
const struct xt_cluster_match_info *info = (void *)match->data;
if (info->flags & XT_CLUSTER_F_INV)
printf(" ! --cluster-local-nodemask 0x%08x", info->node_mask);
else
printf(" --cluster-local-nodemask 0x%08x", info->node_mask);
printf(" --cluster-total-nodes %u --cluster-hash-seed 0x%08x",
info->total_nodes, info->hash_seed);
}
static struct xtables_match cluster_mt_reg = {
.family = NFPROTO_UNSPEC,
.name = "cluster",
.version = XTABLES_VERSION,
.size = XT_ALIGN(sizeof(struct xt_cluster_match_info)),
.userspacesize = XT_ALIGN(sizeof(struct xt_cluster_match_info)),
.help = cluster_help,
.print = cluster_print,
.save = cluster_save,
.x6_parse = cluster_parse,
.x6_fcheck = cluster_check,
.x6_options = cluster_opts,
};
void _init(void)
{
xtables_register_match(&cluster_mt_reg);
}
Allows you to deploy gateway and back-end load-sharing clusters without the
need of load-balancers.
.PP
This match requires that all the nodes see the same packets. Thus, the cluster
match decides if this node has to handle a packet given the following options:
.TP
\fB\-\-cluster\-total\-nodes\fP \fInum\fP
Set number of total nodes in cluster.
.TP
[\fB!\fP] \fB\-\-cluster\-local\-node\fP \fInum\fP
Set the local node number ID.
.TP
[\fB!\fP] \fB\-\-cluster\-local\-nodemask\fP \fImask\fP
Set the local node number ID mask. You can use this option instead
of \fB\-\-cluster\-local\-node\fP.
.TP
\fB\-\-cluster\-hash\-seed\fP \fIvalue\fP
Set seed value of the Jenkins hash.
.PP
Example:
.IP
iptables \-A PREROUTING \-t mangle \-i eth1 \-m cluster
\-\-cluster\-total\-nodes 2 \-\-cluster\-local\-node 1
\-\-cluster\-hash\-seed 0xdeadbeef
\-j MARK \-\-set-mark 0xffff
.IP
iptables \-A PREROUTING \-t mangle \-i eth2 \-m cluster
\-\-cluster\-total\-nodes 2 \-\-cluster\-local\-node 1
\-\-cluster\-hash\-seed 0xdeadbeef
\-j MARK -\-set\-mark 0xffff
.IP
iptables \-A PREROUTING \-t mangle \-i eth1
\-m mark ! \-\-mark 0xffff \-j DROP
.IP
iptables \-A PREROUTING \-t mangle \-i eth2
\-m mark ! \-\-mark 0xffff \-j DROP
.PP
And the following commands to make all nodes see the same packets:
.IP
ip maddr add 01:00:5e:00:01:01 dev eth1
.IP
ip maddr add 01:00:5e:00:01:02 dev eth2
.IP
arptables \-A OUTPUT \-o eth1 \-\-h\-length 6
\-j mangle \-\-mangle-mac-s 01:00:5e:00:01:01
.IP
arptables \-A INPUT \-i eth1 \-\-h-length 6
\-\-destination-mac 01:00:5e:00:01:01
\-j mangle \-\-mangle\-mac\-d 00:zz:yy:xx:5a:27
.IP
arptables \-A OUTPUT \-o eth2 \-\-h\-length 6
\-j mangle \-\-mangle\-mac\-s 01:00:5e:00:01:02
.IP
arptables \-A INPUT \-i eth2 \-\-h\-length 6
\-\-destination\-mac 01:00:5e:00:01:02
\-j mangle \-\-mangle\-mac\-d 00:zz:yy:xx:5a:27
.PP
\fBNOTE\fP: the arptables commands above use mainstream syntax. If you
are using arptables-jf included in some RedHat, CentOS and Fedora
versions, you will hit syntax errors. Therefore, you'll have to adapt
these to the arptables-jf syntax to get them working.
.PP
In the case of TCP connections, pickup facility has to be disabled
to avoid marking TCP ACK packets coming in the reply direction as
valid.
.IP
echo 0 > /proc/sys/net/netfilter/nf_conntrack_tcp_loose
/* Shared library add-on to iptables to add comment match support.
*
* ChangeLog
* 2003-05-13: Brad Fisher <brad@info-link.net>
* Initial comment match
* 2004-05-12: Brad Fisher <brad@info-link.net>
* Port to patch-o-matic-ng
*/
#include <stdio.h>
#include <xtables.h>
#include <linux/netfilter/xt_comment.h>
enum {
O_COMMENT = 0,
};
static void comment_help(void)
{
printf(
"comment match options:\n"
"--comment COMMENT Attach a comment to a rule\n");
}
static const struct xt_option_entry comment_opts[] = {
{.name = "comment", .id = O_COMMENT, .type = XTTYPE_STRING,
.flags = XTOPT_MAND | XTOPT_PUT,
XTOPT_POINTER(struct xt_comment_info, comment)},
XTOPT_TABLEEND,
};
static void
comment_print(const void *ip, const struct xt_entry_match *match, int numeric)
{
struct xt_comment_info *commentinfo = (void *)match->data;
commentinfo->comment[XT_MAX_COMMENT_LEN-1] = '\0';
printf(" /* %s */", commentinfo->comment);
}
/* Saves the union ipt_matchinfo in parsable form to stdout. */
static void
comment_save(const void *ip, const struct xt_entry_match *match)
{
struct xt_comment_info *commentinfo = (void *)match->data;
commentinfo->comment[XT_MAX_COMMENT_LEN-1] = '\0';
printf(" --comment");
xtables_save_string(commentinfo->comment);
}
static struct xtables_match comment_match = {
.family = NFPROTO_UNSPEC,
.name = "comment",
.version = XTABLES_VERSION,
.size = XT_ALIGN(sizeof(struct xt_comment_info)),
.userspacesize = XT_ALIGN(sizeof(struct xt_comment_info)),
.help = comment_help,
.print = comment_print,
.save = comment_save,
.x6_parse = xtables_option_parse,
.x6_options = comment_opts,
};
void _init(void)
{
xtables_register_match(&comment_match);
}
Allows you to add comments (up to 256 characters) to any rule.
.TP
\fB\-\-comment\fP \fIcomment\fP
.TP
Example:
iptables \-A INPUT \-i eth1 \-m comment \-\-comment "my local LAN"
#include <stdio.h>
#include <string.h>
#include <xtables.h>
#include <linux/netfilter/xt_connbytes.h>
enum {
O_CONNBYTES = 0,
O_CONNBYTES_DIR,
O_CONNBYTES_MODE,
};
static void connbytes_help(void)
{
printf(
"connbytes match options:\n"
" [!] --connbytes from:[to]\n"
" --connbytes-dir [original, reply, both]\n"
" --connbytes-mode [packets, bytes, avgpkt]\n");
}
static const struct xt_option_entry connbytes_opts[] = {
{.name = "connbytes", .id = O_CONNBYTES, .type = XTTYPE_UINT64RC,
.flags = XTOPT_MAND | XTOPT_INVERT},
{.name = "connbytes-dir", .id = O_CONNBYTES_DIR, .type = XTTYPE_STRING,
.flags = XTOPT_MAND},
{.name = "connbytes-mode", .id = O_CONNBYTES_MODE,
.type = XTTYPE_STRING, .flags = XTOPT_MAND},
XTOPT_TABLEEND,
};
static void connbytes_parse(struct xt_option_call *cb)
{
struct xt_connbytes_info *sinfo = cb->data;
unsigned long long i;
xtables_option_parse(cb);
switch (cb->entry->id) {
case O_CONNBYTES:
sinfo->count.from = cb->val.u64_range[0];
sinfo->count.to = UINT64_MAX;
if (cb->nvals == 2)
sinfo->count.to = cb->val.u64_range[1];
if (sinfo->count.to < sinfo->count.from)
xtables_error(PARAMETER_PROBLEM, "%llu should be less than %llu",
(unsigned long long)sinfo->count.from,
(unsigned long long)sinfo->count.to);
if (cb->invert) {
i = sinfo->count.from;
sinfo->count.from = sinfo->count.to;
sinfo->count.to = i;
}
break;
case O_CONNBYTES_DIR:
if (strcmp(cb->arg, "original") == 0)
sinfo->direction = XT_CONNBYTES_DIR_ORIGINAL;
else if (strcmp(cb->arg, "reply") == 0)
sinfo->direction = XT_CONNBYTES_DIR_REPLY;
else if (strcmp(cb->arg, "both") == 0)
sinfo->direction = XT_CONNBYTES_DIR_BOTH;
else
xtables_error(PARAMETER_PROBLEM,
"Unknown --connbytes-dir `%s'", cb->arg);
break;
case O_CONNBYTES_MODE:
if (strcmp(cb->arg, "packets") == 0)
sinfo->what = XT_CONNBYTES_PKTS;
else if (strcmp(cb->arg, "bytes") == 0)
sinfo->what = XT_CONNBYTES_BYTES;
else if (strcmp(cb->arg, "avgpkt") == 0)
sinfo->what = XT_CONNBYTES_AVGPKT;
else
xtables_error(PARAMETER_PROBLEM,
"Unknown --connbytes-mode `%s'", cb->arg);
break;
}
}
static void print_mode(const struct xt_connbytes_info *sinfo)
{
switch (sinfo->what) {
case XT_CONNBYTES_PKTS:
fputs(" packets", stdout);
break;
case XT_CONNBYTES_BYTES:
fputs(" bytes", stdout);
break;
case XT_CONNBYTES_AVGPKT:
fputs(" avgpkt", stdout);
break;
default:
fputs(" unknown", stdout);
break;
}
}
static void print_direction(const struct xt_connbytes_info *sinfo)
{
switch (sinfo->direction) {
case XT_CONNBYTES_DIR_ORIGINAL:
fputs(" original", stdout);
break;
case XT_CONNBYTES_DIR_REPLY:
fputs(" reply", stdout);
break;
case XT_CONNBYTES_DIR_BOTH:
fputs(" both", stdout);
break;
default:
fputs(" unknown", stdout);
break;
}
}
static void print_from_to(const struct xt_connbytes_info *sinfo, const char *prefix)
{
unsigned long long from, to;
if (sinfo->count.from > sinfo->count.to) {
fputs(" !", stdout);
from = sinfo->count.to;
to = sinfo->count.from;
} else {
to = sinfo->count.to;
from = sinfo->count.from;
}
printf(" %sconnbytes %llu", prefix, from);
if (to && to < UINT64_MAX)
printf(":%llu", to);
}
static void
connbytes_print(const void *ip, const struct xt_entry_match *match, int numeric)
{
const struct xt_connbytes_info *sinfo = (const void *)match->data;
print_from_to(sinfo, "");
fputs(" connbytes mode", stdout);
print_mode(sinfo);
fputs(" connbytes direction", stdout);
print_direction(sinfo);
}
static void connbytes_save(const void *ip, const struct xt_entry_match *match)
{
const struct xt_connbytes_info *sinfo = (const void *)match->data;
print_from_to(sinfo, "--");
fputs(" --connbytes-mode", stdout);
print_mode(sinfo);
fputs(" --connbytes-dir", stdout);
print_direction(sinfo);
}
static struct xtables_match connbytes_match = {
.family = NFPROTO_UNSPEC,
.name = "connbytes",
.version = XTABLES_VERSION,
.size = XT_ALIGN(sizeof(struct xt_connbytes_info)),
.userspacesize = XT_ALIGN(sizeof(struct xt_connbytes_info)),
.help = connbytes_help,
.print = connbytes_print,
.save = connbytes_save,
.x6_parse = connbytes_parse,
.x6_options = connbytes_opts,
};
void _init(void)
{
xtables_register_match(&connbytes_match);
}
Match by how many bytes or packets a connection (or one of the two
flows constituting the connection) has transferred so far, or by
average bytes per packet.
.PP
The counters are 64-bit and are thus not expected to overflow ;)
.PP
The primary use is to detect long-lived downloads and mark them to be
scheduled using a lower priority band in traffic control.
.PP
The transferred bytes per connection can also be viewed through
`conntrack \-L` and accessed via ctnetlink.
.PP
NOTE that for connections which have no accounting information, the match will
always return false. The "net.netfilter.nf_conntrack_acct" sysctl flag controls
whether \fBnew\fP connections will be byte/packet counted. Existing connection
flows will not be gaining/losing a/the accounting structure when be sysctl flag
is flipped.
.TP
[\fB!\fP] \fB\-\-connbytes\fP \fIfrom\fP[\fB:\fP\fIto\fP]
match packets from a connection whose packets/bytes/average packet
size is more than FROM and less than TO bytes/packets. if TO is
omitted only FROM check is done. "!" is used to match packets not
falling in the range.
.TP
\fB\-\-connbytes\-dir\fP {\fBoriginal\fP|\fBreply\fP|\fBboth\fP}
which packets to consider
.TP
\fB\-\-connbytes\-mode\fP {\fBpackets\fP|\fBbytes\fP|\fBavgpkt\fP}
whether to check the amount of packets, number of bytes transferred or
the average size (in bytes) of all packets received so far. Note that
when "both" is used together with "avgpkt", and data is going (mainly)
only in one direction (for example HTTP), the average packet size will
be about half of the actual data packets.
.TP
Example:
iptables .. \-m connbytes \-\-connbytes 10000:100000 \-\-connbytes\-dir both \-\-connbytes\-mode bytes ...
#include <errno.h>
#include <stdbool.h>
#include <string.h>
#include <stdio.h>
#include <stdint.h>
#include <xtables.h>
#include <linux/netfilter/xt_connlabel.h>
#include <libnetfilter_conntrack/libnetfilter_conntrack.h>
enum {
O_LABEL = 0,
O_SET = 1,
};
static struct nfct_labelmap *map;
static void connlabel_mt_help(void)
{
puts(
"connlabel match options:\n"
"[!] --label name Match if label has been set on connection\n"
" --set Set label on connection");
}
static const struct xt_option_entry connlabel_mt_opts[] = {
{.name = "label", .id = O_LABEL, .type = XTTYPE_STRING,
.min = 1, .flags = XTOPT_MAND|XTOPT_INVERT},
{.name = "set", .id = O_SET, .type = XTTYPE_NONE},
XTOPT_TABLEEND,
};
static void connlabel_mt_parse(struct xt_option_call *cb)
{
struct xt_connlabel_mtinfo *info = cb->data;
int tmp;
xtables_option_parse(cb);
switch (cb->entry->id) {
case O_LABEL:
tmp = nfct_labelmap_get_bit(map, cb->arg);
if (tmp < 0)
xtables_error(PARAMETER_PROBLEM, "label '%s' not found", cb->arg);
info->bit = tmp;
if (cb->invert)
info->options |= XT_CONNLABEL_OP_INVERT;
break;
case O_SET:
info->options |= XT_CONNLABEL_OP_SET;
break;
}
}
static const char *connlabel_get_name(int b)
{
const char *name = nfct_labelmap_get_name(map, b);
if (name && strcmp(name, ""))
return name;
return NULL;
}
static void
connlabel_mt_print_op(const struct xt_connlabel_mtinfo *info, const char *prefix)
{
if (info->options & XT_CONNLABEL_OP_SET)
printf(" %sset", prefix);
}
static void
connlabel_mt_print(const void *ip, const struct xt_entry_match *match, int numeric)
{
const struct xt_connlabel_mtinfo *info = (const void *)match->data;
const char *name = connlabel_get_name(info->bit);
printf(" connlabel");
if (info->options & XT_CONNLABEL_OP_INVERT)
printf(" !");
if (numeric || name == NULL) {
printf(" %u", info->bit);
} else {
printf(" '%s'", name);
}
connlabel_mt_print_op(info, "");
}
static void
connlabel_mt_save(const void *ip, const struct xt_entry_match *match)
{
const struct xt_connlabel_mtinfo *info = (const void *)match->data;
const char *name = connlabel_get_name(info->bit);
if (info->options & XT_CONNLABEL_OP_INVERT)
printf(" !");
if (name)
printf(" --label \"%s\"", name);
else
printf(" --label \"%u\"", info->bit);
connlabel_mt_print_op(info, "--");
}
static struct xtables_match connlabel_mt_reg = {
.family = NFPROTO_UNSPEC,
.name = "connlabel",
.version = XTABLES_VERSION,
.size = XT_ALIGN(sizeof(struct xt_connlabel_mtinfo)),
.userspacesize = offsetof(struct xt_connlabel_mtinfo, bit),
.help = connlabel_mt_help,
.print = connlabel_mt_print,
.save = connlabel_mt_save,
.x6_parse = connlabel_mt_parse,
.x6_options = connlabel_mt_opts,
};
void _init(void)
{
map = nfct_labelmap_new(NULL);
if (!map) {
fprintf(stderr, "cannot open connlabel.conf, not registering '%s' match: %s\n",
connlabel_mt_reg.name, strerror(errno));
return;
}
xtables_register_match(&connlabel_mt_reg);
}
Module matches or adds connlabels to a connection.
connlabels are similar to connmarks, except labels are bit-based; i.e.
all labels may be attached to a flow at the same time.
Up to 128 unique labels are currently supported.
.TP
[\fB!\fP] \fB\-\-label\fP \fBname\fP
matches if label \fBname\fP has been set on a connection.
Instead of a name (which will be translated to a number, see EXAMPLE below),
a number may be used instead. Using a number always overrides connlabel.conf.
.TP
\fB\-\-set\fP
if the label has not been set on the connection, set it.
Note that setting a label can fail. This is because the kernel allocates the
conntrack label storage area when the connection is created, and it only
reserves the amount of memory required by the ruleset that exists at
the time the connection is created.
In this case, the match will fail (or succeed, in case \fB\-\-label\fP
option was negated).
.PP
This match depends on libnetfilter_conntrack 1.0.4 or later.
Label translation is done via the \fB/etc/xtables/connlabel.conf\fP configuration file.
.PP
Example:
.IP
.nf
0 eth0-in
1 eth0-out
2 ppp-in
3 ppp-out
4 bulk-traffic
5 interactive
.fi
.PP
#include <stdio.h>
#include <netdb.h>
#include <string.h>
#include <xtables.h>
#include <linux/netfilter/xt_connlimit.h>
enum {
O_UPTO = 0,
O_ABOVE,
O_MASK,
O_SADDR,
O_DADDR,
F_UPTO = 1 << O_UPTO,
F_ABOVE = 1 << O_ABOVE,
F_MASK = 1 << O_MASK,
F_SADDR = 1 << O_SADDR,
F_DADDR = 1 << O_DADDR,
};
static void connlimit_help(void)
{
printf(
"connlimit match options:\n"
" --connlimit-upto n match if the number of existing connections is 0..n\n"
" --connlimit-above n match if the number of existing connections is >n\n"
" --connlimit-mask n group hosts using prefix length (default: max len)\n"
" --connlimit-saddr select source address for grouping\n"
" --connlimit-daddr select destination addresses for grouping\n");
}
#define s struct xt_connlimit_info
static const struct xt_option_entry connlimit_opts[] = {
{.name = "connlimit-upto", .id = O_UPTO, .excl = F_ABOVE,
.type = XTTYPE_UINT32, .flags = XTOPT_INVERT | XTOPT_PUT,
XTOPT_POINTER(s, limit)},
{.name = "connlimit-above", .id = O_ABOVE, .excl = F_UPTO,
.type = XTTYPE_UINT32, .flags = XTOPT_INVERT | XTOPT_PUT,
XTOPT_POINTER(s, limit)},
{.name = "connlimit-mask", .id = O_MASK, .type = XTTYPE_PLENMASK,
.flags = XTOPT_PUT, XTOPT_POINTER(s, mask)},
{.name = "connlimit-saddr", .id = O_SADDR, .excl = F_DADDR,
.type = XTTYPE_NONE},
{.name = "connlimit-daddr", .id = O_DADDR, .excl = F_SADDR,
.type = XTTYPE_NONE},
XTOPT_TABLEEND,
};
#undef s
static void connlimit_init(struct xt_entry_match *match)
{
struct xt_connlimit_info *info = (void *)match->data;
/* This will also initialize the v4 mask correctly */
memset(info->v6_mask, 0xFF, sizeof(info->v6_mask));
}
static void connlimit_parse(struct xt_option_call *cb, uint8_t family)
{
struct xt_connlimit_info *info = cb->data;
const unsigned int revision = (*cb->match)->u.user.revision;
xtables_option_parse(cb);
switch (cb->entry->id) {
case O_ABOVE:
if (cb->invert)
info->flags |= XT_CONNLIMIT_INVERT;
break;
case O_UPTO:
if (!cb->invert)
info->flags |= XT_CONNLIMIT_INVERT;
break;
case O_SADDR:
if (revision < 1)
xtables_error(PARAMETER_PROBLEM,
"xt_connlimit.0 does not support "
"--connlimit-daddr");
info->flags &= ~XT_CONNLIMIT_DADDR;
break;
case O_DADDR:
if (revision < 1)
xtables_error(PARAMETER_PROBLEM,
"xt_connlimit.0 does not support "
"--connlimit-daddr");
info->flags |= XT_CONNLIMIT_DADDR;
break;
}
}
static void connlimit_parse4(struct xt_option_call *cb)
{
return connlimit_parse(cb, NFPROTO_IPV4);
}
static void connlimit_parse6(struct xt_option_call *cb)
{
return connlimit_parse(cb, NFPROTO_IPV6);
}
static void connlimit_check(struct xt_fcheck_call *cb)
{
if ((cb->xflags & (F_UPTO | F_ABOVE)) == 0)
xtables_error(PARAMETER_PROBLEM,
"You must specify \"--connlimit-above\" or "
"\"--connlimit-upto\".");
}
static unsigned int count_bits4(uint32_t mask)
{
unsigned int bits = 0;
for (mask = ~ntohl(mask); mask != 0; mask >>= 1)
++bits;
return 32 - bits;
}
static unsigned int count_bits6(const uint32_t *mask)
{
unsigned int bits = 0, i;
uint32_t tmp[4];
for (i = 0; i < 4; ++i)
for (tmp[i] = ~ntohl(mask[i]); tmp[i] != 0; tmp[i] >>= 1)
++bits;
return 128 - bits;
}
static void connlimit_print4(const void *ip,
const struct xt_entry_match *match, int numeric)
{
const struct xt_connlimit_info *info = (const void *)match->data;
printf(" #conn %s/%u %s %u",
(info->flags & XT_CONNLIMIT_DADDR) ? "dst" : "src",
count_bits4(info->v4_mask),
(info->flags & XT_CONNLIMIT_INVERT) ? "<=" : ">", info->limit);
}
static void connlimit_print6(const void *ip,
const struct xt_entry_match *match, int numeric)
{
const struct xt_connlimit_info *info = (const void *)match->data;
printf(" #conn %s/%u %s %u",
(info->flags & XT_CONNLIMIT_DADDR) ? "dst" : "src",
count_bits6(info->v6_mask),
(info->flags & XT_CONNLIMIT_INVERT) ? "<=" : ">", info->limit);
}
static void connlimit_save4(const void *ip, const struct xt_entry_match *match)
{
const struct xt_connlimit_info *info = (const void *)match->data;
const int revision = match->u.user.revision;
if (info->flags & XT_CONNLIMIT_INVERT)
printf(" --connlimit-upto %u", info->limit);
else
printf(" --connlimit-above %u", info->limit);
printf(" --connlimit-mask %u", count_bits4(info->v4_mask));
if (revision >= 1) {
if (info->flags & XT_CONNLIMIT_DADDR)
printf(" --connlimit-daddr");
else
printf(" --connlimit-saddr");
}
}
static void connlimit_save6(const void *ip, const struct xt_entry_match *match)
{
const struct xt_connlimit_info *info = (const void *)match->data;
const int revision = match->u.user.revision;
if (info->flags & XT_CONNLIMIT_INVERT)
printf(" --connlimit-upto %u", info->limit);
else
printf(" --connlimit-above %u", info->limit);
printf(" --connlimit-mask %u", count_bits6(info->v6_mask));
if (revision >= 1) {
if (info->flags & XT_CONNLIMIT_DADDR)
printf(" --connlimit-daddr");
else
printf(" --connlimit-saddr");
}
}
static struct xtables_match connlimit_mt_reg[] = {
{
.name = "connlimit",
.revision = 0,
.family = NFPROTO_IPV4,
.version = XTABLES_VERSION,
.size = XT_ALIGN(sizeof(struct xt_connlimit_info)),
.userspacesize = offsetof(struct xt_connlimit_info, data),
.help = connlimit_help,
.init = connlimit_init,
.x6_parse = connlimit_parse4,
.x6_fcheck = connlimit_check,
.print = connlimit_print4,
.save = connlimit_save4,
.x6_options = connlimit_opts,
},
{
.name = "connlimit",
.revision = 0,
.family = NFPROTO_IPV6,
.version = XTABLES_VERSION,
.size = XT_ALIGN(sizeof(struct xt_connlimit_info)),
.userspacesize = offsetof(struct xt_connlimit_info, data),
.help = connlimit_help,
.init = connlimit_init,
.x6_parse = connlimit_parse6,
.x6_fcheck = connlimit_check,
.print = connlimit_print6,
.save = connlimit_save6,
.x6_options = connlimit_opts,
},
{
.name = "connlimit",
.revision = 1,
.family = NFPROTO_IPV4,
.version = XTABLES_VERSION,
.size = XT_ALIGN(sizeof(struct xt_connlimit_info)),
.userspacesize = offsetof(struct xt_connlimit_info, data),
.help = connlimit_help,
.init = connlimit_init,
.x6_parse = connlimit_parse4,
.x6_fcheck = connlimit_check,
.print = connlimit_print4,
.save = connlimit_save4,
.x6_options = connlimit_opts,
},
{
.name = "connlimit",
.revision = 1,
.family = NFPROTO_IPV6,
.version = XTABLES_VERSION,
.size = XT_ALIGN(sizeof(struct xt_connlimit_info)),
.userspacesize = offsetof(struct xt_connlimit_info, data),
.help = connlimit_help,
.init = connlimit_init,
.x6_parse = connlimit_parse6,
.x6_fcheck = connlimit_check,
.print = connlimit_print6,
.save = connlimit_save6,
.x6_options = connlimit_opts,
},
};
void _init(void)
{
xtables_register_matches(connlimit_mt_reg, ARRAY_SIZE(connlimit_mt_reg));
}
Allows you to restrict the number of parallel connections to a server per
client IP address (or client address block).
.TP
\fB\-\-connlimit\-upto\fP \fIn\fP
Match if the number of existing connections is below or equal \fIn\fP.
.TP
\fB\-\-connlimit\-above\fP \fIn\fP
Match if the number of existing connections is above \fIn\fP.
.TP
\fB\-\-connlimit\-mask\fP \fIprefix_length\fP
Group hosts using the prefix length. For IPv4, this must be a number between
(including) 0 and 32. For IPv6, between 0 and 128. If not specified, the
maximum prefix length for the applicable protocol is used.
.TP
\fB\-\-connlimit\-saddr\fP
Apply the limit onto the source group. This is the default if
\-\-connlimit\-daddr is not specified.
.TP
\fB\-\-connlimit\-daddr\fP
Apply the limit onto the destination group.
.PP
Examples:
.TP
# allow 2 telnet connections per client host
iptables \-A INPUT \-p tcp \-\-syn \-\-dport 23 \-m connlimit \-\-connlimit\-above 2 \-j REJECT
.TP
# you can also match the other way around:
iptables \-A INPUT \-p tcp \-\-syn \-\-dport 23 \-m connlimit \-\-connlimit\-upto 2 \-j ACCEPT
.TP
# limit the number of parallel HTTP requests to 16 per class C sized \
source network (24 bit netmask)
iptables \-p tcp \-\-syn \-\-dport 80 \-m connlimit \-\-connlimit\-above 16
\-\-connlimit\-mask 24 \-j REJECT
.TP
# limit the number of parallel HTTP requests to 16 for the link local network
(ipv6)
ip6tables \-p tcp \-\-syn \-\-dport 80 \-s fe80::/64 \-m connlimit \-\-connlimit\-above
16 \-\-connlimit\-mask 64 \-j REJECT
.TP
# Limit the number of connections to a particular host:
ip6tables \-p tcp \-\-syn \-\-dport 49152:65535 \-d 2001:db8::1 \-m connlimit
\-\-connlimit-above 100 \-j REJECT
/* Shared library add-on to iptables to add connmark matching support.
*
* (C) 2002,2004 MARA Systems AB <http://www.marasystems.com>
* by Henrik Nordstrom <hno@marasystems.com>
*
* Version 1.1
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <xtables.h>
#include <linux/netfilter/xt_connmark.h>
struct xt_connmark_info {
unsigned long mark, mask;
uint8_t invert;
};
enum {
O_MARK = 0,
};
static void connmark_mt_help(void)
{
printf(
"connmark match options:\n"
"[!] --mark value[/mask] Match ctmark value with optional mask\n");
}
static const struct xt_option_entry connmark_mt_opts[] = {
{.name = "mark", .id = O_MARK, .type = XTTYPE_MARKMASK32,
.flags = XTOPT_MAND | XTOPT_INVERT},
XTOPT_TABLEEND,
};
static void connmark_mt_parse(struct xt_option_call *cb)
{
struct xt_connmark_mtinfo1 *info = cb->data;
xtables_option_parse(cb);
if (cb->invert)
info->invert = true;
info->mark = cb->val.mark;
info->mask = cb->val.mask;
}
static void connmark_parse(struct xt_option_call *cb)
{
struct xt_connmark_info *markinfo = cb->data;
xtables_option_parse(cb);
markinfo->mark = cb->val.mark;
markinfo->mask = cb->val.mask;
if (cb->invert)
markinfo->invert = 1;
}
static void print_mark(unsigned int mark, unsigned int mask)
{
if (mask != 0xffffffffU)
printf(" 0x%x/0x%x", mark, mask);
else
printf(" 0x%x", mark);
}
static void
connmark_print(const void *ip, const struct xt_entry_match *match, int numeric)
{
const struct xt_connmark_info *info = (const void *)match->data;
printf(" CONNMARK match ");
if (info->invert)
printf("!");
print_mark(info->mark, info->mask);
}
static void
connmark_mt_print(const void *ip, const struct xt_entry_match *match, int numeric)
{
const struct xt_connmark_mtinfo1 *info = (const void *)match->data;
printf(" connmark match ");
if (info->invert)
printf("!");
print_mark(info->mark, info->mask);
}
static void connmark_save(const void *ip, const struct xt_entry_match *match)
{
const struct xt_connmark_info *info = (const void *)match->data;
if (info->invert)
printf(" !");
printf(" --mark");
print_mark(info->mark, info->mask);
}
static void
connmark_mt_save(const void *ip, const struct xt_entry_match *match)
{
const struct xt_connmark_mtinfo1 *info = (const void *)match->data;
if (info->invert)
printf(" !");
printf(" --mark");
print_mark(info->mark, info->mask);
}
static struct xtables_match connmark_mt_reg[] = {
{
.family = NFPROTO_UNSPEC,
.name = "connmark",
.revision = 0,
.version = XTABLES_VERSION,
.size = XT_ALIGN(sizeof(struct xt_connmark_info)),
.userspacesize = XT_ALIGN(sizeof(struct xt_connmark_info)),
.help = connmark_mt_help,
.print = connmark_print,
.save = connmark_save,
.x6_parse = connmark_parse,
.x6_options = connmark_mt_opts,
},
{
.version = XTABLES_VERSION,
.name = "connmark",
.revision = 1,
.family = NFPROTO_UNSPEC,
.size = XT_ALIGN(sizeof(struct xt_connmark_mtinfo1)),
.userspacesize = XT_ALIGN(sizeof(struct xt_connmark_mtinfo1)),
.help = connmark_mt_help,
.print = connmark_mt_print,
.save = connmark_mt_save,
.x6_parse = connmark_mt_parse,
.x6_options = connmark_mt_opts,
},
};
void _init(void)
{
xtables_register_matches(connmark_mt_reg, ARRAY_SIZE(connmark_mt_reg));
}
This module matches the netfilter mark field associated with a connection
(which can be set using the \fBCONNMARK\fP target below).
.TP
[\fB!\fP] \fB\-\-mark\fP \fIvalue\fP[\fB/\fP\fImask\fP]
Matches packets in connections with the given mark value (if a mask is
specified, this is logically ANDed with the mark before the comparison).
/*
* libxt_conntrack
* Shared library add-on to iptables for conntrack matching support.
*
* GPL (C) 2001 Marc Boucher (marc@mbsi.ca).
* Copyright © CC Computer Consultants GmbH, 2007 - 2008
* Jan Engelhardt <jengelh@computergmbh.de>
*/
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <xtables.h>
#include <linux/netfilter/xt_conntrack.h>
#include <linux/netfilter/xt_state.h>
#include <linux/netfilter/nf_conntrack_common.h>
#ifndef XT_STATE_UNTRACKED
#define XT_STATE_UNTRACKED (1 << (IP_CT_NUMBER + 1))
#endif
struct ip_conntrack_old_tuple {
struct {
__be32 ip;
union {
__u16 all;
} u;
} src;
struct {
__be32 ip;
union {
__u16 all;
} u;
/* The protocol. */
__u16 protonum;
} dst;
};
struct xt_conntrack_info {
unsigned int statemask, statusmask;
struct ip_conntrack_old_tuple tuple[IP_CT_DIR_MAX];
struct in_addr sipmsk[IP_CT_DIR_MAX], dipmsk[IP_CT_DIR_MAX];
unsigned long expires_min, expires_max;
/* Flags word */
uint8_t flags;
/* Inverse flags */
uint8_t invflags;
};
enum {
O_CTSTATE = 0,
O_CTPROTO,
O_CTORIGSRC,
O_CTORIGDST,
O_CTREPLSRC,
O_CTREPLDST,
O_CTORIGSRCPORT,
O_CTORIGDSTPORT,
O_CTREPLSRCPORT,
O_CTREPLDSTPORT,
O_CTSTATUS,
O_CTEXPIRE,
O_CTDIR,
};
static void conntrack_mt_help(void)
{
printf(
"conntrack match options:\n"
"[!] --ctstate {INVALID|ESTABLISHED|NEW|RELATED|UNTRACKED|SNAT|DNAT}[,...]\n"
" State(s) to match\n"
"[!] --ctproto proto Protocol to match; by number or name, e.g. \"tcp\"\n"
"[!] --ctorigsrc address[/mask]\n"
"[!] --ctorigdst address[/mask]\n"
"[!] --ctreplsrc address[/mask]\n"
"[!] --ctrepldst address[/mask]\n"
" Original/Reply source/destination address\n"
"[!] --ctorigsrcport port\n"
"[!] --ctorigdstport port\n"
"[!] --ctreplsrcport port\n"
"[!] --ctrepldstport port\n"
" TCP/UDP/SCTP orig./reply source/destination port\n"
"[!] --ctstatus {NONE|EXPECTED|SEEN_REPLY|ASSURED|CONFIRMED}[,...]\n"
" Status(es) to match\n"
"[!] --ctexpire time[:time] Match remaining lifetime in seconds against\n"
" value or range of values (inclusive)\n"
" --ctdir {ORIGINAL|REPLY} Flow direction of packet\n");
}
#define s struct xt_conntrack_info /* for v0 */
static const struct xt_option_entry conntrack_mt_opts_v0[] = {
{.name = "ctstate", .id = O_CTSTATE, .type = XTTYPE_STRING,
.flags = XTOPT_INVERT},
{.name = "ctproto", .id = O_CTPROTO, .type = XTTYPE_PROTOCOL,
.flags = XTOPT_INVERT},
{.name = "ctorigsrc", .id = O_CTORIGSRC, .type = XTTYPE_HOST,
.flags = XTOPT_INVERT},
{.name = "ctorigdst", .id = O_CTORIGDST, .type = XTTYPE_HOST,
.flags = XTOPT_INVERT},
{.name = "ctreplsrc", .id = O_CTREPLSRC, .type = XTTYPE_HOST,
.flags = XTOPT_INVERT},
{.name = "ctrepldst", .id = O_CTREPLDST, .type = XTTYPE_HOST,
.flags = XTOPT_INVERT},
{.name = "ctstatus", .id = O_CTSTATUS, .type = XTTYPE_STRING,
.flags = XTOPT_INVERT},
{.name = "ctexpire", .id = O_CTEXPIRE, .type = XTTYPE_UINT32RC,
.flags = XTOPT_INVERT},
XTOPT_TABLEEND,
};
#undef s
#define s struct xt_conntrack_mtinfo2
/* We exploit the fact that v1-v2 share the same xt_o_e layout */
static const struct xt_option_entry conntrack2_mt_opts[] = {
{.name = "ctstate", .id = O_CTSTATE, .type = XTTYPE_STRING,
.flags = XTOPT_INVERT},
{.name = "ctproto", .id = O_CTPROTO, .type = XTTYPE_PROTOCOL,
.flags = XTOPT_INVERT},
{.name = "ctorigsrc", .id = O_CTORIGSRC, .type = XTTYPE_HOSTMASK,
.flags = XTOPT_INVERT},
{.name = "ctorigdst", .id = O_CTORIGDST, .type = XTTYPE_HOSTMASK,
.flags = XTOPT_INVERT},
{.name = "ctreplsrc", .id = O_CTREPLSRC, .type = XTTYPE_HOSTMASK,
.flags = XTOPT_INVERT},
{.name = "ctrepldst", .id = O_CTREPLDST, .type = XTTYPE_HOSTMASK,
.flags = XTOPT_INVERT},
{.name = "ctstatus", .id = O_CTSTATUS, .type = XTTYPE_STRING,
.flags = XTOPT_INVERT},
{.name = "ctexpire", .id = O_CTEXPIRE, .type = XTTYPE_UINT32RC,
.flags = XTOPT_INVERT},
/*
* Rev 1 and 2 only store one port, and we would normally use
* %XTTYPE_PORT (rather than %XTTYPE_PORTRC) for that. The resulting
* error message - in case a user passed a range nevertheless -
* "port 22:23 resolved to nothing" is not quite as useful as using
* %XTTYPE_PORTC and libxt_conntrack's own range test.
*/
{.name = "ctorigsrcport", .id = O_CTORIGSRCPORT, .type = XTTYPE_PORTRC,
.flags = XTOPT_INVERT | XTOPT_NBO},
{.name = "ctorigdstport", .id = O_CTORIGDSTPORT, .type = XTTYPE_PORTRC,
.flags = XTOPT_INVERT | XTOPT_NBO},
{.name = "ctreplsrcport", .id = O_CTREPLSRCPORT, .type = XTTYPE_PORTRC,
.flags = XTOPT_INVERT | XTOPT_NBO},
{.name = "ctrepldstport", .id = O_CTREPLDSTPORT, .type = XTTYPE_PORTRC,
.flags = XTOPT_INVERT | XTOPT_NBO},
{.name = "ctdir", .id = O_CTDIR, .type = XTTYPE_STRING},
XTOPT_TABLEEND,
};
#undef s
#define s struct xt_conntrack_mtinfo3
/* Difference from v2 is the non-NBO form. */
static const struct xt_option_entry conntrack3_mt_opts[] = {
{.name = "ctstate", .id = O_CTSTATE, .type = XTTYPE_STRING,
.flags = XTOPT_INVERT},
{.name = "ctproto", .id = O_CTPROTO, .type = XTTYPE_PROTOCOL,
.flags = XTOPT_INVERT},
{.name = "ctorigsrc", .id = O_CTORIGSRC, .type = XTTYPE_HOSTMASK,
.flags = XTOPT_INVERT},
{.name = "ctorigdst", .id = O_CTORIGDST, .type = XTTYPE_HOSTMASK,
.flags = XTOPT_INVERT},
{.name = "ctreplsrc", .id = O_CTREPLSRC, .type = XTTYPE_HOSTMASK,
.flags = XTOPT_INVERT},
{.name = "ctrepldst", .id = O_CTREPLDST, .type = XTTYPE_HOSTMASK,
.flags = XTOPT_INVERT},
{.name = "ctstatus", .id = O_CTSTATUS, .type = XTTYPE_STRING,
.flags = XTOPT_INVERT},
{.name = "ctexpire", .id = O_CTEXPIRE, .type = XTTYPE_UINT32RC,
.flags = XTOPT_INVERT},
{.name = "ctorigsrcport", .id = O_CTORIGSRCPORT, .type = XTTYPE_PORTRC,
.flags = XTOPT_INVERT},
{.name = "ctorigdstport", .id = O_CTORIGDSTPORT, .type = XTTYPE_PORTRC,
.flags = XTOPT_INVERT},
{.name = "ctreplsrcport", .id = O_CTREPLSRCPORT, .type = XTTYPE_PORTRC,
.flags = XTOPT_INVERT},
{.name = "ctrepldstport", .id = O_CTREPLDSTPORT, .type = XTTYPE_PORTRC,
.flags = XTOPT_INVERT},
{.name = "ctdir", .id = O_CTDIR, .type = XTTYPE_STRING},
XTOPT_TABLEEND,
};
#undef s
static int
parse_state(const char *state, size_t len, struct xt_conntrack_info *sinfo)
{
if (strncasecmp(state, "INVALID", len) == 0)
sinfo->statemask |= XT_CONNTRACK_STATE_INVALID;
else if (strncasecmp(state, "NEW", len) == 0)
sinfo->statemask |= XT_CONNTRACK_STATE_BIT(IP_CT_NEW);
else if (strncasecmp(state, "ESTABLISHED", len) == 0)
sinfo->statemask |= XT_CONNTRACK_STATE_BIT(IP_CT_ESTABLISHED);
else if (strncasecmp(state, "RELATED", len) == 0)
sinfo->statemask |= XT_CONNTRACK_STATE_BIT(IP_CT_RELATED);
else if (strncasecmp(state, "UNTRACKED", len) == 0)
sinfo->statemask |= XT_CONNTRACK_STATE_UNTRACKED;
else if (strncasecmp(state, "SNAT", len) == 0)
sinfo->statemask |= XT_CONNTRACK_STATE_SNAT;
else if (strncasecmp(state, "DNAT", len) == 0)
sinfo->statemask |= XT_CONNTRACK_STATE_DNAT;
else
return 0;
return 1;
}
static void
parse_states(const char *arg, struct xt_conntrack_info *sinfo)
{
const char *comma;
while ((comma = strchr(arg, ',')) != NULL) {
if (comma == arg || !parse_state(arg, comma-arg, sinfo))
xtables_error(PARAMETER_PROBLEM, "Bad ctstate \"%s\"", arg);
arg = comma+1;
}
if (!*arg)
xtables_error(PARAMETER_PROBLEM, "\"--ctstate\" requires a list of "
"states with no spaces, e.g. "
"ESTABLISHED,RELATED");
if (strlen(arg) == 0 || !parse_state(arg, strlen(arg), sinfo))
xtables_error(PARAMETER_PROBLEM, "Bad ctstate \"%s\"", arg);
}
static bool
conntrack_ps_state(struct xt_conntrack_mtinfo3 *info, const char *state,
size_t z)
{
if (strncasecmp(state, "INVALID", z) == 0)
info->state_mask |= XT_CONNTRACK_STATE_INVALID;
else if (strncasecmp(state, "NEW", z) == 0)
info->state_mask |= XT_CONNTRACK_STATE_BIT(IP_CT_NEW);
else if (strncasecmp(state, "ESTABLISHED", z) == 0)
info->state_mask |= XT_CONNTRACK_STATE_BIT(IP_CT_ESTABLISHED);
else if (strncasecmp(state, "RELATED", z) == 0)
info->state_mask |= XT_CONNTRACK_STATE_BIT(IP_CT_RELATED);
else if (strncasecmp(state, "UNTRACKED", z) == 0)
info->state_mask |= XT_CONNTRACK_STATE_UNTRACKED;
else if (strncasecmp(state, "SNAT", z) == 0)
info->state_mask |= XT_CONNTRACK_STATE_SNAT;
else if (strncasecmp(state, "DNAT", z) == 0)
info->state_mask |= XT_CONNTRACK_STATE_DNAT;
else
return false;
return true;
}
static void
conntrack_ps_states(struct xt_conntrack_mtinfo3 *info, const char *arg)
{
const char *comma;
while ((comma = strchr(arg, ',')) != NULL) {
if (comma == arg || !conntrack_ps_state(info, arg, comma - arg))
xtables_error(PARAMETER_PROBLEM,
"Bad ctstate \"%s\"", arg);
arg = comma + 1;
}
if (strlen(arg) == 0 || !conntrack_ps_state(info, arg, strlen(arg)))
xtables_error(PARAMETER_PROBLEM, "Bad ctstate \"%s\"", arg);
}
static int
parse_status(const char *status, size_t len, struct xt_conntrack_info *sinfo)
{
if (strncasecmp(status, "NONE", len) == 0)
sinfo->statusmask |= 0;
else if (strncasecmp(status, "EXPECTED", len) == 0)
sinfo->statusmask |= IPS_EXPECTED;
else if (strncasecmp(status, "SEEN_REPLY", len) == 0)
sinfo->statusmask |= IPS_SEEN_REPLY;
else if (strncasecmp(status, "ASSURED", len) == 0)
sinfo->statusmask |= IPS_ASSURED;
#ifdef IPS_CONFIRMED
else if (strncasecmp(status, "CONFIRMED", len) == 0)
sinfo->statusmask |= IPS_CONFIRMED;
#endif
else
return 0;
return 1;
}
static void
parse_statuses(const char *arg, struct xt_conntrack_info *sinfo)
{
const char *comma;
while ((comma = strchr(arg, ',')) != NULL) {
if (comma == arg || !parse_status(arg, comma-arg, sinfo))
xtables_error(PARAMETER_PROBLEM, "Bad ctstatus \"%s\"", arg);
arg = comma+1;
}
if (strlen(arg) == 0 || !parse_status(arg, strlen(arg), sinfo))
xtables_error(PARAMETER_PROBLEM, "Bad ctstatus \"%s\"", arg);
}
static bool
conntrack_ps_status(struct xt_conntrack_mtinfo3 *info, const char *status,
size_t z)
{
if (strncasecmp(status, "NONE", z) == 0)
info->status_mask |= 0;
else if (strncasecmp(status, "EXPECTED", z) == 0)
info->status_mask |= IPS_EXPECTED;
else if (strncasecmp(status, "SEEN_REPLY", z) == 0)
info->status_mask |= IPS_SEEN_REPLY;
else if (strncasecmp(status, "ASSURED", z) == 0)
info->status_mask |= IPS_ASSURED;
else if (strncasecmp(status, "CONFIRMED", z) == 0)
info->status_mask |= IPS_CONFIRMED;
else
return false;
return true;
}
static void
conntrack_ps_statuses(struct xt_conntrack_mtinfo3 *info, const char *arg)
{
const char *comma;
while ((comma = strchr(arg, ',')) != NULL) {
if (comma == arg || !conntrack_ps_status(info, arg, comma - arg))
xtables_error(PARAMETER_PROBLEM,
"Bad ctstatus \"%s\"", arg);
arg = comma + 1;
}
if (strlen(arg) == 0 || !conntrack_ps_status(info, arg, strlen(arg)))
xtables_error(PARAMETER_PROBLEM, "Bad ctstatus \"%s\"", arg);
}
static void conntrack_parse(struct xt_option_call *cb)
{
struct xt_conntrack_info *sinfo = cb->data;
xtables_option_parse(cb);
switch (cb->entry->id) {
case O_CTSTATE:
parse_states(cb->arg, sinfo);
if (cb->invert)
sinfo->invflags |= XT_CONNTRACK_STATE;
break;
case O_CTPROTO:
sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.protonum = cb->val.protocol;
if (cb->invert)
sinfo->invflags |= XT_CONNTRACK_PROTO;
if (sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.protonum == 0
&& (sinfo->invflags & XT_INV_PROTO))
xtables_error(PARAMETER_PROBLEM,
"rule would never match protocol");
sinfo->flags |= XT_CONNTRACK_PROTO;
break;
case O_CTORIGSRC:
if (cb->invert)
sinfo->invflags |= XT_CONNTRACK_ORIGSRC;
sinfo->tuple[IP_CT_DIR_ORIGINAL].src.ip = cb->val.haddr.ip;
sinfo->flags |= XT_CONNTRACK_ORIGSRC;
break;
case O_CTORIGDST:
if (cb->invert)
sinfo->invflags |= XT_CONNTRACK_ORIGDST;
sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.ip = cb->val.haddr.ip;
sinfo->flags |= XT_CONNTRACK_ORIGDST;
break;
case O_CTREPLSRC:
if (cb->invert)
sinfo->invflags |= XT_CONNTRACK_REPLSRC;
sinfo->tuple[IP_CT_DIR_REPLY].src.ip = cb->val.haddr.ip;
sinfo->flags |= XT_CONNTRACK_REPLSRC;
break;
case O_CTREPLDST:
if (cb->invert)
sinfo->invflags |= XT_CONNTRACK_REPLDST;
sinfo->tuple[IP_CT_DIR_REPLY].dst.ip = cb->val.haddr.ip;
sinfo->flags |= XT_CONNTRACK_REPLDST;
break;
case O_CTSTATUS:
parse_statuses(cb->arg, sinfo);
if (cb->invert)
sinfo->invflags |= XT_CONNTRACK_STATUS;
sinfo->flags |= XT_CONNTRACK_STATUS;
break;
case O_CTEXPIRE:
sinfo->expires_min = cb->val.u32_range[0];
sinfo->expires_max = cb->val.u32_range[0];
if (cb->nvals >= 2)
sinfo->expires_max = cb->val.u32_range[1];
if (cb->invert)
sinfo->invflags |= XT_CONNTRACK_EXPIRES;
sinfo->flags |= XT_CONNTRACK_EXPIRES;
break;
}
}
static void conntrack_mt_parse(struct xt_option_call *cb, uint8_t rev)
{
struct xt_conntrack_mtinfo3 *info = cb->data;
xtables_option_parse(cb);
switch (cb->entry->id) {
case O_CTSTATE:
conntrack_ps_states(info, cb->arg);
info->match_flags |= XT_CONNTRACK_STATE;
if (cb->invert)
info->invert_flags |= XT_CONNTRACK_STATE;
break;
case O_CTPROTO:
info->l4proto = cb->val.protocol;
if (info->l4proto == 0 && (info->invert_flags & XT_INV_PROTO))
xtables_error(PARAMETER_PROBLEM, "conntrack: rule would "
"never match protocol");
info->match_flags |= XT_CONNTRACK_PROTO;
if (cb->invert)
info->invert_flags |= XT_CONNTRACK_PROTO;
break;
case O_CTORIGSRC:
info->origsrc_addr = cb->val.haddr;
info->origsrc_mask = cb->val.hmask;
info->match_flags |= XT_CONNTRACK_ORIGSRC;
if (cb->invert)
info->invert_flags |= XT_CONNTRACK_ORIGSRC;
break;
case O_CTORIGDST:
info->origdst_addr = cb->val.haddr;
info->origdst_mask = cb->val.hmask;
info->match_flags |= XT_CONNTRACK_ORIGDST;
if (cb->invert)
info->invert_flags |= XT_CONNTRACK_ORIGDST;
break;
case O_CTREPLSRC:
info->replsrc_addr = cb->val.haddr;
info->replsrc_mask = cb->val.hmask;
info->match_flags |= XT_CONNTRACK_REPLSRC;
if (cb->invert)
info->invert_flags |= XT_CONNTRACK_REPLSRC;
break;
case O_CTREPLDST:
info->repldst_addr = cb->val.haddr;
info->repldst_mask = cb->val.hmask;
info->match_flags |= XT_CONNTRACK_REPLDST;
if (cb->invert)
info->invert_flags |= XT_CONNTRACK_REPLDST;
break;
case O_CTSTATUS:
conntrack_ps_statuses(info, cb->arg);
info->match_flags |= XT_CONNTRACK_STATUS;
if (cb->invert)
info->invert_flags |= XT_CONNTRACK_STATUS;
break;
case O_CTEXPIRE:
info->expires_min = cb->val.u32_range[0];
info->expires_max = cb->val.u32_range[0];
if (cb->nvals >= 2)
info->expires_max = cb->val.u32_range[1];
info->match_flags |= XT_CONNTRACK_EXPIRES;
if (cb->invert)
info->invert_flags |= XT_CONNTRACK_EXPIRES;
break;
case O_CTORIGSRCPORT:
info->origsrc_port = cb->val.port_range[0];
info->origsrc_port_high = cb->val.port_range[cb->nvals >= 2];
info->match_flags |= XT_CONNTRACK_ORIGSRC_PORT;
if (cb->invert)
info->invert_flags |= XT_CONNTRACK_ORIGSRC_PORT;
break;
case O_CTORIGDSTPORT:
info->origdst_port = cb->val.port_range[0];
info->origdst_port_high = cb->val.port_range[cb->nvals >= 2];
info->match_flags |= XT_CONNTRACK_ORIGDST_PORT;
if (cb->invert)
info->invert_flags |= XT_CONNTRACK_ORIGDST_PORT;
break;
case O_CTREPLSRCPORT:
info->replsrc_port = cb->val.port_range[0];
info->replsrc_port_high = cb->val.port_range[cb->nvals >= 2];
info->match_flags |= XT_CONNTRACK_REPLSRC_PORT;
if (cb->invert)
info->invert_flags |= XT_CONNTRACK_REPLSRC_PORT;
break;
case O_CTREPLDSTPORT:
info->repldst_port = cb->val.port_range[0];
info->repldst_port_high = cb->val.port_range[cb->nvals >= 2];
info->match_flags |= XT_CONNTRACK_REPLDST_PORT;
if (cb->invert)
info->invert_flags |= XT_CONNTRACK_REPLDST_PORT;
break;
case O_CTDIR:
if (strcasecmp(cb->arg, "ORIGINAL") == 0) {
info->match_flags |= XT_CONNTRACK_DIRECTION;
info->invert_flags &= ~XT_CONNTRACK_DIRECTION;
} else if (strcasecmp(cb->arg, "REPLY") == 0) {
info->match_flags |= XT_CONNTRACK_DIRECTION;
info->invert_flags |= XT_CONNTRACK_DIRECTION;
} else {
xtables_param_act(XTF_BAD_VALUE, "conntrack", "--ctdir", cb->arg);
}
break;
}
}
#define cinfo_transform(r, l) \
do { \
memcpy((r), (l), offsetof(typeof(*(l)), state_mask)); \
(r)->state_mask = (l)->state_mask; \
(r)->status_mask = (l)->status_mask; \
} while (false);
static void conntrack1_mt_parse(struct xt_option_call *cb)
{
struct xt_conntrack_mtinfo1 *info = cb->data;
struct xt_conntrack_mtinfo3 up;
memset(&up, 0, sizeof(up));
cinfo_transform(&up, info);
up.origsrc_port_high = up.origsrc_port;
up.origdst_port_high = up.origdst_port;
up.replsrc_port_high = up.replsrc_port;
up.repldst_port_high = up.repldst_port;
cb->data = &up;
conntrack_mt_parse(cb, 3);
if (up.origsrc_port != up.origsrc_port_high ||
up.origdst_port != up.origdst_port_high ||
up.replsrc_port != up.replsrc_port_high ||
up.repldst_port != up.repldst_port_high)
xtables_error(PARAMETER_PROBLEM,
"conntrack rev 1 does not support port ranges");
cinfo_transform(info, &up);
cb->data = info;
}
static void conntrack2_mt_parse(struct xt_option_call *cb)
{
#define cinfo2_transform(r, l) \
memcpy((r), (l), offsetof(typeof(*(l)), sizeof(*info));
struct xt_conntrack_mtinfo2 *info = cb->data;
struct xt_conntrack_mtinfo3 up;
memset(&up, 0, sizeof(up));
memcpy(&up, info, sizeof(*info));
up.origsrc_port_high = up.origsrc_port;
up.origdst_port_high = up.origdst_port;
up.replsrc_port_high = up.replsrc_port;
up.repldst_port_high = up.repldst_port;
cb->data = &up;
conntrack_mt_parse(cb, 3);
if (up.origsrc_port != up.origsrc_port_high ||
up.origdst_port != up.origdst_port_high ||
up.replsrc_port != up.replsrc_port_high ||
up.repldst_port != up.repldst_port_high)
xtables_error(PARAMETER_PROBLEM,
"conntrack rev 2 does not support port ranges");
memcpy(info, &up, sizeof(*info));
cb->data = info;
#undef cinfo2_transform
}
static void conntrack3_mt_parse(struct xt_option_call *cb)
{
conntrack_mt_parse(cb, 3);
}
static void conntrack_mt_check(struct xt_fcheck_call *cb)
{
if (cb->xflags == 0)
xtables_error(PARAMETER_PROBLEM, "conntrack: At least one option "
"is required");
}
static void
print_state(unsigned int statemask)
{
const char *sep = " ";
if (statemask & XT_CONNTRACK_STATE_INVALID) {
printf("%sINVALID", sep);
sep = ",";
}
if (statemask & XT_CONNTRACK_STATE_BIT(IP_CT_NEW)) {
printf("%sNEW", sep);
sep = ",";
}
if (statemask & XT_CONNTRACK_STATE_BIT(IP_CT_RELATED)) {
printf("%sRELATED", sep);
sep = ",";
}
if (statemask & XT_CONNTRACK_STATE_BIT(IP_CT_ESTABLISHED)) {
printf("%sESTABLISHED", sep);
sep = ",";
}
if (statemask & XT_CONNTRACK_STATE_UNTRACKED) {
printf("%sUNTRACKED", sep);
sep = ",";
}
if (statemask & XT_CONNTRACK_STATE_SNAT) {
printf("%sSNAT", sep);
sep = ",";
}
if (statemask & XT_CONNTRACK_STATE_DNAT) {
printf("%sDNAT", sep);
sep = ",";
}
}
static void
print_status(unsigned int statusmask)
{
const char *sep = " ";
if (statusmask & IPS_EXPECTED) {
printf("%sEXPECTED", sep);
sep = ",";
}
if (statusmask & IPS_SEEN_REPLY) {
printf("%sSEEN_REPLY", sep);
sep = ",";
}
if (statusmask & IPS_ASSURED) {
printf("%sASSURED", sep);
sep = ",";
}
if (statusmask & IPS_CONFIRMED) {
printf("%sCONFIRMED", sep);
sep = ",";
}
if (statusmask == 0)
printf("%sNONE", sep);
}
static void
conntrack_dump_addr(const union nf_inet_addr *addr,
const union nf_inet_addr *mask,
unsigned int family, bool numeric)
{
if (family == NFPROTO_IPV4) {
if (!numeric && addr->ip == 0) {
printf(" anywhere");
return;
}
if (numeric)
printf(" %s%s",
xtables_ipaddr_to_numeric(&addr->in),
xtables_ipmask_to_numeric(&mask->in));
else
printf(" %s%s",
xtables_ipaddr_to_anyname(&addr->in),
xtables_ipmask_to_numeric(&mask->in));
} else if (family == NFPROTO_IPV6) {
if (!numeric && addr->ip6[0] == 0 && addr->ip6[1] == 0 &&
addr->ip6[2] == 0 && addr->ip6[3] == 0) {
printf(" anywhere");
return;
}
if (numeric)
printf(" %s%s",
xtables_ip6addr_to_numeric(&addr->in6),
xtables_ip6mask_to_numeric(&mask->in6));
else
printf(" %s%s",
xtables_ip6addr_to_anyname(&addr->in6),
xtables_ip6mask_to_numeric(&mask->in6));
}
}
static void
print_addr(const struct in_addr *addr, const struct in_addr *mask,
int inv, int numeric)
{
char buf[BUFSIZ];
if (inv)
printf(" !");
if (mask->s_addr == 0L && !numeric)
printf(" %s", "anywhere");
else {
if (numeric)
strcpy(buf, xtables_ipaddr_to_numeric(addr));
else
strcpy(buf, xtables_ipaddr_to_anyname(addr));
strcat(buf, xtables_ipmask_to_numeric(mask));
printf(" %s", buf);
}
}
static void
matchinfo_print(const void *ip, const struct xt_entry_match *match, int numeric, const char *optpfx)
{
const struct xt_conntrack_info *sinfo = (const void *)match->data;
if(sinfo->flags & XT_CONNTRACK_STATE) {
if (sinfo->invflags & XT_CONNTRACK_STATE)
printf(" !");
printf(" %sctstate", optpfx);
print_state(sinfo->statemask);
}
if(sinfo->flags & XT_CONNTRACK_PROTO) {
if (sinfo->invflags & XT_CONNTRACK_PROTO)
printf(" !");
printf(" %sctproto", optpfx);
printf(" %u", sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.protonum);
}
if(sinfo->flags & XT_CONNTRACK_ORIGSRC) {
if (sinfo->invflags & XT_CONNTRACK_ORIGSRC)
printf(" !");
printf(" %sctorigsrc", optpfx);
print_addr(
(struct in_addr *)&sinfo->tuple[IP_CT_DIR_ORIGINAL].src.ip,
&sinfo->sipmsk[IP_CT_DIR_ORIGINAL],
false,
numeric);
}
if(sinfo->flags & XT_CONNTRACK_ORIGDST) {
if (sinfo->invflags & XT_CONNTRACK_ORIGDST)
printf(" !");
printf(" %sctorigdst", optpfx);
print_addr(
(struct in_addr *)&sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.ip,
&sinfo->dipmsk[IP_CT_DIR_ORIGINAL],
false,
numeric);
}
if(sinfo->flags & XT_CONNTRACK_REPLSRC) {
if (sinfo->invflags & XT_CONNTRACK_REPLSRC)
printf(" !");
printf(" %sctreplsrc", optpfx);
print_addr(
(struct in_addr *)&sinfo->tuple[IP_CT_DIR_REPLY].src.ip,
&sinfo->sipmsk[IP_CT_DIR_REPLY],
false,
numeric);
}
if(sinfo->flags & XT_CONNTRACK_REPLDST) {
if (sinfo->invflags & XT_CONNTRACK_REPLDST)
printf(" !");
printf(" %sctrepldst", optpfx);
print_addr(
(struct in_addr *)&sinfo->tuple[IP_CT_DIR_REPLY].dst.ip,
&sinfo->dipmsk[IP_CT_DIR_REPLY],
false,
numeric);
}
if(sinfo->flags & XT_CONNTRACK_STATUS) {
if (sinfo->invflags & XT_CONNTRACK_STATUS)
printf(" !");
printf(" %sctstatus", optpfx);
print_status(sinfo->statusmask);
}
if(sinfo->flags & XT_CONNTRACK_EXPIRES) {
if (sinfo->invflags & XT_CONNTRACK_EXPIRES)
printf(" !");
printf(" %sctexpire ", optpfx);
if (sinfo->expires_max == sinfo->expires_min)
printf("%lu", sinfo->expires_min);
else
printf("%lu:%lu", sinfo->expires_min, sinfo->expires_max);
}
if (sinfo->flags & XT_CONNTRACK_DIRECTION) {
if (sinfo->invflags & XT_CONNTRACK_DIRECTION)
printf(" %sctdir REPLY", optpfx);
else
printf(" %sctdir ORIGINAL", optpfx);
}
}
static void
conntrack_dump_ports(const char *prefix, const char *opt,
u_int16_t port_low, u_int16_t port_high)
{
if (port_high == 0 || port_low == port_high)
printf(" %s%s %u", prefix, opt, port_low);
else
printf(" %s%s %u:%u", prefix, opt, port_low, port_high);
}
static void
conntrack_dump(const struct xt_conntrack_mtinfo3 *info, const char *prefix,
unsigned int family, bool numeric, bool v3)
{
if (info->match_flags & XT_CONNTRACK_STATE) {
if (info->invert_flags & XT_CONNTRACK_STATE)
printf(" !");
printf(" %s%s", prefix,
info->match_flags & XT_CONNTRACK_STATE_ALIAS
? "state" : "ctstate");
print_state(info->state_mask);
}
if (info->match_flags & XT_CONNTRACK_PROTO) {
if (info->invert_flags & XT_CONNTRACK_PROTO)
printf(" !");
printf(" %sctproto %u", prefix, info->l4proto);
}
if (info->match_flags & XT_CONNTRACK_ORIGSRC) {
if (info->invert_flags & XT_CONNTRACK_ORIGSRC)
printf(" !");
printf(" %sctorigsrc", prefix);
conntrack_dump_addr(&info->origsrc_addr, &info->origsrc_mask,
family, numeric);
}
if (info->match_flags & XT_CONNTRACK_ORIGDST) {
if (info->invert_flags & XT_CONNTRACK_ORIGDST)
printf(" !");
printf(" %sctorigdst", prefix);
conntrack_dump_addr(&info->origdst_addr, &info->origdst_mask,
family, numeric);
}
if (info->match_flags & XT_CONNTRACK_REPLSRC) {
if (info->invert_flags & XT_CONNTRACK_REPLSRC)
printf(" !");
printf(" %sctreplsrc", prefix);
conntrack_dump_addr(&info->replsrc_addr, &info->replsrc_mask,
family, numeric);
}
if (info->match_flags & XT_CONNTRACK_REPLDST) {
if (info->invert_flags & XT_CONNTRACK_REPLDST)
printf(" !");
printf(" %sctrepldst", prefix);
conntrack_dump_addr(&info->repldst_addr, &info->repldst_mask,
family, numeric);
}
if (info->match_flags & XT_CONNTRACK_ORIGSRC_PORT) {
if (info->invert_flags & XT_CONNTRACK_ORIGSRC_PORT)
printf(" !");
conntrack_dump_ports(prefix, "ctorigsrcport",
v3 ? info->origsrc_port : ntohs(info->origsrc_port),
v3 ? info->origsrc_port_high : 0);
}
if (info->match_flags & XT_CONNTRACK_ORIGDST_PORT) {
if (info->invert_flags & XT_CONNTRACK_ORIGDST_PORT)
printf(" !");
conntrack_dump_ports(prefix, "ctorigdstport",
v3 ? info->origdst_port : ntohs(info->origdst_port),
v3 ? info->origdst_port_high : 0);
}
if (info->match_flags & XT_CONNTRACK_REPLSRC_PORT) {
if (info->invert_flags & XT_CONNTRACK_REPLSRC_PORT)
printf(" !");
conntrack_dump_ports(prefix, "ctreplsrcport",
v3 ? info->replsrc_port : ntohs(info->replsrc_port),
v3 ? info->replsrc_port_high : 0);
}
if (info->match_flags & XT_CONNTRACK_REPLDST_PORT) {
if (info->invert_flags & XT_CONNTRACK_REPLDST_PORT)
printf(" !");
conntrack_dump_ports(prefix, "ctrepldstport",
v3 ? info->repldst_port : ntohs(info->repldst_port),
v3 ? info->repldst_port_high : 0);
}
if (info->match_flags & XT_CONNTRACK_STATUS) {
if (info->invert_flags & XT_CONNTRACK_STATUS)
printf(" !");
printf(" %sctstatus", prefix);
print_status(info->status_mask);
}
if (info->match_flags & XT_CONNTRACK_EXPIRES) {
if (info->invert_flags & XT_CONNTRACK_EXPIRES)
printf(" !");
printf(" %sctexpire ", prefix);
if (info->expires_max == info->expires_min)
printf("%u", (unsigned int)info->expires_min);
else
printf("%u:%u", (unsigned int)info->expires_min,
(unsigned int)info->expires_max);
}
if (info->match_flags & XT_CONNTRACK_DIRECTION) {
if (info->invert_flags & XT_CONNTRACK_DIRECTION)
printf(" %sctdir REPLY", prefix);
else
printf(" %sctdir ORIGINAL", prefix);
}
}
static const char *
conntrack_print_name_alias(const struct xt_entry_match *match)
{
struct xt_conntrack_mtinfo1 *info = (void *)match->data;
return info->match_flags & XT_CONNTRACK_STATE_ALIAS
? "state" : "conntrack";
}
static void conntrack_print(const void *ip, const struct xt_entry_match *match,
int numeric)
{
matchinfo_print(ip, match, numeric, "");
}
static void
conntrack1_mt4_print(const void *ip, const struct xt_entry_match *match,
int numeric)
{
const struct xt_conntrack_mtinfo1 *info = (void *)match->data;
struct xt_conntrack_mtinfo3 up;
cinfo_transform(&up, info);
conntrack_dump(&up, "", NFPROTO_IPV4, numeric, false);
}
static void
conntrack1_mt6_print(const void *ip, const struct xt_entry_match *match,
int numeric)
{
const struct xt_conntrack_mtinfo1 *info = (void *)match->data;
struct xt_conntrack_mtinfo3 up;
cinfo_transform(&up, info);
conntrack_dump(&up, "", NFPROTO_IPV6, numeric, false);
}
static void
conntrack2_mt_print(const void *ip, const struct xt_entry_match *match,
int numeric)
{
conntrack_dump((const void *)match->data, "", NFPROTO_IPV4, numeric, false);
}
static void
conntrack2_mt6_print(const void *ip, const struct xt_entry_match *match,
int numeric)
{
conntrack_dump((const void *)match->data, "", NFPROTO_IPV6, numeric, false);
}
static void
conntrack3_mt_print(const void *ip, const struct xt_entry_match *match,
int numeric)
{
conntrack_dump((const void *)match->data, "", NFPROTO_IPV4, numeric, true);
}
static void
conntrack3_mt6_print(const void *ip, const struct xt_entry_match *match,
int numeric)
{
conntrack_dump((const void *)match->data, "", NFPROTO_IPV6, numeric, true);
}
static void conntrack_save(const void *ip, const struct xt_entry_match *match)
{
matchinfo_print(ip, match, 1, "--");
}
static void conntrack3_mt_save(const void *ip,
const struct xt_entry_match *match)
{
conntrack_dump((const void *)match->data, "--", NFPROTO_IPV4, true, true);
}
static void conntrack3_mt6_save(const void *ip,
const struct xt_entry_match *match)
{
conntrack_dump((const void *)match->data, "--", NFPROTO_IPV6, true, true);
}
static void conntrack2_mt_save(const void *ip,
const struct xt_entry_match *match)
{
conntrack_dump((const void *)match->data, "--", NFPROTO_IPV4, true, false);
}
static void conntrack2_mt6_save(const void *ip,
const struct xt_entry_match *match)
{
conntrack_dump((const void *)match->data, "--", NFPROTO_IPV6, true, false);
}
static void
conntrack1_mt4_save(const void *ip, const struct xt_entry_match *match)
{
const struct xt_conntrack_mtinfo1 *info = (void *)match->data;
struct xt_conntrack_mtinfo3 up;
cinfo_transform(&up, info);
conntrack_dump(&up, "--", NFPROTO_IPV4, true, false);
}
static void
conntrack1_mt6_save(const void *ip, const struct xt_entry_match *match)
{
const struct xt_conntrack_mtinfo1 *info = (void *)match->data;
struct xt_conntrack_mtinfo3 up;
cinfo_transform(&up, info);
conntrack_dump(&up, "--", NFPROTO_IPV6, true, false);
}
static void
state_help(void)
{
printf(
"state match options:\n"
" [!] --state [INVALID|ESTABLISHED|NEW|RELATED|UNTRACKED][,...]\n"
" State(s) to match\n");
}
static const struct xt_option_entry state_opts[] = {
{.name = "state", .id = O_CTSTATE, .type = XTTYPE_STRING,
.flags = XTOPT_MAND | XTOPT_INVERT},
XTOPT_TABLEEND,
};
static unsigned int
state_parse_state(const char *state, size_t len)
{
if (strncasecmp(state, "INVALID", len) == 0)
return XT_CONNTRACK_STATE_INVALID;
else if (strncasecmp(state, "NEW", len) == 0)
return XT_CONNTRACK_STATE_BIT(IP_CT_NEW);
else if (strncasecmp(state, "ESTABLISHED", len) == 0)
return XT_CONNTRACK_STATE_BIT(IP_CT_ESTABLISHED);
else if (strncasecmp(state, "RELATED", len) == 0)
return XT_CONNTRACK_STATE_BIT(IP_CT_RELATED);
else if (strncasecmp(state, "UNTRACKED", len) == 0)
return XT_CONNTRACK_STATE_UNTRACKED;
return 0;
}
static unsigned int
state_parse_states(const char *arg)
{
const char *comma;
unsigned int mask = 0, flag;
while ((comma = strchr(arg, ',')) != NULL) {
if (comma == arg)
goto badstate;
flag = state_parse_state(arg, comma-arg);
if (flag == 0)
goto badstate;
mask |= flag;
arg = comma+1;
}
if (!*arg)
xtables_error(PARAMETER_PROBLEM, "\"--state\" requires a list of "
"states with no spaces, e.g. "
"ESTABLISHED,RELATED");
if (strlen(arg) == 0)
goto badstate;
flag = state_parse_state(arg, strlen(arg));
if (flag == 0)
goto badstate;
mask |= flag;
return mask;
badstate:
xtables_error(PARAMETER_PROBLEM, "Bad state \"%s\"", arg);
}
static void state_parse(struct xt_option_call *cb)
{
struct xt_state_info *sinfo = cb->data;
xtables_option_parse(cb);
sinfo->statemask = state_parse_states(cb->arg);
if (cb->invert)
sinfo->statemask = ~sinfo->statemask;
}
static void state_ct1_parse(struct xt_option_call *cb)
{
struct xt_conntrack_mtinfo1 *sinfo = cb->data;
xtables_option_parse(cb);
sinfo->match_flags = XT_CONNTRACK_STATE | XT_CONNTRACK_STATE_ALIAS;
sinfo->state_mask = state_parse_states(cb->arg);
if (cb->invert)
sinfo->invert_flags |= XT_CONNTRACK_STATE;
}
static void state_ct23_parse(struct xt_option_call *cb)
{
struct xt_conntrack_mtinfo3 *sinfo = cb->data;
xtables_option_parse(cb);
sinfo->match_flags = XT_CONNTRACK_STATE | XT_CONNTRACK_STATE_ALIAS;
sinfo->state_mask = state_parse_states(cb->arg);
if (cb->invert)
sinfo->invert_flags |= XT_CONNTRACK_STATE;
}
static void state_print_state(unsigned int statemask)
{
const char *sep = "";
if (statemask & XT_CONNTRACK_STATE_INVALID) {
printf("%sINVALID", sep);
sep = ",";
}
if (statemask & XT_CONNTRACK_STATE_BIT(IP_CT_NEW)) {
printf("%sNEW", sep);
sep = ",";
}
if (statemask & XT_CONNTRACK_STATE_BIT(IP_CT_RELATED)) {
printf("%sRELATED", sep);
sep = ",";
}
if (statemask & XT_CONNTRACK_STATE_BIT(IP_CT_ESTABLISHED)) {
printf("%sESTABLISHED", sep);
sep = ",";
}
if (statemask & XT_CONNTRACK_STATE_UNTRACKED) {
printf("%sUNTRACKED", sep);
sep = ",";
}
}
static void
state_print(const void *ip,
const struct xt_entry_match *match,
int numeric)
{
const struct xt_state_info *sinfo = (const void *)match->data;
printf(" state ");
state_print_state(sinfo->statemask);
}
static void state_save(const void *ip, const struct xt_entry_match *match)
{
const struct xt_state_info *sinfo = (const void *)match->data;
printf(" --state ");
state_print_state(sinfo->statemask);
}
static struct xtables_match conntrack_mt_reg[] = {
{
.version = XTABLES_VERSION,
.name = "conntrack",
.revision = 0,
.family = NFPROTO_IPV4,
.size = XT_ALIGN(sizeof(struct xt_conntrack_info)),
.userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_info)),
.help = conntrack_mt_help,
.x6_parse = conntrack_parse,
.x6_fcheck = conntrack_mt_check,
.print = conntrack_print,
.save = conntrack_save,
.alias = conntrack_print_name_alias,
.x6_options = conntrack_mt_opts_v0,
},
{
.version = XTABLES_VERSION,
.name = "conntrack",
.revision = 1,
.family = NFPROTO_IPV4,
.size = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo1)),
.userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo1)),
.help = conntrack_mt_help,
.x6_parse = conntrack1_mt_parse,
.x6_fcheck = conntrack_mt_check,
.print = conntrack1_mt4_print,
.save = conntrack1_mt4_save,
.alias = conntrack_print_name_alias,
.x6_options = conntrack2_mt_opts,
},
{
.version = XTABLES_VERSION,
.name = "conntrack",
.revision = 1,
.family = NFPROTO_IPV6,
.size = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo1)),
.userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo1)),
.help = conntrack_mt_help,
.x6_parse = conntrack1_mt_parse,
.x6_fcheck = conntrack_mt_check,
.print = conntrack1_mt6_print,
.save = conntrack1_mt6_save,
.alias = conntrack_print_name_alias,
.x6_options = conntrack2_mt_opts,
},
{
.version = XTABLES_VERSION,
.name = "conntrack",
.revision = 2,
.family = NFPROTO_IPV4,
.size = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo2)),
.userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo2)),
.help = conntrack_mt_help,
.x6_parse = conntrack2_mt_parse,
.x6_fcheck = conntrack_mt_check,
.print = conntrack2_mt_print,
.save = conntrack2_mt_save,
.alias = conntrack_print_name_alias,
.x6_options = conntrack2_mt_opts,
},
{
.version = XTABLES_VERSION,
.name = "conntrack",
.revision = 2,
.family = NFPROTO_IPV6,
.size = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo2)),
.userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo2)),
.help = conntrack_mt_help,
.x6_parse = conntrack2_mt_parse,
.x6_fcheck = conntrack_mt_check,
.print = conntrack2_mt6_print,
.save = conntrack2_mt6_save,
.alias = conntrack_print_name_alias,
.x6_options = conntrack2_mt_opts,
},
{
.version = XTABLES_VERSION,
.name = "conntrack",
.revision = 3,
.family = NFPROTO_IPV4,
.size = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo3)),
.userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo3)),
.help = conntrack_mt_help,
.x6_parse = conntrack3_mt_parse,
.x6_fcheck = conntrack_mt_check,
.print = conntrack3_mt_print,
.save = conntrack3_mt_save,
.alias = conntrack_print_name_alias,
.x6_options = conntrack3_mt_opts,
},
{
.version = XTABLES_VERSION,
.name = "conntrack",
.revision = 3,
.family = NFPROTO_IPV6,
.size = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo3)),
.userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo3)),
.help = conntrack_mt_help,
.x6_parse = conntrack3_mt_parse,
.x6_fcheck = conntrack_mt_check,
.print = conntrack3_mt6_print,
.save = conntrack3_mt6_save,
.alias = conntrack_print_name_alias,
.x6_options = conntrack3_mt_opts,
},
{
.family = NFPROTO_UNSPEC,
.name = "state",
.real_name = "conntrack",
.revision = 1,
.ext_flags = XTABLES_EXT_ALIAS,
.version = XTABLES_VERSION,
.size = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo1)),
.userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo1)),
.help = state_help,
.print = state_print,
.save = state_save,
.x6_parse = state_ct1_parse,
.x6_options = state_opts,
},
{
.family = NFPROTO_UNSPEC,
.name = "state",
.real_name = "conntrack",
.revision = 2,
.ext_flags = XTABLES_EXT_ALIAS,
.version = XTABLES_VERSION,
.size = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo2)),
.userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo2)),
.help = state_help,
.print = state_print,
.save = state_save,
.x6_parse = state_ct23_parse,
.x6_options = state_opts,
},
{
.family = NFPROTO_UNSPEC,
.name = "state",
.real_name = "conntrack",
.revision = 3,
.ext_flags = XTABLES_EXT_ALIAS,
.version = XTABLES_VERSION,
.size = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo3)),
.userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo3)),
.help = state_help,
.print = state_print,
.save = state_save,
.x6_parse = state_ct23_parse,
.x6_options = state_opts,
},
{
.family = NFPROTO_UNSPEC,
.name = "state",
.revision = 0,
.version = XTABLES_VERSION,
.size = XT_ALIGN(sizeof(struct xt_state_info)),
.userspacesize = XT_ALIGN(sizeof(struct xt_state_info)),
.help = state_help,
.print = state_print,
.save = state_save,
.x6_parse = state_parse,
.x6_options = state_opts,
},
};
void _init(void)
{
xtables_register_matches(conntrack_mt_reg, ARRAY_SIZE(conntrack_mt_reg));
}
This module, when combined with connection tracking, allows access to the
connection tracking state for this packet/connection.
.TP
[\fB!\fP] \fB\-\-ctstate\fP \fIstatelist\fP
\fIstatelist\fP is a comma separated list of the connection states to match.
Possible states are listed below.
.TP
[\fB!\fP] \fB\-\-ctproto\fP \fIl4proto\fP
Layer-4 protocol to match (by number or name)
.TP
[\fB!\fP] \fB\-\-ctorigsrc\fP \fIaddress\fP[\fB/\fP\fImask\fP]
.TP
[\fB!\fP] \fB\-\-ctorigdst\fP \fIaddress\fP[\fB/\fP\fImask\fP]
.TP
[\fB!\fP] \fB\-\-ctreplsrc\fP \fIaddress\fP[\fB/\fP\fImask\fP]
.TP
[\fB!\fP] \fB\-\-ctrepldst\fP \fIaddress\fP[\fB/\fP\fImask\fP]
Match against original/reply source/destination address
.TP
[\fB!\fP] \fB\-\-ctorigsrcport\fP \fIport\fP[\fB:\fP\fIport\fP]
.TP
[\fB!\fP] \fB\-\-ctorigdstport\fP \fIport\fP[\fB:\fP\fIport\fP]
.TP
[\fB!\fP] \fB\-\-ctreplsrcport\fP \fIport\fP[\fB:\fP\fIport\fP]
.TP
[\fB!\fP] \fB\-\-ctrepldstport\fP \fIport\fP[\fB:\fP\fIport\fP]
Match against original/reply source/destination port (TCP/UDP/etc.) or GRE key.
Matching against port ranges is only supported in kernel versions above 2.6.38.
.TP
[\fB!\fP] \fB\-\-ctstatus\fP \fIstatelist\fP
\fIstatuslist\fP is a comma separated list of the connection statuses to match.
Possible statuses are listed below.
.TP
[\fB!\fP] \fB\-\-ctexpire\fP \fItime\fP[\fB:\fP\fItime\fP]
Match remaining lifetime in seconds against given value or range of values
(inclusive)
.TP
\fB\-\-ctdir\fP {\fBORIGINAL\fP|\fBREPLY\fP}
Match packets that are flowing in the specified direction. If this flag is not
specified at all, matches packets in both directions.
.PP
States for \fB\-\-ctstate\fP:
.TP
\fBINVALID\fP
The packet is associated with no known connection.
.TP
\fBNEW\fP
The packet has started a new connection or otherwise associated
with a connection which has not seen packets in both directions.
.TP
\fBESTABLISHED\fP
The packet is associated with a connection which has seen packets
in both directions.
.TP
\fBRELATED\fP
The packet is starting a new connection, but is associated with an
existing connection, such as an FTP data transfer or an ICMP error.
.TP
\fBUNTRACKED\fP
The packet is not tracked at all, which happens if you explicitly untrack it
by using \-j CT \-\-notrack in the raw table.
.TP
\fBSNAT\fP
A virtual state, matching if the original source address differs from the reply
destination.
.TP
\fBDNAT\fP
A virtual state, matching if the original destination differs from the reply
source.
.PP
Statuses for \fB\-\-ctstatus\fP:
.TP
\fBNONE\fP
None of the below.
.TP
\fBEXPECTED\fP
This is an expected connection (i.e. a conntrack helper set it up).
.TP
\fBSEEN_REPLY\fP
Conntrack has seen packets in both directions.
.TP
\fBASSURED\fP
Conntrack entry should never be early-expired.
.TP
\fBCONFIRMED\fP
Connection is confirmed: originating packet has left box.
#include <stdio.h>
#include <xtables.h>
#include <linux/netfilter/xt_cpu.h>
enum {
O_CPU = 0,
};
static void cpu_help(void)
{
printf(
"cpu match options:\n"
"[!] --cpu number Match CPU number\n");
}
static const struct xt_option_entry cpu_opts[] = {
{.name = "cpu", .id = O_CPU, .type = XTTYPE_UINT32,
.flags = XTOPT_INVERT | XTOPT_MAND | XTOPT_PUT,
XTOPT_POINTER(struct xt_cpu_info, cpu)},
XTOPT_TABLEEND,
};
static void cpu_parse(struct xt_option_call *cb)
{
struct xt_cpu_info *cpuinfo = cb->data;
xtables_option_parse(cb);
if (cb->invert)
cpuinfo->invert = true;
}
static void
cpu_print(const void *ip, const struct xt_entry_match *match, int numeric)
{
const struct xt_cpu_info *info = (void *)match->data;
printf(" cpu %s%u", info->invert ? "! ":"", info->cpu);
}
static void cpu_save(const void *ip, const struct xt_entry_match *match)
{
const struct xt_cpu_info *info = (void *)match->data;
printf("%s --cpu %u", info->invert ? " !" : "", info->cpu);
}
static struct xtables_match cpu_match = {
.family = NFPROTO_UNSPEC,
.name = "cpu",
.version = XTABLES_VERSION,
.size = XT_ALIGN(sizeof(struct xt_cpu_info)),
.userspacesize = XT_ALIGN(sizeof(struct xt_cpu_info)),
.help = cpu_help,
.print = cpu_print,
.save = cpu_save,
.x6_parse = cpu_parse,
.x6_options = cpu_opts,
};
void _init(void)
{
xtables_register_match(&cpu_match);
}
.TP
[\fB!\fP] \fB\-\-cpu\fP \fInumber\fP
Match cpu handling this packet. cpus are numbered from 0 to NR_CPUS-1
Can be used in combination with RPS (Remote Packet Steering) or
multiqueue NICs to spread network traffic on different queues.
.PP
Example:
.PP
iptables \-t nat \-A PREROUTING \-p tcp \-\-dport 80 \-m cpu \-\-cpu 0
\-j REDIRECT \-\-to\-port 8080
.PP
iptables \-t nat \-A PREROUTING \-p tcp \-\-dport 80 \-m cpu \-\-cpu 1
\-j REDIRECT \-\-to\-port 8081
.PP
Available since Linux 2.6.36.
/* Shared library add-on to iptables for DCCP matching
*
* (C) 2005 by Harald Welte <laforge@netfilter.org>
*
* This program is distributed under the terms of GNU GPL v2, 1991
*
*/
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <xtables.h>
#include <linux/dccp.h>
#include <linux/netfilter/x_tables.h>
#include <linux/netfilter/xt_dccp.h>
#if 0
#define DEBUGP(format, first...) printf(format, ##first)
#define static
#else
#define DEBUGP(format, fist...)
#endif
enum {
O_SOURCE_PORT = 0,
O_DEST_PORT,
O_DCCP_TYPES,
O_DCCP_OPTION,
};
static void dccp_help(void)
{
printf(
"dccp 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"
"[!] --dccp-types type[,...] match when packet is one of the given types\n"
"[!] --dccp-option option match if option (by number!) is set\n"
);
}
#define s struct xt_dccp_info
static const struct xt_option_entry dccp_opts[] = {
{.name = "source-port", .id = O_SOURCE_PORT, .type = XTTYPE_PORTRC,
.flags = XTOPT_INVERT | XTOPT_PUT, XTOPT_POINTER(s, spts)},
{.name = "sport", .id = O_SOURCE_PORT, .type = XTTYPE_PORTRC,
.flags = XTOPT_INVERT | XTOPT_PUT, XTOPT_POINTER(s, spts)},
{.name = "destination-port", .id = O_DEST_PORT, .type = XTTYPE_PORTRC,
.flags = XTOPT_INVERT | XTOPT_PUT, XTOPT_POINTER(s, dpts)},
{.name = "dport", .id = O_DEST_PORT, .type = XTTYPE_PORTRC,
.flags = XTOPT_INVERT | XTOPT_PUT, XTOPT_POINTER(s, dpts)},
{.name = "dccp-types", .id = O_DCCP_TYPES, .type = XTTYPE_STRING,
.flags = XTOPT_INVERT},
{.name = "dccp-option", .id = O_DCCP_OPTION, .type = XTTYPE_UINT8,
.min = 1, .max = UINT8_MAX, .flags = XTOPT_INVERT | XTOPT_PUT,
XTOPT_POINTER(s, option)},
XTOPT_TABLEEND,
};
#undef s
static const char *const dccp_pkt_types[] = {
[DCCP_PKT_REQUEST] = "REQUEST",
[DCCP_PKT_RESPONSE] = "RESPONSE",
[DCCP_PKT_DATA] = "DATA",
[DCCP_PKT_ACK] = "ACK",
[DCCP_PKT_DATAACK] = "DATAACK",
[DCCP_PKT_CLOSEREQ] = "CLOSEREQ",
[DCCP_PKT_CLOSE] = "CLOSE",
[DCCP_PKT_RESET] = "RESET",
[DCCP_PKT_SYNC] = "SYNC",
[DCCP_PKT_SYNCACK] = "SYNCACK",
[DCCP_PKT_INVALID] = "INVALID",
};
static uint16_t
parse_dccp_types(const char *typestring)
{
uint16_t typemask = 0;
char *ptr, *buffer;
buffer = strdup(typestring);
for (ptr = strtok(buffer, ","); ptr; ptr = strtok(NULL, ",")) {
unsigned int i;
for (i = 0; i < ARRAY_SIZE(dccp_pkt_types); ++i)
if (!strcasecmp(dccp_pkt_types[i], ptr)) {
typemask |= (1 << i);
break;
}
if (i == ARRAY_SIZE(dccp_pkt_types))
xtables_error(PARAMETER_PROBLEM,
"Unknown DCCP type `%s'", ptr);
}
free(buffer);
return typemask;
}
static void dccp_parse(struct xt_option_call *cb)
{
struct xt_dccp_info *einfo = cb->data;
xtables_option_parse(cb);
switch (cb->entry->id) {
case O_SOURCE_PORT:
einfo->flags |= XT_DCCP_SRC_PORTS;
if (cb->invert)
einfo->invflags |= XT_DCCP_SRC_PORTS;
break;
case O_DEST_PORT:
einfo->flags |= XT_DCCP_DEST_PORTS;
if (cb->invert)
einfo->invflags |= XT_DCCP_DEST_PORTS;
break;
case O_DCCP_TYPES:
einfo->flags |= XT_DCCP_TYPE;
einfo->typemask = parse_dccp_types(cb->arg);
if (cb->invert)
einfo->invflags |= XT_DCCP_TYPE;
break;
case O_DCCP_OPTION:
einfo->flags |= XT_DCCP_OPTION;
if (cb->invert)
einfo->invflags |= XT_DCCP_OPTION;
break;
}
}
static const char *
port_to_service(int port)
{
const struct servent *service;
if ((service = getservbyport(htons(port), "dccp")))
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_types(uint16_t types, int inverted, int numeric)
{
int have_type = 0;
if (inverted)
printf(" !");
printf(" ");
while (types) {
unsigned int i;
for (i = 0; !(types & (1 << i)); i++);
if (have_type)
printf(",");
else
have_type = 1;
if (numeric)
printf("%u", i);
else
printf("%s", dccp_pkt_types[i]);
types &= ~(1 << i);
}
}
static void
print_option(uint8_t option, int invert, int numeric)
{
if (option || invert)
printf(" option=%s%u", invert ? "!" : "", option);
}
static void
dccp_print(const void *ip, const struct xt_entry_match *match, int numeric)
{
const struct xt_dccp_info *einfo =
(const struct xt_dccp_info *)match->data;
printf(" dccp");
if (einfo->flags & XT_DCCP_SRC_PORTS) {
print_ports("spt", einfo->spts[0], einfo->spts[1],
einfo->invflags & XT_DCCP_SRC_PORTS,
numeric);
}
if (einfo->flags & XT_DCCP_DEST_PORTS) {
print_ports("dpt", einfo->dpts[0], einfo->dpts[1],
einfo->invflags & XT_DCCP_DEST_PORTS,
numeric);
}
if (einfo->flags & XT_DCCP_TYPE) {
print_types(einfo->typemask,
einfo->invflags & XT_DCCP_TYPE,
numeric);
}
if (einfo->flags & XT_DCCP_OPTION) {
print_option(einfo->option,
einfo->invflags & XT_DCCP_OPTION, numeric);
}
}
static void dccp_save(const void *ip, const struct xt_entry_match *match)
{
const struct xt_dccp_info *einfo =
(const struct xt_dccp_info *)match->data;
if (einfo->flags & XT_DCCP_SRC_PORTS) {
if (einfo->invflags & XT_DCCP_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_DCCP_DEST_PORTS) {
if (einfo->invflags & XT_DCCP_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_DCCP_TYPE) {
printf("%s --dccp-types",
einfo->invflags & XT_DCCP_TYPE ? " !" : "");
print_types(einfo->typemask, false, 0);
}
if (einfo->flags & XT_DCCP_OPTION) {
printf("%s --dccp-option %u",
einfo->invflags & XT_DCCP_OPTION ? " !" : "",
einfo->option);
}
}
static struct xtables_match dccp_match = {
.name = "dccp",
.family = NFPROTO_UNSPEC,
.version = XTABLES_VERSION,
.size = XT_ALIGN(sizeof(struct xt_dccp_info)),
.userspacesize = XT_ALIGN(sizeof(struct xt_dccp_info)),
.help = dccp_help,
.print = dccp_print,
.save = dccp_save,
.x6_parse = dccp_parse,
.x6_options = dccp_opts,
};
void _init(void)
{
xtables_register_match(&dccp_match);
}
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