• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt-6.x.4708/linux/linux-2.6.36/net/netfilter/
1/*
2 *	xt_ipvs - kernel module to match IPVS connection properties
3 *
4 *	Author: Hannes Eder <heder@google.com>
5 */
6
7#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
8
9#include <linux/module.h>
10#include <linux/moduleparam.h>
11#include <linux/spinlock.h>
12#include <linux/skbuff.h>
13#ifdef CONFIG_IP_VS_IPV6
14#include <net/ipv6.h>
15#endif
16#include <linux/ip_vs.h>
17#include <linux/types.h>
18#include <linux/netfilter/x_tables.h>
19#include <linux/netfilter/x_tables.h>
20#include <linux/netfilter/xt_ipvs.h>
21#include <net/netfilter/nf_conntrack.h>
22
23#include <net/ip_vs.h>
24
25MODULE_AUTHOR("Hannes Eder <heder@google.com>");
26MODULE_DESCRIPTION("Xtables: match IPVS connection properties");
27MODULE_LICENSE("GPL");
28MODULE_ALIAS("ipt_ipvs");
29MODULE_ALIAS("ip6t_ipvs");
30
31/* borrowed from xt_conntrack */
32static bool ipvs_mt_addrcmp(const union nf_inet_addr *kaddr,
33			    const union nf_inet_addr *uaddr,
34			    const union nf_inet_addr *umask,
35			    unsigned int l3proto)
36{
37	if (l3proto == NFPROTO_IPV4)
38		return ((kaddr->ip ^ uaddr->ip) & umask->ip) == 0;
39#ifdef CONFIG_IP_VS_IPV6
40	else if (l3proto == NFPROTO_IPV6)
41		return ipv6_masked_addr_cmp(&kaddr->in6, &umask->in6,
42		       &uaddr->in6) == 0;
43#endif
44	else
45		return false;
46}
47
48static bool
49ipvs_mt(const struct sk_buff *skb, struct xt_action_param *par)
50{
51	const struct xt_ipvs_mtinfo *data = par->matchinfo;
52	/* ipvs_mt_check ensures that family is only NFPROTO_IPV[46]. */
53	const u_int8_t family = par->family;
54	struct ip_vs_iphdr iph;
55	struct ip_vs_protocol *pp;
56	struct ip_vs_conn *cp;
57	bool match = true;
58
59	if (data->bitmask == XT_IPVS_IPVS_PROPERTY) {
60		match = skb->ipvs_property ^
61			!!(data->invert & XT_IPVS_IPVS_PROPERTY);
62		goto out;
63	}
64
65	/* other flags than XT_IPVS_IPVS_PROPERTY are set */
66	if (!skb->ipvs_property) {
67		match = false;
68		goto out;
69	}
70
71	ip_vs_fill_iphdr(family, skb_network_header(skb), &iph);
72
73	if (data->bitmask & XT_IPVS_PROTO)
74		if ((iph.protocol == data->l4proto) ^
75		    !(data->invert & XT_IPVS_PROTO)) {
76			match = false;
77			goto out;
78		}
79
80	pp = ip_vs_proto_get(iph.protocol);
81	if (unlikely(!pp)) {
82		match = false;
83		goto out;
84	}
85
86	/*
87	 * Check if the packet belongs to an existing entry
88	 */
89	cp = pp->conn_out_get(family, skb, pp, &iph, iph.len, 1 /* inverse */);
90	if (unlikely(cp == NULL)) {
91		match = false;
92		goto out;
93	}
94
95	/*
96	 * We found a connection, i.e. ct != 0, make sure to call
97	 * __ip_vs_conn_put before returning.  In our case jump to out_put_con.
98	 */
99
100	if (data->bitmask & XT_IPVS_VPORT)
101		if ((cp->vport == data->vport) ^
102		    !(data->invert & XT_IPVS_VPORT)) {
103			match = false;
104			goto out_put_cp;
105		}
106
107	if (data->bitmask & XT_IPVS_VPORTCTL)
108		if ((cp->control != NULL &&
109		     cp->control->vport == data->vportctl) ^
110		    !(data->invert & XT_IPVS_VPORTCTL)) {
111			match = false;
112			goto out_put_cp;
113		}
114
115	if (data->bitmask & XT_IPVS_DIR) {
116		enum ip_conntrack_info ctinfo;
117		struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
118
119		if (ct == NULL || nf_ct_is_untracked(ct)) {
120			match = false;
121			goto out_put_cp;
122		}
123
124		if ((ctinfo >= IP_CT_IS_REPLY) ^
125		    !!(data->invert & XT_IPVS_DIR)) {
126			match = false;
127			goto out_put_cp;
128		}
129	}
130
131	if (data->bitmask & XT_IPVS_METHOD)
132		if (((cp->flags & IP_VS_CONN_F_FWD_MASK) == data->fwd_method) ^
133		    !(data->invert & XT_IPVS_METHOD)) {
134			match = false;
135			goto out_put_cp;
136		}
137
138	if (data->bitmask & XT_IPVS_VADDR) {
139		if (ipvs_mt_addrcmp(&cp->vaddr, &data->vaddr,
140				    &data->vmask, family) ^
141		    !(data->invert & XT_IPVS_VADDR)) {
142			match = false;
143			goto out_put_cp;
144		}
145	}
146
147out_put_cp:
148	__ip_vs_conn_put(cp);
149out:
150	pr_debug("match=%d\n", match);
151	return match;
152}
153
154static int ipvs_mt_check(const struct xt_mtchk_param *par)
155{
156	if (par->family != NFPROTO_IPV4
157#ifdef CONFIG_IP_VS_IPV6
158	    && par->family != NFPROTO_IPV6
159#endif
160		) {
161		pr_info("protocol family %u not supported\n", par->family);
162		return -EINVAL;
163	}
164
165	return 0;
166}
167
168static struct xt_match xt_ipvs_mt_reg __read_mostly = {
169	.name       = "ipvs",
170	.revision   = 0,
171	.family     = NFPROTO_UNSPEC,
172	.match      = ipvs_mt,
173	.checkentry = ipvs_mt_check,
174	.matchsize  = XT_ALIGN(sizeof(struct xt_ipvs_mtinfo)),
175	.me         = THIS_MODULE,
176};
177
178static int __init ipvs_mt_init(void)
179{
180	return xt_register_match(&xt_ipvs_mt_reg);
181}
182
183static void __exit ipvs_mt_exit(void)
184{
185	xt_unregister_match(&xt_ipvs_mt_reg);
186}
187
188module_init(ipvs_mt_init);
189module_exit(ipvs_mt_exit);
190