blob: 28a9caa083638cb5e0e06d512916df76f049db31 [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
27static void connlimit_init(struct ipt_entry_match *match, unsigned int *nfc)
28{
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,
104 unsigned int *nfcache,
105 struct xt_entry_match **match)
106{
107 return connlimit_parse(c, argv, invert, flags,
108 (void *)(*match)->data, AF_INET);
109}
110
111static int connlimit_parse6(int c, char **argv, int invert,
112 unsigned int *flags, const void *entry,
113 unsigned int *nfcache,
114 struct xt_entry_match **match)
115{
116 return connlimit_parse(c, argv, invert, flags,
117 (void *)(*match)->data, AF_INET6);
118}
119
120static void connlimit_check(unsigned int flags)
121{
122 if (!(flags & 0x1))
123 exit_error(PARAMETER_PROBLEM,
124 "You must specify \"--connlimit-above\"");
125}
126
127static unsigned int count_bits4(u_int32_t mask)
128{
129 unsigned int bits = 0;
130
131 for (mask = ~ntohl(mask); mask != 0; mask >>= 1)
132 ++bits;
133
134 return 32 - bits;
135}
136
137static unsigned int count_bits6(const u_int32_t *mask)
138{
139 unsigned int bits = 0, i;
140 u_int32_t tmp[4];
141
142 for (i = 0; i < 4; ++i)
143 for (tmp[i] = ~ntohl(mask[i]); tmp[i] != 0; tmp[i] >>= 1)
144 ++bits;
145 return 128 - bits;
146}
147
148static void connlimit_print4(const void *ip,
149 const struct xt_entry_match *match, int numeric)
150{
151 const struct xt_connlimit_info *info = (const void *)match->data;
152
153 printf("#conn/%u %s %u ", count_bits4(info->v4_mask),
154 info->inverse ? "<" : ">", info->limit);
155}
156
157static void connlimit_print6(const void *ip,
158 const struct xt_entry_match *match, int numeric)
159{
160 const struct xt_connlimit_info *info = (const void *)match->data;
161 printf("#conn/%u %s %u ", count_bits6(info->v6_mask),
162 info->inverse ? "<" : ">", info->limit);
163}
164
165static void connlimit_save4(const void *ip, const struct xt_entry_match *match)
166{
167 const struct xt_connlimit_info *info = (const void *)match->data;
168
169 printf("%s--connlimit-above %u --connlimit-mask %u ",
170 info->inverse ? "! " : "", info->limit,
171 count_bits4(info->v4_mask));
172}
173
174static void connlimit_save6(const void *ip, const struct xt_entry_match *match)
175{
176 const struct xt_connlimit_info *info = (const void *)match->data;
177
178 printf("%s--connlimit-above %u --connlimit-mask %u ",
179 info->inverse ? "! " : "", info->limit,
180 count_bits6(info->v6_mask));
181}
182
183static struct xtables_match connlimit_reg4 = {
184 .name = "connlimit",
185 .family = AF_INET,
186 .version = IPTABLES_VERSION,
187 .size = XT_ALIGN(sizeof(struct xt_connlimit_info)),
188 .userspacesize = offsetof(struct xt_connlimit_info, data),
189 .help = connlimit_help,
190 .init = connlimit_init,
191 .parse = connlimit_parse4,
192 .final_check = connlimit_check,
193 .print = connlimit_print4,
194 .save = connlimit_save4,
195 .extra_opts = connlimit_opts,
196};
197
198static struct xtables_match connlimit_reg6 = {
199 .name = "connlimit",
200 .family = AF_INET6,
201 .version = IPTABLES_VERSION,
202 .size = XT_ALIGN(sizeof(struct xt_connlimit_info)),
203 .userspacesize = offsetof(struct xt_connlimit_info, data),
204 .help = connlimit_help,
205 .init = connlimit_init,
206 .parse = connlimit_parse6,
207 .final_check = connlimit_check,
208 .print = connlimit_print6,
209 .save = connlimit_save6,
210 .extra_opts = connlimit_opts,
211};
212
213void _init(void)
214{
215 xtables_register_match(&connlimit_reg4);
216 xtables_register_match(&connlimit_reg6);
217}