static unsigned int ipt_tcpmss_target(struct sk_buff **pskb, unsigned int hooknum, const struct net_device *in, const struct net_device *out, const void *targinfo, void *userinfo) { const struct ipt_tcpmss_info *tcpmssinfo = targinfo; struct tcphdr *tcph; struct iphdr *iph; u_int16_t tcplen, newtotlen, oldval, newmss, mtu; unsigned int i; u_int8_t *opt; /* raw socket (tcpdump) may have clone of incoming skb: don't disturb it --RR */ if (skb_cloned(*pskb) && !(*pskb)->sk) { struct sk_buff *nskb = skb_copy(*pskb, GFP_ATOMIC); if (!nskb) return IPT_CONTINUE; kfree_skb(*pskb); *pskb = nskb; } iph = (*pskb)->nh.iph; tcplen = (*pskb)->len - iph->ihl*4; tcph = (void *)iph + iph->ihl*4; /* Since it passed flags test in tcp match, we know it is is not a fragment, and has data >= tcp header length. SYN packets should not contain data: if they did, then we risk running over MTU, sending Frag Needed and breaking things badly. --RR */ if (tcplen != tcph->doff*4) { if (net_ratelimit()) printk(KERN_ERR "ipt_tcpmss_target: bad length (%d bytes)\n", (*pskb)->len); return IPT_CONTINUE; } if(tcpmssinfo->mss == IPT_TCPMSS_CLAMP_PMTU) { if(!(*pskb)->dst) { if (net_ratelimit()) printk(KERN_ERR "ipt_tcpmss_target: no dst?! can't determine path-MTU\n"); return IPT_CONTINUE; } if((*pskb)->dst->pmtu <= (sizeof(struct iphdr) + sizeof(struct tcphdr))) { if (net_ratelimit()) printk(KERN_ERR "ipt_tcpmss_target: unknown or invalid path-MTU (%d)\n", (*pskb)->dst->pmtu); return IPT_CONTINUE; } mtu = (*pskb)->dst->pmtu; if (in) { if (in->mtu <= (sizeof(struct iphdr) + sizeof(struct tcphdr))) { if (net_ratelimit()) printk(KERN_ERR "ipt_tcpmss_target: invalid interface MTU (%d)\n", in->mtu); return IPT_CONTINUE; } if (in->mtu < mtu) mtu = in->mtu; } newmss = mtu - sizeof(struct iphdr) - sizeof(struct tcphdr); } else newmss = tcpmssinfo->mss; opt = (u_int8_t *)tcph; for (i = sizeof(struct tcphdr); i < tcph->doff*4; i += optlen(opt, i)){ if ((opt[i] == TCPOPT_MSS) && ((tcph->doff*4 - i) >= TCPOLEN_MSS) && (opt[i+1] == TCPOLEN_MSS)) { u_int16_t oldmss; oldmss = (opt[i+2] << 8) | opt[i+3]; if((tcpmssinfo->mss == IPT_TCPMSS_CLAMP_PMTU) && (oldmss <= newmss)) return IPT_CONTINUE; opt[i+2] = (newmss & 0xff00) >> 8; opt[i+3] = (newmss & 0x00ff); tcph->check = cheat_check(htons(oldmss)^0xFFFF, htons(newmss), tcph->check); DEBUGP(KERN_INFO "ipt_tcpmss_target: %u.%u.%u.%u:%hu" "->%u.%u.%u.%u:%hu changed TCP MSS option" " (from %u to %u)\n", NIPQUAD((*pskb)->nh.iph->saddr), ntohs(tcph->source), NIPQUAD((*pskb)->nh.iph->daddr), ntohs(tcph->dest), oldmss, newmss); goto retmodified; } }
static unsigned int ipt_tcpmss_target(struct sk_buff **pskb, const struct net_device *in, const struct net_device *out, unsigned int hooknum, const void *targinfo, void *userinfo) { const struct ipt_tcpmss_info *tcpmssinfo = targinfo; struct tcphdr *tcph; struct iphdr *iph; u_int16_t tcplen, newtotlen, oldval, newmss; unsigned int i; u_int8_t *opt; if (!skb_make_writable(pskb, (*pskb)->len)) return NF_DROP; if ((*pskb)->ip_summed == CHECKSUM_HW && skb_checksum_help(*pskb, out == NULL)) return NF_DROP; iph = (*pskb)->nh.iph; tcplen = (*pskb)->len - iph->ihl*4; tcph = (void *)iph + iph->ihl*4; /* Since it passed flags test in tcp match, we know it is is not a fragment, and has data >= tcp header length. SYN packets should not contain data: if they did, then we risk running over MTU, sending Frag Needed and breaking things badly. --RR */ if (tcplen != tcph->doff*4) { if (net_ratelimit()) printk(KERN_ERR "ipt_tcpmss_target: bad length (%d bytes)\n", (*pskb)->len); return NF_DROP; } if(tcpmssinfo->mss == IPT_TCPMSS_CLAMP_PMTU) { if(!(*pskb)->dst) { if (net_ratelimit()) printk(KERN_ERR "ipt_tcpmss_target: no dst?! can't determine path-MTU\n"); return NF_DROP; /* or IPT_CONTINUE ?? */ } if(dst_mtu((*pskb)->dst) <= (sizeof(struct iphdr) + sizeof(struct tcphdr))) { if (net_ratelimit()) printk(KERN_ERR "ipt_tcpmss_target: unknown or invalid path-MTU (%d)\n", dst_mtu((*pskb)->dst)); return NF_DROP; /* or IPT_CONTINUE ?? */ } newmss = dst_mtu((*pskb)->dst) - sizeof(struct iphdr) - sizeof(struct tcphdr); } else newmss = tcpmssinfo->mss; opt = (u_int8_t *)tcph; for (i = sizeof(struct tcphdr); i < tcph->doff*4; i += optlen(opt, i)){ if ((opt[i] == TCPOPT_MSS) && ((tcph->doff*4 - i) >= TCPOLEN_MSS) && (opt[i+1] == TCPOLEN_MSS)) { u_int16_t oldmss; oldmss = (opt[i+2] << 8) | opt[i+3]; if((tcpmssinfo->mss == IPT_TCPMSS_CLAMP_PMTU) && (oldmss <= newmss)) return IPT_CONTINUE; opt[i+2] = (newmss & 0xff00) >> 8; opt[i+3] = (newmss & 0x00ff); tcph->check = cheat_check(htons(oldmss)^0xFFFF, htons(newmss), tcph->check); DEBUGP(KERN_INFO "ipt_tcpmss_target: %u.%u.%u.%u:%hu" "->%u.%u.%u.%u:%hu changed TCP MSS option" " (from %u to %u)\n", NIPQUAD((*pskb)->nh.iph->saddr), ntohs(tcph->source), NIPQUAD((*pskb)->nh.iph->daddr), ntohs(tcph->dest), oldmss, newmss); goto retmodified; } }