1 #include <stdbool.h> 2 #include <stdio.h> 3 #include <string.h> 4 #include <xtables.h> 5 #include <linux/netfilter/xt_recent.h> 6 7 enum { 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 29 static 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 55 static 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 82 static 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 (at) snowman.net>. http://snowman.net/projects/ipt_recent/\n"); 108 } 109 110 enum { 111 XT_RECENT_REV_0 = 0, 112 XT_RECENT_REV_1, 113 }; 114 115 static 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 130 static 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 171 static 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 179 static 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 220 static 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 261 static void recent_init_v0(struct xt_entry_match *match) 262 { 263 recent_init(match, XT_RECENT_REV_0); 264 } 265 266 static void recent_init_v1(struct xt_entry_match *match) 267 { 268 recent_init(match, XT_RECENT_REV_1); 269 } 270 271 static void recent_save_v0(const void *ip, const struct xt_entry_match *match) 272 { 273 recent_save(ip, match, NFPROTO_UNSPEC); 274 } 275 276 static void recent_save_v4(const void *ip, const struct xt_entry_match *match) 277 { 278 recent_save(ip, match, NFPROTO_IPV4); 279 } 280 281 static void recent_save_v6(const void *ip, const struct xt_entry_match *match) 282 { 283 recent_save(ip, match, NFPROTO_IPV6); 284 } 285 286 static 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 292 static 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 298 static 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 304 static 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 352 void _init(void) 353 { 354 xtables_register_matches(recent_mt_reg, ARRAY_SIZE(recent_mt_reg)); 355 } 356