libxt_connlimit.c revision 73866357e4a7a0fdc1b293bf8863fee2bd56da9e
1/* Shared library add-on to iptables to add connection limit support. */ 2#include <stdbool.h> 3#include <stdio.h> 4#include <netdb.h> 5#include <string.h> 6#include <stdlib.h> 7#include <stddef.h> 8#include <getopt.h> 9#include <xtables.h> 10#include <linux/netfilter/xt_connlimit.h> 11 12enum { 13 FL_LIMIT = 1 << 0, 14 FL_MASK = 1 << 1, 15 FL_ADDR = 1 << 2, 16}; 17 18static void connlimit_help(void) 19{ 20 printf( 21"connlimit match options:\n" 22" --connlimit-upto n match if the number of existing connections is 0..n\n" 23" --connlimit-above n match if the number of existing connections is >n\n" 24" --connlimit-mask n group hosts using prefix length (default: max len)\n" 25" --connlimit-saddr select source address for grouping\n" 26" --connlimit-daddr select destination addresses for grouping\n"); 27} 28 29static const struct option connlimit_opts[] = { 30 {.name = "connlimit-upto", .has_arg = true, .val = 'U'}, 31 {.name = "connlimit-above", .has_arg = true, .val = 'A'}, 32 {.name = "connlimit-mask", .has_arg = true, .val = 'M'}, 33 {.name = "connlimit-saddr", .has_arg = false, .val = 's'}, 34 {.name = "connlimit-daddr", .has_arg = false, .val = 'd'}, 35 XT_GETOPT_TABLEEND, 36}; 37 38static void connlimit_init(struct xt_entry_match *match) 39{ 40 struct xt_connlimit_info *info = (void *)match->data; 41 42 /* This will also initialize the v4 mask correctly */ 43 memset(info->v6_mask, 0xFF, sizeof(info->v6_mask)); 44} 45 46static void prefix_to_netmask(uint32_t *mask, unsigned int prefix_len) 47{ 48 if (prefix_len == 0) { 49 mask[0] = mask[1] = mask[2] = mask[3] = 0; 50 } else if (prefix_len <= 32) { 51 mask[0] <<= 32 - prefix_len; 52 mask[1] = mask[2] = mask[3] = 0; 53 } else if (prefix_len <= 64) { 54 mask[1] <<= 32 - (prefix_len - 32); 55 mask[2] = mask[3] = 0; 56 } else if (prefix_len <= 96) { 57 mask[2] <<= 32 - (prefix_len - 64); 58 mask[3] = 0; 59 } else if (prefix_len <= 128) { 60 mask[3] <<= 32 - (prefix_len - 96); 61 } 62 mask[0] = htonl(mask[0]); 63 mask[1] = htonl(mask[1]); 64 mask[2] = htonl(mask[2]); 65 mask[3] = htonl(mask[3]); 66} 67 68static int 69connlimit_parse(int c, char **argv, int invert, unsigned int *flags, 70 struct xt_entry_match **match, unsigned int family) 71{ 72 struct xt_connlimit_info *info = (void *)(*match)->data; 73 const unsigned int revision = (*match)->u.user.revision; 74 char *err; 75 int i; 76 77 switch (c) { 78 case 'A': /* --connlimit-above */ 79 xtables_param_act(XTF_ONLY_ONCE, "connlimit", 80 "--connlimit-{upto,above}", *flags & FL_LIMIT); 81 *flags |= FL_LIMIT; 82 if (invert) 83 info->flags |= XT_CONNLIMIT_INVERT; 84 info->limit = strtoul(optarg, NULL, 0); 85 return true; 86 case 'U': /* --connlimit-upto */ 87 xtables_param_act(XTF_ONLY_ONCE, "connlimit", 88 "--connlimit-{upto,above}", *flags & FL_LIMIT); 89 *flags |= FL_LIMIT; 90 if (!invert) 91 info->flags |= XT_CONNLIMIT_INVERT; 92 info->limit = strtoul(optarg, NULL, 0); 93 return true; 94 case 'M': /* --connlimit-mask */ 95 xtables_param_act(XTF_NO_INVERT, "connlimit", 96 "--connlimit-mask", invert); 97 xtables_param_act(XTF_ONLY_ONCE, "connlimit", 98 "--connlimit-mask", *flags & FL_MASK); 99 *flags |= FL_MASK; 100 i = strtoul(optarg, &err, 0); 101 if (family == NFPROTO_IPV6) { 102 if (i > 128 || *err != '\0') 103 xtables_error(PARAMETER_PROBLEM, 104 "--connlimit-mask must be between " 105 "0 and 128"); 106 prefix_to_netmask(info->v6_mask, i); 107 } else { 108 if (i > 32 || *err != '\0') 109 xtables_error(PARAMETER_PROBLEM, 110 "--connlimit-mask must be between " 111 "0 and 32"); 112 if (i == 0) 113 info->v4_mask = 0; 114 else 115 info->v4_mask = htonl(0xFFFFFFFF << (32 - i)); 116 } 117 return true; 118 case 's': /* --connlimit-saddr */ 119 info->flags &= ~XT_CONNLIMIT_DADDR; 120 return true; 121 case 'd': /* --connlimit-daddr */ 122 if (revision < 1) 123 xtables_error(PARAMETER_PROBLEM, 124 "xt_connlimit.0 does not support " 125 "--connlimit-daddr"); 126 info->flags |= XT_CONNLIMIT_DADDR; 127 return true; 128 } 129 return false; 130} 131 132static int connlimit_parse4(int c, char **argv, int invert, 133 unsigned int *flags, const void *entry, 134 struct xt_entry_match **match) 135{ 136 return connlimit_parse(c, argv, invert, flags, match, NFPROTO_IPV4); 137} 138 139static int connlimit_parse6(int c, char **argv, int invert, 140 unsigned int *flags, const void *entry, 141 struct xt_entry_match **match) 142{ 143 return connlimit_parse(c, argv, invert, flags, match, NFPROTO_IPV6); 144} 145 146static void connlimit_check(unsigned int flags) 147{ 148 if (!(flags & 0x1)) 149 xtables_error(PARAMETER_PROBLEM, 150 "You must specify \"--connlimit-above\""); 151} 152 153static unsigned int count_bits4(uint32_t mask) 154{ 155 unsigned int bits = 0; 156 157 for (mask = ~ntohl(mask); mask != 0; mask >>= 1) 158 ++bits; 159 160 return 32 - bits; 161} 162 163static unsigned int count_bits6(const uint32_t *mask) 164{ 165 unsigned int bits = 0, i; 166 uint32_t tmp[4]; 167 168 for (i = 0; i < 4; ++i) 169 for (tmp[i] = ~ntohl(mask[i]); tmp[i] != 0; tmp[i] >>= 1) 170 ++bits; 171 return 128 - bits; 172} 173 174static void connlimit_print4(const void *ip, 175 const struct xt_entry_match *match, int numeric) 176{ 177 const struct xt_connlimit_info *info = (const void *)match->data; 178 179 printf(" #conn %s/%u %s %u", 180 (info->flags & XT_CONNLIMIT_DADDR) ? "dst" : "src", 181 count_bits4(info->v4_mask), 182 (info->flags & XT_CONNLIMIT_INVERT) ? "<=" : ">", info->limit); 183} 184 185static void connlimit_print6(const void *ip, 186 const struct xt_entry_match *match, int numeric) 187{ 188 const struct xt_connlimit_info *info = (const void *)match->data; 189 190 printf(" #conn %s/%u %s %u", 191 (info->flags & XT_CONNLIMIT_DADDR) ? "dst" : "src", 192 count_bits6(info->v6_mask), 193 (info->flags & XT_CONNLIMIT_INVERT) ? "<=" : ">", info->limit); 194} 195 196static void connlimit_save4(const void *ip, const struct xt_entry_match *match) 197{ 198 const struct xt_connlimit_info *info = (const void *)match->data; 199 const int revision = match->u.user.revision; 200 201 if (info->flags & XT_CONNLIMIT_INVERT) 202 printf(" --connlimit-upto %u", info->limit); 203 else 204 printf(" --connlimit-above %u", info->limit); 205 printf(" --connlimit-mask %u", count_bits4(info->v4_mask)); 206 if (revision >= 1) { 207 if (info->flags & XT_CONNLIMIT_DADDR) 208 printf(" --connlimit-daddr"); 209 else 210 printf(" --connlimit-saddr"); 211 } 212} 213 214static void connlimit_save6(const void *ip, const struct xt_entry_match *match) 215{ 216 const struct xt_connlimit_info *info = (const void *)match->data; 217 const int revision = match->u.user.revision; 218 219 if (info->flags & XT_CONNLIMIT_INVERT) 220 printf(" --connlimit-upto %u", info->limit); 221 else 222 printf(" --connlimit-above %u", info->limit); 223 printf(" --connlimit-mask %u", count_bits6(info->v6_mask)); 224 if (revision >= 1) { 225 if (info->flags & XT_CONNLIMIT_DADDR) 226 printf(" --connlimit-daddr"); 227 else 228 printf(" --connlimit-saddr"); 229 } 230} 231 232static struct xtables_match connlimit_mt_reg[] = { 233 { 234 .name = "connlimit", 235 .revision = 0, 236 .family = NFPROTO_IPV4, 237 .version = XTABLES_VERSION, 238 .size = XT_ALIGN(sizeof(struct xt_connlimit_info)), 239 .userspacesize = offsetof(struct xt_connlimit_info, data), 240 .help = connlimit_help, 241 .init = connlimit_init, 242 .parse = connlimit_parse4, 243 .final_check = connlimit_check, 244 .print = connlimit_print4, 245 .save = connlimit_save4, 246 .extra_opts = connlimit_opts, 247 }, 248 { 249 .name = "connlimit", 250 .revision = 0, 251 .family = NFPROTO_IPV6, 252 .version = XTABLES_VERSION, 253 .size = XT_ALIGN(sizeof(struct xt_connlimit_info)), 254 .userspacesize = offsetof(struct xt_connlimit_info, data), 255 .help = connlimit_help, 256 .init = connlimit_init, 257 .parse = connlimit_parse6, 258 .final_check = connlimit_check, 259 .print = connlimit_print6, 260 .save = connlimit_save6, 261 .extra_opts = connlimit_opts, 262 }, 263 { 264 .name = "connlimit", 265 .revision = 1, 266 .family = NFPROTO_IPV4, 267 .version = XTABLES_VERSION, 268 .size = XT_ALIGN(sizeof(struct xt_connlimit_info)), 269 .userspacesize = offsetof(struct xt_connlimit_info, data), 270 .help = connlimit_help, 271 .init = connlimit_init, 272 .parse = connlimit_parse4, 273 .final_check = connlimit_check, 274 .print = connlimit_print4, 275 .save = connlimit_save4, 276 .extra_opts = connlimit_opts, 277 }, 278 { 279 .name = "connlimit", 280 .revision = 1, 281 .family = NFPROTO_IPV6, 282 .version = XTABLES_VERSION, 283 .size = XT_ALIGN(sizeof(struct xt_connlimit_info)), 284 .userspacesize = offsetof(struct xt_connlimit_info, data), 285 .help = connlimit_help, 286 .init = connlimit_init, 287 .parse = connlimit_parse6, 288 .final_check = connlimit_check, 289 .print = connlimit_print6, 290 .save = connlimit_save6, 291 .extra_opts = connlimit_opts, 292 }, 293}; 294 295void _init(void) 296{ 297 xtables_register_matches(connlimit_mt_reg, ARRAY_SIZE(connlimit_mt_reg)); 298} 299