1 /*
2 * Shared library add-on to iptables to add IPVS matching.
3 *
4 * Detailed doc is in the kernel module source net/netfilter/xt_ipvs.c
5 *
6 * Author: Hannes Eder <[email protected]>
7 */
8 #include <stdbool.h>
9 #include <stdio.h>
10 #include <string.h>
11 #include <xtables.h>
12 #include <linux/ip_vs.h>
13 #include <linux/netfilter/xt_ipvs.h>
14
15 enum {
16 /* For xt_ipvs: make sure this matches up with %XT_IPVS_*'s order */
17 O_IPVS = 0,
18 O_VPROTO,
19 O_VADDR,
20 O_VPORT,
21 O_VDIR,
22 O_VMETHOD,
23 O_VPORTCTL,
24 };
25
26 #define s struct xt_ipvs_mtinfo
27 static const struct xt_option_entry ipvs_mt_opts[] = {
28 {.name = "ipvs", .id = O_IPVS, .type = XTTYPE_NONE,
29 .flags = XTOPT_INVERT},
30 {.name = "vproto", .id = O_VPROTO, .type = XTTYPE_PROTOCOL,
31 .flags = XTOPT_INVERT | XTOPT_PUT, XTOPT_POINTER(s, l4proto)},
32 {.name = "vaddr", .id = O_VADDR, .type = XTTYPE_HOSTMASK,
33 .flags = XTOPT_INVERT},
34 {.name = "vport", .id = O_VPORT, .type = XTTYPE_PORT,
35 .flags = XTOPT_NBO | XTOPT_INVERT | XTOPT_PUT,
36 XTOPT_POINTER(s, vport)},
37 {.name = "vdir", .id = O_VDIR, .type = XTTYPE_STRING},
38 {.name = "vmethod", .id = O_VMETHOD, .type = XTTYPE_STRING,
39 .flags = XTOPT_INVERT},
40 {.name = "vportctl", .id = O_VPORTCTL, .type = XTTYPE_PORT,
41 .flags = XTOPT_NBO | XTOPT_INVERT | XTOPT_PUT,
42 XTOPT_POINTER(s, vportctl)},
43 XTOPT_TABLEEND,
44 };
45 #undef s
46
ipvs_mt_help(void)47 static void ipvs_mt_help(void)
48 {
49 printf(
50 "IPVS match options:\n"
51 "[!] --ipvs packet belongs to an IPVS connection\n"
52 "\n"
53 "Any of the following options implies --ipvs (even negated)\n"
54 "[!] --vproto protocol VIP protocol to match; by number or name,\n"
55 " e.g. \"tcp\"\n"
56 "[!] --vaddr address[/mask] VIP address to match\n"
57 "[!] --vport port VIP port to match; by number or name,\n"
58 " e.g. \"http\"\n"
59 " --vdir {ORIGINAL|REPLY} flow direction of packet\n"
60 "[!] --vmethod {GATE|IPIP|MASQ} IPVS forwarding method used\n"
61 "[!] --vportctl port VIP port of the controlling connection to\n"
62 " match, e.g. 21 for FTP\n"
63 );
64 }
65
ipvs_mt_parse(struct xt_option_call * cb)66 static void ipvs_mt_parse(struct xt_option_call *cb)
67 {
68 struct xt_ipvs_mtinfo *data = cb->data;
69
70 xtables_option_parse(cb);
71 switch (cb->entry->id) {
72 case O_VADDR:
73 memcpy(&data->vaddr, &cb->val.haddr, sizeof(cb->val.haddr));
74 memcpy(&data->vmask, &cb->val.hmask, sizeof(cb->val.hmask));
75 break;
76 case O_VDIR:
77 if (strcasecmp(cb->arg, "ORIGINAL") == 0) {
78 data->bitmask |= XT_IPVS_DIR;
79 data->invert &= ~XT_IPVS_DIR;
80 } else if (strcasecmp(cb->arg, "REPLY") == 0) {
81 data->bitmask |= XT_IPVS_DIR;
82 data->invert |= XT_IPVS_DIR;
83 } else {
84 xtables_param_act(XTF_BAD_VALUE,
85 "ipvs", "--vdir", cb->arg);
86 }
87 break;
88 case O_VMETHOD:
89 if (strcasecmp(cb->arg, "GATE") == 0)
90 data->fwd_method = IP_VS_CONN_F_DROUTE;
91 else if (strcasecmp(cb->arg, "IPIP") == 0)
92 data->fwd_method = IP_VS_CONN_F_TUNNEL;
93 else if (strcasecmp(cb->arg, "MASQ") == 0)
94 data->fwd_method = IP_VS_CONN_F_MASQ;
95 else
96 xtables_param_act(XTF_BAD_VALUE,
97 "ipvs", "--vmethod", cb->arg);
98 break;
99 }
100 data->bitmask |= 1 << cb->entry->id;
101 if (cb->invert)
102 data->invert |= 1 << cb->entry->id;
103 }
104
ipvs_mt_check(struct xt_fcheck_call * cb)105 static void ipvs_mt_check(struct xt_fcheck_call *cb)
106 {
107 struct xt_ipvs_mtinfo *info = cb->data;
108
109 if (cb->xflags == 0)
110 xtables_error(PARAMETER_PROBLEM,
111 "IPVS: At least one option is required");
112 if (info->bitmask & XT_IPVS_ONCE_MASK) {
113 if (info->invert & XT_IPVS_IPVS_PROPERTY)
114 xtables_error(PARAMETER_PROBLEM,
115 "! --ipvs cannot be together with"
116 " other options");
117 info->bitmask |= XT_IPVS_IPVS_PROPERTY;
118 }
119 }
120
121 /* Shamelessly copied from libxt_conntrack.c */
ipvs_mt_dump_addr(const union nf_inet_addr * addr,const union nf_inet_addr * mask,unsigned int family,bool numeric)122 static void ipvs_mt_dump_addr(const union nf_inet_addr *addr,
123 const union nf_inet_addr *mask,
124 unsigned int family, bool numeric)
125 {
126 if (family == NFPROTO_IPV4) {
127 if (!numeric && addr->ip == 0) {
128 printf(" anywhere");
129 return;
130 }
131 if (numeric)
132 printf(" %s%s",
133 xtables_ipaddr_to_numeric(&addr->in),
134 xtables_ipmask_to_numeric(&mask->in));
135 else
136 printf(" %s%s",
137 xtables_ipaddr_to_anyname(&addr->in),
138 xtables_ipmask_to_numeric(&mask->in));
139 } else if (family == NFPROTO_IPV6) {
140 if (!numeric && addr->ip6[0] == 0 && addr->ip6[1] == 0 &&
141 addr->ip6[2] == 0 && addr->ip6[3] == 0) {
142 printf(" anywhere");
143 return;
144 }
145 if (numeric)
146 printf(" %s%s",
147 xtables_ip6addr_to_numeric(&addr->in6),
148 xtables_ip6mask_to_numeric(&mask->in6));
149 else
150 printf(" %s%s",
151 xtables_ip6addr_to_anyname(&addr->in6),
152 xtables_ip6mask_to_numeric(&mask->in6));
153 }
154 }
155
ipvs_mt_dump(const void * ip,const struct xt_ipvs_mtinfo * data,unsigned int family,bool numeric,const char * prefix)156 static void ipvs_mt_dump(const void *ip, const struct xt_ipvs_mtinfo *data,
157 unsigned int family, bool numeric, const char *prefix)
158 {
159 if (data->bitmask == XT_IPVS_IPVS_PROPERTY) {
160 if (data->invert & XT_IPVS_IPVS_PROPERTY)
161 printf(" !");
162 printf(" %sipvs", prefix);
163 }
164
165 if (data->bitmask & XT_IPVS_PROTO) {
166 if (data->invert & XT_IPVS_PROTO)
167 printf(" !");
168 printf(" %svproto %u", prefix, data->l4proto);
169 }
170
171 if (data->bitmask & XT_IPVS_VADDR) {
172 if (data->invert & XT_IPVS_VADDR)
173 printf(" !");
174
175 printf(" %svaddr", prefix);
176 ipvs_mt_dump_addr(&data->vaddr, &data->vmask, family, numeric);
177 }
178
179 if (data->bitmask & XT_IPVS_VPORT) {
180 if (data->invert & XT_IPVS_VPORT)
181 printf(" !");
182
183 printf(" %svport %u", prefix, ntohs(data->vport));
184 }
185
186 if (data->bitmask & XT_IPVS_DIR) {
187 if (data->invert & XT_IPVS_DIR)
188 printf(" %svdir REPLY", prefix);
189 else
190 printf(" %svdir ORIGINAL", prefix);
191 }
192
193 if (data->bitmask & XT_IPVS_METHOD) {
194 if (data->invert & XT_IPVS_METHOD)
195 printf(" !");
196
197 printf(" %svmethod", prefix);
198 switch (data->fwd_method) {
199 case IP_VS_CONN_F_DROUTE:
200 printf(" GATE");
201 break;
202 case IP_VS_CONN_F_TUNNEL:
203 printf(" IPIP");
204 break;
205 case IP_VS_CONN_F_MASQ:
206 printf(" MASQ");
207 break;
208 default:
209 /* Hu? */
210 printf(" UNKNOWN");
211 break;
212 }
213 }
214
215 if (data->bitmask & XT_IPVS_VPORTCTL) {
216 if (data->invert & XT_IPVS_VPORTCTL)
217 printf(" !");
218
219 printf(" %svportctl %u", prefix, ntohs(data->vportctl));
220 }
221 }
222
ipvs_mt4_print(const void * ip,const struct xt_entry_match * match,int numeric)223 static void ipvs_mt4_print(const void *ip, const struct xt_entry_match *match,
224 int numeric)
225 {
226 const struct xt_ipvs_mtinfo *data = (const void *)match->data;
227 ipvs_mt_dump(ip, data, NFPROTO_IPV4, numeric, "");
228 }
229
ipvs_mt6_print(const void * ip,const struct xt_entry_match * match,int numeric)230 static void ipvs_mt6_print(const void *ip, const struct xt_entry_match *match,
231 int numeric)
232 {
233 const struct xt_ipvs_mtinfo *data = (const void *)match->data;
234 ipvs_mt_dump(ip, data, NFPROTO_IPV6, numeric, "");
235 }
236
ipvs_mt4_save(const void * ip,const struct xt_entry_match * match)237 static void ipvs_mt4_save(const void *ip, const struct xt_entry_match *match)
238 {
239 const struct xt_ipvs_mtinfo *data = (const void *)match->data;
240 ipvs_mt_dump(ip, data, NFPROTO_IPV4, true, "--");
241 }
242
ipvs_mt6_save(const void * ip,const struct xt_entry_match * match)243 static void ipvs_mt6_save(const void *ip, const struct xt_entry_match *match)
244 {
245 const struct xt_ipvs_mtinfo *data = (const void *)match->data;
246 ipvs_mt_dump(ip, data, NFPROTO_IPV6, true, "--");
247 }
248
249 static struct xtables_match ipvs_matches_reg[] = {
250 {
251 .version = XTABLES_VERSION,
252 .name = "ipvs",
253 .revision = 0,
254 .family = NFPROTO_IPV4,
255 .size = XT_ALIGN(sizeof(struct xt_ipvs_mtinfo)),
256 .userspacesize = XT_ALIGN(sizeof(struct xt_ipvs_mtinfo)),
257 .help = ipvs_mt_help,
258 .x6_parse = ipvs_mt_parse,
259 .x6_fcheck = ipvs_mt_check,
260 .print = ipvs_mt4_print,
261 .save = ipvs_mt4_save,
262 .x6_options = ipvs_mt_opts,
263 },
264 {
265 .version = XTABLES_VERSION,
266 .name = "ipvs",
267 .revision = 0,
268 .family = NFPROTO_IPV6,
269 .size = XT_ALIGN(sizeof(struct xt_ipvs_mtinfo)),
270 .userspacesize = XT_ALIGN(sizeof(struct xt_ipvs_mtinfo)),
271 .help = ipvs_mt_help,
272 .x6_parse = ipvs_mt_parse,
273 .x6_fcheck = ipvs_mt_check,
274 .print = ipvs_mt6_print,
275 .save = ipvs_mt6_save,
276 .x6_options = ipvs_mt_opts,
277 },
278 };
279
_init(void)280 void _init(void)
281 {
282 xtables_register_matches(ipvs_matches_reg,
283 ARRAY_SIZE(ipvs_matches_reg));
284 }
285