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