1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * iptables module to match inet_addr_type() of an ip. |
4 | * |
5 | * Copyright (c) 2004 Patrick McHardy <kaber@trash.net> |
6 | * (C) 2007 Laszlo Attila Toth <panther@balabit.hu> |
7 | */ |
8 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
9 | #include <linux/kernel.h> |
10 | #include <linux/module.h> |
11 | #include <linux/skbuff.h> |
12 | #include <linux/netdevice.h> |
13 | #include <linux/ip.h> |
14 | #include <net/route.h> |
15 | |
16 | #if IS_ENABLED(CONFIG_IP6_NF_IPTABLES) |
17 | #include <net/ipv6.h> |
18 | #include <net/ip6_route.h> |
19 | #include <net/ip6_fib.h> |
20 | #endif |
21 | |
22 | #include <linux/netfilter_ipv6.h> |
23 | #include <linux/netfilter/xt_addrtype.h> |
24 | #include <linux/netfilter/x_tables.h> |
25 | |
26 | MODULE_LICENSE("GPL" ); |
27 | MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>" ); |
28 | MODULE_DESCRIPTION("Xtables: address type match" ); |
29 | MODULE_ALIAS("ipt_addrtype" ); |
30 | MODULE_ALIAS("ip6t_addrtype" ); |
31 | |
32 | #if IS_ENABLED(CONFIG_IP6_NF_IPTABLES) |
33 | static u32 match_lookup_rt6(struct net *net, const struct net_device *dev, |
34 | const struct in6_addr *addr, u16 mask) |
35 | { |
36 | struct flowi6 flow; |
37 | struct rt6_info *rt; |
38 | u32 ret = 0; |
39 | int route_err; |
40 | |
41 | memset(&flow, 0, sizeof(flow)); |
42 | flow.daddr = *addr; |
43 | if (dev) |
44 | flow.flowi6_oif = dev->ifindex; |
45 | |
46 | if (dev && (mask & XT_ADDRTYPE_LOCAL)) { |
47 | if (nf_ipv6_chk_addr(net, addr, dev, strict: true)) |
48 | ret = XT_ADDRTYPE_LOCAL; |
49 | } |
50 | |
51 | route_err = nf_ip6_route(net, dst: (struct dst_entry **)&rt, |
52 | fl: flowi6_to_flowi(fl6: &flow), strict: false); |
53 | if (route_err) |
54 | return XT_ADDRTYPE_UNREACHABLE; |
55 | |
56 | if (rt->rt6i_flags & RTF_REJECT) |
57 | ret = XT_ADDRTYPE_UNREACHABLE; |
58 | |
59 | if (dev == NULL && rt->rt6i_flags & RTF_LOCAL) |
60 | ret |= XT_ADDRTYPE_LOCAL; |
61 | if (ipv6_anycast_destination(dst: (struct dst_entry *)rt, daddr: addr)) |
62 | ret |= XT_ADDRTYPE_ANYCAST; |
63 | |
64 | dst_release(dst: &rt->dst); |
65 | return ret; |
66 | } |
67 | |
68 | static bool match_type6(struct net *net, const struct net_device *dev, |
69 | const struct in6_addr *addr, u16 mask) |
70 | { |
71 | int addr_type = ipv6_addr_type(addr); |
72 | |
73 | if ((mask & XT_ADDRTYPE_MULTICAST) && |
74 | !(addr_type & IPV6_ADDR_MULTICAST)) |
75 | return false; |
76 | if ((mask & XT_ADDRTYPE_UNICAST) && !(addr_type & IPV6_ADDR_UNICAST)) |
77 | return false; |
78 | if ((mask & XT_ADDRTYPE_UNSPEC) && addr_type != IPV6_ADDR_ANY) |
79 | return false; |
80 | |
81 | if ((XT_ADDRTYPE_LOCAL | XT_ADDRTYPE_ANYCAST | |
82 | XT_ADDRTYPE_UNREACHABLE) & mask) |
83 | return !!(mask & match_lookup_rt6(net, dev, addr, mask)); |
84 | return true; |
85 | } |
86 | |
87 | static bool |
88 | addrtype_mt6(struct net *net, const struct net_device *dev, |
89 | const struct sk_buff *skb, const struct xt_addrtype_info_v1 *info) |
90 | { |
91 | const struct ipv6hdr *iph = ipv6_hdr(skb); |
92 | bool ret = true; |
93 | |
94 | if (info->source) |
95 | ret &= match_type6(net, dev, addr: &iph->saddr, mask: info->source) ^ |
96 | (info->flags & XT_ADDRTYPE_INVERT_SOURCE); |
97 | if (ret && info->dest) |
98 | ret &= match_type6(net, dev, addr: &iph->daddr, mask: info->dest) ^ |
99 | !!(info->flags & XT_ADDRTYPE_INVERT_DEST); |
100 | return ret; |
101 | } |
102 | #endif |
103 | |
104 | static inline bool match_type(struct net *net, const struct net_device *dev, |
105 | __be32 addr, u_int16_t mask) |
106 | { |
107 | return !!(mask & (1 << inet_dev_addr_type(net, dev, addr))); |
108 | } |
109 | |
110 | static bool |
111 | addrtype_mt_v0(const struct sk_buff *skb, struct xt_action_param *par) |
112 | { |
113 | struct net *net = xt_net(par); |
114 | const struct xt_addrtype_info *info = par->matchinfo; |
115 | const struct iphdr *iph = ip_hdr(skb); |
116 | bool ret = true; |
117 | |
118 | if (info->source) |
119 | ret &= match_type(net, NULL, addr: iph->saddr, mask: info->source) ^ |
120 | info->invert_source; |
121 | if (info->dest) |
122 | ret &= match_type(net, NULL, addr: iph->daddr, mask: info->dest) ^ |
123 | info->invert_dest; |
124 | |
125 | return ret; |
126 | } |
127 | |
128 | static bool |
129 | addrtype_mt_v1(const struct sk_buff *skb, struct xt_action_param *par) |
130 | { |
131 | struct net *net = xt_net(par); |
132 | const struct xt_addrtype_info_v1 *info = par->matchinfo; |
133 | const struct iphdr *iph; |
134 | const struct net_device *dev = NULL; |
135 | bool ret = true; |
136 | |
137 | if (info->flags & XT_ADDRTYPE_LIMIT_IFACE_IN) |
138 | dev = xt_in(par); |
139 | else if (info->flags & XT_ADDRTYPE_LIMIT_IFACE_OUT) |
140 | dev = xt_out(par); |
141 | |
142 | #if IS_ENABLED(CONFIG_IP6_NF_IPTABLES) |
143 | if (xt_family(par) == NFPROTO_IPV6) |
144 | return addrtype_mt6(net, dev, skb, info); |
145 | #endif |
146 | iph = ip_hdr(skb); |
147 | if (info->source) |
148 | ret &= match_type(net, dev, addr: iph->saddr, mask: info->source) ^ |
149 | (info->flags & XT_ADDRTYPE_INVERT_SOURCE); |
150 | if (ret && info->dest) |
151 | ret &= match_type(net, dev, addr: iph->daddr, mask: info->dest) ^ |
152 | !!(info->flags & XT_ADDRTYPE_INVERT_DEST); |
153 | return ret; |
154 | } |
155 | |
156 | static int addrtype_mt_checkentry_v1(const struct xt_mtchk_param *par) |
157 | { |
158 | const char *errmsg = "both incoming and outgoing interface limitation cannot be selected" ; |
159 | struct xt_addrtype_info_v1 *info = par->matchinfo; |
160 | |
161 | if (info->flags & XT_ADDRTYPE_LIMIT_IFACE_IN && |
162 | info->flags & XT_ADDRTYPE_LIMIT_IFACE_OUT) |
163 | goto err; |
164 | |
165 | if (par->hook_mask & ((1 << NF_INET_PRE_ROUTING) | |
166 | (1 << NF_INET_LOCAL_IN)) && |
167 | info->flags & XT_ADDRTYPE_LIMIT_IFACE_OUT) { |
168 | errmsg = "output interface limitation not valid in PREROUTING and INPUT" ; |
169 | goto err; |
170 | } |
171 | |
172 | if (par->hook_mask & ((1 << NF_INET_POST_ROUTING) | |
173 | (1 << NF_INET_LOCAL_OUT)) && |
174 | info->flags & XT_ADDRTYPE_LIMIT_IFACE_IN) { |
175 | errmsg = "input interface limitation not valid in POSTROUTING and OUTPUT" ; |
176 | goto err; |
177 | } |
178 | |
179 | #if IS_ENABLED(CONFIG_IP6_NF_IPTABLES) |
180 | if (par->family == NFPROTO_IPV6) { |
181 | if ((info->source | info->dest) & XT_ADDRTYPE_BLACKHOLE) { |
182 | errmsg = "ipv6 BLACKHOLE matching not supported" ; |
183 | goto err; |
184 | } |
185 | if ((info->source | info->dest) >= XT_ADDRTYPE_PROHIBIT) { |
186 | errmsg = "ipv6 PROHIBIT (THROW, NAT ..) matching not supported" ; |
187 | goto err; |
188 | } |
189 | if ((info->source | info->dest) & XT_ADDRTYPE_BROADCAST) { |
190 | errmsg = "ipv6 does not support BROADCAST matching" ; |
191 | goto err; |
192 | } |
193 | } |
194 | #endif |
195 | return 0; |
196 | err: |
197 | pr_info_ratelimited("%s\n" , errmsg); |
198 | return -EINVAL; |
199 | } |
200 | |
201 | static struct xt_match addrtype_mt_reg[] __read_mostly = { |
202 | { |
203 | .name = "addrtype" , |
204 | .family = NFPROTO_IPV4, |
205 | .match = addrtype_mt_v0, |
206 | .matchsize = sizeof(struct xt_addrtype_info), |
207 | .me = THIS_MODULE |
208 | }, |
209 | { |
210 | .name = "addrtype" , |
211 | .family = NFPROTO_UNSPEC, |
212 | .revision = 1, |
213 | .match = addrtype_mt_v1, |
214 | .checkentry = addrtype_mt_checkentry_v1, |
215 | .matchsize = sizeof(struct xt_addrtype_info_v1), |
216 | .me = THIS_MODULE |
217 | } |
218 | }; |
219 | |
220 | static int __init addrtype_mt_init(void) |
221 | { |
222 | return xt_register_matches(match: addrtype_mt_reg, |
223 | ARRAY_SIZE(addrtype_mt_reg)); |
224 | } |
225 | |
226 | static void __exit addrtype_mt_exit(void) |
227 | { |
228 | xt_unregister_matches(match: addrtype_mt_reg, ARRAY_SIZE(addrtype_mt_reg)); |
229 | } |
230 | |
231 | module_init(addrtype_mt_init); |
232 | module_exit(addrtype_mt_exit); |
233 | |