static inline unsigned int tse6_sack_adjust(struct sk_buff **pskb, struct tcphdr *tcph, struct nf_conn *ct, enum ip_conntrack_info ctinfo, unsigned int thoff) { unsigned int dir, optoff, optend; struct nf_conn_nat *nat = &ct->nat; optoff = thoff + sizeof(struct tcphdr); optend = thoff + tcph->doff*4; if (!skb_make_writable(pskb, optend)) return 0; dir = CTINFO2DIR(ctinfo); while (optoff < optend) { /* Usually: option, length. */ unsigned char *op = (*pskb)->data + optoff; switch (op[0]) { case TCPOPT_EOL: return 1; case TCPOPT_NOP: optoff++; continue; default: /* no partial options */ if (optoff + 1 == optend || optoff + op[1] > optend || op[1] < 2) return 0; if (op[0] == TCPOPT_SACK && op[1] >= 2+TCPOLEN_SACK_PERBLOCK && ((op[1] - 2) % TCPOLEN_SACK_PERBLOCK) == 0) sack_adjust(*pskb, tcph, optoff+2, optoff+op[1], &nat->info.seq[!dir]); optoff += op[1]; } } return 1; }
static inline unsigned int nf_nat_sack_adjust(struct sk_buff *skb, struct tcphdr *tcph, struct nf_conn *ct, enum ip_conntrack_info ctinfo) { unsigned int dir, optoff, optend; struct nf_conn_nat *nat = nfct_nat(ct); optoff = ip_hdrlen(skb) + sizeof(struct tcphdr); optend = ip_hdrlen(skb) + tcph->doff * 4; if (!skb_make_writable(skb, optend)) return 0; dir = CTINFO2DIR(ctinfo); while (optoff < optend) { unsigned char *op = skb->data + optoff; switch (op[0]) { case TCPOPT_EOL: return 1; case TCPOPT_NOP: optoff++; continue; default: if (optoff + 1 == optend || optoff + op[1] > optend || op[1] < 2) return 0; if (op[0] == TCPOPT_SACK && op[1] >= 2+TCPOLEN_SACK_PERBLOCK && ((op[1] - 2) % TCPOLEN_SACK_PERBLOCK) == 0) sack_adjust(skb, tcph, optoff+2, optoff+op[1], &nat->seq[!dir]); optoff += op[1]; } } return 1; }