0001
0002
0003
0004
0005
0006
0007
0008 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
0009 #include <linux/in.h>
0010 #include <linux/ip.h>
0011 #include <net/ip.h>
0012 #include <linux/module.h>
0013 #include <linux/skbuff.h>
0014 #include <linux/tcp.h>
0015
0016 #include <linux/netfilter/x_tables.h>
0017 #include <linux/netfilter/xt_ecn.h>
0018 #include <linux/netfilter_ipv4/ip_tables.h>
0019 #include <linux/netfilter_ipv6/ip6_tables.h>
0020
0021 MODULE_AUTHOR("Harald Welte <laforge@netfilter.org>");
0022 MODULE_DESCRIPTION("Xtables: Explicit Congestion Notification (ECN) flag match");
0023 MODULE_LICENSE("GPL");
0024 MODULE_ALIAS("ipt_ecn");
0025 MODULE_ALIAS("ip6t_ecn");
0026
0027 static bool match_tcp(const struct sk_buff *skb, struct xt_action_param *par)
0028 {
0029 const struct xt_ecn_info *einfo = par->matchinfo;
0030 struct tcphdr _tcph;
0031 const struct tcphdr *th;
0032
0033
0034
0035
0036 th = skb_header_pointer(skb, par->thoff, sizeof(_tcph), &_tcph);
0037 if (th == NULL)
0038 return false;
0039
0040 if (einfo->operation & XT_ECN_OP_MATCH_ECE) {
0041 if (einfo->invert & XT_ECN_OP_MATCH_ECE) {
0042 if (th->ece == 1)
0043 return false;
0044 } else {
0045 if (th->ece == 0)
0046 return false;
0047 }
0048 }
0049
0050 if (einfo->operation & XT_ECN_OP_MATCH_CWR) {
0051 if (einfo->invert & XT_ECN_OP_MATCH_CWR) {
0052 if (th->cwr == 1)
0053 return false;
0054 } else {
0055 if (th->cwr == 0)
0056 return false;
0057 }
0058 }
0059
0060 return true;
0061 }
0062
0063 static inline bool match_ip(const struct sk_buff *skb,
0064 const struct xt_ecn_info *einfo)
0065 {
0066 return ((ip_hdr(skb)->tos & XT_ECN_IP_MASK) == einfo->ip_ect) ^
0067 !!(einfo->invert & XT_ECN_OP_MATCH_IP);
0068 }
0069
0070 static bool ecn_mt4(const struct sk_buff *skb, struct xt_action_param *par)
0071 {
0072 const struct xt_ecn_info *info = par->matchinfo;
0073
0074 if (info->operation & XT_ECN_OP_MATCH_IP && !match_ip(skb, info))
0075 return false;
0076
0077 if (info->operation & (XT_ECN_OP_MATCH_ECE | XT_ECN_OP_MATCH_CWR) &&
0078 !match_tcp(skb, par))
0079 return false;
0080
0081 return true;
0082 }
0083
0084 static int ecn_mt_check4(const struct xt_mtchk_param *par)
0085 {
0086 const struct xt_ecn_info *info = par->matchinfo;
0087 const struct ipt_ip *ip = par->entryinfo;
0088
0089 if (info->operation & XT_ECN_OP_MATCH_MASK)
0090 return -EINVAL;
0091
0092 if (info->invert & XT_ECN_OP_MATCH_MASK)
0093 return -EINVAL;
0094
0095 if (info->operation & (XT_ECN_OP_MATCH_ECE | XT_ECN_OP_MATCH_CWR) &&
0096 (ip->proto != IPPROTO_TCP || ip->invflags & IPT_INV_PROTO)) {
0097 pr_info_ratelimited("cannot match TCP bits for non-tcp packets\n");
0098 return -EINVAL;
0099 }
0100
0101 return 0;
0102 }
0103
0104 static inline bool match_ipv6(const struct sk_buff *skb,
0105 const struct xt_ecn_info *einfo)
0106 {
0107 return (((ipv6_hdr(skb)->flow_lbl[0] >> 4) & XT_ECN_IP_MASK) ==
0108 einfo->ip_ect) ^
0109 !!(einfo->invert & XT_ECN_OP_MATCH_IP);
0110 }
0111
0112 static bool ecn_mt6(const struct sk_buff *skb, struct xt_action_param *par)
0113 {
0114 const struct xt_ecn_info *info = par->matchinfo;
0115
0116 if (info->operation & XT_ECN_OP_MATCH_IP && !match_ipv6(skb, info))
0117 return false;
0118
0119 if (info->operation & (XT_ECN_OP_MATCH_ECE | XT_ECN_OP_MATCH_CWR) &&
0120 !match_tcp(skb, par))
0121 return false;
0122
0123 return true;
0124 }
0125
0126 static int ecn_mt_check6(const struct xt_mtchk_param *par)
0127 {
0128 const struct xt_ecn_info *info = par->matchinfo;
0129 const struct ip6t_ip6 *ip = par->entryinfo;
0130
0131 if (info->operation & XT_ECN_OP_MATCH_MASK)
0132 return -EINVAL;
0133
0134 if (info->invert & XT_ECN_OP_MATCH_MASK)
0135 return -EINVAL;
0136
0137 if (info->operation & (XT_ECN_OP_MATCH_ECE | XT_ECN_OP_MATCH_CWR) &&
0138 (ip->proto != IPPROTO_TCP || ip->invflags & IP6T_INV_PROTO)) {
0139 pr_info_ratelimited("cannot match TCP bits for non-tcp packets\n");
0140 return -EINVAL;
0141 }
0142
0143 return 0;
0144 }
0145
0146 static struct xt_match ecn_mt_reg[] __read_mostly = {
0147 {
0148 .name = "ecn",
0149 .family = NFPROTO_IPV4,
0150 .match = ecn_mt4,
0151 .matchsize = sizeof(struct xt_ecn_info),
0152 .checkentry = ecn_mt_check4,
0153 .me = THIS_MODULE,
0154 },
0155 {
0156 .name = "ecn",
0157 .family = NFPROTO_IPV6,
0158 .match = ecn_mt6,
0159 .matchsize = sizeof(struct xt_ecn_info),
0160 .checkentry = ecn_mt_check6,
0161 .me = THIS_MODULE,
0162 },
0163 };
0164
0165 static int __init ecn_mt_init(void)
0166 {
0167 return xt_register_matches(ecn_mt_reg, ARRAY_SIZE(ecn_mt_reg));
0168 }
0169
0170 static void __exit ecn_mt_exit(void)
0171 {
0172 xt_unregister_matches(ecn_mt_reg, ARRAY_SIZE(ecn_mt_reg));
0173 }
0174
0175 module_init(ecn_mt_init);
0176 module_exit(ecn_mt_exit);