xref: /openbmc/linux/net/netfilter/xt_iprange.c (revision 1a50c5a1)
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