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 <heder@google.com>
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_STRING,
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_VPROTO:
73 data->l4proto = cb->val.protocol;
74 break;
75 case O_VADDR:
76 memcpy(&data->vaddr, &cb->val.haddr, sizeof(cb->val.haddr));
77 memcpy(&data->vmask, &cb->val.hmask, sizeof(cb->val.hmask));
78 break;
79 case O_VDIR:
80 if (strcasecmp(cb->arg, "ORIGINAL") == 0) {
81 data->bitmask |= XT_IPVS_DIR;
82 data->invert &= ~XT_IPVS_DIR;
83 } else if (strcasecmp(cb->arg, "REPLY") == 0) {
84 data->bitmask |= XT_IPVS_DIR;
85 data->invert |= XT_IPVS_DIR;
86 } else {
87 xtables_param_act(XTF_BAD_VALUE,
88 "ipvs", "--vdir", cb->arg);
89 }
90 break;
91 case O_VMETHOD:
92 if (strcasecmp(cb->arg, "GATE") == 0)
93 data->fwd_method = IP_VS_CONN_F_DROUTE;
94 else if (strcasecmp(cb->arg, "IPIP") == 0)
95 data->fwd_method = IP_VS_CONN_F_TUNNEL;
96 else if (strcasecmp(cb->arg, "MASQ") == 0)
97 data->fwd_method = IP_VS_CONN_F_MASQ;
98 else
99 xtables_param_act(XTF_BAD_VALUE,
100 "ipvs", "--vmethod", cb->arg);
101 break;
102 }
103 data->bitmask |= 1 << cb->entry->id;
104 if (cb->invert)
105 data->invert |= 1 << cb->entry->id;
106 }
107
ipvs_mt_check(struct xt_fcheck_call * cb)108 static void ipvs_mt_check(struct xt_fcheck_call *cb)
109 {
110 struct xt_ipvs_mtinfo *info = cb->data;
111
112 if (cb->xflags == 0)
113 xtables_error(PARAMETER_PROBLEM,
114 "IPVS: At least one option is required");
115 if (info->bitmask & XT_IPVS_ONCE_MASK) {
116 if (info->invert & XT_IPVS_IPVS_PROPERTY)
117 xtables_error(PARAMETER_PROBLEM,
118 "! --ipvs cannot be together with"
119 " other options");
120 info->bitmask |= XT_IPVS_IPVS_PROPERTY;
121 }
122 }
123
124 /* 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)125 static void ipvs_mt_dump_addr(const union nf_inet_addr *addr,
126 const union nf_inet_addr *mask,
127 unsigned int family, bool numeric)
128 {
129 char buf[BUFSIZ];
130
131 if (family == NFPROTO_IPV4) {
132 if (!numeric && addr->ip == 0) {
133 printf(" anywhere");
134 return;
135 }
136 if (numeric)
137 strcpy(buf, xtables_ipaddr_to_numeric(&addr->in));
138 else
139 strcpy(buf, xtables_ipaddr_to_anyname(&addr->in));
140 strcat(buf, xtables_ipmask_to_numeric(&mask->in));
141 printf(" %s", buf);
142 } else if (family == NFPROTO_IPV6) {
143 if (!numeric && addr->ip6[0] == 0 && addr->ip6[1] == 0 &&
144 addr->ip6[2] == 0 && addr->ip6[3] == 0) {
145 printf(" anywhere");
146 return;
147 }
148 if (numeric)
149 strcpy(buf, xtables_ip6addr_to_numeric(&addr->in6));
150 else
151 strcpy(buf, xtables_ip6addr_to_anyname(&addr->in6));
152 strcat(buf, xtables_ip6mask_to_numeric(&mask->in6));
153 printf(" %s", buf);
154 }
155 }
156
ipvs_mt_dump(const void * ip,const struct xt_ipvs_mtinfo * data,unsigned int family,bool numeric,const char * prefix)157 static void ipvs_mt_dump(const void *ip, const struct xt_ipvs_mtinfo *data,
158 unsigned int family, bool numeric, const char *prefix)
159 {
160 if (data->bitmask == XT_IPVS_IPVS_PROPERTY) {
161 if (data->invert & XT_IPVS_IPVS_PROPERTY)
162 printf(" !");
163 printf(" %sipvs", prefix);
164 }
165
166 if (data->bitmask & XT_IPVS_PROTO) {
167 if (data->invert & XT_IPVS_PROTO)
168 printf(" !");
169 printf(" %sproto %u", prefix, data->l4proto);
170 }
171
172 if (data->bitmask & XT_IPVS_VADDR) {
173 if (data->invert & XT_IPVS_VADDR)
174 printf(" !");
175
176 printf(" %svaddr", prefix);
177 ipvs_mt_dump_addr(&data->vaddr, &data->vmask, family, numeric);
178 }
179
180 if (data->bitmask & XT_IPVS_VPORT) {
181 if (data->invert & XT_IPVS_VPORT)
182 printf(" !");
183
184 printf(" %svport %u", prefix, ntohs(data->vport));
185 }
186
187 if (data->bitmask & XT_IPVS_DIR) {
188 if (data->invert & XT_IPVS_DIR)
189 printf(" %svdir REPLY", prefix);
190 else
191 printf(" %svdir ORIGINAL", prefix);
192 }
193
194 if (data->bitmask & XT_IPVS_METHOD) {
195 if (data->invert & XT_IPVS_METHOD)
196 printf(" !");
197
198 printf(" %svmethod", prefix);
199 switch (data->fwd_method) {
200 case IP_VS_CONN_F_DROUTE:
201 printf(" GATE");
202 break;
203 case IP_VS_CONN_F_TUNNEL:
204 printf(" IPIP");
205 break;
206 case IP_VS_CONN_F_MASQ:
207 printf(" MASQ");
208 break;
209 default:
210 /* Hu? */
211 printf(" UNKNOWN");
212 break;
213 }
214 }
215
216 if (data->bitmask & XT_IPVS_VPORTCTL) {
217 if (data->invert & XT_IPVS_VPORTCTL)
218 printf(" !");
219
220 printf(" %svportctl %u", prefix, ntohs(data->vportctl));
221 }
222 }
223
ipvs_mt4_print(const void * ip,const struct xt_entry_match * match,int numeric)224 static void ipvs_mt4_print(const void *ip, const struct xt_entry_match *match,
225 int numeric)
226 {
227 const struct xt_ipvs_mtinfo *data = (const void *)match->data;
228 ipvs_mt_dump(ip, data, NFPROTO_IPV4, numeric, "");
229 }
230
ipvs_mt6_print(const void * ip,const struct xt_entry_match * match,int numeric)231 static void ipvs_mt6_print(const void *ip, const struct xt_entry_match *match,
232 int numeric)
233 {
234 const struct xt_ipvs_mtinfo *data = (const void *)match->data;
235 ipvs_mt_dump(ip, data, NFPROTO_IPV6, numeric, "");
236 }
237
ipvs_mt4_save(const void * ip,const struct xt_entry_match * match)238 static void ipvs_mt4_save(const void *ip, const struct xt_entry_match *match)
239 {
240 const struct xt_ipvs_mtinfo *data = (const void *)match->data;
241 ipvs_mt_dump(ip, data, NFPROTO_IPV4, true, "--");
242 }
243
ipvs_mt6_save(const void * ip,const struct xt_entry_match * match)244 static void ipvs_mt6_save(const void *ip, const struct xt_entry_match *match)
245 {
246 const struct xt_ipvs_mtinfo *data = (const void *)match->data;
247 ipvs_mt_dump(ip, data, NFPROTO_IPV6, true, "--");
248 }
249
250 static struct xtables_match ipvs_matches_reg[] = {
251 {
252 .version = XTABLES_VERSION,
253 .name = "ipvs",
254 .revision = 0,
255 .family = NFPROTO_IPV4,
256 .size = XT_ALIGN(sizeof(struct xt_ipvs_mtinfo)),
257 .userspacesize = XT_ALIGN(sizeof(struct xt_ipvs_mtinfo)),
258 .help = ipvs_mt_help,
259 .x6_parse = ipvs_mt_parse,
260 .x6_fcheck = ipvs_mt_check,
261 .print = ipvs_mt4_print,
262 .save = ipvs_mt4_save,
263 .x6_options = ipvs_mt_opts,
264 },
265 {
266 .version = XTABLES_VERSION,
267 .name = "ipvs",
268 .revision = 0,
269 .family = NFPROTO_IPV6,
270 .size = XT_ALIGN(sizeof(struct xt_ipvs_mtinfo)),
271 .userspacesize = XT_ALIGN(sizeof(struct xt_ipvs_mtinfo)),
272 .help = ipvs_mt_help,
273 .x6_parse = ipvs_mt_parse,
274 .x6_fcheck = ipvs_mt_check,
275 .print = ipvs_mt6_print,
276 .save = ipvs_mt6_save,
277 .x6_options = ipvs_mt_opts,
278 },
279 };
280
_init(void)281 void _init(void)
282 {
283 xtables_register_matches(ipvs_matches_reg,
284 ARRAY_SIZE(ipvs_matches_reg));
285 }
286