| 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_IPV4, |
| 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 | #if IS_ENABLED(CONFIG_IP6_NF_IPTABLES) |
| 219 | { |
| 220 | .name = "addrtype" , |
| 221 | .family = NFPROTO_IPV6, |
| 222 | .revision = 1, |
| 223 | .match = addrtype_mt_v1, |
| 224 | .checkentry = addrtype_mt_checkentry_v1, |
| 225 | .matchsize = sizeof(struct xt_addrtype_info_v1), |
| 226 | .me = THIS_MODULE |
| 227 | }, |
| 228 | #endif |
| 229 | }; |
| 230 | |
| 231 | static int __init addrtype_mt_init(void) |
| 232 | { |
| 233 | return xt_register_matches(match: addrtype_mt_reg, |
| 234 | ARRAY_SIZE(addrtype_mt_reg)); |
| 235 | } |
| 236 | |
| 237 | static void __exit addrtype_mt_exit(void) |
| 238 | { |
| 239 | xt_unregister_matches(match: addrtype_mt_reg, ARRAY_SIZE(addrtype_mt_reg)); |
| 240 | } |
| 241 | |
| 242 | module_init(addrtype_mt_init); |
| 243 | module_exit(addrtype_mt_exit); |
| 244 | |