diff options
author | Hannes Eder <heder@google.com> | 2010-07-23 06:42:58 -0400 |
---|---|---|
committer | Patrick McHardy <kaber@trash.net> | 2010-07-23 06:42:58 -0400 |
commit | 9c3e1c39679144c250dda95098333ecb5f1f407a (patch) | |
tree | 3a734fdf60ff07d46a14cbc0a67c134e6cfe0e80 /net | |
parent | 22cb516696304a9b85892b18c483a27d97cfa51b (diff) |
netfilter: xt_ipvs (netfilter matcher for IPVS)
This implements the kernel-space side of the netfilter matcher xt_ipvs.
[ minor fixes by Simon Horman <horms@verge.net.au> ]
Signed-off-by: Hannes Eder <heder@google.com>
Signed-off-by: Simon Horman <horms@verge.net.au>
[ Patrick: added xt_ipvs.h to Kbuild ]
Signed-off-by: Patrick McHardy <kaber@trash.net>
Diffstat (limited to 'net')
-rw-r--r-- | net/netfilter/Kconfig | 10 | ||||
-rw-r--r-- | net/netfilter/Makefile | 1 | ||||
-rw-r--r-- | net/netfilter/ipvs/ip_vs_proto.c | 1 | ||||
-rw-r--r-- | net/netfilter/xt_ipvs.c | 189 |
4 files changed, 201 insertions, 0 deletions
diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig index 5fb8efa84df3..551b58419df9 100644 --- a/net/netfilter/Kconfig +++ b/net/netfilter/Kconfig | |||
@@ -742,6 +742,16 @@ config NETFILTER_XT_MATCH_IPRANGE | |||
742 | 742 | ||
743 | If unsure, say M. | 743 | If unsure, say M. |
744 | 744 | ||
745 | config NETFILTER_XT_MATCH_IPVS | ||
746 | tristate '"ipvs" match support' | ||
747 | depends on IP_VS | ||
748 | depends on NETFILTER_ADVANCED | ||
749 | depends on NF_CONNTRACK | ||
750 | help | ||
751 | This option allows you to match against IPVS properties of a packet. | ||
752 | |||
753 | If unsure, say N. | ||
754 | |||
745 | config NETFILTER_XT_MATCH_LENGTH | 755 | config NETFILTER_XT_MATCH_LENGTH |
746 | tristate '"length" match support' | 756 | tristate '"length" match support' |
747 | depends on NETFILTER_ADVANCED | 757 | depends on NETFILTER_ADVANCED |
diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile index 36ef8e63be1e..4366c79a6683 100644 --- a/net/netfilter/Makefile +++ b/net/netfilter/Makefile | |||
@@ -77,6 +77,7 @@ obj-$(CONFIG_NETFILTER_XT_MATCH_HASHLIMIT) += xt_hashlimit.o | |||
77 | obj-$(CONFIG_NETFILTER_XT_MATCH_HELPER) += xt_helper.o | 77 | obj-$(CONFIG_NETFILTER_XT_MATCH_HELPER) += xt_helper.o |
78 | obj-$(CONFIG_NETFILTER_XT_MATCH_HL) += xt_hl.o | 78 | obj-$(CONFIG_NETFILTER_XT_MATCH_HL) += xt_hl.o |
79 | obj-$(CONFIG_NETFILTER_XT_MATCH_IPRANGE) += xt_iprange.o | 79 | obj-$(CONFIG_NETFILTER_XT_MATCH_IPRANGE) += xt_iprange.o |
80 | obj-$(CONFIG_NETFILTER_XT_MATCH_IPVS) += xt_ipvs.o | ||
80 | obj-$(CONFIG_NETFILTER_XT_MATCH_LENGTH) += xt_length.o | 81 | obj-$(CONFIG_NETFILTER_XT_MATCH_LENGTH) += xt_length.o |
81 | obj-$(CONFIG_NETFILTER_XT_MATCH_LIMIT) += xt_limit.o | 82 | obj-$(CONFIG_NETFILTER_XT_MATCH_LIMIT) += xt_limit.o |
82 | obj-$(CONFIG_NETFILTER_XT_MATCH_MAC) += xt_mac.o | 83 | obj-$(CONFIG_NETFILTER_XT_MATCH_MAC) += xt_mac.o |
diff --git a/net/netfilter/ipvs/ip_vs_proto.c b/net/netfilter/ipvs/ip_vs_proto.c index 2d3d5e4b35f8..027f654799fe 100644 --- a/net/netfilter/ipvs/ip_vs_proto.c +++ b/net/netfilter/ipvs/ip_vs_proto.c | |||
@@ -98,6 +98,7 @@ struct ip_vs_protocol * ip_vs_proto_get(unsigned short proto) | |||
98 | 98 | ||
99 | return NULL; | 99 | return NULL; |
100 | } | 100 | } |
101 | EXPORT_SYMBOL(ip_vs_proto_get); | ||
101 | 102 | ||
102 | 103 | ||
103 | /* | 104 | /* |
diff --git a/net/netfilter/xt_ipvs.c b/net/netfilter/xt_ipvs.c new file mode 100644 index 000000000000..7a4d66db95ae --- /dev/null +++ b/net/netfilter/xt_ipvs.c | |||
@@ -0,0 +1,189 @@ | |||
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 | |||
25 | MODULE_AUTHOR("Hannes Eder <heder@google.com>"); | ||
26 | MODULE_DESCRIPTION("Xtables: match IPVS connection properties"); | ||
27 | MODULE_LICENSE("GPL"); | ||
28 | MODULE_ALIAS("ipt_ipvs"); | ||
29 | MODULE_ALIAS("ip6t_ipvs"); | ||
30 | |||
31 | /* borrowed from xt_conntrack */ | ||
32 | static 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 | |||
48 | static bool | ||
49 | ipvs_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 | |||
147 | out_put_cp: | ||
148 | __ip_vs_conn_put(cp); | ||
149 | out: | ||
150 | pr_debug("match=%d\n", match); | ||
151 | return match; | ||
152 | } | ||
153 | |||
154 | static 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 | |||
168 | static 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 | |||
178 | static int __init ipvs_mt_init(void) | ||
179 | { | ||
180 | return xt_register_match(&xt_ipvs_mt_reg); | ||
181 | } | ||
182 | |||
183 | static void __exit ipvs_mt_exit(void) | ||
184 | { | ||
185 | xt_unregister_match(&xt_ipvs_mt_reg); | ||
186 | } | ||
187 | |||
188 | module_init(ipvs_mt_init); | ||
189 | module_exit(ipvs_mt_exit); | ||