blob: 117222a7efa03244e70b0fcdb5945cbda79e98ba [file] [log] [blame]
Jan Engelhardt6053fe02007-07-31 16:47:38 +00001/* Shared library add-on to iptables to add connection limit support. */
2#include <stdio.h>
3#include <netdb.h>
4#include <string.h>
5#include <stdlib.h>
6#include <stddef.h>
7#include <getopt.h>
Jan Engelhardt5d9678a2008-11-20 10:15:35 +01008#include <xtables.h>
Jan Engelhardta2a7f2b2008-09-01 14:20:13 +02009#include <linux/netfilter/xt_connlimit.h>
Jan Engelhardt6053fe02007-07-31 16:47:38 +000010
11static void connlimit_help(void)
12{
13 printf(
Jan Engelhardt8b7c64d2008-04-15 11:48:25 +020014"connlimit match options:\n"
Jan Engelhardt6053fe02007-07-31 16:47:38 +000015"[!] --connlimit-above n match if the number of existing "
16" connections is (not) above n\n"
Jan Engelhardt8b7c64d2008-04-15 11:48:25 +020017" --connlimit-mask n group hosts using mask\n");
Jan Engelhardt6053fe02007-07-31 16:47:38 +000018}
19
20static const struct option connlimit_opts[] = {
21 {"connlimit-above", 1, NULL, 'A'},
22 {"connlimit-mask", 1, NULL, 'M'},
Max Kellermann9ee386a2008-01-29 13:48:05 +000023 { .name = NULL }
Jan Engelhardt6053fe02007-07-31 16:47:38 +000024};
25
Jan Engelhardt926bde82007-10-04 16:26:33 +000026static void connlimit_init(struct xt_entry_match *match)
Jan Engelhardt6053fe02007-07-31 16:47:38 +000027{
28 struct xt_connlimit_info *info = (void *)match->data;
29 info->v4_mask = 0xFFFFFFFFUL;
30}
31
32static void prefix_to_netmask(u_int32_t *mask, unsigned int prefix_len)
33{
34 if (prefix_len == 0) {
35 mask[0] = mask[1] = mask[2] = mask[3] = 0;
36 } else if (prefix_len <= 32) {
37 mask[0] <<= 32 - prefix_len;
38 mask[1] = mask[2] = mask[3] = 0;
39 } else if (prefix_len <= 64) {
40 mask[1] <<= 32 - (prefix_len - 32);
41 mask[2] = mask[3] = 0;
42 } else if (prefix_len <= 96) {
43 mask[2] <<= 32 - (prefix_len - 64);
44 mask[3] = 0;
45 } else if (prefix_len <= 128) {
46 mask[3] <<= 32 - (prefix_len - 96);
47 }
48 mask[0] = htonl(mask[0]);
49 mask[1] = htonl(mask[1]);
50 mask[2] = htonl(mask[2]);
51 mask[3] = htonl(mask[3]);
52}
53
54static int connlimit_parse(int c, char **argv, int invert, unsigned int *flags,
55 struct xt_connlimit_info *info, unsigned int family)
56{
57 char *err;
58 int i;
59
60 switch (c) {
61 case 'A':
62 if (*flags & 0x1)
63 exit_error(PARAMETER_PROBLEM,
64 "--connlimit-above may be given only once");
65 *flags |= 0x1;
66 check_inverse(optarg, &invert, &optind, 0);
67 info->limit = strtoul(argv[optind-1], NULL, 0);
68 info->inverse = invert;
69 break;
70 case 'M':
71 if (*flags & 0x2)
72 exit_error(PARAMETER_PROBLEM,
73 "--connlimit-mask may be given only once");
74
75 *flags |= 0x2;
76 i = strtoul(argv[optind-1], &err, 0);
Jan Engelhardt03d99482008-11-18 12:27:54 +010077 if (family == NFPROTO_IPV6) {
Jan Engelhardt6053fe02007-07-31 16:47:38 +000078 if (i > 128 || *err != '\0')
79 exit_error(PARAMETER_PROBLEM,
80 "--connlimit-mask must be between "
81 "0 and 128");
82 prefix_to_netmask(info->v6_mask, i);
83 } else {
84 if (i > 32 || *err != '\0')
85 exit_error(PARAMETER_PROBLEM,
86 "--connlimit-mask must be between "
87 "0 and 32");
88 if (i == 0)
89 info->v4_mask = 0;
90 else
91 info->v4_mask = htonl(0xFFFFFFFF << (32 - i));
92 }
93 break;
94 default:
95 return 0;
96 }
97
98 return 1;
99}
100
101static int connlimit_parse4(int c, char **argv, int invert,
102 unsigned int *flags, const void *entry,
Jan Engelhardt6053fe02007-07-31 16:47:38 +0000103 struct xt_entry_match **match)
104{
105 return connlimit_parse(c, argv, invert, flags,
Jan Engelhardt03d99482008-11-18 12:27:54 +0100106 (void *)(*match)->data, NFPROTO_IPV4);
Jan Engelhardt6053fe02007-07-31 16:47:38 +0000107}
108
109static int connlimit_parse6(int c, char **argv, int invert,
110 unsigned int *flags, const void *entry,
Jan Engelhardt6053fe02007-07-31 16:47:38 +0000111 struct xt_entry_match **match)
112{
113 return connlimit_parse(c, argv, invert, flags,
Jan Engelhardt03d99482008-11-18 12:27:54 +0100114 (void *)(*match)->data, NFPROTO_IPV6);
Jan Engelhardt6053fe02007-07-31 16:47:38 +0000115}
116
117static void connlimit_check(unsigned int flags)
118{
119 if (!(flags & 0x1))
120 exit_error(PARAMETER_PROBLEM,
121 "You must specify \"--connlimit-above\"");
122}
123
124static unsigned int count_bits4(u_int32_t mask)
125{
126 unsigned int bits = 0;
127
128 for (mask = ~ntohl(mask); mask != 0; mask >>= 1)
129 ++bits;
130
131 return 32 - bits;
132}
133
134static unsigned int count_bits6(const u_int32_t *mask)
135{
136 unsigned int bits = 0, i;
137 u_int32_t tmp[4];
138
139 for (i = 0; i < 4; ++i)
140 for (tmp[i] = ~ntohl(mask[i]); tmp[i] != 0; tmp[i] >>= 1)
141 ++bits;
142 return 128 - bits;
143}
144
145static void connlimit_print4(const void *ip,
146 const struct xt_entry_match *match, int numeric)
147{
148 const struct xt_connlimit_info *info = (const void *)match->data;
149
150 printf("#conn/%u %s %u ", count_bits4(info->v4_mask),
Patrick McHardye33bf8e2007-11-28 09:50:22 +0000151 info->inverse ? "<=" : ">", info->limit);
Jan Engelhardt6053fe02007-07-31 16:47:38 +0000152}
153
154static void connlimit_print6(const void *ip,
155 const struct xt_entry_match *match, int numeric)
156{
157 const struct xt_connlimit_info *info = (const void *)match->data;
158 printf("#conn/%u %s %u ", count_bits6(info->v6_mask),
Patrick McHardye33bf8e2007-11-28 09:50:22 +0000159 info->inverse ? "<=" : ">", info->limit);
Jan Engelhardt6053fe02007-07-31 16:47:38 +0000160}
161
162static void connlimit_save4(const void *ip, const struct xt_entry_match *match)
163{
164 const struct xt_connlimit_info *info = (const void *)match->data;
165
166 printf("%s--connlimit-above %u --connlimit-mask %u ",
167 info->inverse ? "! " : "", info->limit,
168 count_bits4(info->v4_mask));
169}
170
171static void connlimit_save6(const void *ip, const struct xt_entry_match *match)
172{
173 const struct xt_connlimit_info *info = (const void *)match->data;
174
175 printf("%s--connlimit-above %u --connlimit-mask %u ",
176 info->inverse ? "! " : "", info->limit,
177 count_bits6(info->v6_mask));
178}
179
Jan Engelhardt181dead2007-10-04 16:27:07 +0000180static struct xtables_match connlimit_match = {
Jan Engelhardt6053fe02007-07-31 16:47:38 +0000181 .name = "connlimit",
Jan Engelhardt03d99482008-11-18 12:27:54 +0100182 .family = NFPROTO_IPV4,
Jan Engelhardt8b7c64d2008-04-15 11:48:25 +0200183 .version = XTABLES_VERSION,
Jan Engelhardt6053fe02007-07-31 16:47:38 +0000184 .size = XT_ALIGN(sizeof(struct xt_connlimit_info)),
185 .userspacesize = offsetof(struct xt_connlimit_info, data),
186 .help = connlimit_help,
187 .init = connlimit_init,
188 .parse = connlimit_parse4,
189 .final_check = connlimit_check,
190 .print = connlimit_print4,
191 .save = connlimit_save4,
192 .extra_opts = connlimit_opts,
193};
194
Jan Engelhardt181dead2007-10-04 16:27:07 +0000195static struct xtables_match connlimit_match6 = {
Jan Engelhardt6053fe02007-07-31 16:47:38 +0000196 .name = "connlimit",
Jan Engelhardt03d99482008-11-18 12:27:54 +0100197 .family = NFPROTO_IPV6,
Jan Engelhardt8b7c64d2008-04-15 11:48:25 +0200198 .version = XTABLES_VERSION,
Jan Engelhardt6053fe02007-07-31 16:47:38 +0000199 .size = XT_ALIGN(sizeof(struct xt_connlimit_info)),
200 .userspacesize = offsetof(struct xt_connlimit_info, data),
201 .help = connlimit_help,
202 .init = connlimit_init,
203 .parse = connlimit_parse6,
204 .final_check = connlimit_check,
205 .print = connlimit_print6,
206 .save = connlimit_save6,
207 .extra_opts = connlimit_opts,
208};
209
210void _init(void)
211{
Jan Engelhardt181dead2007-10-04 16:27:07 +0000212 xtables_register_match(&connlimit_match);
213 xtables_register_match(&connlimit_match6);
Jan Engelhardt6053fe02007-07-31 16:47:38 +0000214}