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