x86/smpboot: Init apic mapping before usage
[cascardo/linux.git] / net / netfilter / xt_ipvs.c
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/xt_ipvs.h>
20 #include <net/netfilter/nf_conntrack.h>
21
22 #include <net/ip_vs.h>
23
24 MODULE_AUTHOR("Hannes Eder <heder@google.com>");
25 MODULE_DESCRIPTION("Xtables: match IPVS connection properties");
26 MODULE_LICENSE("GPL");
27 MODULE_ALIAS("ipt_ipvs");
28 MODULE_ALIAS("ip6t_ipvs");
29
30 /* borrowed from xt_conntrack */
31 static 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
47 static bool
48 ipvs_mt(const struct sk_buff *skb, struct xt_action_param *par)
49 {
50         const struct xt_ipvs_mtinfo *data = par->matchinfo;
51         struct netns_ipvs *ipvs = net_ipvs(par->net);
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_iph_skb(family, skb, true, &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(ipvs, family, skb, &iph);
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);