Fork for IVI and add .changes file
[profile/ivi/iptables.git] / extensions / libxt_connlimit.c
1 /* 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 <xtables.h>
9 #include <linux/netfilter/xt_connlimit.h>
10
11 static void connlimit_help(void)
12 {
13         printf(
14 "connlimit match 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 }
19
20 static const struct option connlimit_opts[] = {
21         {"connlimit-above", 1, NULL, 'A'},
22         {"connlimit-mask",  1, NULL, 'M'},
23         { .name = NULL }
24 };
25
26 static void connlimit_init(struct xt_entry_match *match)
27 {
28         struct xt_connlimit_info *info = (void *)match->data;
29
30         /* This will also initialize the v4 mask correctly */
31         memset(info->v6_mask, 0xFF, sizeof(info->v6_mask));
32 }
33
34 static void prefix_to_netmask(u_int32_t *mask, unsigned int prefix_len)
35 {
36         if (prefix_len == 0) {
37                 mask[0] = mask[1] = mask[2] = mask[3] = 0;
38         } else if (prefix_len <= 32) {
39                 mask[0] <<= 32 - prefix_len;
40                 mask[1] = mask[2] = mask[3] = 0;
41         } else if (prefix_len <= 64) {
42                 mask[1] <<= 32 - (prefix_len - 32);
43                 mask[2] = mask[3] = 0;
44         } else if (prefix_len <= 96) {
45                 mask[2] <<= 32 - (prefix_len - 64);
46                 mask[3] = 0;
47         } else if (prefix_len <= 128) {
48                 mask[3] <<= 32 - (prefix_len - 96);
49         }
50         mask[0] = htonl(mask[0]);
51         mask[1] = htonl(mask[1]);
52         mask[2] = htonl(mask[2]);
53         mask[3] = htonl(mask[3]);
54 }
55
56 static int connlimit_parse(int c, char **argv, int invert, unsigned int *flags,
57                            struct xt_connlimit_info *info, unsigned int family)
58 {
59         char *err;
60         int i;
61
62         switch (c) {
63         case 'A':
64                 if (*flags & 0x1)
65                         xtables_error(PARAMETER_PROBLEM,
66                                 "--connlimit-above may be given only once");
67                 *flags |= 0x1;
68                 xtables_check_inverse(optarg, &invert, &optind, 0, argv);
69                 info->limit   = strtoul(optarg, NULL, 0);
70                 info->inverse = invert;
71                 break;
72         case 'M':
73                 if (*flags & 0x2)
74                         xtables_error(PARAMETER_PROBLEM,
75                                 "--connlimit-mask may be given only once");
76
77                 *flags |= 0x2;
78                 i = strtoul(optarg, &err, 0);
79                 if (family == NFPROTO_IPV6) {
80                         if (i > 128 || *err != '\0')
81                                 xtables_error(PARAMETER_PROBLEM,
82                                         "--connlimit-mask must be between "
83                                         "0 and 128");
84                         prefix_to_netmask(info->v6_mask, i);
85                 } else {
86                         if (i > 32 || *err != '\0')
87                                 xtables_error(PARAMETER_PROBLEM,
88                                         "--connlimit-mask must be between "
89                                         "0 and 32");
90                         if (i == 0)
91                                 info->v4_mask = 0;
92                         else
93                                 info->v4_mask = htonl(0xFFFFFFFF << (32 - i));
94                 }
95                 break;
96         default:
97                 return 0;
98         }
99
100         return 1;
101 }
102
103 static int connlimit_parse4(int c, char **argv, int invert,
104                             unsigned int *flags, const void *entry,
105                             struct xt_entry_match **match)
106 {
107         return connlimit_parse(c, argv, invert, flags,
108                (void *)(*match)->data, NFPROTO_IPV4);
109 }
110
111 static int connlimit_parse6(int c, char **argv, int invert,
112                             unsigned int *flags, const void *entry,
113                             struct xt_entry_match **match)
114 {
115         return connlimit_parse(c, argv, invert, flags,
116                (void *)(*match)->data, NFPROTO_IPV6);
117 }
118
119 static void connlimit_check(unsigned int flags)
120 {
121         if (!(flags & 0x1))
122                 xtables_error(PARAMETER_PROBLEM,
123                         "You must specify \"--connlimit-above\"");
124 }
125
126 static unsigned int count_bits4(u_int32_t mask)
127 {
128         unsigned int bits = 0;
129
130         for (mask = ~ntohl(mask); mask != 0; mask >>= 1)
131                 ++bits;
132
133         return 32 - bits;
134 }
135
136 static unsigned int count_bits6(const u_int32_t *mask)
137 {
138         unsigned int bits = 0, i;
139         u_int32_t tmp[4];
140
141         for (i = 0; i < 4; ++i)
142                 for (tmp[i] = ~ntohl(mask[i]); tmp[i] != 0; tmp[i] >>= 1)
143                         ++bits;
144         return 128 - bits;
145 }
146
147 static void connlimit_print4(const void *ip,
148                              const struct xt_entry_match *match, int numeric)
149 {
150         const struct xt_connlimit_info *info = (const void *)match->data;
151
152         printf("#conn/%u %s %u ", count_bits4(info->v4_mask),
153                info->inverse ? "<=" : ">", info->limit);
154 }
155
156 static void connlimit_print6(const void *ip,
157                              const struct xt_entry_match *match, int numeric)
158 {
159         const struct xt_connlimit_info *info = (const void *)match->data;
160         printf("#conn/%u %s %u ", count_bits6(info->v6_mask),
161                info->inverse ? "<=" : ">", info->limit);
162 }
163
164 static void connlimit_save4(const void *ip, const struct xt_entry_match *match)
165 {
166         const struct xt_connlimit_info *info = (const void *)match->data;
167
168         printf("%s--connlimit-above %u --connlimit-mask %u ",
169                info->inverse ? "! " : "", info->limit,
170                count_bits4(info->v4_mask));
171 }
172
173 static void connlimit_save6(const void *ip, const struct xt_entry_match *match)
174 {
175         const struct xt_connlimit_info *info = (const void *)match->data;
176
177         printf("%s--connlimit-above %u --connlimit-mask %u ",
178                info->inverse ? "! " : "", info->limit,
179                count_bits6(info->v6_mask));
180 }
181
182 static struct xtables_match connlimit_mt_reg[] = {
183         {
184                 .name          = "connlimit",
185                 .family        = NFPROTO_IPV4,
186                 .version       = XTABLES_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         {
198                 .name          = "connlimit",
199                 .family        = NFPROTO_IPV6,
200                 .version       = XTABLES_VERSION,
201                 .size          = XT_ALIGN(sizeof(struct xt_connlimit_info)),
202                 .userspacesize = offsetof(struct xt_connlimit_info, data),
203                 .help          = connlimit_help,
204                 .init          = connlimit_init,
205                 .parse         = connlimit_parse6,
206                 .final_check   = connlimit_check,
207                 .print         = connlimit_print6,
208                 .save          = connlimit_save6,
209                 .extra_opts    = connlimit_opts,
210         },
211 };
212
213 void _init(void)
214 {
215         xtables_register_matches(connlimit_mt_reg, ARRAY_SIZE(connlimit_mt_reg));
216 }