1f72e25a8SJan Engelhardt /* 2f72e25a8SJan Engelhardt * xt_iprange - Netfilter module to match IP address ranges 3f72e25a8SJan Engelhardt * 4f72e25a8SJan Engelhardt * (C) 2003 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu> 51a50c5a1SJan Engelhardt * (C) CC Computer Consultants GmbH, 2008 6f72e25a8SJan Engelhardt * 7f72e25a8SJan Engelhardt * This program is free software; you can redistribute it and/or modify 8f72e25a8SJan Engelhardt * it under the terms of the GNU General Public License version 2 as 9f72e25a8SJan Engelhardt * published by the Free Software Foundation. 10f72e25a8SJan Engelhardt */ 11f72e25a8SJan Engelhardt #include <linux/module.h> 12f72e25a8SJan Engelhardt #include <linux/skbuff.h> 13f72e25a8SJan Engelhardt #include <linux/ip.h> 141a50c5a1SJan Engelhardt #include <linux/ipv6.h> 15f72e25a8SJan Engelhardt #include <linux/netfilter/x_tables.h> 16f72e25a8SJan Engelhardt #include <linux/netfilter_ipv4/ipt_iprange.h> 17f72e25a8SJan Engelhardt 18f72e25a8SJan Engelhardt static bool 19f72e25a8SJan Engelhardt iprange_mt_v0(const struct sk_buff *skb, const struct net_device *in, 20f72e25a8SJan Engelhardt const struct net_device *out, const struct xt_match *match, 21f72e25a8SJan Engelhardt const void *matchinfo, int offset, unsigned int protoff, 22f72e25a8SJan Engelhardt bool *hotdrop) 23f72e25a8SJan Engelhardt { 24f72e25a8SJan Engelhardt const struct ipt_iprange_info *info = matchinfo; 25f72e25a8SJan Engelhardt const struct iphdr *iph = ip_hdr(skb); 26f72e25a8SJan Engelhardt 27f72e25a8SJan Engelhardt if (info->flags & IPRANGE_SRC) { 28f72e25a8SJan Engelhardt if ((ntohl(iph->saddr) < ntohl(info->src.min_ip) 29f72e25a8SJan Engelhardt || ntohl(iph->saddr) > ntohl(info->src.max_ip)) 30f72e25a8SJan Engelhardt ^ !!(info->flags & IPRANGE_SRC_INV)) { 31f72e25a8SJan Engelhardt pr_debug("src IP %u.%u.%u.%u NOT in range %s" 32f72e25a8SJan Engelhardt "%u.%u.%u.%u-%u.%u.%u.%u\n", 33f72e25a8SJan Engelhardt NIPQUAD(iph->saddr), 34f72e25a8SJan Engelhardt info->flags & IPRANGE_SRC_INV ? "(INV) " : "", 35f72e25a8SJan Engelhardt NIPQUAD(info->src.min_ip), 36f72e25a8SJan Engelhardt NIPQUAD(info->src.max_ip)); 37f72e25a8SJan Engelhardt return false; 38f72e25a8SJan Engelhardt } 39f72e25a8SJan Engelhardt } 40f72e25a8SJan Engelhardt if (info->flags & IPRANGE_DST) { 41f72e25a8SJan Engelhardt if ((ntohl(iph->daddr) < ntohl(info->dst.min_ip) 42f72e25a8SJan Engelhardt || ntohl(iph->daddr) > ntohl(info->dst.max_ip)) 43f72e25a8SJan Engelhardt ^ !!(info->flags & IPRANGE_DST_INV)) { 44f72e25a8SJan Engelhardt pr_debug("dst IP %u.%u.%u.%u NOT in range %s" 45f72e25a8SJan Engelhardt "%u.%u.%u.%u-%u.%u.%u.%u\n", 46f72e25a8SJan Engelhardt NIPQUAD(iph->daddr), 47f72e25a8SJan Engelhardt info->flags & IPRANGE_DST_INV ? "(INV) " : "", 48f72e25a8SJan Engelhardt NIPQUAD(info->dst.min_ip), 49f72e25a8SJan Engelhardt NIPQUAD(info->dst.max_ip)); 50f72e25a8SJan Engelhardt return false; 51f72e25a8SJan Engelhardt } 52f72e25a8SJan Engelhardt } 53f72e25a8SJan Engelhardt return true; 54f72e25a8SJan Engelhardt } 55f72e25a8SJan Engelhardt 561a50c5a1SJan Engelhardt static bool 571a50c5a1SJan Engelhardt iprange_mt4(const struct sk_buff *skb, const struct net_device *in, 581a50c5a1SJan Engelhardt const struct net_device *out, const struct xt_match *match, 591a50c5a1SJan Engelhardt const void *matchinfo, int offset, unsigned int protoff, 601a50c5a1SJan Engelhardt bool *hotdrop) 611a50c5a1SJan Engelhardt { 621a50c5a1SJan Engelhardt const struct xt_iprange_mtinfo *info = matchinfo; 631a50c5a1SJan Engelhardt const struct iphdr *iph = ip_hdr(skb); 641a50c5a1SJan Engelhardt bool m; 651a50c5a1SJan Engelhardt 661a50c5a1SJan Engelhardt if (info->flags & IPRANGE_SRC) { 671a50c5a1SJan Engelhardt m = ntohl(iph->saddr) < ntohl(info->src_min.ip); 681a50c5a1SJan Engelhardt m |= ntohl(iph->saddr) > ntohl(info->src_max.ip); 691a50c5a1SJan Engelhardt m ^= info->flags & IPRANGE_SRC_INV; 701a50c5a1SJan Engelhardt if (m) { 711a50c5a1SJan Engelhardt pr_debug("src IP " NIPQUAD_FMT " NOT in range %s" 721a50c5a1SJan Engelhardt NIPQUAD_FMT "-" NIPQUAD_FMT "\n", 731a50c5a1SJan Engelhardt NIPQUAD(iph->saddr), 741a50c5a1SJan Engelhardt (info->flags & IPRANGE_SRC_INV) ? "(INV) " : "", 751a50c5a1SJan Engelhardt NIPQUAD(info->src_max.ip), 761a50c5a1SJan Engelhardt NIPQUAD(info->src_max.ip)); 771a50c5a1SJan Engelhardt return false; 781a50c5a1SJan Engelhardt } 791a50c5a1SJan Engelhardt } 801a50c5a1SJan Engelhardt if (info->flags & IPRANGE_DST) { 811a50c5a1SJan Engelhardt m = ntohl(iph->daddr) < ntohl(info->dst_min.ip); 821a50c5a1SJan Engelhardt m |= ntohl(iph->daddr) > ntohl(info->dst_max.ip); 831a50c5a1SJan Engelhardt m ^= info->flags & IPRANGE_DST_INV; 841a50c5a1SJan Engelhardt if (m) { 851a50c5a1SJan Engelhardt pr_debug("dst IP " NIPQUAD_FMT " NOT in range %s" 861a50c5a1SJan Engelhardt NIPQUAD_FMT "-" NIPQUAD_FMT "\n", 871a50c5a1SJan Engelhardt NIPQUAD(iph->daddr), 881a50c5a1SJan Engelhardt (info->flags & IPRANGE_DST_INV) ? "(INV) " : "", 891a50c5a1SJan Engelhardt NIPQUAD(info->dst_min.ip), 901a50c5a1SJan Engelhardt NIPQUAD(info->dst_max.ip)); 911a50c5a1SJan Engelhardt return false; 921a50c5a1SJan Engelhardt } 931a50c5a1SJan Engelhardt } 941a50c5a1SJan Engelhardt return true; 951a50c5a1SJan Engelhardt } 961a50c5a1SJan Engelhardt 971a50c5a1SJan Engelhardt static inline int 981a50c5a1SJan Engelhardt iprange_ipv6_sub(const struct in6_addr *a, const struct in6_addr *b) 991a50c5a1SJan Engelhardt { 1001a50c5a1SJan Engelhardt unsigned int i; 1011a50c5a1SJan Engelhardt int r; 1021a50c5a1SJan Engelhardt 1031a50c5a1SJan Engelhardt for (i = 0; i < 4; ++i) { 1041a50c5a1SJan Engelhardt r = a->s6_addr32[i] - b->s6_addr32[i]; 1051a50c5a1SJan Engelhardt if (r != 0) 1061a50c5a1SJan Engelhardt return r; 1071a50c5a1SJan Engelhardt } 1081a50c5a1SJan Engelhardt 1091a50c5a1SJan Engelhardt return 0; 1101a50c5a1SJan Engelhardt } 1111a50c5a1SJan Engelhardt 1121a50c5a1SJan Engelhardt static bool 1131a50c5a1SJan Engelhardt iprange_mt6(const struct sk_buff *skb, const struct net_device *in, 1141a50c5a1SJan Engelhardt const struct net_device *out, const struct xt_match *match, 1151a50c5a1SJan Engelhardt const void *matchinfo, int offset, unsigned int protoff, 1161a50c5a1SJan Engelhardt bool *hotdrop) 1171a50c5a1SJan Engelhardt { 1181a50c5a1SJan Engelhardt const struct xt_iprange_mtinfo *info = matchinfo; 1191a50c5a1SJan Engelhardt const struct ipv6hdr *iph = ipv6_hdr(skb); 1201a50c5a1SJan Engelhardt bool m; 1211a50c5a1SJan Engelhardt 1221a50c5a1SJan Engelhardt if (info->flags & IPRANGE_SRC) { 1231a50c5a1SJan Engelhardt m = iprange_ipv6_sub(&iph->saddr, &info->src_min.in6) < 0; 1241a50c5a1SJan Engelhardt m |= iprange_ipv6_sub(&iph->saddr, &info->src_max.in6) > 0; 1251a50c5a1SJan Engelhardt m ^= info->flags & IPRANGE_SRC_INV; 1261a50c5a1SJan Engelhardt if (m) 1271a50c5a1SJan Engelhardt return false; 1281a50c5a1SJan Engelhardt } 1291a50c5a1SJan Engelhardt if (info->flags & IPRANGE_DST) { 1301a50c5a1SJan Engelhardt m = iprange_ipv6_sub(&iph->daddr, &info->dst_min.in6) < 0; 1311a50c5a1SJan Engelhardt m |= iprange_ipv6_sub(&iph->daddr, &info->dst_max.in6) > 0; 1321a50c5a1SJan Engelhardt m ^= info->flags & IPRANGE_DST_INV; 1331a50c5a1SJan Engelhardt if (m) 1341a50c5a1SJan Engelhardt return false; 1351a50c5a1SJan Engelhardt } 1361a50c5a1SJan Engelhardt return true; 1371a50c5a1SJan Engelhardt } 1381a50c5a1SJan Engelhardt 1391a50c5a1SJan Engelhardt static struct xt_match iprange_mt_reg[] __read_mostly = { 1401a50c5a1SJan Engelhardt { 141f72e25a8SJan Engelhardt .name = "iprange", 1421a50c5a1SJan Engelhardt .revision = 0, 143f72e25a8SJan Engelhardt .family = AF_INET, 144f72e25a8SJan Engelhardt .match = iprange_mt_v0, 145f72e25a8SJan Engelhardt .matchsize = sizeof(struct ipt_iprange_info), 1461a50c5a1SJan Engelhardt .me = THIS_MODULE, 1471a50c5a1SJan Engelhardt }, 1481a50c5a1SJan Engelhardt { 1491a50c5a1SJan Engelhardt .name = "iprange", 1501a50c5a1SJan Engelhardt .revision = 1, 1511a50c5a1SJan Engelhardt .family = AF_INET6, 1521a50c5a1SJan Engelhardt .match = iprange_mt4, 1531a50c5a1SJan Engelhardt .matchsize = sizeof(struct xt_iprange_mtinfo), 1541a50c5a1SJan Engelhardt .me = THIS_MODULE, 1551a50c5a1SJan Engelhardt }, 1561a50c5a1SJan Engelhardt { 1571a50c5a1SJan Engelhardt .name = "iprange", 1581a50c5a1SJan Engelhardt .revision = 1, 1591a50c5a1SJan Engelhardt .family = AF_INET6, 1601a50c5a1SJan Engelhardt .match = iprange_mt6, 1611a50c5a1SJan Engelhardt .matchsize = sizeof(struct xt_iprange_mtinfo), 1621a50c5a1SJan Engelhardt .me = THIS_MODULE, 1631a50c5a1SJan Engelhardt }, 164f72e25a8SJan Engelhardt }; 165f72e25a8SJan Engelhardt 166f72e25a8SJan Engelhardt static int __init iprange_mt_init(void) 167f72e25a8SJan Engelhardt { 1681a50c5a1SJan Engelhardt return xt_register_matches(iprange_mt_reg, ARRAY_SIZE(iprange_mt_reg)); 169f72e25a8SJan Engelhardt } 170f72e25a8SJan Engelhardt 171f72e25a8SJan Engelhardt static void __exit iprange_mt_exit(void) 172f72e25a8SJan Engelhardt { 1731a50c5a1SJan Engelhardt xt_unregister_matches(iprange_mt_reg, ARRAY_SIZE(iprange_mt_reg)); 174f72e25a8SJan Engelhardt } 175f72e25a8SJan Engelhardt 176f72e25a8SJan Engelhardt module_init(iprange_mt_init); 177f72e25a8SJan Engelhardt module_exit(iprange_mt_exit); 178f72e25a8SJan Engelhardt MODULE_LICENSE("GPL"); 1791a50c5a1SJan Engelhardt MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>, Jan Engelhardt <jengelh@computergmbh.de>"); 180f72e25a8SJan Engelhardt MODULE_DESCRIPTION("Xtables: arbitrary IPv4 range matching"); 181