Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * xt_HMARK - Netfilter module to set mark by means of hashing
0004  *
0005  * (C) 2012 by Hans Schillstrom <hans.schillstrom@ericsson.com>
0006  * (C) 2012 by Pablo Neira Ayuso <pablo@netfilter.org>
0007  */
0008 
0009 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
0010 
0011 #include <linux/module.h>
0012 #include <linux/skbuff.h>
0013 #include <linux/icmp.h>
0014 
0015 #include <linux/netfilter/x_tables.h>
0016 #include <linux/netfilter/xt_HMARK.h>
0017 
0018 #include <net/ip.h>
0019 #if IS_ENABLED(CONFIG_NF_CONNTRACK)
0020 #include <net/netfilter/nf_conntrack.h>
0021 #endif
0022 #if IS_ENABLED(CONFIG_IP6_NF_IPTABLES)
0023 #include <net/ipv6.h>
0024 #include <linux/netfilter_ipv6/ip6_tables.h>
0025 #endif
0026 
0027 MODULE_LICENSE("GPL");
0028 MODULE_AUTHOR("Hans Schillstrom <hans.schillstrom@ericsson.com>");
0029 MODULE_DESCRIPTION("Xtables: packet marking using hash calculation");
0030 MODULE_ALIAS("ipt_HMARK");
0031 MODULE_ALIAS("ip6t_HMARK");
0032 
0033 struct hmark_tuple {
0034     __be32          src;
0035     __be32          dst;
0036     union hmark_ports   uports;
0037     u8          proto;
0038 };
0039 
0040 static inline __be32 hmark_addr6_mask(const __be32 *addr32, const __be32 *mask)
0041 {
0042     return (addr32[0] & mask[0]) ^
0043            (addr32[1] & mask[1]) ^
0044            (addr32[2] & mask[2]) ^
0045            (addr32[3] & mask[3]);
0046 }
0047 
0048 static inline __be32
0049 hmark_addr_mask(int l3num, const __be32 *addr32, const __be32 *mask)
0050 {
0051     switch (l3num) {
0052     case AF_INET:
0053         return *addr32 & *mask;
0054     case AF_INET6:
0055         return hmark_addr6_mask(addr32, mask);
0056     }
0057     return 0;
0058 }
0059 
0060 static inline void hmark_swap_ports(union hmark_ports *uports,
0061                     const struct xt_hmark_info *info)
0062 {
0063     union hmark_ports hp;
0064     u16 src, dst;
0065 
0066     hp.b32 = (uports->b32 & info->port_mask.b32) | info->port_set.b32;
0067     src = ntohs(hp.b16.src);
0068     dst = ntohs(hp.b16.dst);
0069 
0070     if (dst > src)
0071         uports->v32 = (dst << 16) | src;
0072     else
0073         uports->v32 = (src << 16) | dst;
0074 }
0075 
0076 static int
0077 hmark_ct_set_htuple(const struct sk_buff *skb, struct hmark_tuple *t,
0078             const struct xt_hmark_info *info)
0079 {
0080 #if IS_ENABLED(CONFIG_NF_CONNTRACK)
0081     enum ip_conntrack_info ctinfo;
0082     struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
0083     struct nf_conntrack_tuple *otuple;
0084     struct nf_conntrack_tuple *rtuple;
0085 
0086     if (ct == NULL)
0087         return -1;
0088 
0089     otuple = &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple;
0090     rtuple = &ct->tuplehash[IP_CT_DIR_REPLY].tuple;
0091 
0092     t->src = hmark_addr_mask(otuple->src.l3num, otuple->src.u3.ip6,
0093                  info->src_mask.ip6);
0094     t->dst = hmark_addr_mask(otuple->src.l3num, rtuple->src.u3.ip6,
0095                  info->dst_mask.ip6);
0096 
0097     if (info->flags & XT_HMARK_FLAG(XT_HMARK_METHOD_L3))
0098         return 0;
0099 
0100     t->proto = nf_ct_protonum(ct);
0101     if (t->proto != IPPROTO_ICMP) {
0102         t->uports.b16.src = otuple->src.u.all;
0103         t->uports.b16.dst = rtuple->src.u.all;
0104         hmark_swap_ports(&t->uports, info);
0105     }
0106 
0107     return 0;
0108 #else
0109     return -1;
0110 #endif
0111 }
0112 
0113 /* This hash function is endian independent, to ensure consistent hashing if
0114  * the cluster is composed of big and little endian systems. */
0115 static inline u32
0116 hmark_hash(struct hmark_tuple *t, const struct xt_hmark_info *info)
0117 {
0118     u32 hash;
0119     u32 src = ntohl(t->src);
0120     u32 dst = ntohl(t->dst);
0121 
0122     if (dst < src)
0123         swap(src, dst);
0124 
0125     hash = jhash_3words(src, dst, t->uports.v32, info->hashrnd);
0126     hash = hash ^ (t->proto & info->proto_mask);
0127 
0128     return reciprocal_scale(hash, info->hmodulus) + info->hoffset;
0129 }
0130 
0131 static void
0132 hmark_set_tuple_ports(const struct sk_buff *skb, unsigned int nhoff,
0133               struct hmark_tuple *t, const struct xt_hmark_info *info)
0134 {
0135     int protoff;
0136 
0137     protoff = proto_ports_offset(t->proto);
0138     if (protoff < 0)
0139         return;
0140 
0141     nhoff += protoff;
0142     if (skb_copy_bits(skb, nhoff, &t->uports, sizeof(t->uports)) < 0)
0143         return;
0144 
0145     hmark_swap_ports(&t->uports, info);
0146 }
0147 
0148 #if IS_ENABLED(CONFIG_IP6_NF_IPTABLES)
0149 static int get_inner6_hdr(const struct sk_buff *skb, int *offset)
0150 {
0151     struct icmp6hdr *icmp6h, _ih6;
0152 
0153     icmp6h = skb_header_pointer(skb, *offset, sizeof(_ih6), &_ih6);
0154     if (icmp6h == NULL)
0155         return 0;
0156 
0157     if (icmp6h->icmp6_type && icmp6h->icmp6_type < 128) {
0158         *offset += sizeof(struct icmp6hdr);
0159         return 1;
0160     }
0161     return 0;
0162 }
0163 
0164 static int
0165 hmark_pkt_set_htuple_ipv6(const struct sk_buff *skb, struct hmark_tuple *t,
0166               const struct xt_hmark_info *info)
0167 {
0168     struct ipv6hdr *ip6, _ip6;
0169     int flag = IP6_FH_F_AUTH;
0170     unsigned int nhoff = 0;
0171     u16 fragoff = 0;
0172     int nexthdr;
0173 
0174     ip6 = (struct ipv6hdr *) (skb->data + skb_network_offset(skb));
0175     nexthdr = ipv6_find_hdr(skb, &nhoff, -1, &fragoff, &flag);
0176     if (nexthdr < 0)
0177         return 0;
0178     /* No need to check for icmp errors on fragments */
0179     if ((flag & IP6_FH_F_FRAG) || (nexthdr != IPPROTO_ICMPV6))
0180         goto noicmp;
0181     /* Use inner header in case of ICMP errors */
0182     if (get_inner6_hdr(skb, &nhoff)) {
0183         ip6 = skb_header_pointer(skb, nhoff, sizeof(_ip6), &_ip6);
0184         if (ip6 == NULL)
0185             return -1;
0186         /* If AH present, use SPI like in ESP. */
0187         flag = IP6_FH_F_AUTH;
0188         nexthdr = ipv6_find_hdr(skb, &nhoff, -1, &fragoff, &flag);
0189         if (nexthdr < 0)
0190             return -1;
0191     }
0192 noicmp:
0193     t->src = hmark_addr6_mask(ip6->saddr.s6_addr32, info->src_mask.ip6);
0194     t->dst = hmark_addr6_mask(ip6->daddr.s6_addr32, info->dst_mask.ip6);
0195 
0196     if (info->flags & XT_HMARK_FLAG(XT_HMARK_METHOD_L3))
0197         return 0;
0198 
0199     t->proto = nexthdr;
0200     if (t->proto == IPPROTO_ICMPV6)
0201         return 0;
0202 
0203     if (flag & IP6_FH_F_FRAG)
0204         return 0;
0205 
0206     hmark_set_tuple_ports(skb, nhoff, t, info);
0207     return 0;
0208 }
0209 
0210 static unsigned int
0211 hmark_tg_v6(struct sk_buff *skb, const struct xt_action_param *par)
0212 {
0213     const struct xt_hmark_info *info = par->targinfo;
0214     struct hmark_tuple t;
0215 
0216     memset(&t, 0, sizeof(struct hmark_tuple));
0217 
0218     if (info->flags & XT_HMARK_FLAG(XT_HMARK_CT)) {
0219         if (hmark_ct_set_htuple(skb, &t, info) < 0)
0220             return XT_CONTINUE;
0221     } else {
0222         if (hmark_pkt_set_htuple_ipv6(skb, &t, info) < 0)
0223             return XT_CONTINUE;
0224     }
0225 
0226     skb->mark = hmark_hash(&t, info);
0227     return XT_CONTINUE;
0228 }
0229 #endif
0230 
0231 static int get_inner_hdr(const struct sk_buff *skb, int iphsz, int *nhoff)
0232 {
0233     const struct icmphdr *icmph;
0234     struct icmphdr _ih;
0235 
0236     /* Not enough header? */
0237     icmph = skb_header_pointer(skb, *nhoff + iphsz, sizeof(_ih), &_ih);
0238     if (icmph == NULL || icmph->type > NR_ICMP_TYPES)
0239         return 0;
0240 
0241     /* Error message? */
0242     if (!icmp_is_err(icmph->type))
0243         return 0;
0244 
0245     *nhoff += iphsz + sizeof(_ih);
0246     return 1;
0247 }
0248 
0249 static int
0250 hmark_pkt_set_htuple_ipv4(const struct sk_buff *skb, struct hmark_tuple *t,
0251               const struct xt_hmark_info *info)
0252 {
0253     struct iphdr *ip, _ip;
0254     int nhoff = skb_network_offset(skb);
0255 
0256     ip = (struct iphdr *) (skb->data + nhoff);
0257     if (ip->protocol == IPPROTO_ICMP) {
0258         /* Use inner header in case of ICMP errors */
0259         if (get_inner_hdr(skb, ip->ihl * 4, &nhoff)) {
0260             ip = skb_header_pointer(skb, nhoff, sizeof(_ip), &_ip);
0261             if (ip == NULL)
0262                 return -1;
0263         }
0264     }
0265 
0266     t->src = ip->saddr & info->src_mask.ip;
0267     t->dst = ip->daddr & info->dst_mask.ip;
0268 
0269     if (info->flags & XT_HMARK_FLAG(XT_HMARK_METHOD_L3))
0270         return 0;
0271 
0272     t->proto = ip->protocol;
0273 
0274     /* ICMP has no ports, skip */
0275     if (t->proto == IPPROTO_ICMP)
0276         return 0;
0277 
0278     /* follow-up fragments don't contain ports, skip all fragments */
0279     if (ip_is_fragment(ip))
0280         return 0;
0281 
0282     hmark_set_tuple_ports(skb, (ip->ihl * 4) + nhoff, t, info);
0283 
0284     return 0;
0285 }
0286 
0287 static unsigned int
0288 hmark_tg_v4(struct sk_buff *skb, const struct xt_action_param *par)
0289 {
0290     const struct xt_hmark_info *info = par->targinfo;
0291     struct hmark_tuple t;
0292 
0293     memset(&t, 0, sizeof(struct hmark_tuple));
0294 
0295     if (info->flags & XT_HMARK_FLAG(XT_HMARK_CT)) {
0296         if (hmark_ct_set_htuple(skb, &t, info) < 0)
0297             return XT_CONTINUE;
0298     } else {
0299         if (hmark_pkt_set_htuple_ipv4(skb, &t, info) < 0)
0300             return XT_CONTINUE;
0301     }
0302 
0303     skb->mark = hmark_hash(&t, info);
0304     return XT_CONTINUE;
0305 }
0306 
0307 static int hmark_tg_check(const struct xt_tgchk_param *par)
0308 {
0309     const struct xt_hmark_info *info = par->targinfo;
0310     const char *errmsg = "proto mask must be zero with L3 mode";
0311 
0312     if (!info->hmodulus)
0313         return -EINVAL;
0314 
0315     if (info->proto_mask &&
0316         (info->flags & XT_HMARK_FLAG(XT_HMARK_METHOD_L3)))
0317         goto err;
0318 
0319     if (info->flags & XT_HMARK_FLAG(XT_HMARK_SPI_MASK) &&
0320         (info->flags & (XT_HMARK_FLAG(XT_HMARK_SPORT_MASK) |
0321                  XT_HMARK_FLAG(XT_HMARK_DPORT_MASK))))
0322         return -EINVAL;
0323 
0324     if (info->flags & XT_HMARK_FLAG(XT_HMARK_SPI) &&
0325         (info->flags & (XT_HMARK_FLAG(XT_HMARK_SPORT) |
0326                  XT_HMARK_FLAG(XT_HMARK_DPORT)))) {
0327         errmsg = "spi-set and port-set can't be combined";
0328         goto err;
0329     }
0330     return 0;
0331 err:
0332     pr_info_ratelimited("%s\n", errmsg);
0333     return -EINVAL;
0334 }
0335 
0336 static struct xt_target hmark_tg_reg[] __read_mostly = {
0337     {
0338         .name       = "HMARK",
0339         .family     = NFPROTO_IPV4,
0340         .target     = hmark_tg_v4,
0341         .targetsize = sizeof(struct xt_hmark_info),
0342         .checkentry = hmark_tg_check,
0343         .me     = THIS_MODULE,
0344     },
0345 #if IS_ENABLED(CONFIG_IP6_NF_IPTABLES)
0346     {
0347         .name       = "HMARK",
0348         .family     = NFPROTO_IPV6,
0349         .target     = hmark_tg_v6,
0350         .targetsize = sizeof(struct xt_hmark_info),
0351         .checkentry = hmark_tg_check,
0352         .me     = THIS_MODULE,
0353     },
0354 #endif
0355 };
0356 
0357 static int __init hmark_tg_init(void)
0358 {
0359     return xt_register_targets(hmark_tg_reg, ARRAY_SIZE(hmark_tg_reg));
0360 }
0361 
0362 static void __exit hmark_tg_exit(void)
0363 {
0364     xt_unregister_targets(hmark_tg_reg, ARRAY_SIZE(hmark_tg_reg));
0365 }
0366 
0367 module_init(hmark_tg_init);
0368 module_exit(hmark_tg_exit);