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