Home | History | Annotate | Download | only in extensions
      1 /* Shared library add-on to iptables to add limit support.
      2  *
      3  * Jrme de Vivie   <devivie (at) info.enserb.u-bordeaux.fr>
      4  * Herv Eychenne    <rv (at) wallfire.org>
      5  */
      6 
      7 #include <stdio.h>
      8 #include <string.h>
      9 #include <stdlib.h>
     10 #include <getopt.h>
     11 #include <ip6tables.h>
     12 #include <stddef.h>
     13 #include <linux/netfilter_ipv6/ip6_tables.h>
     14 /* For 64bit kernel / 32bit userspace */
     15 #include "../include/linux/netfilter_ipv6/ip6t_limit.h"
     16 
     17 #define IP6T_LIMIT_AVG	"3/hour"
     18 #define IP6T_LIMIT_BURST	5
     19 
     20 /* Function which prints out usage message. */
     21 static void
     22 help(void)
     23 {
     24 	printf(
     25 "limit v%s options:\n"
     26 "--limit avg			max average match rate: default "IP6T_LIMIT_AVG"\n"
     27 "                                [Packets per second unless followed by \n"
     28 "                                /sec /minute /hour /day postfixes]\n"
     29 "--limit-burst number		number to match in a burst, default %u\n"
     30 "\n", IPTABLES_VERSION, IP6T_LIMIT_BURST);
     31 }
     32 
     33 static struct option opts[] = {
     34 	{ "limit", 1, 0, '%' },
     35 	{ "limit-burst", 1, 0, '$' },
     36 	{ 0 }
     37 };
     38 
     39 static
     40 int parse_rate(const char *rate, u_int32_t *val)
     41 {
     42 	const char *delim;
     43 	u_int32_t r;
     44 	u_int32_t mult = 1;  /* Seconds by default. */
     45 
     46 	delim = strchr(rate, '/');
     47 	if (delim) {
     48 		if (strlen(delim+1) == 0)
     49 			return 0;
     50 
     51 		if (strncasecmp(delim+1, "second", strlen(delim+1)) == 0)
     52 			mult = 1;
     53 		else if (strncasecmp(delim+1, "minute", strlen(delim+1)) == 0)
     54 			mult = 60;
     55 		else if (strncasecmp(delim+1, "hour", strlen(delim+1)) == 0)
     56 			mult = 60*60;
     57 		else if (strncasecmp(delim+1, "day", strlen(delim+1)) == 0)
     58 			mult = 24*60*60;
     59 		else
     60 			return 0;
     61 	}
     62 	r = atoi(rate);
     63 	if (!r)
     64 		return 0;
     65 
     66 	/* This would get mapped to infinite (1/day is minimum they
     67            can specify, so we're ok at that end). */
     68 	if (r / mult > IP6T_LIMIT_SCALE)
     69 		exit_error(PARAMETER_PROBLEM, "Rate too fast `%s'\n", rate);
     70 
     71 	*val = IP6T_LIMIT_SCALE * mult / r;
     72 	return 1;
     73 }
     74 
     75 /* Initialize the match. */
     76 static void
     77 init(struct ip6t_entry_match *m, unsigned int *nfcache)
     78 {
     79 	struct ip6t_rateinfo *r = (struct ip6t_rateinfo *)m->data;
     80 
     81 	parse_rate(IP6T_LIMIT_AVG, &r->avg);
     82 	r->burst = IP6T_LIMIT_BURST;
     83 
     84 }
     85 
     86 /* FIXME: handle overflow:
     87 	if (r->avg*r->burst/r->burst != r->avg)
     88 		exit_error(PARAMETER_PROBLEM,
     89 			   "Sorry: burst too large for that avg rate.\n");
     90 */
     91 
     92 /* Function which parses command options; returns true if it
     93    ate an option */
     94 static int
     95 parse(int c, char **argv, int invert, unsigned int *flags,
     96       const struct ip6t_entry *entry,
     97       unsigned int *nfcache,
     98       struct ip6t_entry_match **match)
     99 {
    100 	struct ip6t_rateinfo *r = (struct ip6t_rateinfo *)(*match)->data;
    101 	unsigned int num;
    102 
    103 	switch(c) {
    104 	case '%':
    105 		if (check_inverse(argv[optind-1], &invert, &optind, 0)) break;
    106 		if (!parse_rate(optarg, &r->avg))
    107 			exit_error(PARAMETER_PROBLEM,
    108 				   "bad rate `%s'", optarg);
    109 		break;
    110 
    111 	case '$':
    112 		if (check_inverse(argv[optind-1], &invert, &optind, 0)) break;
    113 		if (string_to_number(optarg, 0, 10000, &num) == -1)
    114 			exit_error(PARAMETER_PROBLEM,
    115 				   "bad --limit-burst `%s'", optarg);
    116 		r->burst = num;
    117 		break;
    118 
    119 	default:
    120 		return 0;
    121 	}
    122 
    123 	if (invert)
    124 		exit_error(PARAMETER_PROBLEM,
    125 			   "limit does not support invert");
    126 
    127 	return 1;
    128 }
    129 
    130 /* Final check; nothing. */
    131 static void final_check(unsigned int flags)
    132 {
    133 }
    134 
    135 static struct rates
    136 {
    137 	const char *name;
    138 	u_int32_t mult;
    139 } rates[] = { { "day", IP6T_LIMIT_SCALE*24*60*60 },
    140 	      { "hour", IP6T_LIMIT_SCALE*60*60 },
    141 	      { "min", IP6T_LIMIT_SCALE*60 },
    142 	      { "sec", IP6T_LIMIT_SCALE } };
    143 
    144 static void print_rate(u_int32_t period)
    145 {
    146 	unsigned int i;
    147 
    148 	for (i = 1; i < sizeof(rates)/sizeof(struct rates); i++) {
    149 		if (period > rates[i].mult
    150 		    || rates[i].mult % period != 0)
    151 			break;
    152 	}
    153 
    154 	printf("%u/%s ", rates[i-1].mult / period, rates[i-1].name);
    155 }
    156 
    157 /* Prints out the matchinfo. */
    158 static void
    159 print(const struct ip6t_ip6 *ip,
    160       const struct ip6t_entry_match *match,
    161       int numeric)
    162 {
    163 	struct ip6t_rateinfo *r = (struct ip6t_rateinfo *)match->data;
    164 	printf("limit: avg "); print_rate(r->avg);
    165 	printf("burst %u ", r->burst);
    166 }
    167 
    168 /* FIXME: Make minimalist: only print rate if not default --RR */
    169 static void save(const struct ip6t_ip6 *ip, const struct ip6t_entry_match *match)
    170 {
    171 	struct ip6t_rateinfo *r = (struct ip6t_rateinfo *)match->data;
    172 
    173 	printf("--limit "); print_rate(r->avg);
    174 	if (r->burst != IP6T_LIMIT_BURST)
    175 		printf("--limit-burst %u ", r->burst);
    176 }
    177 
    178 static struct ip6tables_match limit = {
    179 	.name 		= "limit",
    180 	.version	= IPTABLES_VERSION,
    181 	.size		= IP6T_ALIGN(sizeof(struct ip6t_rateinfo)),
    182 	.userspacesize	= offsetof(struct ip6t_rateinfo, prev),
    183 	.help		= &help,
    184 	.init		= &init,
    185 	.parse		= &parse,
    186 	.final_check	= &final_check,
    187 	.print		= &print,
    188 	.save		= &save,
    189 	.extra_opts	= opts,
    190 };
    191 
    192 void _init(void)
    193 {
    194 	register_match6(&limit);
    195 }
    196