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

Imported Upstream version 1.4.21

parents
.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\-\-dccp\-types\fP \fImask\fP
Match when the DCCP packet type is one of 'mask'. 'mask' is a comma-separated
list of packet types. Packet types are:
.BR "REQUEST RESPONSE DATA ACK DATAACK CLOSEREQ CLOSE RESET SYNC SYNCACK INVALID" .
.TP
[\fB!\fP] \fB\-\-dccp\-option\fP \fInumber\fP
Match if DCCP option set.
/* Shared library add-on to iptables to add devgroup matching support.
*
* Copyright (c) 2011 Patrick McHardy <kaber@trash.net>
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <xtables.h>
#include <linux/netfilter/xt_devgroup.h>
static void devgroup_help(void)
{
printf(
"devgroup match options:\n"
"[!] --src-group value[/mask] Match device group of incoming device\n"
"[!] --dst-group value[/mask] Match device group of outgoing device\n"
);
}
enum {
O_SRC_GROUP = 0,
O_DST_GROUP,
};
static const struct xt_option_entry devgroup_opts[] = {
{.name = "src-group", .id = O_SRC_GROUP, .type = XTTYPE_STRING,
.flags = XTOPT_INVERT},
{.name = "dst-group", .id = O_DST_GROUP, .type = XTTYPE_STRING,
.flags = XTOPT_INVERT},
XTOPT_TABLEEND,
};
/* array of devgroups from /etc/iproute2/group_map */
static struct xtables_lmap *devgroups;
static void devgroup_init(struct xt_entry_match *match)
{
const char file[] = "/etc/iproute2/group_map";
devgroups = xtables_lmap_init(file);
if (devgroups == NULL && errno != ENOENT)
fprintf(stderr, "Warning: %s: %s\n", file, strerror(errno));
}
static void devgroup_parse_groupspec(const char *arg, unsigned int *group,
unsigned int *mask)
{
char *end;
bool ok;
ok = xtables_strtoui(arg, &end, group, 0, UINT32_MAX);
if (ok && (*end == '/' || *end == '\0')) {
if (*end == '/')
ok = xtables_strtoui(end + 1, NULL, mask,
0, UINT32_MAX);
else
*mask = ~0U;
if (!ok)
xtables_error(PARAMETER_PROBLEM,
"Bad group value \"%s\"", arg);
} else {
*group = xtables_lmap_name2id(devgroups, arg);
if (*group == -1)
xtables_error(PARAMETER_PROBLEM,
"Device group \"%s\" not found", arg);
*mask = ~0U;
}
}
static void devgroup_parse(struct xt_option_call *cb)
{
struct xt_devgroup_info *info = cb->data;
unsigned int id, mask;
xtables_option_parse(cb);
switch (cb->entry->id) {
case O_SRC_GROUP:
devgroup_parse_groupspec(cb->arg, &id, &mask);
info->src_group = id;
info->src_mask = mask;
info->flags |= XT_DEVGROUP_MATCH_SRC;
if (cb->invert)
info->flags |= XT_DEVGROUP_INVERT_SRC;
break;
case O_DST_GROUP:
devgroup_parse_groupspec(cb->arg, &id, &mask);
info->dst_group = id;
info->dst_mask = mask;
info->flags |= XT_DEVGROUP_MATCH_DST;
if (cb->invert)
info->flags |= XT_DEVGROUP_INVERT_DST;
break;
}
}
static void
print_devgroup(unsigned int id, unsigned int mask, int numeric)
{
const char *name = NULL;
if (mask != 0xffffffff)
printf("0x%x/0x%x", id, mask);
else {
if (numeric == 0)
name = xtables_lmap_id2name(devgroups, id);
if (name)
printf("%s", name);
else
printf("0x%x", id);
}
}
static void devgroup_show(const char *pfx, const struct xt_devgroup_info *info,
int numeric)
{
if (info->flags & XT_DEVGROUP_MATCH_SRC) {
if (info->flags & XT_DEVGROUP_INVERT_SRC)
printf(" !");
printf(" %ssrc-group ", pfx);
print_devgroup(info->src_group, info->src_mask, numeric);
}
if (info->flags & XT_DEVGROUP_MATCH_DST) {
if (info->flags & XT_DEVGROUP_INVERT_DST)
printf(" !");
printf(" %sdst-group ", pfx);
print_devgroup(info->src_group, info->src_mask, numeric);
}
}
static void devgroup_print(const void *ip, const struct xt_entry_match *match,
int numeric)
{
const struct xt_devgroup_info *info = (const void *)match->data;
devgroup_show("", info, numeric);
}
static void devgroup_save(const void *ip, const struct xt_entry_match *match)
{
const struct xt_devgroup_info *info = (const void *)match->data;
devgroup_show("--", info, 0);
}
static void devgroup_check(struct xt_fcheck_call *cb)
{
if (cb->xflags == 0)
xtables_error(PARAMETER_PROBLEM,
"devgroup match: You must specify either "
"'--src-group' or '--dst-group'");
}
static struct xtables_match devgroup_mt_reg = {
.name = "devgroup",
.version = XTABLES_VERSION,
.family = NFPROTO_UNSPEC,
.size = XT_ALIGN(sizeof(struct xt_devgroup_info)),
.userspacesize = XT_ALIGN(sizeof(struct xt_devgroup_info)),
.init = devgroup_init,
.help = devgroup_help,
.print = devgroup_print,
.save = devgroup_save,
.x6_parse = devgroup_parse,
.x6_fcheck = devgroup_check,
.x6_options = devgroup_opts,
};
void _init(void)
{
xtables_register_match(&devgroup_mt_reg);
}
Match device group of a packets incoming/outgoing interface.
.TP
[\fB!\fP] \fB\-\-src\-group\fP \fIname\fP
Match device group of incoming device
.TP
[\fB!\fP] \fB\-\-dst\-group\fP \fIname\fP
Match device group of outgoing device
/* Shared library add-on to iptables for DSCP
*
* (C) 2002 by Harald Welte <laforge@gnumonks.org>
*
* This program is distributed under the terms of GNU GPL v2, 1991
*
* libipt_dscp.c borrowed heavily from libipt_tos.c
*
* --class support added by Iain Barnes
*
* For a list of DSCP codepoints see
* http://www.iana.org/assignments/dscp-registry
*
*/
#include <stdio.h>
#include <string.h>
#include <xtables.h>
#include <linux/netfilter/xt_dscp.h>
/* This is evil, but it's my code - HW*/
#include "dscp_helper.c"
enum {
O_DSCP = 0,
O_DSCP_CLASS,
F_DSCP = 1 << O_DSCP,
F_DSCP_CLASS = 1 << O_DSCP_CLASS,
};
static void dscp_help(void)
{
printf(
"dscp match options\n"
"[!] --dscp value Match DSCP codepoint with numerical value\n"
" This value can be in decimal (ex: 32)\n"
" or in hex (ex: 0x20)\n"
"[!] --dscp-class name Match the DiffServ class. This value may\n"
" be any of the BE,EF, AFxx or CSx classes\n"
"\n"
" These two options are mutually exclusive !\n");
}
static const struct xt_option_entry dscp_opts[] = {
{.name = "dscp", .id = O_DSCP, .excl = F_DSCP_CLASS,
.type = XTTYPE_UINT8, .min = 0, .max = XT_DSCP_MAX,
.flags = XTOPT_INVERT | XTOPT_PUT,
XTOPT_POINTER(struct xt_dscp_info, dscp)},
{.name = "dscp-class", .id = O_DSCP_CLASS, .excl = F_DSCP,
.type = XTTYPE_STRING, .flags = XTOPT_INVERT},
XTOPT_TABLEEND,
};
static void dscp_parse(struct xt_option_call *cb)
{
struct xt_dscp_info *dinfo = cb->data;
xtables_option_parse(cb);
switch (cb->entry->id) {
case O_DSCP:
if (cb->invert)
dinfo->invert = 1;
break;
case O_DSCP_CLASS:
dinfo->dscp = class_to_dscp(cb->arg);
if (cb->invert)
dinfo->invert = 1;
break;
}
}
static void dscp_check(struct xt_fcheck_call *cb)
{
if (cb->xflags == 0)
xtables_error(PARAMETER_PROBLEM,
"DSCP match: Parameter --dscp is required");
}
static void
dscp_print(const void *ip, const struct xt_entry_match *match, int numeric)
{
const struct xt_dscp_info *dinfo =
(const struct xt_dscp_info *)match->data;
printf(" DSCP match %s0x%02x", dinfo->invert ? "!" : "", dinfo->dscp);
}
static void dscp_save(const void *ip, const struct xt_entry_match *match)
{
const struct xt_dscp_info *dinfo =
(const struct xt_dscp_info *)match->data;
printf("%s --dscp 0x%02x", dinfo->invert ? " !" : "", dinfo->dscp);
}
static struct xtables_match dscp_match = {
.family = NFPROTO_UNSPEC,
.name = "dscp",
.version = XTABLES_VERSION,
.size = XT_ALIGN(sizeof(struct xt_dscp_info)),
.userspacesize = XT_ALIGN(sizeof(struct xt_dscp_info)),
.help = dscp_help,
.print = dscp_print,
.save = dscp_save,
.x6_parse = dscp_parse,
.x6_fcheck = dscp_check,
.x6_options = dscp_opts,
};
void _init(void)
{
xtables_register_match(&dscp_match);
}
This module matches the 6 bit DSCP field within the TOS field in the
IP header. DSCP has superseded TOS within the IETF.
.TP
[\fB!\fP] \fB\-\-dscp\fP \fIvalue\fP
Match against a numeric (decimal or hex) value [0-63].
.TP
[\fB!\fP] \fB\-\-dscp\-class\fP \fIclass\fP
Match the DiffServ class. This value may be any of the
BE, EF, AFxx or CSx classes. It will then be converted
into its according numeric value.
/* Shared library add-on to iptables for ECN matching
*
* (C) 2002 by Harald Welte <laforge@netfilter.org>
* (C) 2011 by Patrick McHardy <kaber@trash.net>
*
* This program is distributed under the terms of GNU GPL v2, 1991
*
* libipt_ecn.c borrowed heavily from libipt_dscp.c
*
*/
#include <stdio.h>
#include <xtables.h>
#include <linux/netfilter/xt_ecn.h>
enum {
O_ECN_TCP_CWR = 0,
O_ECN_TCP_ECE,
O_ECN_IP_ECT,
};
static void ecn_help(void)
{
printf(
"ECN match options\n"
"[!] --ecn-tcp-cwr Match CWR bit of TCP header\n"
"[!] --ecn-tcp-ece Match ECE bit of TCP header\n"
"[!] --ecn-ip-ect [0..3] Match ECN codepoint in IPv4/IPv6 header\n");
}
static const struct xt_option_entry ecn_opts[] = {
{.name = "ecn-tcp-cwr", .id = O_ECN_TCP_CWR, .type = XTTYPE_NONE,
.flags = XTOPT_INVERT},
{.name = "ecn-tcp-ece", .id = O_ECN_TCP_ECE, .type = XTTYPE_NONE,
.flags = XTOPT_INVERT},
{.name = "ecn-ip-ect", .id = O_ECN_IP_ECT, .type = XTTYPE_UINT8,
.min = 0, .max = 3, .flags = XTOPT_INVERT},
XTOPT_TABLEEND,
};
static void ecn_parse(struct xt_option_call *cb)
{
struct xt_ecn_info *einfo = cb->data;
xtables_option_parse(cb);
switch (cb->entry->id) {
case O_ECN_TCP_CWR:
einfo->operation |= XT_ECN_OP_MATCH_CWR;
if (cb->invert)
einfo->invert |= XT_ECN_OP_MATCH_CWR;
break;
case O_ECN_TCP_ECE:
einfo->operation |= XT_ECN_OP_MATCH_ECE;
if (cb->invert)
einfo->invert |= XT_ECN_OP_MATCH_ECE;
break;
case O_ECN_IP_ECT:
if (cb->invert)
einfo->invert |= XT_ECN_OP_MATCH_IP;
einfo->operation |= XT_ECN_OP_MATCH_IP;
einfo->ip_ect = cb->val.u8;
break;
}
}
static void ecn_check(struct xt_fcheck_call *cb)
{
if (cb->xflags == 0)
xtables_error(PARAMETER_PROBLEM,
"ECN match: some option required");
}
static void ecn_print(const void *ip, const struct xt_entry_match *match,
int numeric)
{
const struct xt_ecn_info *einfo =
(const struct xt_ecn_info *)match->data;
printf(" ECN match");
if (einfo->operation & XT_ECN_OP_MATCH_ECE) {
printf(" %sECE",
(einfo->invert & XT_ECN_OP_MATCH_ECE) ? "!" : "");
}
if (einfo->operation & XT_ECN_OP_MATCH_CWR) {
printf(" %sCWR",
(einfo->invert & XT_ECN_OP_MATCH_CWR) ? "!" : "");
}
if (einfo->operation & XT_ECN_OP_MATCH_IP) {
printf(" %sECT=%d",
(einfo->invert & XT_ECN_OP_MATCH_IP) ? "!" : "",
einfo->ip_ect);
}
}
static void ecn_save(const void *ip, const struct xt_entry_match *match)
{
const struct xt_ecn_info *einfo =
(const struct xt_ecn_info *)match->data;
if (einfo->operation & XT_ECN_OP_MATCH_ECE) {
if (einfo->invert & XT_ECN_OP_MATCH_ECE)
printf(" !");
printf(" --ecn-tcp-ece");
}
if (einfo->operation & XT_ECN_OP_MATCH_CWR) {
if (einfo->invert & XT_ECN_OP_MATCH_CWR)
printf(" !");
printf(" --ecn-tcp-cwr");
}
if (einfo->operation & XT_ECN_OP_MATCH_IP) {
if (einfo->invert & XT_ECN_OP_MATCH_IP)
printf(" !");
printf(" --ecn-ip-ect %d", einfo->ip_ect);
}
}
static struct xtables_match ecn_mt_reg = {
.name = "ecn",
.version = XTABLES_VERSION,
.family = NFPROTO_UNSPEC,
.size = XT_ALIGN(sizeof(struct xt_ecn_info)),
.userspacesize = XT_ALIGN(sizeof(struct xt_ecn_info)),
.help = ecn_help,
.print = ecn_print,
.save = ecn_save,
.x6_parse = ecn_parse,
.x6_fcheck = ecn_check,
.x6_options = ecn_opts,
};
void _init(void)
{
xtables_register_match(&ecn_mt_reg);
}
This allows you to match the ECN bits of the IPv4/IPv6 and TCP header. ECN is the Explicit Congestion Notification mechanism as specified in RFC3168
.TP
[\fB!\fP] \fB\-\-ecn\-tcp\-cwr\fP
This matches if the TCP ECN CWR (Congestion Window Received) bit is set.
.TP
[\fB!\fP] \fB\-\-ecn\-tcp\-ece\fP
This matches if the TCP ECN ECE (ECN Echo) bit is set.
.TP
[\fB!\fP] \fB\-\-ecn\-ip\-ect\fP \fInum\fP
This matches a particular IPv4/IPv6 ECT (ECN-Capable Transport). You have to specify
a number between `0' and `3'.
#include <stdio.h>
#include <xtables.h>
#include <linux/netfilter/xt_esp.h>
enum {
O_ESPSPI = 0,
};
static void esp_help(void)
{
printf(
"esp match options:\n"
"[!] --espspi spi[:spi]\n"
" match spi (range)\n");
}
static const struct xt_option_entry esp_opts[] = {
{.name = "espspi", .id = O_ESPSPI, .type = XTTYPE_UINT32RC,
.flags = XTOPT_INVERT | XTOPT_PUT,
XTOPT_POINTER(struct xt_esp, spis)},
XTOPT_TABLEEND,
};
static void esp_parse(struct xt_option_call *cb)
{
struct xt_esp *espinfo = cb->data;
xtables_option_parse(cb);
if (cb->nvals == 1)
espinfo->spis[1] = espinfo->spis[0];
if (cb->invert)
espinfo->invflags |= XT_ESP_INV_SPI;
}
static void
print_spis(const char *name, uint32_t min, uint32_t max,
int invert)
{
const char *inv = invert ? "!" : "";
if (min != 0 || max != 0xFFFFFFFF || invert) {
if (min == max)
printf(" %s:%s%u", name, inv, min);
else
printf(" %ss:%s%u:%u", name, inv, min, max);
}
}
static void
esp_print(const void *ip, const struct xt_entry_match *match, int numeric)
{
const struct xt_esp *esp = (struct xt_esp *)match->data;
printf(" esp");
print_spis("spi", esp->spis[0], esp->spis[1],
esp->invflags & XT_ESP_INV_SPI);
if (esp->invflags & ~XT_ESP_INV_MASK)
printf(" Unknown invflags: 0x%X",
esp->invflags & ~XT_ESP_INV_MASK);
}
static void esp_save(const void *ip, const struct xt_entry_match *match)
{
const struct xt_esp *espinfo = (struct xt_esp *)match->data;
if (!(espinfo->spis[0] == 0
&& espinfo->spis[1] == 0xFFFFFFFF)) {
printf("%s --espspi ",
(espinfo->invflags & XT_ESP_INV_SPI) ? " !" : "");
if (espinfo->spis[0]
!= espinfo->spis[1])
printf("%u:%u",
espinfo->spis[0],
espinfo->spis[1]);
else
printf("%u",
espinfo->spis[0]);
}
}
static struct xtables_match esp_match = {
.family = NFPROTO_UNSPEC,
.name = "esp",
.version = XTABLES_VERSION,
.size = XT_ALIGN(sizeof(struct xt_esp)),
.userspacesize = XT_ALIGN(sizeof(struct xt_esp)),
.help = esp_help,
.print = esp_print,
.save = esp_save,
.x6_parse = esp_parse,
.x6_options = esp_opts,
};
void
_init(void)
{
xtables_register_match(&esp_match);
}
This module matches the SPIs in ESP header of IPsec packets.
.TP
[\fB!\fP] \fB\-\-espspi\fP \fIspi\fP[\fB:\fP\fIspi\fP]
/* ip6tables match extension for limiting packets per destination
*
* (C) 2003-2004 by Harald Welte <laforge@netfilter.org>
*
* Development of this code was funded by Astaro AG, http://www.astaro.com/
*
* Based on ipt_limit.c by
* Jérôme de Vivie <devivie@info.enserb.u-bordeaux.fr>
* Hervé Eychenne <rv@wallfire.org>
*
* Error corections by nmalykh@bilim.com (22.01.2005)
*/
#define _BSD_SOURCE 1
#define _ISOC99_SOURCE 1
#include <math.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <xtables.h>
#include <linux/netfilter/x_tables.h>
#include <linux/netfilter/xt_hashlimit.h>
#define XT_HASHLIMIT_BURST 5
#define XT_HASHLIMIT_BURST_MAX 10000
#define XT_HASHLIMIT_BYTE_EXPIRE 15
#define XT_HASHLIMIT_BYTE_EXPIRE_BURST 60
/* miliseconds */
#define XT_HASHLIMIT_GCINTERVAL 1000
struct hashlimit_mt_udata {
uint32_t mult;
};
static void hashlimit_help(void)
{
printf(
"hashlimit match options:\n"
"--hashlimit <avg> max average match rate\n"
" [Packets per second unless followed by \n"
" /sec /minute /hour /day postfixes]\n"
"--hashlimit-mode <mode> mode is a comma-separated list of\n"
" dstip,srcip,dstport,srcport\n"
"--hashlimit-name <name> name for /proc/net/ipt_hashlimit/\n"
"[--hashlimit-burst <num>] number to match in a burst, default %u\n"
"[--hashlimit-htable-size <num>] number of hashtable buckets\n"
"[--hashlimit-htable-max <num>] number of hashtable entries\n"
"[--hashlimit-htable-gcinterval] interval between garbage collection runs\n"
"[--hashlimit-htable-expire] after which time are idle entries expired?\n",
XT_HASHLIMIT_BURST);
}
enum {
O_UPTO = 0,
O_ABOVE,
O_LIMIT,
O_MODE,
O_SRCMASK,
O_DSTMASK,
O_NAME,
O_BURST,
O_HTABLE_SIZE,
O_HTABLE_MAX,
O_HTABLE_GCINT,
O_HTABLE_EXPIRE,
F_BURST = 1 << O_BURST,
F_UPTO = 1 << O_UPTO,
F_ABOVE = 1 << O_ABOVE,
F_HTABLE_EXPIRE = 1 << O_HTABLE_EXPIRE,
};
static void hashlimit_mt_help(void)
{
printf(
"hashlimit match options:\n"
" --hashlimit-upto <avg> max average match rate\n"
" [Packets per second unless followed by \n"
" /sec /minute /hour /day postfixes]\n"
" --hashlimit-above <avg> min average match rate\n"
" --hashlimit-mode <mode> mode is a comma-separated list of\n"
" dstip,srcip,dstport,srcport (or none)\n"
" --hashlimit-srcmask <length> source address grouping prefix length\n"
" --hashlimit-dstmask <length> destination address grouping prefix length\n"
" --hashlimit-name <name> name for /proc/net/ipt_hashlimit\n"
" --hashlimit-burst <num> number to match in a burst, default %u\n"
" --hashlimit-htable-size <num> number of hashtable buckets\n"
" --hashlimit-htable-max <num> number of hashtable entries\n"
" --hashlimit-htable-gcinterval interval between garbage collection runs\n"
" --hashlimit-htable-expire after which time are idle entries expired?\n"
"\n", XT_HASHLIMIT_BURST);
}
#define s struct xt_hashlimit_info
static const struct xt_option_entry hashlimit_opts[] = {
{.name = "hashlimit", .id = O_UPTO, .excl = F_ABOVE,
.type = XTTYPE_STRING},
{.name = "hashlimit-burst", .id = O_BURST, .type = XTTYPE_UINT32,
.min = 1, .max = XT_HASHLIMIT_BURST_MAX, .flags = XTOPT_PUT,
XTOPT_POINTER(s, cfg.burst)},
{.name = "hashlimit-htable-size", .id = O_HTABLE_SIZE,
.type = XTTYPE_UINT32, .flags = XTOPT_PUT,
XTOPT_POINTER(s, cfg.size)},
{.name = "hashlimit-htable-max", .id = O_HTABLE_MAX,
.type = XTTYPE_UINT32, .flags = XTOPT_PUT,
XTOPT_POINTER(s, cfg.max)},
{.name = "hashlimit-htable-gcinterval", .id = O_HTABLE_GCINT,
.type = XTTYPE_UINT32, .flags = XTOPT_PUT,
XTOPT_POINTER(s, cfg.gc_interval)},
{.name = "hashlimit-htable-expire", .id = O_HTABLE_EXPIRE,
.type = XTTYPE_UINT32, .flags = XTOPT_PUT,
XTOPT_POINTER(s, cfg.expire)},
{.name = "hashlimit-mode", .id = O_MODE, .type = XTTYPE_STRING,
.flags = XTOPT_MAND},
{.name = "hashlimit-name", .id = O_NAME, .type = XTTYPE_STRING,
.flags = XTOPT_MAND | XTOPT_PUT, XTOPT_POINTER(s, name), .min = 1},
XTOPT_TABLEEND,
};
#undef s
#define s struct xt_hashlimit_mtinfo1
static const struct xt_option_entry hashlimit_mt_opts[] = {
{.name = "hashlimit-upto", .id = O_UPTO, .excl = F_ABOVE,
.type = XTTYPE_STRING, .flags = XTOPT_INVERT},
{.name = "hashlimit-above", .id = O_ABOVE, .excl = F_UPTO,
.type = XTTYPE_STRING, .flags = XTOPT_INVERT},
{.name = "hashlimit", .id = O_UPTO, .excl = F_ABOVE,
.type = XTTYPE_STRING, .flags = XTOPT_INVERT}, /* old name */
{.name = "hashlimit-srcmask", .id = O_SRCMASK, .type = XTTYPE_PLEN},
{.name = "hashlimit-dstmask", .id = O_DSTMASK, .type = XTTYPE_PLEN},
{.name = "hashlimit-burst", .id = O_BURST, .type = XTTYPE_STRING},
{.name = "hashlimit-htable-size", .id = O_HTABLE_SIZE,
.type = XTTYPE_UINT32, .flags = XTOPT_PUT,
XTOPT_POINTER(s, cfg.size)},
{.name = "hashlimit-htable-max", .id = O_HTABLE_MAX,
.type = XTTYPE_UINT32, .flags = XTOPT_PUT,
XTOPT_POINTER(s, cfg.max)},
{.name = "hashlimit-htable-gcinterval", .id = O_HTABLE_GCINT,
.type = XTTYPE_UINT32, .flags = XTOPT_PUT,
XTOPT_POINTER(s, cfg.gc_interval)},
{.name = "hashlimit-htable-expire", .id = O_HTABLE_EXPIRE,
.type = XTTYPE_UINT32, .flags = XTOPT_PUT,
XTOPT_POINTER(s, cfg.expire)},
{.name = "hashlimit-mode", .id = O_MODE, .type = XTTYPE_STRING},
{.name = "hashlimit-name", .id = O_NAME, .type = XTTYPE_STRING,
.flags = XTOPT_MAND | XTOPT_PUT, XTOPT_POINTER(s, name), .min = 1},
XTOPT_TABLEEND,
};
#undef s
static uint32_t cost_to_bytes(uint32_t cost)
{
uint32_t r;
r = cost ? UINT32_MAX / cost : UINT32_MAX;
r = (r - 1) << XT_HASHLIMIT_BYTE_SHIFT;
return r;
}
static uint64_t bytes_to_cost(uint32_t bytes)
{
uint32_t r = bytes >> XT_HASHLIMIT_BYTE_SHIFT;
return UINT32_MAX / (r+1);
}
static uint32_t get_factor(int chr)
{
switch (chr) {
case 'm': return 1024 * 1024;
case 'k': return 1024;
}
return 1;
}
static void burst_error(void)
{
xtables_error(PARAMETER_PROBLEM, "bad value for option "
"\"--hashlimit-burst\", or out of range (1-%u).", XT_HASHLIMIT_BURST_MAX);
}
static uint32_t parse_burst(const char *burst, struct xt_hashlimit_mtinfo1 *info)
{
uintmax_t v;
char *end;
if (!xtables_strtoul(burst, &end, &v, 1, UINT32_MAX) ||
(*end == 0 && v > XT_HASHLIMIT_BURST_MAX))
burst_error();
v *= get_factor(*end);
if (v > UINT32_MAX)
xtables_error(PARAMETER_PROBLEM, "bad value for option "
"\"--hashlimit-burst\", value \"%s\" too large "
"(max %umb).", burst, UINT32_MAX/1024/1024);
return v;
}
static bool parse_bytes(const char *rate, uint32_t *val, struct hashlimit_mt_udata *ud)
{
unsigned int factor = 1;
uint64_t tmp;
int r;
const char *mode = strstr(rate, "b/s");
if (!mode || mode == rate)
return false;
mode--;
r = atoi(rate);
if (r == 0)
return false;
factor = get_factor(*mode);
tmp = (uint64_t) r * factor;
if (tmp > UINT32_MAX)
xtables_error(PARAMETER_PROBLEM,
"Rate value too large \"%llu\" (max %u)\n",
(unsigned long long)tmp, UINT32_MAX);
*val = bytes_to_cost(tmp);
if (*val == 0)
xtables_error(PARAMETER_PROBLEM, "Rate too high \"%s\"\n", rate);
ud->mult = XT_HASHLIMIT_BYTE_EXPIRE;
return true;
}
static
int parse_rate(const char *rate, uint32_t *val, struct hashlimit_mt_udata *ud)
{
const char *delim;
uint32_t r;
ud->mult = 1; /* Seconds by default. */
delim = strchr(rate, '/');
if (delim) {
if (strlen(delim+1) == 0)
return 0;
if (strncasecmp(delim+1, "second", strlen(delim+1)) == 0)
ud->mult = 1;
else if (strncasecmp(delim+1, "minute", strlen(delim+1)) == 0)
ud->mult = 60;
else if (strncasecmp(delim+1, "hour", strlen(delim+1)) == 0)
ud->mult = 60*60;
else if (strncasecmp(delim+1, "day", strlen(delim+1)) == 0)
ud->mult = 24*60*60;
else
return 0;
}
r = atoi(rate);
if (!r)
return 0;
*val = XT_HASHLIMIT_SCALE * ud->mult / r;
if (*val == 0)
/*
* The rate maps to infinity. (1/day is the minimum they can
* specify, so we are ok at that end).
*/
xtables_error(PARAMETER_PROBLEM, "Rate too fast \"%s\"\n", rate);
return 1;
}
static void hashlimit_init(struct xt_entry_match *m)
{
struct xt_hashlimit_info *r = (struct xt_hashlimit_info *)m->data;
r->cfg.burst = XT_HASHLIMIT_BURST;
r->cfg.gc_interval = XT_HASHLIMIT_GCINTERVAL;
}
static void hashlimit_mt4_init(struct xt_entry_match *match)
{
struct xt_hashlimit_mtinfo1 *info = (void *)match->data;
info->cfg.mode = 0;
info->cfg.burst = XT_HASHLIMIT_BURST;
info->cfg.gc_interval = XT_HASHLIMIT_GCINTERVAL;
info->cfg.srcmask = 32;
info->cfg.dstmask = 32;
}
static void hashlimit_mt6_init(struct xt_entry_match *match)
{
struct xt_hashlimit_mtinfo1 *info = (void *)match->data;
info->cfg.mode = 0;
info->cfg.burst = XT_HASHLIMIT_BURST;
info->cfg.gc_interval = XT_HASHLIMIT_GCINTERVAL;
info->cfg.srcmask = 128;
info->cfg.dstmask = 128;
}
/* Parse a 'mode' parameter into the required bitmask */
static int parse_mode(uint32_t *mode, const char *option_arg)
{
char *tok;
char *arg = strdup(option_arg);
if (!arg)
return -1;
for (tok = strtok(arg, ",|");
tok;
tok = strtok(NULL, ",|")) {
if (!strcmp(tok, "dstip"))
*mode |= XT_HASHLIMIT_HASH_DIP;
else if (!strcmp(tok, "srcip"))
*mode |= XT_HASHLIMIT_HASH_SIP;
else if (!strcmp(tok, "srcport"))
*mode |= XT_HASHLIMIT_HASH_SPT;
else if (!strcmp(tok, "dstport"))
*mode |= XT_HASHLIMIT_HASH_DPT;
else {
free(arg);
return -1;
}
}
free(arg);
return 0;
}
static void hashlimit_parse(struct xt_option_call *cb)
{
struct xt_hashlimit_info *info = cb->data;
xtables_option_parse(cb);
switch (cb->entry->id) {
case O_UPTO:
if (!parse_rate(cb->arg, &info->cfg.avg, cb->udata))
xtables_param_act(XTF_BAD_VALUE, "hashlimit",
"--hashlimit-upto", cb->arg);
break;
case O_MODE:
if (parse_mode(&info->cfg.mode, cb->arg) < 0)
xtables_param_act(XTF_BAD_VALUE, "hashlimit",
"--hashlimit-mode", cb->arg);
break;
}
}
static void hashlimit_mt_parse(struct xt_option_call *cb)
{
struct xt_hashlimit_mtinfo1 *info = cb->data;
xtables_option_parse(cb);
switch (cb->entry->id) {
case O_BURST:
info->cfg.burst = parse_burst(cb->arg, info);
break;
case O_UPTO:
if (cb->invert)
info->cfg.mode |= XT_HASHLIMIT_INVERT;
if (parse_bytes(cb->arg, &info->cfg.avg, cb->udata))
info->cfg.mode |= XT_HASHLIMIT_BYTES;
else if (!parse_rate(cb->arg, &info->cfg.avg, cb->udata))
xtables_param_act(XTF_BAD_VALUE, "hashlimit",
"--hashlimit-upto", cb->arg);
break;
case O_ABOVE:
if (!cb->invert)
info->cfg.mode |= XT_HASHLIMIT_INVERT;
if (parse_bytes(cb->arg, &info->cfg.avg, cb->udata))
info->cfg.mode |= XT_HASHLIMIT_BYTES;
else if (!parse_rate(cb->arg, &info->cfg.avg, cb->udata))
xtables_param_act(XTF_BAD_VALUE, "hashlimit",
"--hashlimit-above", cb->arg);
break;
case O_MODE:
if (parse_mode(&info->cfg.mode, cb->arg) < 0)
xtables_param_act(XTF_BAD_VALUE, "hashlimit",
"--hashlimit-mode", cb->arg);
break;
case O_SRCMASK:
info->cfg.srcmask = cb->val.hlen;
break;
case O_DSTMASK:
info->cfg.dstmask = cb->val.hlen;
break;
}
}
static void hashlimit_check(struct xt_fcheck_call *cb)
{
const struct hashlimit_mt_udata *udata = cb->udata;
struct xt_hashlimit_info *info = cb->data;
if (!(cb->xflags & (F_UPTO | F_ABOVE)))
xtables_error(PARAMETER_PROBLEM,
"You have to specify --hashlimit");
if (!(cb->xflags & F_HTABLE_EXPIRE))
info->cfg.expire = udata->mult * 1000; /* from s to msec */
}
static void hashlimit_mt_check(struct xt_fcheck_call *cb)
{
const struct hashlimit_mt_udata *udata = cb->udata;
struct xt_hashlimit_mtinfo1 *info = cb->data;
if (!(cb->xflags & (F_UPTO | F_ABOVE)))
xtables_error(PARAMETER_PROBLEM,
"You have to specify --hashlimit");
if (!(cb->xflags & F_HTABLE_EXPIRE))
info->cfg.expire = udata->mult * 1000; /* from s to msec */
if (info->cfg.mode & XT_HASHLIMIT_BYTES) {
uint32_t burst = 0;
if (cb->xflags & F_BURST) {
if (info->cfg.burst < cost_to_bytes(info->cfg.avg))
xtables_error(PARAMETER_PROBLEM,
"burst cannot be smaller than %ub", cost_to_bytes(info->cfg.avg));
burst = info->cfg.burst;
burst /= cost_to_bytes(info->cfg.avg);
if (info->cfg.burst % cost_to_bytes(info->cfg.avg))
burst++;
if (!(cb->xflags & F_HTABLE_EXPIRE))
info->cfg.expire = XT_HASHLIMIT_BYTE_EXPIRE_BURST * 1000;
}
info->cfg.burst = burst;
} else if (info->cfg.burst > XT_HASHLIMIT_BURST_MAX)
burst_error();
}
static const struct rates
{
const char *name;
uint32_t mult;
} rates[] = { { "day", XT_HASHLIMIT_SCALE*24*60*60 },
{ "hour", XT_HASHLIMIT_SCALE*60*60 },
{ "min", XT_HASHLIMIT_SCALE*60 },
{ "sec", XT_HASHLIMIT_SCALE } };
static uint32_t print_rate(uint32_t period)
{
unsigned int i;
if (period == 0) {
printf(" %f", INFINITY);
return 0;
}
for (i = 1; i < ARRAY_SIZE(rates); ++i)
if (period > rates[i].mult
|| rates[i].mult/period < rates[i].mult%period)
break;
printf(" %u/%s", rates[i-1].mult / period, rates[i-1].name);
/* return in msec */
return rates[i-1].mult / XT_HASHLIMIT_SCALE * 1000;
}
static const struct {
const char *name;
uint32_t thresh;
} units[] = {
{ "m", 1024 * 1024 },
{ "k", 1024 },
{ "", 1 },
};
static uint32_t print_bytes(uint32_t avg, uint32_t burst, const char *prefix)
{
unsigned int i;
unsigned long long r;
r = cost_to_bytes(avg);
for (i = 0; i < ARRAY_SIZE(units) -1; ++i)
if (r >= units[i].thresh &&
bytes_to_cost(r & ~(units[i].thresh - 1)) == avg)
break;
printf(" %llu%sb/s", r/units[i].thresh, units[i].name);
if (burst == 0)
return XT_HASHLIMIT_BYTE_EXPIRE * 1000;
r *= burst;
printf(" %s", prefix);
for (i = 0; i < ARRAY_SIZE(units) -1; ++i)
if (r >= units[i].thresh)
break;
printf("burst %llu%sb", r / units[i].thresh, units[i].name);
return XT_HASHLIMIT_BYTE_EXPIRE_BURST * 1000;
}
static void print_mode(unsigned int mode, char separator)
{
bool prevmode = false;
putchar(' ');
if (mode & XT_HASHLIMIT_HASH_SIP) {
fputs("srcip", stdout);
prevmode = 1;
}
if (mode & XT_HASHLIMIT_HASH_SPT) {
if (prevmode)
putchar(separator);
fputs("srcport", stdout);
prevmode = 1;
}
if (mode & XT_HASHLIMIT_HASH_DIP) {
if (prevmode)
putchar(separator);
fputs("dstip", stdout);
prevmode = 1;
}
if (mode & XT_HASHLIMIT_HASH_DPT) {
if (prevmode)
putchar(separator);
fputs("dstport", stdout);
}
}
static void hashlimit_print(const void *ip,
const struct xt_entry_match *match, int numeric)
{
const struct xt_hashlimit_info *r = (const void *)match->data;
uint32_t quantum;
fputs(" limit: avg", stdout);
quantum = print_rate(r->cfg.avg);
printf(" burst %u", r->cfg.burst);
fputs(" mode", stdout);
print_mode(r->cfg.mode, '-');
if (r->cfg.size)
printf(" htable-size %u", r->cfg.size);
if (r->cfg.max)
printf(" htable-max %u", r->cfg.max);
if (r->cfg.gc_interval != XT_HASHLIMIT_GCINTERVAL)
printf(" htable-gcinterval %u", r->cfg.gc_interval);
if (r->cfg.expire != quantum)
printf(" htable-expire %u", r->cfg.expire);
}
static void
hashlimit_mt_print(const struct xt_hashlimit_mtinfo1 *info, unsigned int dmask)
{
uint32_t quantum;
if (info->cfg.mode & XT_HASHLIMIT_INVERT)
fputs(" limit: above", stdout);
else
fputs(" limit: up to", stdout);
if (info->cfg.mode & XT_HASHLIMIT_BYTES) {
quantum = print_bytes(info->cfg.avg, info->cfg.burst, "");
} else {
quantum = print_rate(info->cfg.avg);
printf(" burst %u", info->cfg.burst);
}
if (info->cfg.mode & (XT_HASHLIMIT_HASH_SIP | XT_HASHLIMIT_HASH_SPT |
XT_HASHLIMIT_HASH_DIP | XT_HASHLIMIT_HASH_DPT)) {
fputs(" mode", stdout);
print_mode(info->cfg.mode, '-');
}
if (info->cfg.size != 0)
printf(" htable-size %u", info->cfg.size);
if (info->cfg.max != 0)
printf(" htable-max %u", info->cfg.max);
if (info->cfg.gc_interval != XT_HASHLIMIT_GCINTERVAL)
printf(" htable-gcinterval %u", info->cfg.gc_interval);
if (info->cfg.expire != quantum)
printf(" htable-expire %u", info->cfg.expire);
if (info->cfg.srcmask != dmask)
printf(" srcmask %u", info->cfg.srcmask);
if (info->cfg.dstmask != dmask)
printf(" dstmask %u", info->cfg.dstmask);
}
static void
hashlimit_mt4_print(const void *ip, const struct xt_entry_match *match,
int numeric)
{
const struct xt_hashlimit_mtinfo1 *info = (const void *)match->data;
hashlimit_mt_print(info, 32);
}
static void
hashlimit_mt6_print(const void *ip, const struct xt_entry_match *match,
int numeric)
{
const struct xt_hashlimit_mtinfo1 *info = (const void *)match->data;
hashlimit_mt_print(info, 128);
}
static void hashlimit_save(const void *ip, const struct xt_entry_match *match)
{
const struct xt_hashlimit_info *r = (const void *)match->data;
uint32_t quantum;
fputs(" --hashlimit", stdout);
quantum = print_rate(r->cfg.avg);
printf(" --hashlimit-burst %u", r->cfg.burst);
fputs(" --hashlimit-mode", stdout);
print_mode(r->cfg.mode, ',');
printf(" --hashlimit-name %s", r->name);
if (r->cfg.size)
printf(" --hashlimit-htable-size %u", r->cfg.size);
if (r->cfg.max)
printf(" --hashlimit-htable-max %u", r->cfg.max);
if (r->cfg.gc_interval != XT_HASHLIMIT_GCINTERVAL)
printf(" --hashlimit-htable-gcinterval %u", r->cfg.gc_interval);
if (r->cfg.expire != quantum)
printf(" --hashlimit-htable-expire %u", r->cfg.expire);
}
static void
hashlimit_mt_save(const struct xt_hashlimit_mtinfo1 *info, unsigned int dmask)
{
uint32_t quantum;
if (info->cfg.mode & XT_HASHLIMIT_INVERT)
fputs(" --hashlimit-above", stdout);
else
fputs(" --hashlimit-upto", stdout);
if (info->cfg.mode & XT_HASHLIMIT_BYTES) {
quantum = print_bytes(info->cfg.avg, info->cfg.burst, "--hashlimit-");
} else {
quantum = print_rate(info->cfg.avg);
printf(" --hashlimit-burst %u", info->cfg.burst);
}
if (info->cfg.mode & (XT_HASHLIMIT_HASH_SIP | XT_HASHLIMIT_HASH_SPT |
XT_HASHLIMIT_HASH_DIP | XT_HASHLIMIT_HASH_DPT)) {
fputs(" --hashlimit-mode", stdout);
print_mode(info->cfg.mode, ',');
}
printf(" --hashlimit-name %s", info->name);
if (info->cfg.size != 0)
printf(" --hashlimit-htable-size %u", info->cfg.size);
if (info->cfg.max != 0)
printf(" --hashlimit-htable-max %u", info->cfg.max);
if (info->cfg.gc_interval != XT_HASHLIMIT_GCINTERVAL)
printf(" --hashlimit-htable-gcinterval %u", info->cfg.gc_interval);
if (info->cfg.expire != quantum)
printf(" --hashlimit-htable-expire %u", info->cfg.expire);
if (info->cfg.srcmask != dmask)
printf(" --hashlimit-srcmask %u", info->cfg.srcmask);
if (info->cfg.dstmask != dmask)
printf(" --hashlimit-dstmask %u", info->cfg.dstmask);
}
static void
hashlimit_mt4_save(const void *ip, const struct xt_entry_match *match)
{
const struct xt_hashlimit_mtinfo1 *info = (const void *)match->data;
hashlimit_mt_save(info, 32);
}
static void
hashlimit_mt6_save(const void *ip, const struct xt_entry_match *match)
{
const struct xt_hashlimit_mtinfo1 *info = (const void *)match->data;
hashlimit_mt_save(info, 128);
}
static struct xtables_match hashlimit_mt_reg[] = {
{
.family = NFPROTO_UNSPEC,
.name = "hashlimit",
.version = XTABLES_VERSION,
.revision = 0,
.size = XT_ALIGN(sizeof(struct xt_hashlimit_info)),
.userspacesize = offsetof(struct xt_hashlimit_info, hinfo),
.help = hashlimit_help,
.init = hashlimit_init,
.x6_parse = hashlimit_parse,
.x6_fcheck = hashlimit_check,
.print = hashlimit_print,
.save = hashlimit_save,
.x6_options = hashlimit_opts,
.udata_size = sizeof(struct hashlimit_mt_udata),
},
{
.version = XTABLES_VERSION,
.name = "hashlimit",
.revision = 1,
.family = NFPROTO_IPV4,
.size = XT_ALIGN(sizeof(struct xt_hashlimit_mtinfo1)),
.userspacesize = offsetof(struct xt_hashlimit_mtinfo1, hinfo),
.help = hashlimit_mt_help,
.init = hashlimit_mt4_init,
.x6_parse = hashlimit_mt_parse,
.x6_fcheck = hashlimit_mt_check,
.print = hashlimit_mt4_print,
.save = hashlimit_mt4_save,
.x6_options = hashlimit_mt_opts,
.udata_size = sizeof(struct hashlimit_mt_udata),
},
{
.version = XTABLES_VERSION,
.name = "hashlimit",
.revision = 1,
.family = NFPROTO_IPV6,
.size = XT_ALIGN(sizeof(struct xt_hashlimit_mtinfo1)),
.userspacesize = offsetof(struct xt_hashlimit_mtinfo1, hinfo),
.help = hashlimit_mt_help,
.init = hashlimit_mt6_init,
.x6_parse = hashlimit_mt_parse,
.x6_fcheck = hashlimit_mt_check,
.print = hashlimit_mt6_print,
.save = hashlimit_mt6_save,
.x6_options = hashlimit_mt_opts,
.udata_size = sizeof(struct hashlimit_mt_udata),
},
};
void _init(void)
{
xtables_register_matches(hashlimit_mt_reg, ARRAY_SIZE(hashlimit_mt_reg));
}
\fBhashlimit\fP uses hash buckets to express a rate limiting match (like the
\fBlimit\fP match) for a group of connections using a \fBsingle\fP iptables
rule. Grouping can be done per-hostgroup (source and/or destination address)
and/or per-port. It gives you the ability to express "\fIN\fP packets per time
quantum per group" or "\fIN\fP bytes per seconds" (see below for some examples).
.PP
A hash limit option (\fB\-\-hashlimit\-upto\fP, \fB\-\-hashlimit\-above\fP) and
\fB\-\-hashlimit\-name\fP are required.
.TP
\fB\-\-hashlimit\-upto\fP \fIamount\fP[\fB/second\fP|\fB/minute\fP|\fB/hour\fP|\fB/day\fP]
Match if the rate is below or equal to \fIamount\fP/quantum. It is specified either as
a number, with an optional time quantum suffix (the default is 3/hour), or as
\fIamount\fPb/second (number of bytes per second).
.TP
\fB\-\-hashlimit\-above\fP \fIamount\fP[\fB/second\fP|\fB/minute\fP|\fB/hour\fP|\fB/day\fP]
Match if the rate is above \fIamount\fP/quantum.
.TP
\fB\-\-hashlimit\-burst\fP \fIamount\fP
Maximum initial number of packets to match: this number gets recharged by one
every time the limit specified above is not reached, up to this number; the
default is 5. When byte-based rate matching is requested, this option specifies
the amount of bytes that can exceed the given rate. This option should be used
with caution -- if the entry expires, the burst value is reset too.
.TP
\fB\-\-hashlimit\-mode\fP {\fBsrcip\fP|\fBsrcport\fP|\fBdstip\fP|\fBdstport\fP}\fB,\fP...
A comma-separated list of objects to take into consideration. If no
\-\-hashlimit\-mode option is given, hashlimit acts like limit, but at the
expensive of doing the hash housekeeping.
.TP
\fB\-\-hashlimit\-srcmask\fP \fIprefix\fP
When \-\-hashlimit\-mode srcip is used, all source addresses encountered will be
grouped according to the given prefix length and the so-created subnet will be
subject to hashlimit. \fIprefix\fP must be between (inclusive) 0 and 32. Note
that \-\-hashlimit\-srcmask 0 is basically doing the same thing as not specifying
srcip for \-\-hashlimit\-mode, but is technically more expensive.
.TP
\fB\-\-hashlimit\-dstmask\fP \fIprefix\fP
Like \-\-hashlimit\-srcmask, but for destination addresses.
.TP
\fB\-\-hashlimit\-name\fP \fIfoo\fP
The name for the /proc/net/ipt_hashlimit/foo entry.
.TP
\fB\-\-hashlimit\-htable\-size\fP \fIbuckets\fP
The number of buckets of the hash table
.TP
\fB\-\-hashlimit\-htable\-max\fP \fIentries\fP
Maximum entries in the hash.
.TP
\fB\-\-hashlimit\-htable\-expire\fP \fImsec\fP
After how many milliseconds do hash entries expire.
.TP
\fB\-\-hashlimit\-htable\-gcinterval\fP \fImsec\fP
How many milliseconds between garbage collection intervals.
.PP
Examples:
.TP
matching on source host
"1000 packets per second for every host in 192.168.0.0/16" =>
\-s 192.168.0.0/16 \-\-hashlimit\-mode srcip \-\-hashlimit\-upto 1000/sec
.TP
matching on source port
"100 packets per second for every service of 192.168.1.1" =>
\-s 192.168.1.1 \-\-hashlimit\-mode srcport \-\-hashlimit\-upto 100/sec
.TP
matching on subnet
"10000 packets per minute for every /28 subnet (groups of 8 addresses)
in 10.0.0.0/8" =>
\-s 10.0.0.0/8 \-\-hashlimit\-mask 28 \-\-hashlimit\-upto 10000/min
.TP
matching bytes per second
"flows exceeding 512kbyte/s" =>
\-\-hashlimit-mode srcip,dstip,srcport,dstport \-\-hashlimit\-above 512kb/s
.TP
matching bytes per second
"hosts that exceed 512kbyte/s, but permit up to 1Megabytes without matching"
\-\-hashlimit-mode dstip \-\-hashlimit\-above 512kb/s \-\-hashlimit-burst 1mb
#include <stdio.h>
#include <xtables.h>
#include <linux/netfilter/xt_helper.h>
enum {
O_HELPER = 0,
};
static void helper_help(void)
{
printf(
"helper match options:\n"
"[!] --helper string Match helper identified by string\n");
}
static const struct xt_option_entry helper_opts[] = {
{.name = "helper", .id = O_HELPER, .type = XTTYPE_STRING,
.flags = XTOPT_MAND | XTOPT_INVERT | XTOPT_PUT,
XTOPT_POINTER(struct xt_helper_info, name)},
XTOPT_TABLEEND,
};
static void helper_parse(struct xt_option_call *cb)
{
struct xt_helper_info *info = cb->data;
xtables_option_parse(cb);
if (cb->invert)
info->invert = 1;
}
static void
helper_print(const void *ip, const struct xt_entry_match *match, int numeric)
{
const struct xt_helper_info *info = (const void *)match->data;
printf(" helper match %s\"%s\"", info->invert ? "! " : "", info->name);
}
static void helper_save(const void *ip, const struct xt_entry_match *match)
{
const struct xt_helper_info *info = (const void *)match->data;
printf("%s --helper", info->invert ? " !" : "");
xtables_save_string(info->name);
}
static struct xtables_match helper_match = {
.family = NFPROTO_UNSPEC,
.name = "helper",
.version = XTABLES_VERSION,
.size = XT_ALIGN(sizeof(struct xt_helper_info)),
.help = helper_help,
.print = helper_print,
.save = helper_save,
.x6_parse = helper_parse,
.x6_options = helper_opts,
};
void _init(void)
{
xtables_register_match(&helper_match);
}
This module matches packets related to a specific conntrack-helper.
.TP
[\fB!\fP] \fB\-\-helper\fP \fIstring\fP
Matches packets related to the specified conntrack-helper.
.RS
.PP
string can be "ftp" for packets related to a ftp-session on default port.
For other ports append \-portnr to the value, ie. "ftp\-2121".
.PP
Same rules apply for other conntrack-helpers.
.RE
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <xtables.h>
#include <linux/netfilter.h>
#include <linux/netfilter/xt_iprange.h>
struct ipt_iprange {
/* Inclusive: network order. */
__be32 min_ip, max_ip;
};
struct ipt_iprange_info {
struct ipt_iprange src;
struct ipt_iprange dst;
/* Flags from above */
uint8_t flags;
};
enum {
O_SRC_RANGE = 0,
O_DST_RANGE,
};
static void iprange_mt_help(void)
{
printf(
"iprange match options:\n"
"[!] --src-range ip[-ip] Match source IP in the specified range\n"
"[!] --dst-range ip[-ip] Match destination IP in the specified range\n");
}
static const struct xt_option_entry iprange_mt_opts[] = {
{.name = "src-range", .id = O_SRC_RANGE, .type = XTTYPE_STRING,
.flags = XTOPT_INVERT},
{.name = "dst-range", .id = O_DST_RANGE, .type = XTTYPE_STRING,
.flags = XTOPT_INVERT},
XTOPT_TABLEEND,
};
static void
iprange_parse_spec(const char *from, const char *to, union nf_inet_addr *range,
uint8_t family, const char *optname)
{
const char *spec[2] = {from, to};
struct in6_addr *ia6;
struct in_addr *ia4;
unsigned int i;
memset(range, 0, sizeof(union nf_inet_addr) * 2);
if (family == NFPROTO_IPV6) {
for (i = 0; i < ARRAY_SIZE(spec); ++i) {
ia6 = xtables_numeric_to_ip6addr(spec[i]);
if (ia6 == NULL)
xtables_param_act(XTF_BAD_VALUE, "iprange",
optname, spec[i]);
range[i].in6 = *ia6;
}
} else {
for (i = 0; i < ARRAY_SIZE(spec); ++i) {
ia4 = xtables_numeric_to_ipaddr(spec[i]);
if (ia4 == NULL)
xtables_param_act(XTF_BAD_VALUE, "iprange",
optname, spec[i]);
range[i].in = *ia4;
}
}
}
static void iprange_parse_range(const char *oarg, union nf_inet_addr *range,
uint8_t family, const char *optname)
{
char *arg = strdup(oarg);
char *dash;
if (arg == NULL)
xtables_error(RESOURCE_PROBLEM, "strdup");
dash = strchr(arg, '-');
if (dash == NULL) {
iprange_parse_spec(arg, arg, range, family, optname);
free(arg);
return;
}
*dash = '\0';
iprange_parse_spec(arg, dash + 1, range, family, optname);
if (memcmp(&range[0], &range[1], sizeof(*range)) > 0)
fprintf(stderr, "xt_iprange: range %s-%s is reversed and "
"will never match\n", arg, dash + 1);
free(arg);
}
static void iprange_parse(struct xt_option_call *cb)
{
struct ipt_iprange_info *info = cb->data;
union nf_inet_addr range[2];
xtables_option_parse(cb);
switch (cb->entry->id) {
case O_SRC_RANGE:
info->flags |= IPRANGE_SRC;
if (cb->invert)
info->flags |= IPRANGE_SRC_INV;
iprange_parse_range(cb->arg, range, NFPROTO_IPV4, "--src-range");
info->src.min_ip = range[0].ip;
info->src.max_ip = range[1].ip;
break;
case O_DST_RANGE:
info->flags |= IPRANGE_DST;
if (cb->invert)
info->flags |= IPRANGE_DST_INV;
iprange_parse_range(cb->arg, range, NFPROTO_IPV4, "--dst-range");
info->dst.min_ip = range[0].ip;
info->dst.max_ip = range[1].ip;
break;
}
}
static void iprange_mt_parse(struct xt_option_call *cb, uint8_t nfproto)
{
struct xt_iprange_mtinfo *info = cb->data;
xtables_option_parse(cb);
switch (cb->entry->id) {
case O_SRC_RANGE:
iprange_parse_range(cb->arg, &info->src_min, nfproto,
"--src-range");
info->flags |= IPRANGE_SRC;
if (cb->invert)
info->flags |= IPRANGE_SRC_INV;
break;
case O_DST_RANGE:
iprange_parse_range(cb->arg, &info->dst_min, nfproto,
"--dst-range");
info->flags |= IPRANGE_DST;
if (cb->invert)
info->flags |= IPRANGE_DST_INV;
break;
}
}
static void iprange_mt4_parse(struct xt_option_call *cb)
{
iprange_mt_parse(cb, NFPROTO_IPV4);
}
static void iprange_mt6_parse(struct xt_option_call *cb)
{
iprange_mt_parse(cb, NFPROTO_IPV6);
}
static void iprange_mt_check(struct xt_fcheck_call *cb)
{
if (cb->xflags == 0)
xtables_error(PARAMETER_PROBLEM,
"iprange match: You must specify `--src-range' or `--dst-range'");
}
static void
print_iprange(const struct ipt_iprange *range)
{
const unsigned char *byte_min, *byte_max;
byte_min = (const unsigned char *)&range->min_ip;
byte_max = (const unsigned char *)&range->max_ip;
printf(" %u.%u.%u.%u-%u.%u.%u.%u",
byte_min[0], byte_min[1], byte_min[2], byte_min[3],
byte_max[0], byte_max[1], byte_max[2], byte_max[3]);
}
static void iprange_print(const void *ip, const struct xt_entry_match *match,
int numeric)
{
const struct ipt_iprange_info *info = (const void *)match->data;
if (info->flags & IPRANGE_SRC) {
printf(" source IP range");
if (info->flags & IPRANGE_SRC_INV)
printf(" !");
print_iprange(&info->src);
}
if (info->flags & IPRANGE_DST) {
printf(" destination IP range");
if (info->flags & IPRANGE_DST_INV)
printf(" !");
print_iprange(&info->dst);
}
}
static void
iprange_mt4_print(const void *ip, const struct xt_entry_match *match,
int numeric)
{
const struct xt_iprange_mtinfo *info = (const void *)match->data;
if (info->flags & IPRANGE_SRC) {
printf(" source IP range");
if (info->flags & IPRANGE_SRC_INV)
printf(" !");
/*
* ipaddr_to_numeric() uses a static buffer, so cannot
* combine the printf() calls.
*/
printf(" %s", xtables_ipaddr_to_numeric(&info->src_min.in));
printf("-%s", xtables_ipaddr_to_numeric(&info->src_max.in));
}
if (info->flags & IPRANGE_DST) {
printf(" destination IP range");
if (info->flags & IPRANGE_DST_INV)
printf(" !");
printf(" %s", xtables_ipaddr_to_numeric(&info->dst_min.in));
printf("-%s", xtables_ipaddr_to_numeric(&info->dst_max.in));
}
}
static void
iprange_mt6_print(const void *ip, const struct xt_entry_match *match,
int numeric)
{
const struct xt_iprange_mtinfo *info = (const void *)match->data;
if (info->flags & IPRANGE_SRC) {
printf(" source IP range");
if (info->flags & IPRANGE_SRC_INV)
printf(" !");
/*
* ipaddr_to_numeric() uses a static buffer, so cannot
* combine the printf() calls.
*/
printf(" %s", xtables_ip6addr_to_numeric(&info->src_min.in6));
printf("-%s", xtables_ip6addr_to_numeric(&info->src_max.in6));
}
if (info->flags & IPRANGE_DST) {
printf(" destination IP range");
if (info->flags & IPRANGE_DST_INV)
printf(" !");
printf(" %s", xtables_ip6addr_to_numeric(&info->dst_min.in6));
printf("-%s", xtables_ip6addr_to_numeric(&info->dst_max.in6));
}
}
static void iprange_save(const void *ip, const struct xt_entry_match *match)
{
const struct ipt_iprange_info *info = (const void *)match->data;
if (info->flags & IPRANGE_SRC) {
if (info->flags & IPRANGE_SRC_INV)
printf(" !");
printf(" --src-range");
print_iprange(&info->src);
}
if (info->flags & IPRANGE_DST) {
if (info->flags & IPRANGE_DST_INV)
printf(" !");
printf(" --dst-range");
print_iprange(&info->dst);
}
}
static void iprange_mt4_save(const void *ip, const struct xt_entry_match *match)
{
const struct xt_iprange_mtinfo *info = (const void *)match->data;
if (info->flags & IPRANGE_SRC) {
if (info->flags & IPRANGE_SRC_INV)
printf(" !");
printf(" --src-range %s", xtables_ipaddr_to_numeric(&info->src_min.in));
printf("-%s", xtables_ipaddr_to_numeric(&info->src_max.in));
}
if (info->flags & IPRANGE_DST) {
if (info->flags & IPRANGE_DST_INV)
printf(" !");
printf(" --dst-range %s", xtables_ipaddr_to_numeric(&info->dst_min.in));
printf("-%s", xtables_ipaddr_to_numeric(&info->dst_max.in));
}
}
static void iprange_mt6_save(const void *ip, const struct xt_entry_match *match)
{
const struct xt_iprange_mtinfo *info = (const void *)match->data;
if (info->flags & IPRANGE_SRC) {
if (info->flags & IPRANGE_SRC_INV)
printf(" !");
printf(" --src-range %s", xtables_ip6addr_to_numeric(&info->src_min.in6));
printf("-%s", xtables_ip6addr_to_numeric(&info->src_max.in6));
}
if (info->flags & IPRANGE_DST) {
if (info->flags & IPRANGE_DST_INV)
printf(" !");
printf(" --dst-range %s", xtables_ip6addr_to_numeric(&info->dst_min.in6));
printf("-%s", xtables_ip6addr_to_numeric(&info->dst_max.in6));
}
}
static struct xtables_match iprange_mt_reg[] = {
{
.version = XTABLES_VERSION,
.name = "iprange",
.revision = 0,
.family = NFPROTO_IPV4,
.size = XT_ALIGN(sizeof(struct ipt_iprange_info)),
.userspacesize = XT_ALIGN(sizeof(struct ipt_iprange_info)),
.help = iprange_mt_help,
.x6_parse = iprange_parse,
.x6_fcheck = iprange_mt_check,
.print = iprange_print,
.save = iprange_save,
.x6_options = iprange_mt_opts,
},
{
.version = XTABLES_VERSION,
.name = "iprange",
.revision = 1,
.family = NFPROTO_IPV4,
.size = XT_ALIGN(sizeof(struct xt_iprange_mtinfo)),
.userspacesize = XT_ALIGN(sizeof(struct xt_iprange_mtinfo)),
.help = iprange_mt_help,
.x6_parse = iprange_mt4_parse,
.x6_fcheck = iprange_mt_check,
.print = iprange_mt4_print,
.save = iprange_mt4_save,
.x6_options = iprange_mt_opts,
},
{
.version = XTABLES_VERSION,
.name = "iprange",
.revision = 1,
.family = NFPROTO_IPV6,
.size = XT_ALIGN(sizeof(struct xt_iprange_mtinfo)),
.userspacesize = XT_ALIGN(sizeof(struct xt_iprange_mtinfo)),
.help = iprange_mt_help,
.x6_parse = iprange_mt6_parse,
.x6_fcheck = iprange_mt_check,
.print = iprange_mt6_print,
.save = iprange_mt6_save,
.x6_options = iprange_mt_opts,
},
};
void _init(void)
{
xtables_register_matches(iprange_mt_reg, ARRAY_SIZE(iprange_mt_reg));
}
This matches on a given arbitrary range of IP addresses.
.TP
[\fB!\fP] \fB\-\-src\-range\fP \fIfrom\fP[\fB\-\fP\fIto\fP]
Match source IP in the specified range.
.TP
[\fB!\fP] \fB\-\-dst\-range\fP \fIfrom\fP[\fB\-\fP\fIto\fP]
Match destination IP in the specified range.
/*
* Shared library add-on to iptables to add IPVS matching.
*
* Detailed doc is in the kernel module source net/netfilter/xt_ipvs.c
*
* Author: Hannes Eder <heder@google.com>
*/
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <xtables.h>
#include <linux/ip_vs.h>
#include <linux/netfilter/xt_ipvs.h>
enum {
/* For xt_ipvs: make sure this matches up with %XT_IPVS_*'s order */
O_IPVS = 0,
O_VPROTO,
O_VADDR,
O_VPORT,
O_VDIR,
O_VMETHOD,
O_VPORTCTL,
};
#define s struct xt_ipvs_mtinfo
static const struct xt_option_entry ipvs_mt_opts[] = {
{.name = "ipvs", .id = O_IPVS, .type = XTTYPE_NONE,
.flags = XTOPT_INVERT},
{.name = "vproto", .id = O_VPROTO, .type = XTTYPE_STRING,
.flags = XTOPT_INVERT | XTOPT_PUT, XTOPT_POINTER(s, l4proto)},
{.name = "vaddr", .id = O_VADDR, .type = XTTYPE_HOSTMASK,
.flags = XTOPT_INVERT},
{.name = "vport", .id = O_VPORT, .type = XTTYPE_PORT,
.flags = XTOPT_NBO | XTOPT_INVERT | XTOPT_PUT,
XTOPT_POINTER(s, vport)},
{.name = "vdir", .id = O_VDIR, .type = XTTYPE_STRING},
{.name = "vmethod", .id = O_VMETHOD, .type = XTTYPE_STRING,
.flags = XTOPT_INVERT},
{.name = "vportctl", .id = O_VPORTCTL, .type = XTTYPE_PORT,
.flags = XTOPT_NBO | XTOPT_INVERT | XTOPT_PUT,
XTOPT_POINTER(s, vportctl)},
XTOPT_TABLEEND,
};
#undef s
static void ipvs_mt_help(void)
{
printf(
"IPVS match options:\n"
"[!] --ipvs packet belongs to an IPVS connection\n"
"\n"
"Any of the following options implies --ipvs (even negated)\n"
"[!] --vproto protocol VIP protocol to match; by number or name,\n"
" e.g. \"tcp\"\n"
"[!] --vaddr address[/mask] VIP address to match\n"
"[!] --vport port VIP port to match; by number or name,\n"
" e.g. \"http\"\n"
" --vdir {ORIGINAL|REPLY} flow direction of packet\n"
"[!] --vmethod {GATE|IPIP|MASQ} IPVS forwarding method used\n"
"[!] --vportctl port VIP port of the controlling connection to\n"
" match, e.g. 21 for FTP\n"
);
}
static void ipvs_mt_parse(struct xt_option_call *cb)
{
struct xt_ipvs_mtinfo *data = cb->data;
xtables_option_parse(cb);
switch (cb->entry->id) {
case O_VPROTO:
data->l4proto = cb->val.protocol;
break;
case O_VADDR:
memcpy(&data->vaddr, &cb->val.haddr, sizeof(cb->val.haddr));
memcpy(&data->vmask, &cb->val.hmask, sizeof(cb->val.hmask));
break;
case O_VDIR:
if (strcasecmp(cb->arg, "ORIGINAL") == 0) {
data->bitmask |= XT_IPVS_DIR;
data->invert &= ~XT_IPVS_DIR;
} else if (strcasecmp(cb->arg, "REPLY") == 0) {
data->bitmask |= XT_IPVS_DIR;
data->invert |= XT_IPVS_DIR;
} else {
xtables_param_act(XTF_BAD_VALUE,
"ipvs", "--vdir", cb->arg);
}
break;
case O_VMETHOD:
if (strcasecmp(cb->arg, "GATE") == 0)
data->fwd_method = IP_VS_CONN_F_DROUTE;
else if (strcasecmp(cb->arg, "IPIP") == 0)
data->fwd_method = IP_VS_CONN_F_TUNNEL;
else if (strcasecmp(cb->arg, "MASQ") == 0)
data->fwd_method = IP_VS_CONN_F_MASQ;
else
xtables_param_act(XTF_BAD_VALUE,
"ipvs", "--vmethod", cb->arg);
break;
}
data->bitmask |= 1 << cb->entry->id;
if (cb->invert)
data->invert |= 1 << cb->entry->id;
}
static void ipvs_mt_check(struct xt_fcheck_call *cb)
{
struct xt_ipvs_mtinfo *info = cb->data;
if (cb->xflags == 0)
xtables_error(PARAMETER_PROBLEM,
"IPVS: At least one option is required");
if (info->bitmask & XT_IPVS_ONCE_MASK) {
if (info->invert & XT_IPVS_IPVS_PROPERTY)
xtables_error(PARAMETER_PROBLEM,
"! --ipvs cannot be together with"
" other options");
info->bitmask |= XT_IPVS_IPVS_PROPERTY;
}
}
/* Shamelessly copied from libxt_conntrack.c */
static void ipvs_mt_dump_addr(const union nf_inet_addr *addr,
const union nf_inet_addr *mask,
unsigned int family, bool numeric)
{
char buf[BUFSIZ];
if (family == NFPROTO_IPV4) {
if (!numeric && addr->ip == 0) {
printf(" anywhere");
return;
}
if (numeric)
strcpy(buf, xtables_ipaddr_to_numeric(&addr->in));
else
strcpy(buf, xtables_ipaddr_to_anyname(&addr->in));
strcat(buf, xtables_ipmask_to_numeric(&mask->in));
printf(" %s", buf);
} 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)
strcpy(buf, xtables_ip6addr_to_numeric(&addr->in6));
else
strcpy(buf, xtables_ip6addr_to_anyname(&addr->in6));
strcat(buf, xtables_ip6mask_to_numeric(&mask->in6));
printf(" %s", buf);
}
}
static void ipvs_mt_dump(const void *ip, const struct xt_ipvs_mtinfo *data,
unsigned int family, bool numeric, const char *prefix)
{
if (data->bitmask == XT_IPVS_IPVS_PROPERTY) {
if (data->invert & XT_IPVS_IPVS_PROPERTY)
printf(" !");
printf(" %sipvs", prefix);
}
if (data->bitmask & XT_IPVS_PROTO) {
if (data->invert & XT_IPVS_PROTO)
printf(" !");
printf(" %sproto %u", prefix, data->l4proto);
}
if (data->bitmask & XT_IPVS_VADDR) {
if (data->invert & XT_IPVS_VADDR)
printf(" !");
printf(" %svaddr", prefix);
ipvs_mt_dump_addr(&data->vaddr, &data->vmask, family, numeric);
}
if (data->bitmask & XT_IPVS_VPORT) {
if (data->invert & XT_IPVS_VPORT)
printf(" !");
printf(" %svport %u", prefix, ntohs(data->vport));
}
if (data->bitmask & XT_IPVS_DIR) {
if (data->invert & XT_IPVS_DIR)
printf(" %svdir REPLY", prefix);
else
printf(" %svdir ORIGINAL", prefix);
}
if (data->bitmask & XT_IPVS_METHOD) {
if (data->invert & XT_IPVS_METHOD)
printf(" !");
printf(" %svmethod", prefix);
switch (data->fwd_method) {
case IP_VS_CONN_F_DROUTE:
printf(" GATE");
break;
case IP_VS_CONN_F_TUNNEL:
printf(" IPIP");
break;
case IP_VS_CONN_F_MASQ:
printf(" MASQ");
break;
default:
/* Hu? */
printf(" UNKNOWN");
break;
}
}
if (data->bitmask & XT_IPVS_VPORTCTL) {
if (data->invert & XT_IPVS_VPORTCTL)
printf(" !");
printf(" %svportctl %u", prefix, ntohs(data->vportctl));
}
}
static void ipvs_mt4_print(const void *ip, const struct xt_entry_match *match,
int numeric)
{
const struct xt_ipvs_mtinfo *data = (const void *)match->data;
ipvs_mt_dump(ip, data, NFPROTO_IPV4, numeric, "");
}
static void ipvs_mt6_print(const void *ip, const struct xt_entry_match *match,
int numeric)
{
const struct xt_ipvs_mtinfo *data = (const void *)match->data;
ipvs_mt_dump(ip, data, NFPROTO_IPV6, numeric, "");
}
static void ipvs_mt4_save(const void *ip, const struct xt_entry_match *match)
{
const struct xt_ipvs_mtinfo *data = (const void *)match->data;
ipvs_mt_dump(ip, data, NFPROTO_IPV4, true, "--");
}
static void ipvs_mt6_save(const void *ip, const struct xt_entry_match *match)
{
const struct xt_ipvs_mtinfo *data = (const void *)match->data;
ipvs_mt_dump(ip, data, NFPROTO_IPV6, true, "--");
}
static struct xtables_match ipvs_matches_reg[] = {
{
.version = XTABLES_VERSION,
.name = "ipvs",
.revision = 0,
.family = NFPROTO_IPV4,
.size = XT_ALIGN(sizeof(struct xt_ipvs_mtinfo)),
.userspacesize = XT_ALIGN(sizeof(struct xt_ipvs_mtinfo)),
.help = ipvs_mt_help,
.x6_parse = ipvs_mt_parse,
.x6_fcheck = ipvs_mt_check,
.print = ipvs_mt4_print,
.save = ipvs_mt4_save,
.x6_options = ipvs_mt_opts,
},
{
.version = XTABLES_VERSION,
.name = "ipvs",
.revision = 0,
.family = NFPROTO_IPV6,
.size = XT_ALIGN(sizeof(struct xt_ipvs_mtinfo)),
.userspacesize = XT_ALIGN(sizeof(struct xt_ipvs_mtinfo)),
.help = ipvs_mt_help,
.x6_parse = ipvs_mt_parse,
.x6_fcheck = ipvs_mt_check,
.print = ipvs_mt6_print,
.save = ipvs_mt6_save,
.x6_options = ipvs_mt_opts,
},
};
void _init(void)
{
xtables_register_matches(ipvs_matches_reg,
ARRAY_SIZE(ipvs_matches_reg));
}
Match IPVS connection properties.
.TP
[\fB!\fP] \fB\-\-ipvs\fP
packet belongs to an IPVS connection
.TP
Any of the following options implies \-\-ipvs (even negated)
.TP
[\fB!\fP] \fB\-\-vproto\fP \fIprotocol\fP
VIP protocol to match; by number or name, e.g. "tcp"
.TP
[\fB!\fP] \fB\-\-vaddr\fP \fIaddress\fP[\fB/\fP\fImask\fP]
VIP address to match
.TP
[\fB!\fP] \fB\-\-vport\fP \fIport\fP
VIP port to match; by number or name, e.g. "http"
.TP
\fB\-\-vdir\fP {\fBORIGINAL\fP|\fBREPLY\fP}
flow direction of packet
.TP
[\fB!\fP] \fB\-\-vmethod\fP {\fBGATE\fP|\fBIPIP\fP|\fBMASQ\fP}
IPVS forwarding method used
.TP
[\fB!\fP] \fB\-\-vportctl\fP \fIport\fP
VIP port of the controlling connection to match, e.g. 21 for FTP
#include <stdio.h>
#include <xtables.h>
#include <linux/netfilter/xt_length.h>
enum {
O_LENGTH = 0,
};
static void length_help(void)
{
printf(
"length match options:\n"
"[!] --length length[:length] Match packet length against value or range\n"
" of values (inclusive)\n");
}
static const struct xt_option_entry length_opts[] = {
{.name = "length", .id = O_LENGTH, .type = XTTYPE_UINT16RC,
.flags = XTOPT_MAND | XTOPT_INVERT},
XTOPT_TABLEEND,
};
static void length_parse(struct xt_option_call *cb)
{
struct xt_length_info *info = cb->data;
xtables_option_parse(cb);
info->min = cb->val.u16_range[0];
info->max = cb->val.u16_range[0];
if (cb->nvals >= 2)
info->max = cb->val.u16_range[1];
if (cb->invert)
info->invert = 1;
}
static void
length_print(const void *ip, const struct xt_entry_match *match, int numeric)
{
const struct xt_length_info *info = (void *)match->data;
printf(" length %s", info->invert ? "!" : "");
if (info->min == info->max)
printf("%u", info->min);
else
printf("%u:%u", info->min, info->max);
}
static void length_save(const void *ip, const struct xt_entry_match *match)
{
const struct xt_length_info *info = (void *)match->data;
printf("%s --length ", info->invert ? " !" : "");
if (info->min == info->max)
printf("%u", info->min);
else
printf("%u:%u", info->min, info->max);
}
static struct xtables_match length_match = {
.family = NFPROTO_UNSPEC,
.name = "length",
.version = XTABLES_VERSION,
.size = XT_ALIGN(sizeof(struct xt_length_info)),
.userspacesize = XT_ALIGN(sizeof(struct xt_length_info)),
.help = length_help,
.print = length_print,
.save = length_save,
.x6_parse = length_parse,
.x6_options = length_opts,
};
void _init(void)
{
xtables_register_match(&length_match);
}
This module matches the length of the layer-3 payload (e.g. layer-4 packet)
of a packet against a specific value
or range of values.
.TP
[\fB!\fP] \fB\-\-length\fP \fIlength\fP[\fB:\fP\fIlength\fP]
/* Shared library add-on to iptables to add limit support.
*
* Jérôme de Vivie <devivie@info.enserb.u-bordeaux.fr>
* Hervé Eychenne <rv@wallfire.org>
*/
#define _BSD_SOURCE 1
#define _ISOC99_SOURCE 1
#include <math.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <xtables.h>
#include <linux/netfilter/x_tables.h>
#include <linux/netfilter/xt_limit.h>
#define XT_LIMIT_AVG "3/hour"
#define XT_LIMIT_BURST 5
enum {
O_LIMIT = 0,
O_BURST,
};
static void limit_help(void)
{
printf(
"limit match options:\n"
"--limit avg max average match rate: default "XT_LIMIT_AVG"\n"
" [Packets per second unless followed by \n"
" /sec /minute /hour /day postfixes]\n"
"--limit-burst number number to match in a burst, default %u\n",
XT_LIMIT_BURST);
}
static const struct xt_option_entry limit_opts[] = {
{.name = "limit", .id = O_LIMIT, .type = XTTYPE_STRING},
{.name = "limit-burst", .id = O_BURST, .type = XTTYPE_UINT32,
.flags = XTOPT_PUT, XTOPT_POINTER(struct xt_rateinfo, burst),
.min = 0, .max = 10000},
XTOPT_TABLEEND,
};
static
int parse_rate(const char *rate, uint32_t *val)
{
const char *delim;
uint32_t r;
uint32_t mult = 1; /* Seconds by default. */
delim = strchr(rate, '/');
if (delim) {
if (strlen(delim+1) == 0)
return 0;
if (strncasecmp(delim+1, "second", strlen(delim+1)) == 0)
mult = 1;
else if (strncasecmp(delim+1, "minute", strlen(delim+1)) == 0)
mult = 60;
else if (strncasecmp(delim+1, "hour", strlen(delim+1)) == 0)
mult = 60*60;
else if (strncasecmp(delim+1, "day", strlen(delim+1)) == 0)
mult = 24*60*60;
else
return 0;
}
r = atoi(rate);
if (!r)
return 0;
*val = XT_LIMIT_SCALE * mult / r;
if (*val == 0)
/*
* The rate maps to infinity. (1/day is the minimum they can
* specify, so we are ok at that end).
*/
xtables_error(PARAMETER_PROBLEM, "Rate too fast \"%s\"\n", rate);
return 1;
}
static void limit_init(struct xt_entry_match *m)
{
struct xt_rateinfo *r = (struct xt_rateinfo *)m->data;
parse_rate(XT_LIMIT_AVG, &r->avg);
r->burst = XT_LIMIT_BURST;
}
/* FIXME: handle overflow:
if (r->avg*r->burst/r->burst != r->avg)
xtables_error(PARAMETER_PROBLEM,
"Sorry: burst too large for that avg rate.\n");
*/
static void limit_parse(struct xt_option_call *cb)
{
struct xt_rateinfo *r = cb->data;
xtables_option_parse(cb);
switch (cb->entry->id) {
case O_LIMIT:
if (!parse_rate(cb->arg, &r->avg))
xtables_error(PARAMETER_PROBLEM,
"bad rate \"%s\"'", cb->arg);
break;
}
if (cb->invert)
xtables_error(PARAMETER_PROBLEM,
"limit does not support invert");
}
static const struct rates
{
const char *name;
uint32_t mult;
} rates[] = { { "day", XT_LIMIT_SCALE*24*60*60 },
{ "hour", XT_LIMIT_SCALE*60*60 },
{ "min", XT_LIMIT_SCALE*60 },
{ "sec", XT_LIMIT_SCALE } };
static void print_rate(uint32_t period)
{
unsigned int i;
if (period == 0) {
printf(" %f", INFINITY);
return;
}
for (i = 1; i < ARRAY_SIZE(rates); ++i)
if (period > rates[i].mult
|| rates[i].mult/period < rates[i].mult%period)
break;
printf(" %u/%s", rates[i-1].mult / period, rates[i-1].name);
}
static void
limit_print(const void *ip, const struct xt_entry_match *match, int numeric)
{
const struct xt_rateinfo *r = (const void *)match->data;
printf(" limit: avg"); print_rate(r->avg);
printf(" burst %u", r->burst);
}
static void limit_save(const void *ip, const struct xt_entry_match *match)
{
const struct xt_rateinfo *r = (const void *)match->data;
printf(" --limit"); print_rate(r->avg);
if (r->burst != XT_LIMIT_BURST)
printf(" --limit-burst %u", r->burst);
}
static struct xtables_match limit_match = {
.family = NFPROTO_UNSPEC,
.name = "limit",
.version = XTABLES_VERSION,
.size = XT_ALIGN(sizeof(struct xt_rateinfo)),
.userspacesize = offsetof(struct xt_rateinfo, prev),
.help = limit_help,
.init = limit_init,
.x6_parse = limit_parse,
.print = limit_print,
.save = limit_save,
.x6_options = limit_opts,
};
void _init(void)
{
xtables_register_match(&limit_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