xref: /openbmc/linux/net/netfilter/xt_iprange.c (revision 14d5e834)
1 /*
2  *	xt_iprange - Netfilter module to match IP address ranges
3  *
4  *	(C) 2003 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
5  *	(C) CC Computer Consultants GmbH, 2008
6  *
7  *	This program is free software; you can redistribute it and/or modify
8  *	it under the terms of the GNU General Public License version 2 as
9  *	published by the Free Software Foundation.
10  */
11 #include <linux/module.h>
12 #include <linux/skbuff.h>
13 #include <linux/ip.h>
14 #include <linux/ipv6.h>
15 #include <linux/netfilter/x_tables.h>
16 #include <linux/netfilter/xt_iprange.h>
17 #include <linux/netfilter_ipv4/ipt_iprange.h>
18 
19 static bool
20 iprange_mt_v0(const struct sk_buff *skb, const struct xt_match_param *par)
21 {
22 	const struct ipt_iprange_info *info = par->matchinfo;
23 	const struct iphdr *iph = ip_hdr(skb);
24 
25 	if (info->flags & IPRANGE_SRC) {
26 		if ((ntohl(iph->saddr) < ntohl(info->src.min_ip)
27 			  || ntohl(iph->saddr) > ntohl(info->src.max_ip))
28 			 ^ !!(info->flags & IPRANGE_SRC_INV)) {
29 			pr_debug("src IP %pI4 NOT in range %s%pI4-%pI4\n",
30 				 &iph->saddr,
31 				 info->flags & IPRANGE_SRC_INV ? "(INV) " : "",
32 				 &info->src.min_ip,
33 				 &info->src.max_ip);
34 			return false;
35 		}
36 	}
37 	if (info->flags & IPRANGE_DST) {
38 		if ((ntohl(iph->daddr) < ntohl(info->dst.min_ip)
39 			  || ntohl(iph->daddr) > ntohl(info->dst.max_ip))
40 			 ^ !!(info->flags & IPRANGE_DST_INV)) {
41 			pr_debug("dst IP %pI4 NOT in range %s%pI4-%pI4\n",
42 				 &iph->daddr,
43 				 info->flags & IPRANGE_DST_INV ? "(INV) " : "",
44 				 &info->dst.min_ip,
45 				 &info->dst.max_ip);
46 			return false;
47 		}
48 	}
49 	return true;
50 }
51 
52 static bool
53 iprange_mt4(const struct sk_buff *skb, const struct xt_match_param *par)
54 {
55 	const struct xt_iprange_mtinfo *info = par->matchinfo;
56 	const struct iphdr *iph = ip_hdr(skb);
57 	bool m;
58 
59 	if (info->flags & IPRANGE_SRC) {
60 		m  = ntohl(iph->saddr) < ntohl(info->src_min.ip);
61 		m |= ntohl(iph->saddr) > ntohl(info->src_max.ip);
62 		m ^= !!(info->flags & IPRANGE_SRC_INV);
63 		if (m) {
64 			pr_debug("src IP %pI4 NOT in range %s%pI4-%pI4\n",
65 			         &iph->saddr,
66 			         (info->flags & IPRANGE_SRC_INV) ? "(INV) " : "",
67 			         &info->src_max.ip,
68 			         &info->src_max.ip);
69 			return false;
70 		}
71 	}
72 	if (info->flags & IPRANGE_DST) {
73 		m  = ntohl(iph->daddr) < ntohl(info->dst_min.ip);
74 		m |= ntohl(iph->daddr) > ntohl(info->dst_max.ip);
75 		m ^= !!(info->flags & IPRANGE_DST_INV);
76 		if (m) {
77 			pr_debug("dst IP %pI4 NOT in range %s%pI4-%pI4\n",
78 			         &iph->daddr,
79 			         (info->flags & IPRANGE_DST_INV) ? "(INV) " : "",
80 			         &info->dst_min.ip,
81 			         &info->dst_max.ip);
82 			return false;
83 		}
84 	}
85 	return true;
86 }
87 
88 static inline int
89 iprange_ipv6_sub(const struct in6_addr *a, const struct in6_addr *b)
90 {
91 	unsigned int i;
92 	int r;
93 
94 	for (i = 0; i < 4; ++i) {
95 		r = ntohl(a->s6_addr32[i]) - ntohl(b->s6_addr32[i]);
96 		if (r != 0)
97 			return r;
98 	}
99 
100 	return 0;
101 }
102 
103 static bool
104 iprange_mt6(const struct sk_buff *skb, const struct xt_match_param *par)
105 {
106 	const struct xt_iprange_mtinfo *info = par->matchinfo;
107 	const struct ipv6hdr *iph = ipv6_hdr(skb);
108 	bool m;
109 
110 	if (info->flags & IPRANGE_SRC) {
111 		m  = iprange_ipv6_sub(&iph->saddr, &info->src_min.in6) < 0;
112 		m |= iprange_ipv6_sub(&iph->saddr, &info->src_max.in6) > 0;
113 		m ^= !!(info->flags & IPRANGE_SRC_INV);
114 		if (m)
115 			return false;
116 	}
117 	if (info->flags & IPRANGE_DST) {
118 		m  = iprange_ipv6_sub(&iph->daddr, &info->dst_min.in6) < 0;
119 		m |= iprange_ipv6_sub(&iph->daddr, &info->dst_max.in6) > 0;
120 		m ^= !!(info->flags & IPRANGE_DST_INV);
121 		if (m)
122 			return false;
123 	}
124 	return true;
125 }
126 
127 static struct xt_match iprange_mt_reg[] __read_mostly = {
128 	{
129 		.name      = "iprange",
130 		.revision  = 0,
131 		.family    = NFPROTO_IPV4,
132 		.match     = iprange_mt_v0,
133 		.matchsize = sizeof(struct ipt_iprange_info),
134 		.me        = THIS_MODULE,
135 	},
136 	{
137 		.name      = "iprange",
138 		.revision  = 1,
139 		.family    = NFPROTO_IPV4,
140 		.match     = iprange_mt4,
141 		.matchsize = sizeof(struct xt_iprange_mtinfo),
142 		.me        = THIS_MODULE,
143 	},
144 	{
145 		.name      = "iprange",
146 		.revision  = 1,
147 		.family    = NFPROTO_IPV6,
148 		.match     = iprange_mt6,
149 		.matchsize = sizeof(struct xt_iprange_mtinfo),
150 		.me        = THIS_MODULE,
151 	},
152 };
153 
154 static int __init iprange_mt_init(void)
155 {
156 	return xt_register_matches(iprange_mt_reg, ARRAY_SIZE(iprange_mt_reg));
157 }
158 
159 static void __exit iprange_mt_exit(void)
160 {
161 	xt_unregister_matches(iprange_mt_reg, ARRAY_SIZE(iprange_mt_reg));
162 }
163 
164 module_init(iprange_mt_init);
165 module_exit(iprange_mt_exit);
166 MODULE_LICENSE("GPL");
167 MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>, Jan Engelhardt <jengelh@computergmbh.de>");
168 MODULE_DESCRIPTION("Xtables: arbitrary IPv4 range matching");
169 MODULE_ALIAS("ipt_iprange");
170 MODULE_ALIAS("ip6t_iprange");
171