blob: 1b86d6e97cad951bbdceb5483c81d66ed60b616d [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>
8#include <iptables.h>
9#include "../include/linux/netfilter/xt_connlimit.h"
10
11static void connlimit_help(void)
12{
13 printf(
14"connlimit v%s options:\n"
15"[!] --connlimit-above n match if the number of existing "
16" connections is (not) above n\n"
17" --connlimit-mask n group hosts using mask\n"
18"\n", IPTABLES_VERSION);
19}
20
21static const struct option connlimit_opts[] = {
22 {"connlimit-above", 1, NULL, 'A'},
23 {"connlimit-mask", 1, NULL, 'M'},
24 {NULL},
25};
26
Jan Engelhardt926bde82007-10-04 16:26:33 +000027static void connlimit_init(struct xt_entry_match *match)
Jan Engelhardt6053fe02007-07-31 16:47:38 +000028{
29 struct xt_connlimit_info *info = (void *)match->data;
30 info->v4_mask = 0xFFFFFFFFUL;
31}
32
33static void prefix_to_netmask(u_int32_t *mask, unsigned int prefix_len)
34{
35 if (prefix_len == 0) {
36 mask[0] = mask[1] = mask[2] = mask[3] = 0;
37 } else if (prefix_len <= 32) {
38 mask[0] <<= 32 - prefix_len;
39 mask[1] = mask[2] = mask[3] = 0;
40 } else if (prefix_len <= 64) {
41 mask[1] <<= 32 - (prefix_len - 32);
42 mask[2] = mask[3] = 0;
43 } else if (prefix_len <= 96) {
44 mask[2] <<= 32 - (prefix_len - 64);
45 mask[3] = 0;
46 } else if (prefix_len <= 128) {
47 mask[3] <<= 32 - (prefix_len - 96);
48 }
49 mask[0] = htonl(mask[0]);
50 mask[1] = htonl(mask[1]);
51 mask[2] = htonl(mask[2]);
52 mask[3] = htonl(mask[3]);
53}
54
55static int connlimit_parse(int c, char **argv, int invert, unsigned int *flags,
56 struct xt_connlimit_info *info, unsigned int family)
57{
58 char *err;
59 int i;
60
61 switch (c) {
62 case 'A':
63 if (*flags & 0x1)
64 exit_error(PARAMETER_PROBLEM,
65 "--connlimit-above may be given only once");
66 *flags |= 0x1;
67 check_inverse(optarg, &invert, &optind, 0);
68 info->limit = strtoul(argv[optind-1], NULL, 0);
69 info->inverse = invert;
70 break;
71 case 'M':
72 if (*flags & 0x2)
73 exit_error(PARAMETER_PROBLEM,
74 "--connlimit-mask may be given only once");
75
76 *flags |= 0x2;
77 i = strtoul(argv[optind-1], &err, 0);
78 if (family == AF_INET6) {
79 if (i > 128 || *err != '\0')
80 exit_error(PARAMETER_PROBLEM,
81 "--connlimit-mask must be between "
82 "0 and 128");
83 prefix_to_netmask(info->v6_mask, i);
84 } else {
85 if (i > 32 || *err != '\0')
86 exit_error(PARAMETER_PROBLEM,
87 "--connlimit-mask must be between "
88 "0 and 32");
89 if (i == 0)
90 info->v4_mask = 0;
91 else
92 info->v4_mask = htonl(0xFFFFFFFF << (32 - i));
93 }
94 break;
95 default:
96 return 0;
97 }
98
99 return 1;
100}
101
102static int connlimit_parse4(int c, char **argv, int invert,
103 unsigned int *flags, const void *entry,
Jan Engelhardt6053fe02007-07-31 16:47:38 +0000104 struct xt_entry_match **match)
105{
106 return connlimit_parse(c, argv, invert, flags,
107 (void *)(*match)->data, AF_INET);
108}
109
110static int connlimit_parse6(int c, char **argv, int invert,
111 unsigned int *flags, const void *entry,
Jan Engelhardt6053fe02007-07-31 16:47:38 +0000112 struct xt_entry_match **match)
113{
114 return connlimit_parse(c, argv, invert, flags,
115 (void *)(*match)->data, AF_INET6);
116}
117
118static void connlimit_check(unsigned int flags)
119{
120 if (!(flags & 0x1))
121 exit_error(PARAMETER_PROBLEM,
122 "You must specify \"--connlimit-above\"");
123}
124
125static unsigned int count_bits4(u_int32_t mask)
126{
127 unsigned int bits = 0;
128
129 for (mask = ~ntohl(mask); mask != 0; mask >>= 1)
130 ++bits;
131
132 return 32 - bits;
133}
134
135static unsigned int count_bits6(const u_int32_t *mask)
136{
137 unsigned int bits = 0, i;
138 u_int32_t tmp[4];
139
140 for (i = 0; i < 4; ++i)
141 for (tmp[i] = ~ntohl(mask[i]); tmp[i] != 0; tmp[i] >>= 1)
142 ++bits;
143 return 128 - bits;
144}
145
146static void connlimit_print4(const void *ip,
147 const struct xt_entry_match *match, int numeric)
148{
149 const struct xt_connlimit_info *info = (const void *)match->data;
150
151 printf("#conn/%u %s %u ", count_bits4(info->v4_mask),
152 info->inverse ? "<" : ">", info->limit);
153}
154
155static void connlimit_print6(const void *ip,
156 const struct xt_entry_match *match, int numeric)
157{
158 const struct xt_connlimit_info *info = (const void *)match->data;
159 printf("#conn/%u %s %u ", count_bits6(info->v6_mask),
160 info->inverse ? "<" : ">", info->limit);
161}
162
163static void connlimit_save4(const void *ip, const struct xt_entry_match *match)
164{
165 const struct xt_connlimit_info *info = (const void *)match->data;
166
167 printf("%s--connlimit-above %u --connlimit-mask %u ",
168 info->inverse ? "! " : "", info->limit,
169 count_bits4(info->v4_mask));
170}
171
172static void connlimit_save6(const void *ip, const struct xt_entry_match *match)
173{
174 const struct xt_connlimit_info *info = (const void *)match->data;
175
176 printf("%s--connlimit-above %u --connlimit-mask %u ",
177 info->inverse ? "! " : "", info->limit,
178 count_bits6(info->v6_mask));
179}
180
Jan Engelhardt181dead2007-10-04 16:27:07 +0000181static struct xtables_match connlimit_match = {
Jan Engelhardt6053fe02007-07-31 16:47:38 +0000182 .name = "connlimit",
183 .family = AF_INET,
184 .version = IPTABLES_VERSION,
185 .size = XT_ALIGN(sizeof(struct xt_connlimit_info)),
186 .userspacesize = offsetof(struct xt_connlimit_info, data),
187 .help = connlimit_help,
188 .init = connlimit_init,
189 .parse = connlimit_parse4,
190 .final_check = connlimit_check,
191 .print = connlimit_print4,
192 .save = connlimit_save4,
193 .extra_opts = connlimit_opts,
194};
195
Jan Engelhardt181dead2007-10-04 16:27:07 +0000196static struct xtables_match connlimit_match6 = {
Jan Engelhardt6053fe02007-07-31 16:47:38 +0000197 .name = "connlimit",
198 .family = AF_INET6,
199 .version = IPTABLES_VERSION,
200 .size = XT_ALIGN(sizeof(struct xt_connlimit_info)),
201 .userspacesize = offsetof(struct xt_connlimit_info, data),
202 .help = connlimit_help,
203 .init = connlimit_init,
204 .parse = connlimit_parse6,
205 .final_check = connlimit_check,
206 .print = connlimit_print6,
207 .save = connlimit_save6,
208 .extra_opts = connlimit_opts,
209};
210
211void _init(void)
212{
Jan Engelhardt181dead2007-10-04 16:27:07 +0000213 xtables_register_match(&connlimit_match);
214 xtables_register_match(&connlimit_match6);
Jan Engelhardt6053fe02007-07-31 16:47:38 +0000215}