]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blame - net/netfilter/xt_ipvs.c
Merge tag 'armsoc-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc
[mirror_ubuntu-artful-kernel.git] / net / netfilter / xt_ipvs.c
CommitLineData
9c3e1c39
HE
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>
9c3e1c39
HE
19#include <linux/netfilter/xt_ipvs.h>
20#include <net/netfilter/nf_conntrack.h>
21
22#include <net/ip_vs.h>
23
24MODULE_AUTHOR("Hannes Eder <heder@google.com>");
25MODULE_DESCRIPTION("Xtables: match IPVS connection properties");
26MODULE_LICENSE("GPL");
27MODULE_ALIAS("ipt_ipvs");
28MODULE_ALIAS("ip6t_ipvs");
29
30/* borrowed from xt_conntrack */
31static bool ipvs_mt_addrcmp(const union nf_inet_addr *kaddr,
32 const union nf_inet_addr *uaddr,
33 const union nf_inet_addr *umask,
34 unsigned int l3proto)
35{
36 if (l3proto == NFPROTO_IPV4)
37 return ((kaddr->ip ^ uaddr->ip) & umask->ip) == 0;
38#ifdef CONFIG_IP_VS_IPV6
39 else if (l3proto == NFPROTO_IPV6)
40 return ipv6_masked_addr_cmp(&kaddr->in6, &umask->in6,
41 &uaddr->in6) == 0;
42#endif
43 else
44 return false;
45}
46
47static bool
48ipvs_mt(const struct sk_buff *skb, struct xt_action_param *par)
49{
50 const struct xt_ipvs_mtinfo *data = par->matchinfo;
613dbd95 51 struct netns_ipvs *ipvs = net_ipvs(xt_net(par));
9c3e1c39 52 /* ipvs_mt_check ensures that family is only NFPROTO_IPV[46]. */
613dbd95 53 const u_int8_t family = xt_family(par);
9c3e1c39
HE
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
4fd9beef 71 ip_vs_fill_iph_skb(family, skb, true, &iph);
9c3e1c39
HE
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 */
0cf705c8 89 cp = pp->conn_out_get(ipvs, family, skb, &iph);
9c3e1c39
HE
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
ab8bc7ed 119 if (ct == NULL) {
9c3e1c39
HE
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);