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