1/* iptables module for the IPv4 and TCP ECN bits, Version 1.2 2 * 3 * (C) 2002 by Harald Welte <laforge@gnumonks.org> 4 * 5 * This software is distributed under GNU GPL v2, 1991 6 * 7 * ipt_ECN.c,v 1.4 2002/08/05 19:36:51 laforge Exp 8*/ 9 10#include <linux/module.h> 11#include <linux/skbuff.h> 12#include <linux/ip.h> 13#include <net/checksum.h> 14 15#include <linux/netfilter_ipv4/ip_tables.h> 16#include <linux/netfilter_ipv4/ipt_ECN.h> 17 18MODULE_LICENSE("GPL"); 19 20/* set ECT codepoint from IP header. 21 * return 0 in case there was no ECT codepoint 22 * return 1 in case ECT codepoint has been overwritten 23 * return < 0 in case there was error */ 24static int inline 25set_ect_ip(struct sk_buff **pskb, struct iphdr *iph, 26 const struct ipt_ECN_info *einfo) 27{ 28 if ((iph->tos & IPT_ECN_IP_MASK) 29 != (einfo->ip_ect & IPT_ECN_IP_MASK)) { 30 u_int16_t diffs[2]; 31 32 /* raw socket (tcpdump) may have clone of incoming 33 * skb: don't disturb it --RR */ 34 if (skb_cloned(*pskb) && !(*pskb)->sk) { 35 struct sk_buff *nskb = skb_copy(*pskb, GFP_ATOMIC); 36 if (!nskb) 37 return NF_DROP; 38 kfree_skb(*pskb); 39 *pskb = nskb; 40 iph = (*pskb)->nh.iph; 41 } 42 43 diffs[0] = htons(iph->tos) ^ 0xFFFF; 44 iph->tos = iph->tos & ~IPT_ECN_IP_MASK; 45 iph->tos = iph->tos | (einfo->ip_ect & IPT_ECN_IP_MASK); 46 diffs[1] = htons(iph->tos); 47 iph->check = csum_fold(csum_partial((char *)diffs, 48 sizeof(diffs), 49 iph->check^0xFFFF)); 50 (*pskb)->nfcache |= NFC_ALTERED; 51 52 return 1; 53 } 54 return 0; 55} 56 57static int inline 58set_ect_tcp(struct sk_buff **pskb, struct iphdr *iph, 59 const struct ipt_ECN_info *einfo) 60{ 61 62 struct tcphdr *tcph = (void *) iph + iph->ihl * 4; 63 u_int16_t *tcpflags = (u_int16_t *)tcph + 6; 64 u_int16_t diffs[2]; 65 66 /* raw socket (tcpdump) may have clone of incoming 67 * skb: don't disturb it --RR */ 68 if (skb_cloned(*pskb) && !(*pskb)->sk) { 69 struct sk_buff *nskb = skb_copy(*pskb, GFP_ATOMIC); 70 if (!nskb) 71 return NF_DROP; 72 kfree_skb(*pskb); 73 *pskb = nskb; 74 iph = (*pskb)->nh.iph; 75 } 76 77 diffs[0] = *tcpflags; 78 79 if (einfo->operation & IPT_ECN_OP_SET_ECE 80 && tcph->ece != einfo->proto.tcp.ece) { 81 tcph->ece = einfo->proto.tcp.ece; 82 } 83 84 if (einfo->operation & IPT_ECN_OP_SET_CWR 85 && tcph->cwr != einfo->proto.tcp.cwr) { 86 tcph->cwr = einfo->proto.tcp.cwr; 87 } 88 89 if (diffs[0] != *tcpflags) { 90 diffs[0] = diffs[0] ^ 0xFFFF; 91 diffs[1] = *tcpflags; 92 tcph->check = csum_fold(csum_partial((char *)diffs, 93 sizeof(diffs), 94 tcph->check^0xFFFF)); 95 (*pskb)->nfcache |= NFC_ALTERED; 96 97 return 1; 98 } 99 100 return 0; 101} 102 103static unsigned int 104target(struct sk_buff **pskb, 105 unsigned int hooknum, 106 const struct net_device *in, 107 const struct net_device *out, 108 const void *targinfo, 109 void *userinfo) 110{ 111 struct iphdr *iph = (*pskb)->nh.iph; 112 const struct ipt_ECN_info *einfo = targinfo; 113 114 if (einfo->operation & IPT_ECN_OP_SET_IP) 115 set_ect_ip(pskb, iph, einfo); 116 117 if (einfo->operation & (IPT_ECN_OP_SET_ECE | IPT_ECN_OP_SET_CWR) 118 && iph->protocol == IPPROTO_TCP) 119 set_ect_tcp(pskb, iph, einfo); 120 121 return IPT_CONTINUE; 122} 123 124static int 125checkentry(const char *tablename, 126 const struct ipt_entry *e, 127 void *targinfo, 128 unsigned int targinfosize, 129 unsigned int hook_mask) 130{ 131 const struct ipt_ECN_info *einfo = (struct ipt_ECN_info *)targinfo; 132 133 if (targinfosize != IPT_ALIGN(sizeof(struct ipt_ECN_info))) { 134 printk(KERN_WARNING "ECN: targinfosize %u != %Zu\n", 135 targinfosize, 136 IPT_ALIGN(sizeof(struct ipt_ECN_info))); 137 return 0; 138 } 139 140 if (strcmp(tablename, "mangle") != 0) { 141 printk(KERN_WARNING "ECN: can only be called from \"mangle\" table, not \"%s\"\n", tablename); 142 return 0; 143 } 144 145 if (einfo->operation & IPT_ECN_OP_MASK) { 146 printk(KERN_WARNING "ECN: unsupported ECN operation %x\n", 147 einfo->operation); 148 return 0; 149 } 150 if (einfo->ip_ect & ~IPT_ECN_IP_MASK) { 151 printk(KERN_WARNING "ECN: new ECT codepoint %x out of mask\n", 152 einfo->ip_ect); 153 return 0; 154 } 155 156 if ((einfo->operation & (IPT_ECN_OP_SET_ECE|IPT_ECN_OP_SET_CWR)) 157 && e->ip.proto != IPPROTO_TCP) { 158 printk(KERN_WARNING "ECN: cannot use TCP operations on a " 159 "non-tcp rule\n"); 160 return 0; 161 } 162 163 return 1; 164} 165 166static struct ipt_target ipt_ecn_reg 167= { { NULL, NULL }, "ECN", target, checkentry, NULL, THIS_MODULE }; 168 169static int __init init(void) 170{ 171 if (ipt_register_target(&ipt_ecn_reg)) 172 return -EINVAL; 173 174 return 0; 175} 176 177static void __exit fini(void) 178{ 179 ipt_unregister_target(&ipt_ecn_reg); 180} 181 182module_init(init); 183module_exit(fini); 184