linux/net/netfilter/xt_ecn.c
<<
>>
Prefs
   1/*
   2 * Xtables module for matching the value of the IPv4/IPv6 and TCP ECN bits
   3 *
   4 * (C) 2002 by Harald Welte <laforge@gnumonks.org>
   5 * (C) 2011 Patrick McHardy <kaber@trash.net>
   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/in.h>
  13#include <linux/ip.h>
  14#include <net/ip.h>
  15#include <linux/module.h>
  16#include <linux/skbuff.h>
  17#include <linux/tcp.h>
  18
  19#include <linux/netfilter/x_tables.h>
  20#include <linux/netfilter/xt_ecn.h>
  21#include <linux/netfilter_ipv4/ip_tables.h>
  22#include <linux/netfilter_ipv6/ip6_tables.h>
  23
  24MODULE_AUTHOR("Harald Welte <laforge@netfilter.org>");
  25MODULE_DESCRIPTION("Xtables: Explicit Congestion Notification (ECN) flag match");
  26MODULE_LICENSE("GPL");
  27MODULE_ALIAS("ipt_ecn");
  28MODULE_ALIAS("ip6t_ecn");
  29
  30static bool match_tcp(const struct sk_buff *skb, struct xt_action_param *par)
  31{
  32        const struct xt_ecn_info *einfo = par->matchinfo;
  33        struct tcphdr _tcph;
  34        const struct tcphdr *th;
  35
  36        /* In practice, TCP match does this, so can't fail.  But let's
  37         * be good citizens.
  38         */
  39        th = skb_header_pointer(skb, par->thoff, sizeof(_tcph), &_tcph);
  40        if (th == NULL)
  41                return false;
  42
  43        if (einfo->operation & XT_ECN_OP_MATCH_ECE) {
  44                if (einfo->invert & XT_ECN_OP_MATCH_ECE) {
  45                        if (th->ece == 1)
  46                                return false;
  47                } else {
  48                        if (th->ece == 0)
  49                                return false;
  50                }
  51        }
  52
  53        if (einfo->operation & XT_ECN_OP_MATCH_CWR) {
  54                if (einfo->invert & XT_ECN_OP_MATCH_CWR) {
  55                        if (th->cwr == 1)
  56                                return false;
  57                } else {
  58                        if (th->cwr == 0)
  59                                return false;
  60                }
  61        }
  62
  63        return true;
  64}
  65
  66static inline bool match_ip(const struct sk_buff *skb,
  67                            const struct xt_ecn_info *einfo)
  68{
  69        return ((ip_hdr(skb)->tos & XT_ECN_IP_MASK) == einfo->ip_ect) ^
  70               !!(einfo->invert & XT_ECN_OP_MATCH_IP);
  71}
  72
  73static bool ecn_mt4(const struct sk_buff *skb, struct xt_action_param *par)
  74{
  75        const struct xt_ecn_info *info = par->matchinfo;
  76
  77        if (info->operation & XT_ECN_OP_MATCH_IP && !match_ip(skb, info))
  78                return false;
  79
  80        if (info->operation & (XT_ECN_OP_MATCH_ECE | XT_ECN_OP_MATCH_CWR) &&
  81            !match_tcp(skb, par))
  82                return false;
  83
  84        return true;
  85}
  86
  87static int ecn_mt_check4(const struct xt_mtchk_param *par)
  88{
  89        const struct xt_ecn_info *info = par->matchinfo;
  90        const struct ipt_ip *ip = par->entryinfo;
  91
  92        if (info->operation & XT_ECN_OP_MATCH_MASK)
  93                return -EINVAL;
  94
  95        if (info->invert & XT_ECN_OP_MATCH_MASK)
  96                return -EINVAL;
  97
  98        if (info->operation & (XT_ECN_OP_MATCH_ECE | XT_ECN_OP_MATCH_CWR) &&
  99            (ip->proto != IPPROTO_TCP || ip->invflags & IPT_INV_PROTO)) {
 100                pr_info("cannot match TCP bits in rule for non-tcp packets\n");
 101                return -EINVAL;
 102        }
 103
 104        return 0;
 105}
 106
 107static inline bool match_ipv6(const struct sk_buff *skb,
 108                              const struct xt_ecn_info *einfo)
 109{
 110        return (((ipv6_hdr(skb)->flow_lbl[0] >> 4) & XT_ECN_IP_MASK) ==
 111                einfo->ip_ect) ^
 112               !!(einfo->invert & XT_ECN_OP_MATCH_IP);
 113}
 114
 115static bool ecn_mt6(const struct sk_buff *skb, struct xt_action_param *par)
 116{
 117        const struct xt_ecn_info *info = par->matchinfo;
 118
 119        if (info->operation & XT_ECN_OP_MATCH_IP && !match_ipv6(skb, info))
 120                return false;
 121
 122        if (info->operation & (XT_ECN_OP_MATCH_ECE | XT_ECN_OP_MATCH_CWR) &&
 123            !match_tcp(skb, par))
 124                return false;
 125
 126        return true;
 127}
 128
 129static int ecn_mt_check6(const struct xt_mtchk_param *par)
 130{
 131        const struct xt_ecn_info *info = par->matchinfo;
 132        const struct ip6t_ip6 *ip = par->entryinfo;
 133
 134        if (info->operation & XT_ECN_OP_MATCH_MASK)
 135                return -EINVAL;
 136
 137        if (info->invert & XT_ECN_OP_MATCH_MASK)
 138                return -EINVAL;
 139
 140        if (info->operation & (XT_ECN_OP_MATCH_ECE | XT_ECN_OP_MATCH_CWR) &&
 141            (ip->proto != IPPROTO_TCP || ip->invflags & IP6T_INV_PROTO)) {
 142                pr_info("cannot match TCP bits in rule for non-tcp packets\n");
 143                return -EINVAL;
 144        }
 145
 146        return 0;
 147}
 148
 149static struct xt_match ecn_mt_reg[] __read_mostly = {
 150        {
 151                .name           = "ecn",
 152                .family         = NFPROTO_IPV4,
 153                .match          = ecn_mt4,
 154                .matchsize      = sizeof(struct xt_ecn_info),
 155                .checkentry     = ecn_mt_check4,
 156                .me             = THIS_MODULE,
 157        },
 158        {
 159                .name           = "ecn",
 160                .family         = NFPROTO_IPV6,
 161                .match          = ecn_mt6,
 162                .matchsize      = sizeof(struct xt_ecn_info),
 163                .checkentry     = ecn_mt_check6,
 164                .me             = THIS_MODULE,
 165        },
 166};
 167
 168static int __init ecn_mt_init(void)
 169{
 170        return xt_register_matches(ecn_mt_reg, ARRAY_SIZE(ecn_mt_reg));
 171}
 172
 173static void __exit ecn_mt_exit(void)
 174{
 175        xt_unregister_matches(ecn_mt_reg, ARRAY_SIZE(ecn_mt_reg));
 176}
 177
 178module_init(ecn_mt_init);
 179module_exit(ecn_mt_exit);
 180