ipt_ECN.c (3540B)
1// SPDX-License-Identifier: GPL-2.0-only 2/* iptables module for the IPv4 and TCP ECN bits, Version 1.5 3 * 4 * (C) 2002 by Harald Welte <laforge@netfilter.org> 5*/ 6#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 7#include <linux/in.h> 8#include <linux/module.h> 9#include <linux/skbuff.h> 10#include <linux/ip.h> 11#include <net/ip.h> 12#include <linux/tcp.h> 13#include <net/checksum.h> 14 15#include <linux/netfilter/x_tables.h> 16#include <linux/netfilter_ipv4/ip_tables.h> 17#include <linux/netfilter_ipv4/ipt_ECN.h> 18 19MODULE_LICENSE("GPL"); 20MODULE_AUTHOR("Harald Welte <laforge@netfilter.org>"); 21MODULE_DESCRIPTION("Xtables: Explicit Congestion Notification (ECN) flag modification"); 22 23/* set ECT codepoint from IP header. 24 * return false if there was an error. */ 25static inline bool 26set_ect_ip(struct sk_buff *skb, const struct ipt_ECN_info *einfo) 27{ 28 struct iphdr *iph = ip_hdr(skb); 29 30 if ((iph->tos & IPT_ECN_IP_MASK) != (einfo->ip_ect & IPT_ECN_IP_MASK)) { 31 __u8 oldtos; 32 if (skb_ensure_writable(skb, sizeof(struct iphdr))) 33 return false; 34 iph = ip_hdr(skb); 35 oldtos = iph->tos; 36 iph->tos &= ~IPT_ECN_IP_MASK; 37 iph->tos |= (einfo->ip_ect & IPT_ECN_IP_MASK); 38 csum_replace2(&iph->check, htons(oldtos), htons(iph->tos)); 39 } 40 return true; 41} 42 43/* Return false if there was an error. */ 44static inline bool 45set_ect_tcp(struct sk_buff *skb, const struct ipt_ECN_info *einfo) 46{ 47 struct tcphdr _tcph, *tcph; 48 __be16 oldval; 49 50 /* Not enough header? */ 51 tcph = skb_header_pointer(skb, ip_hdrlen(skb), sizeof(_tcph), &_tcph); 52 if (!tcph) 53 return false; 54 55 if ((!(einfo->operation & IPT_ECN_OP_SET_ECE) || 56 tcph->ece == einfo->proto.tcp.ece) && 57 (!(einfo->operation & IPT_ECN_OP_SET_CWR) || 58 tcph->cwr == einfo->proto.tcp.cwr)) 59 return true; 60 61 if (skb_ensure_writable(skb, ip_hdrlen(skb) + sizeof(*tcph))) 62 return false; 63 tcph = (void *)ip_hdr(skb) + ip_hdrlen(skb); 64 65 oldval = ((__be16 *)tcph)[6]; 66 if (einfo->operation & IPT_ECN_OP_SET_ECE) 67 tcph->ece = einfo->proto.tcp.ece; 68 if (einfo->operation & IPT_ECN_OP_SET_CWR) 69 tcph->cwr = einfo->proto.tcp.cwr; 70 71 inet_proto_csum_replace2(&tcph->check, skb, 72 oldval, ((__be16 *)tcph)[6], false); 73 return true; 74} 75 76static unsigned int 77ecn_tg(struct sk_buff *skb, const struct xt_action_param *par) 78{ 79 const struct ipt_ECN_info *einfo = par->targinfo; 80 81 if (einfo->operation & IPT_ECN_OP_SET_IP) 82 if (!set_ect_ip(skb, einfo)) 83 return NF_DROP; 84 85 if (einfo->operation & (IPT_ECN_OP_SET_ECE | IPT_ECN_OP_SET_CWR) && 86 ip_hdr(skb)->protocol == IPPROTO_TCP) 87 if (!set_ect_tcp(skb, einfo)) 88 return NF_DROP; 89 90 return XT_CONTINUE; 91} 92 93static int ecn_tg_check(const struct xt_tgchk_param *par) 94{ 95 const struct ipt_ECN_info *einfo = par->targinfo; 96 const struct ipt_entry *e = par->entryinfo; 97 98 if (einfo->operation & IPT_ECN_OP_MASK) 99 return -EINVAL; 100 101 if (einfo->ip_ect & ~IPT_ECN_IP_MASK) 102 return -EINVAL; 103 104 if ((einfo->operation & (IPT_ECN_OP_SET_ECE|IPT_ECN_OP_SET_CWR)) && 105 (e->ip.proto != IPPROTO_TCP || (e->ip.invflags & XT_INV_PROTO))) { 106 pr_info_ratelimited("cannot use operation on non-tcp rule\n"); 107 return -EINVAL; 108 } 109 return 0; 110} 111 112static struct xt_target ecn_tg_reg __read_mostly = { 113 .name = "ECN", 114 .family = NFPROTO_IPV4, 115 .target = ecn_tg, 116 .targetsize = sizeof(struct ipt_ECN_info), 117 .table = "mangle", 118 .checkentry = ecn_tg_check, 119 .me = THIS_MODULE, 120}; 121 122static int __init ecn_tg_init(void) 123{ 124 return xt_register_target(&ecn_tg_reg); 125} 126 127static void __exit ecn_tg_exit(void) 128{ 129 xt_unregister_target(&ecn_tg_reg); 130} 131 132module_init(ecn_tg_init); 133module_exit(ecn_tg_exit);