Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  *  xt_ipvs - kernel module to match IPVS connection properties
0004  *
0005  *  Author: Hannes Eder <heder@google.com>
0006  */
0007 
0008 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
0009 
0010 #include <linux/module.h>
0011 #include <linux/moduleparam.h>
0012 #include <linux/spinlock.h>
0013 #include <linux/skbuff.h>
0014 #ifdef CONFIG_IP_VS_IPV6
0015 #include <net/ipv6.h>
0016 #endif
0017 #include <linux/ip_vs.h>
0018 #include <linux/types.h>
0019 #include <linux/netfilter/x_tables.h>
0020 #include <linux/netfilter/xt_ipvs.h>
0021 #include <net/netfilter/nf_conntrack.h>
0022 
0023 #include <net/ip_vs.h>
0024 
0025 MODULE_AUTHOR("Hannes Eder <heder@google.com>");
0026 MODULE_DESCRIPTION("Xtables: match IPVS connection properties");
0027 MODULE_LICENSE("GPL");
0028 MODULE_ALIAS("ipt_ipvs");
0029 MODULE_ALIAS("ip6t_ipvs");
0030 
0031 /* borrowed from xt_conntrack */
0032 static bool ipvs_mt_addrcmp(const union nf_inet_addr *kaddr,
0033                 const union nf_inet_addr *uaddr,
0034                 const union nf_inet_addr *umask,
0035                 unsigned int l3proto)
0036 {
0037     if (l3proto == NFPROTO_IPV4)
0038         return ((kaddr->ip ^ uaddr->ip) & umask->ip) == 0;
0039 #ifdef CONFIG_IP_VS_IPV6
0040     else if (l3proto == NFPROTO_IPV6)
0041         return ipv6_masked_addr_cmp(&kaddr->in6, &umask->in6,
0042                &uaddr->in6) == 0;
0043 #endif
0044     else
0045         return false;
0046 }
0047 
0048 static bool
0049 ipvs_mt(const struct sk_buff *skb, struct xt_action_param *par)
0050 {
0051     const struct xt_ipvs_mtinfo *data = par->matchinfo;
0052     struct netns_ipvs *ipvs = net_ipvs(xt_net(par));
0053     /* ipvs_mt_check ensures that family is only NFPROTO_IPV[46]. */
0054     const u_int8_t family = xt_family(par);
0055     struct ip_vs_iphdr iph;
0056     struct ip_vs_protocol *pp;
0057     struct ip_vs_conn *cp;
0058     bool match = true;
0059 
0060     if (data->bitmask == XT_IPVS_IPVS_PROPERTY) {
0061         match = skb->ipvs_property ^
0062             !!(data->invert & XT_IPVS_IPVS_PROPERTY);
0063         goto out;
0064     }
0065 
0066     /* other flags than XT_IPVS_IPVS_PROPERTY are set */
0067     if (!skb->ipvs_property) {
0068         match = false;
0069         goto out;
0070     }
0071 
0072     ip_vs_fill_iph_skb(family, skb, true, &iph);
0073 
0074     if (data->bitmask & XT_IPVS_PROTO)
0075         if ((iph.protocol == data->l4proto) ^
0076             !(data->invert & XT_IPVS_PROTO)) {
0077             match = false;
0078             goto out;
0079         }
0080 
0081     pp = ip_vs_proto_get(iph.protocol);
0082     if (unlikely(!pp)) {
0083         match = false;
0084         goto out;
0085     }
0086 
0087     /*
0088      * Check if the packet belongs to an existing entry
0089      */
0090     cp = pp->conn_out_get(ipvs, family, skb, &iph);
0091     if (unlikely(cp == NULL)) {
0092         match = false;
0093         goto out;
0094     }
0095 
0096     /*
0097      * We found a connection, i.e. ct != 0, make sure to call
0098      * __ip_vs_conn_put before returning.  In our case jump to out_put_con.
0099      */
0100 
0101     if (data->bitmask & XT_IPVS_VPORT)
0102         if ((cp->vport == data->vport) ^
0103             !(data->invert & XT_IPVS_VPORT)) {
0104             match = false;
0105             goto out_put_cp;
0106         }
0107 
0108     if (data->bitmask & XT_IPVS_VPORTCTL)
0109         if ((cp->control != NULL &&
0110              cp->control->vport == data->vportctl) ^
0111             !(data->invert & XT_IPVS_VPORTCTL)) {
0112             match = false;
0113             goto out_put_cp;
0114         }
0115 
0116     if (data->bitmask & XT_IPVS_DIR) {
0117         enum ip_conntrack_info ctinfo;
0118         struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
0119 
0120         if (ct == NULL) {
0121             match = false;
0122             goto out_put_cp;
0123         }
0124 
0125         if ((ctinfo >= IP_CT_IS_REPLY) ^
0126             !!(data->invert & XT_IPVS_DIR)) {
0127             match = false;
0128             goto out_put_cp;
0129         }
0130     }
0131 
0132     if (data->bitmask & XT_IPVS_METHOD)
0133         if (((cp->flags & IP_VS_CONN_F_FWD_MASK) == data->fwd_method) ^
0134             !(data->invert & XT_IPVS_METHOD)) {
0135             match = false;
0136             goto out_put_cp;
0137         }
0138 
0139     if (data->bitmask & XT_IPVS_VADDR) {
0140         if (ipvs_mt_addrcmp(&cp->vaddr, &data->vaddr,
0141                     &data->vmask, family) ^
0142             !(data->invert & XT_IPVS_VADDR)) {
0143             match = false;
0144             goto out_put_cp;
0145         }
0146     }
0147 
0148 out_put_cp:
0149     __ip_vs_conn_put(cp);
0150 out:
0151     pr_debug("match=%d\n", match);
0152     return match;
0153 }
0154 
0155 static int ipvs_mt_check(const struct xt_mtchk_param *par)
0156 {
0157     if (par->family != NFPROTO_IPV4
0158 #ifdef CONFIG_IP_VS_IPV6
0159         && par->family != NFPROTO_IPV6
0160 #endif
0161         ) {
0162         pr_info_ratelimited("protocol family %u not supported\n",
0163                     par->family);
0164         return -EINVAL;
0165     }
0166 
0167     return 0;
0168 }
0169 
0170 static struct xt_match xt_ipvs_mt_reg __read_mostly = {
0171     .name       = "ipvs",
0172     .revision   = 0,
0173     .family     = NFPROTO_UNSPEC,
0174     .match      = ipvs_mt,
0175     .checkentry = ipvs_mt_check,
0176     .matchsize  = XT_ALIGN(sizeof(struct xt_ipvs_mtinfo)),
0177     .me         = THIS_MODULE,
0178 };
0179 
0180 static int __init ipvs_mt_init(void)
0181 {
0182     return xt_register_match(&xt_ipvs_mt_reg);
0183 }
0184 
0185 static void __exit ipvs_mt_exit(void)
0186 {
0187     xt_unregister_match(&xt_ipvs_mt_reg);
0188 }
0189 
0190 module_init(ipvs_mt_init);
0191 module_exit(ipvs_mt_exit);