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