blob: 6652849abc27e44110a533557639d0f64fedabf1 [file] [log] [blame]
Marc Bouchere6869a82000-03-20 06:03:29 +00001/* Shared library add-on to iptables to add limit support.
2 *
Jan Engelhardt81bd5882008-09-04 17:49:18 +02003 * Jérôme de Vivie <devivie@info.enserb.u-bordeaux.fr>
4 * Hervé Eychenne <rv@wallfire.org>
Marc Bouchere6869a82000-03-20 06:03:29 +00005 */
Jan Engelhardt9921f2b2012-10-10 00:35:14 +00006#define _BSD_SOURCE 1
7#define _ISOC99_SOURCE 1
Jan Engelhardt9d69da42012-07-28 19:10:08 +02008#include <math.h>
Marc Bouchere6869a82000-03-20 06:03:29 +00009#include <stdio.h>
10#include <string.h>
11#include <stdlib.h>
Yasuyuki KOZAKAI4489c0d2007-07-24 07:11:26 +000012#include <xtables.h>
Yasuyuki KOZAKAI4489c0d2007-07-24 07:11:26 +000013#include <linux/netfilter/x_tables.h>
Jan Engelhardta2a7f2b2008-09-01 14:20:13 +020014#include <linux/netfilter/xt_limit.h>
Marc Bouchere6869a82000-03-20 06:03:29 +000015
Yasuyuki KOZAKAI4489c0d2007-07-24 07:11:26 +000016#define XT_LIMIT_AVG "3/hour"
17#define XT_LIMIT_BURST 5
Marc Bouchere6869a82000-03-20 06:03:29 +000018
Jan Engelhardt0f77e2e2011-05-07 03:26:08 +020019enum {
20 O_LIMIT = 0,
21 O_BURST,
22};
23
Jan Engelhardt181dead2007-10-04 16:27:07 +000024static void limit_help(void)
Marc Bouchere6869a82000-03-20 06:03:29 +000025{
26 printf(
Jan Engelhardt8b7c64d2008-04-15 11:48:25 +020027"limit match options:\n"
Yasuyuki KOZAKAI4489c0d2007-07-24 07:11:26 +000028"--limit avg max average match rate: default "XT_LIMIT_AVG"\n"
Marc Bouchere6869a82000-03-20 06:03:29 +000029" [Packets per second unless followed by \n"
30" /sec /minute /hour /day postfixes]\n"
Jan Engelhardt8b7c64d2008-04-15 11:48:25 +020031"--limit-burst number number to match in a burst, default %u\n",
32XT_LIMIT_BURST);
Marc Bouchere6869a82000-03-20 06:03:29 +000033}
34
Jan Engelhardt0f77e2e2011-05-07 03:26:08 +020035static const struct xt_option_entry limit_opts[] = {
36 {.name = "limit", .id = O_LIMIT, .type = XTTYPE_STRING},
37 {.name = "limit-burst", .id = O_BURST, .type = XTTYPE_UINT32,
38 .flags = XTOPT_PUT, XTOPT_POINTER(struct xt_rateinfo, burst),
39 .min = 0, .max = 10000},
40 XTOPT_TABLEEND,
Marc Bouchere6869a82000-03-20 06:03:29 +000041};
42
43static
Jan Engelhardt7ac40522011-01-07 12:34:04 +010044int parse_rate(const char *rate, uint32_t *val)
Marc Bouchere6869a82000-03-20 06:03:29 +000045{
46 const char *delim;
Jan Engelhardt7ac40522011-01-07 12:34:04 +010047 uint32_t r;
48 uint32_t mult = 1; /* Seconds by default. */
Marc Bouchere6869a82000-03-20 06:03:29 +000049
50 delim = strchr(rate, '/');
51 if (delim) {
52 if (strlen(delim+1) == 0)
53 return 0;
54
55 if (strncasecmp(delim+1, "second", strlen(delim+1)) == 0)
56 mult = 1;
57 else if (strncasecmp(delim+1, "minute", strlen(delim+1)) == 0)
58 mult = 60;
59 else if (strncasecmp(delim+1, "hour", strlen(delim+1)) == 0)
60 mult = 60*60;
61 else if (strncasecmp(delim+1, "day", strlen(delim+1)) == 0)
62 mult = 24*60*60;
63 else
64 return 0;
65 }
66 r = atoi(rate);
67 if (!r)
68 return 0;
69
Yasuyuki KOZAKAI4489c0d2007-07-24 07:11:26 +000070 *val = XT_LIMIT_SCALE * mult / r;
Jan Engelhardt9d69da42012-07-28 19:10:08 +020071 if (*val == 0)
72 /*
73 * The rate maps to infinity. (1/day is the minimum they can
74 * specify, so we are ok at that end).
75 */
76 xtables_error(PARAMETER_PROBLEM, "Rate too fast \"%s\"\n", rate);
Marc Bouchere6869a82000-03-20 06:03:29 +000077 return 1;
78}
79
Jan Engelhardt181dead2007-10-04 16:27:07 +000080static void limit_init(struct xt_entry_match *m)
Marc Bouchere6869a82000-03-20 06:03:29 +000081{
Yasuyuki KOZAKAI4489c0d2007-07-24 07:11:26 +000082 struct xt_rateinfo *r = (struct xt_rateinfo *)m->data;
Marc Bouchere6869a82000-03-20 06:03:29 +000083
Yasuyuki KOZAKAI4489c0d2007-07-24 07:11:26 +000084 parse_rate(XT_LIMIT_AVG, &r->avg);
85 r->burst = XT_LIMIT_BURST;
Marc Bouchere6869a82000-03-20 06:03:29 +000086
Marc Bouchere6869a82000-03-20 06:03:29 +000087}
88
89/* FIXME: handle overflow:
90 if (r->avg*r->burst/r->burst != r->avg)
Jan Engelhardt1829ed42009-02-21 03:29:44 +010091 xtables_error(PARAMETER_PROBLEM,
Marc Bouchere6869a82000-03-20 06:03:29 +000092 "Sorry: burst too large for that avg rate.\n");
93*/
94
Jan Engelhardt0f77e2e2011-05-07 03:26:08 +020095static void limit_parse(struct xt_option_call *cb)
Marc Bouchere6869a82000-03-20 06:03:29 +000096{
Jan Engelhardt0f77e2e2011-05-07 03:26:08 +020097 struct xt_rateinfo *r = cb->data;
Marc Bouchere6869a82000-03-20 06:03:29 +000098
Jan Engelhardt0f77e2e2011-05-07 03:26:08 +020099 xtables_option_parse(cb);
100 switch (cb->entry->id) {
101 case O_LIMIT:
102 if (!parse_rate(cb->arg, &r->avg))
Jan Engelhardt1829ed42009-02-21 03:29:44 +0100103 xtables_error(PARAMETER_PROBLEM,
Jan Engelhardt0f77e2e2011-05-07 03:26:08 +0200104 "bad rate \"%s\"'", cb->arg);
Marc Bouchere6869a82000-03-20 06:03:29 +0000105 break;
Marc Bouchere6869a82000-03-20 06:03:29 +0000106 }
Jan Engelhardt0f77e2e2011-05-07 03:26:08 +0200107 if (cb->invert)
Jan Engelhardt1829ed42009-02-21 03:29:44 +0100108 xtables_error(PARAMETER_PROBLEM,
Phil Oester35160ee2004-09-21 10:43:45 +0000109 "limit does not support invert");
Marc Bouchere6869a82000-03-20 06:03:29 +0000110}
111
Jan Engelhardt0e2abed2007-10-04 16:25:58 +0000112static const struct rates
Marc Bouchere6869a82000-03-20 06:03:29 +0000113{
114 const char *name;
Jan Engelhardt7ac40522011-01-07 12:34:04 +0100115 uint32_t mult;
Yasuyuki KOZAKAI4489c0d2007-07-24 07:11:26 +0000116} rates[] = { { "day", XT_LIMIT_SCALE*24*60*60 },
117 { "hour", XT_LIMIT_SCALE*60*60 },
118 { "min", XT_LIMIT_SCALE*60 },
119 { "sec", XT_LIMIT_SCALE } };
Marc Bouchere6869a82000-03-20 06:03:29 +0000120
Jan Engelhardt7ac40522011-01-07 12:34:04 +0100121static void print_rate(uint32_t period)
Marc Bouchere6869a82000-03-20 06:03:29 +0000122{
123 unsigned int i;
124
Jan Engelhardt9d69da42012-07-28 19:10:08 +0200125 if (period == 0) {
126 printf(" %f", INFINITY);
127 return;
128 }
129
Jan Engelhardt2c69b552009-04-30 19:32:02 +0200130 for (i = 1; i < ARRAY_SIZE(rates); ++i)
Marc Bouchere6869a82000-03-20 06:03:29 +0000131 if (period > rates[i].mult
Harald Welte1412e452001-10-16 08:26:37 +0000132 || rates[i].mult/period < rates[i].mult%period)
Marc Bouchere6869a82000-03-20 06:03:29 +0000133 break;
Marc Bouchere6869a82000-03-20 06:03:29 +0000134
Jan Engelhardt73866352010-12-18 02:04:59 +0100135 printf(" %u/%s", rates[i-1].mult / period, rates[i-1].name);
Marc Bouchere6869a82000-03-20 06:03:29 +0000136}
137
Marc Bouchere6869a82000-03-20 06:03:29 +0000138static void
Jan Engelhardt181dead2007-10-04 16:27:07 +0000139limit_print(const void *ip, const struct xt_entry_match *match, int numeric)
Marc Bouchere6869a82000-03-20 06:03:29 +0000140{
Jan Engelhardt69f564e2009-05-26 13:14:06 +0200141 const struct xt_rateinfo *r = (const void *)match->data;
Jan Engelhardt73866352010-12-18 02:04:59 +0100142 printf(" limit: avg"); print_rate(r->avg);
143 printf(" burst %u", r->burst);
Marc Bouchere6869a82000-03-20 06:03:29 +0000144}
145
Jan Engelhardt181dead2007-10-04 16:27:07 +0000146static void limit_save(const void *ip, const struct xt_entry_match *match)
Marc Bouchere6869a82000-03-20 06:03:29 +0000147{
Jan Engelhardt69f564e2009-05-26 13:14:06 +0200148 const struct xt_rateinfo *r = (const void *)match->data;
Marc Bouchere6869a82000-03-20 06:03:29 +0000149
Jan Engelhardt73866352010-12-18 02:04:59 +0100150 printf(" --limit"); print_rate(r->avg);
Yasuyuki KOZAKAI4489c0d2007-07-24 07:11:26 +0000151 if (r->burst != XT_LIMIT_BURST)
Jan Engelhardt73866352010-12-18 02:04:59 +0100152 printf(" --limit-burst %u", r->burst);
Marc Bouchere6869a82000-03-20 06:03:29 +0000153}
154
Shivani Bhardwaja8dfbe32015-12-23 03:25:21 +0530155static const struct rates rates_xlate[] = {
156 { "day", XT_LIMIT_SCALE * 24 * 60 * 60 },
157 { "hour", XT_LIMIT_SCALE * 60 * 60 },
158 { "minute", XT_LIMIT_SCALE * 60 },
159 { "second", XT_LIMIT_SCALE }
160};
161
Pablo Neira Ayuso6b60dc52016-02-01 19:24:38 +0100162static void print_rate_xlate(uint32_t period, struct xt_xlate *xl)
Shivani Bhardwaja8dfbe32015-12-23 03:25:21 +0530163{
164 unsigned int i;
165
166 if (period == 0) {
Pablo Neira Ayuso6b60dc52016-02-01 19:24:38 +0100167 xt_xlate_add(xl, " %f ", INFINITY);
Shivani Bhardwaja8dfbe32015-12-23 03:25:21 +0530168 return;
169 }
170
171 for (i = 1; i < ARRAY_SIZE(rates); ++i)
172 if (period > rates_xlate[i].mult ||
173 rates_xlate[i].mult / period < rates_xlate[i].mult % period)
174 break;
175
Pablo Neira Ayuso6b60dc52016-02-01 19:24:38 +0100176 xt_xlate_add(xl, " %u/%s ", rates_xlate[i - 1].mult / period,
Shivani Bhardwaja8dfbe32015-12-23 03:25:21 +0530177 rates_xlate[i - 1].name);
178}
179
Pablo Neira Ayuso9e14d432016-03-09 18:18:11 +0100180static int limit_xlate(const void *ip, const struct xt_entry_match *match,
Pablo Neira Ayuso6b60dc52016-02-01 19:24:38 +0100181 struct xt_xlate *xl, int numeric)
Shivani Bhardwaja8dfbe32015-12-23 03:25:21 +0530182{
183 const struct xt_rateinfo *r = (const void *)match->data;
184
Pablo Neira Ayuso6b60dc52016-02-01 19:24:38 +0100185 xt_xlate_add(xl, "limit rate");
186 print_rate_xlate(r->avg, xl);
Liping Zhang99164702016-05-21 18:07:16 +0800187 if (r->burst != 0)
Pablo Neira Ayuso6b60dc52016-02-01 19:24:38 +0100188 xt_xlate_add(xl, "burst %u packets ", r->burst);
Shivani Bhardwaja8dfbe32015-12-23 03:25:21 +0530189
190 return 1;
191}
192
Jan Engelhardt181dead2007-10-04 16:27:07 +0000193static struct xtables_match limit_match = {
Jan Engelhardt42979362009-06-01 11:56:23 +0200194 .family = NFPROTO_UNSPEC,
Yasuyuki KOZAKAI4489c0d2007-07-24 07:11:26 +0000195 .name = "limit",
Jan Engelhardt8b7c64d2008-04-15 11:48:25 +0200196 .version = XTABLES_VERSION,
Yasuyuki KOZAKAI4489c0d2007-07-24 07:11:26 +0000197 .size = XT_ALIGN(sizeof(struct xt_rateinfo)),
198 .userspacesize = offsetof(struct xt_rateinfo, prev),
Jan Engelhardt181dead2007-10-04 16:27:07 +0000199 .help = limit_help,
200 .init = limit_init,
Jan Engelhardt0f77e2e2011-05-07 03:26:08 +0200201 .x6_parse = limit_parse,
Jan Engelhardt181dead2007-10-04 16:27:07 +0000202 .print = limit_print,
203 .save = limit_save,
Jan Engelhardt0f77e2e2011-05-07 03:26:08 +0200204 .x6_options = limit_opts,
Shivani Bhardwaja8dfbe32015-12-23 03:25:21 +0530205 .xlate = limit_xlate,
Marc Bouchere6869a82000-03-20 06:03:29 +0000206};
207
208void _init(void)
209{
Jan Engelhardt181dead2007-10-04 16:27:07 +0000210 xtables_register_match(&limit_match);
Marc Bouchere6869a82000-03-20 06:03:29 +0000211}