xref: /aosp_15_r20/external/iptables/extensions/libxt_ipvs.c (revision a71a954618bbadd4a345637e5edcf36eec826889)
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