libxt_recent.c revision 74ded7257e5da5e309844d386290f24ae91950a6
1#include <stdbool.h> 2#include <stdio.h> 3#include <string.h> 4#include <xtables.h> 5#include <linux/netfilter/xt_recent.h> 6 7enum { 8 O_SET = 0, 9 O_RCHECK, 10 O_UPDATE, 11 O_REMOVE, 12 O_SECONDS, 13 O_REAP, 14 O_HITCOUNT, 15 O_RTTL, 16 O_NAME, 17 O_RSOURCE, 18 O_RDEST, 19 O_MASK, 20 F_SET = 1 << O_SET, 21 F_RCHECK = 1 << O_RCHECK, 22 F_UPDATE = 1 << O_UPDATE, 23 F_REMOVE = 1 << O_REMOVE, 24 F_SECONDS = 1 << O_SECONDS, 25 F_ANY_OP = F_SET | F_RCHECK | F_UPDATE | F_REMOVE, 26}; 27 28#define s struct xt_recent_mtinfo 29static const struct xt_option_entry recent_opts_v0[] = { 30 {.name = "set", .id = O_SET, .type = XTTYPE_NONE, 31 .excl = F_ANY_OP, .flags = XTOPT_INVERT}, 32 {.name = "rcheck", .id = O_RCHECK, .type = XTTYPE_NONE, 33 .excl = F_ANY_OP, .flags = XTOPT_INVERT}, 34 {.name = "update", .id = O_UPDATE, .type = XTTYPE_NONE, 35 .excl = F_ANY_OP, .flags = XTOPT_INVERT}, 36 {.name = "remove", .id = O_REMOVE, .type = XTTYPE_NONE, 37 .excl = F_ANY_OP, .flags = XTOPT_INVERT}, 38 {.name = "seconds", .id = O_SECONDS, .type = XTTYPE_UINT32, 39 .flags = XTOPT_PUT, XTOPT_POINTER(s, seconds), .min = 1}, 40 {.name = "reap", .id = O_REAP, .type = XTTYPE_NONE, 41 .also = F_SECONDS }, 42 {.name = "hitcount", .id = O_HITCOUNT, .type = XTTYPE_UINT32, 43 .flags = XTOPT_PUT, XTOPT_POINTER(s, hit_count)}, 44 {.name = "rttl", .id = O_RTTL, .type = XTTYPE_NONE, 45 .excl = F_SET | F_REMOVE}, 46 {.name = "name", .id = O_NAME, .type = XTTYPE_STRING, 47 .flags = XTOPT_PUT, XTOPT_POINTER(s, name)}, 48 {.name = "rsource", .id = O_RSOURCE, .type = XTTYPE_NONE}, 49 {.name = "rdest", .id = O_RDEST, .type = XTTYPE_NONE}, 50 XTOPT_TABLEEND, 51}; 52#undef s 53 54#define s struct xt_recent_mtinfo_v1 55static const struct xt_option_entry recent_opts_v1[] = { 56 {.name = "set", .id = O_SET, .type = XTTYPE_NONE, 57 .excl = F_ANY_OP, .flags = XTOPT_INVERT}, 58 {.name = "rcheck", .id = O_RCHECK, .type = XTTYPE_NONE, 59 .excl = F_ANY_OP, .flags = XTOPT_INVERT}, 60 {.name = "update", .id = O_UPDATE, .type = XTTYPE_NONE, 61 .excl = F_ANY_OP, .flags = XTOPT_INVERT}, 62 {.name = "remove", .id = O_REMOVE, .type = XTTYPE_NONE, 63 .excl = F_ANY_OP, .flags = XTOPT_INVERT}, 64 {.name = "seconds", .id = O_SECONDS, .type = XTTYPE_UINT32, 65 .flags = XTOPT_PUT, XTOPT_POINTER(s, seconds)}, 66 {.name = "hitcount", .id = O_HITCOUNT, .type = XTTYPE_UINT32, 67 .flags = XTOPT_PUT, XTOPT_POINTER(s, hit_count)}, 68 {.name = "rttl", .id = O_RTTL, .type = XTTYPE_NONE, 69 .excl = F_SET | F_REMOVE}, 70 {.name = "name", .id = O_NAME, .type = XTTYPE_STRING, 71 .flags = XTOPT_PUT, XTOPT_POINTER(s, name)}, 72 {.name = "rsource", .id = O_RSOURCE, .type = XTTYPE_NONE}, 73 {.name = "rdest", .id = O_RDEST, .type = XTTYPE_NONE}, 74 {.name = "mask", .id = O_MASK, .type = XTTYPE_HOST, 75 .flags = XTOPT_PUT, XTOPT_POINTER(s, mask)}, 76 XTOPT_TABLEEND, 77}; 78#undef s 79 80static void recent_help(void) 81{ 82 printf( 83"recent match options:\n" 84"[!] --set Add source address to list, always matches.\n" 85"[!] --rcheck Match if source address in list.\n" 86"[!] --update Match if source address in list, also update last-seen time.\n" 87"[!] --remove Match if source address in list, also removes that address from list.\n" 88" --seconds seconds For check and update commands above.\n" 89" Specifies that the match will only occur if source address last seen within\n" 90" the last 'seconds' seconds.\n" 91" --reap Purge entries older then 'seconds'.\n" 92" Can only be used in conjunction with the seconds option.\n" 93" --hitcount hits For check and update commands above.\n" 94" Specifies that the match will only occur if source address seen hits times.\n" 95" May be used in conjunction with the seconds option.\n" 96" --rttl For check and update commands above.\n" 97" Specifies that the match will only occur if the source address and the TTL\n" 98" match between this packet and the one which was set.\n" 99" Useful if you have problems with people spoofing their source address in order\n" 100" to DoS you via this module.\n" 101" --name name Name of the recent list to be used. DEFAULT used if none given.\n" 102" --rsource Match/Save the source address of each packet in the recent list table (default).\n" 103" --rdest Match/Save the destination address of each packet in the recent list table.\n" 104" --mask netmask Netmask that will be applied to this recent list.\n" 105"xt_recent by: Stephen Frost <sfrost@snowman.net>. http://snowman.net/projects/ipt_recent/\n"); 106} 107 108enum { 109 XT_RECENT_REV_0 = 0, 110 XT_RECENT_REV_1, 111}; 112 113static void recent_init(struct xt_entry_match *match, unsigned int rev) 114{ 115 struct xt_recent_mtinfo *info = (struct xt_recent_mtinfo *)match->data; 116 struct xt_recent_mtinfo_v1 *info_v1 = 117 (struct xt_recent_mtinfo_v1 *)match->data; 118 119 strncpy(info->name,"DEFAULT", XT_RECENT_NAME_LEN); 120 /* even though XT_RECENT_NAME_LEN is currently defined as 200, 121 * better be safe, than sorry */ 122 info->name[XT_RECENT_NAME_LEN-1] = '\0'; 123 info->side = XT_RECENT_SOURCE; 124 if (rev == XT_RECENT_REV_1) 125 memset(&info_v1->mask, 0xFF, sizeof(info_v1->mask)); 126} 127 128static void recent_parse(struct xt_option_call *cb) 129{ 130 struct xt_recent_mtinfo *info = cb->data; 131 132 xtables_option_parse(cb); 133 switch (cb->entry->id) { 134 case O_SET: 135 info->check_set |= XT_RECENT_SET; 136 if (cb->invert) 137 info->invert = true; 138 break; 139 case O_RCHECK: 140 info->check_set |= XT_RECENT_CHECK; 141 if (cb->invert) 142 info->invert = true; 143 break; 144 case O_UPDATE: 145 info->check_set |= XT_RECENT_UPDATE; 146 if (cb->invert) 147 info->invert = true; 148 break; 149 case O_REMOVE: 150 info->check_set |= XT_RECENT_REMOVE; 151 if (cb->invert) 152 info->invert = true; 153 break; 154 case O_RTTL: 155 info->check_set |= XT_RECENT_TTL; 156 break; 157 case O_RSOURCE: 158 info->side = XT_RECENT_SOURCE; 159 break; 160 case O_RDEST: 161 info->side = XT_RECENT_DEST; 162 break; 163 case O_REAP: 164 info->check_set |= XT_RECENT_REAP; 165 break; 166 } 167} 168 169static void recent_check(struct xt_fcheck_call *cb) 170{ 171 if (!(cb->xflags & F_ANY_OP)) 172 xtables_error(PARAMETER_PROBLEM, 173 "recent: you must specify one of `--set', `--rcheck' " 174 "`--update' or `--remove'"); 175} 176 177static void recent_print(const void *ip, const struct xt_entry_match *match, 178 unsigned int family) 179{ 180 const struct xt_recent_mtinfo_v1 *info = (const void *)match->data; 181 182 if (info->invert) 183 printf(" !"); 184 185 printf(" recent:"); 186 if (info->check_set & XT_RECENT_SET) 187 printf(" SET"); 188 if (info->check_set & XT_RECENT_CHECK) 189 printf(" CHECK"); 190 if (info->check_set & XT_RECENT_UPDATE) 191 printf(" UPDATE"); 192 if (info->check_set & XT_RECENT_REMOVE) 193 printf(" REMOVE"); 194 if(info->seconds) printf(" seconds: %d", info->seconds); 195 if (info->check_set & XT_RECENT_REAP) 196 printf(" reap"); 197 if(info->hit_count) printf(" hit_count: %d", info->hit_count); 198 if (info->check_set & XT_RECENT_TTL) 199 printf(" TTL-Match"); 200 if(info->name) printf(" name: %s", info->name); 201 if (info->side == XT_RECENT_SOURCE) 202 printf(" side: source"); 203 if (info->side == XT_RECENT_DEST) 204 printf(" side: dest"); 205 206 switch(family) { 207 case NFPROTO_IPV4: 208 printf(" mask: %s", 209 xtables_ipaddr_to_numeric(&info->mask.in)); 210 break; 211 case NFPROTO_IPV6: 212 printf(" mask: %s", 213 xtables_ip6addr_to_numeric(&info->mask.in6)); 214 break; 215 } 216} 217 218static void recent_save(const void *ip, const struct xt_entry_match *match, 219 unsigned int family) 220{ 221 const struct xt_recent_mtinfo_v1 *info = (const void *)match->data; 222 223 if (info->invert) 224 printf(" !"); 225 226 if (info->check_set & XT_RECENT_SET) 227 printf(" --set"); 228 if (info->check_set & XT_RECENT_CHECK) 229 printf(" --rcheck"); 230 if (info->check_set & XT_RECENT_UPDATE) 231 printf(" --update"); 232 if (info->check_set & XT_RECENT_REMOVE) 233 printf(" --remove"); 234 if(info->seconds) printf(" --seconds %d", info->seconds); 235 if (info->check_set & XT_RECENT_REAP) 236 printf(" --reap"); 237 if(info->hit_count) printf(" --hitcount %d", info->hit_count); 238 if (info->check_set & XT_RECENT_TTL) 239 printf(" --rttl"); 240 if(info->name) printf(" --name %s",info->name); 241 242 switch(family) { 243 case NFPROTO_IPV4: 244 printf(" --mask %s", 245 xtables_ipaddr_to_numeric(&info->mask.in)); 246 break; 247 case NFPROTO_IPV6: 248 printf(" --mask %s", 249 xtables_ip6addr_to_numeric(&info->mask.in6)); 250 break; 251 } 252 253 if (info->side == XT_RECENT_SOURCE) 254 printf(" --rsource"); 255 if (info->side == XT_RECENT_DEST) 256 printf(" --rdest"); 257} 258 259static void recent_init_v0(struct xt_entry_match *match) 260{ 261 recent_init(match, XT_RECENT_REV_0); 262} 263 264static void recent_init_v1(struct xt_entry_match *match) 265{ 266 recent_init(match, XT_RECENT_REV_1); 267} 268 269static void recent_save_v0(const void *ip, const struct xt_entry_match *match) 270{ 271 recent_save(ip, match, NFPROTO_UNSPEC); 272} 273 274static void recent_save_v4(const void *ip, const struct xt_entry_match *match) 275{ 276 recent_save(ip, match, NFPROTO_IPV4); 277} 278 279static void recent_save_v6(const void *ip, const struct xt_entry_match *match) 280{ 281 recent_save(ip, match, NFPROTO_IPV6); 282} 283 284static void recent_print_v0(const void *ip, const struct xt_entry_match *match, 285 int numeric) 286{ 287 recent_print(ip, match, NFPROTO_UNSPEC); 288} 289 290static void recent_print_v4(const void *ip, const struct xt_entry_match *match, 291 int numeric) 292{ 293 recent_print(ip, match, NFPROTO_IPV4); 294} 295 296static void recent_print_v6(const void *ip, const struct xt_entry_match *match, 297 int numeric) 298{ 299 recent_print(ip, match, NFPROTO_IPV6); 300} 301 302static struct xtables_match recent_mt_reg[] = { 303 { 304 .name = "recent", 305 .version = XTABLES_VERSION, 306 .revision = 0, 307 .family = NFPROTO_UNSPEC, 308 .size = XT_ALIGN(sizeof(struct xt_recent_mtinfo)), 309 .userspacesize = XT_ALIGN(sizeof(struct xt_recent_mtinfo)), 310 .help = recent_help, 311 .init = recent_init_v0, 312 .x6_parse = recent_parse, 313 .x6_fcheck = recent_check, 314 .print = recent_print_v0, 315 .save = recent_save_v0, 316 .x6_options = recent_opts_v0, 317 }, 318 { 319 .name = "recent", 320 .version = XTABLES_VERSION, 321 .revision = 1, 322 .family = NFPROTO_IPV4, 323 .size = XT_ALIGN(sizeof(struct xt_recent_mtinfo_v1)), 324 .userspacesize = XT_ALIGN(sizeof(struct xt_recent_mtinfo_v1)), 325 .help = recent_help, 326 .init = recent_init_v1, 327 .x6_parse = recent_parse, 328 .x6_fcheck = recent_check, 329 .print = recent_print_v4, 330 .save = recent_save_v4, 331 .x6_options = recent_opts_v1, 332 }, 333 { 334 .name = "recent", 335 .version = XTABLES_VERSION, 336 .revision = 1, 337 .family = NFPROTO_IPV6, 338 .size = XT_ALIGN(sizeof(struct xt_recent_mtinfo_v1)), 339 .userspacesize = XT_ALIGN(sizeof(struct xt_recent_mtinfo_v1)), 340 .help = recent_help, 341 .init = recent_init_v1, 342 .x6_parse = recent_parse, 343 .x6_fcheck = recent_check, 344 .print = recent_print_v6, 345 .save = recent_save_v6, 346 .x6_options = recent_opts_v1, 347 }, 348}; 349 350void _init(void) 351{ 352 xtables_register_matches(recent_mt_reg, ARRAY_SIZE(recent_mt_reg)); 353} 354