int tse6_seq_adjust(struct sk_buff **pskb, struct nf_conn *ct, enum ip_conntrack_info ctinfo) { struct tcphdr *tcph; int dir; int protoff = 0; __be32 newseq, newack; u8 pnum = (*pskb)->nh.ipv6h->nexthdr; struct nf_conn_nat *nat = &ct->nat; struct nf_nat_seq *this_way, *other_way; dir = CTINFO2DIR(ctinfo); this_way = &nat->info.seq[dir]; other_way = &nat->info.seq[!dir]; protoff = tse6_skip_exthdr(*pskb, sizeof(struct ipv6hdr), &pnum, (*pskb)->len - sizeof(struct ipv6hdr)); if ((protoff < 0) || (protoff > (*pskb)->len) || (pnum != IPPROTO_TCP)) { return -NF_ACCEPT; } tcph = (void *)(*pskb)->nh.ipv6h + protoff; if (!skb_make_writable(pskb, protoff+sizeof(*tcph))) return 0; tcph = (void *)(*pskb)->data + protoff; if (after(ntohl(tcph->seq), this_way->correction_pos)) newseq = htonl(ntohl(tcph->seq) + this_way->offset_after); else newseq = htonl(ntohl(tcph->seq) + this_way->offset_before); if (after(ntohl(tcph->ack_seq) - other_way->offset_before, other_way->correction_pos)) newack = htonl(ntohl(tcph->ack_seq) - other_way->offset_after); else newack = htonl(ntohl(tcph->ack_seq) - other_way->offset_before); nf_proto_csum_replace4(&tcph->check, *pskb, tcph->seq, newseq, 0); nf_proto_csum_replace4(&tcph->check, *pskb, tcph->ack_seq, newack, 0); tcph->seq = newseq; tcph->ack_seq = newack; if (!tse6_sack_adjust(pskb, tcph, ct, ctinfo, protoff)) { return 0; } nf_conntrack_tcp_update(*pskb, protoff, ct, dir); return 1; }
/* Generic function for mangling variable-length address changes inside * NATed TCP connections (like the PORT XXX,XXX,XXX,XXX,XXX,XXX * command in FTP). * * Takes care about all the nasty sequence number changes, checksumming, * skb enlargement, ... * * */ int nf_nat_mangle_tcp_packet(struct sk_buff **pskb, struct nf_conn *ct, enum ip_conntrack_info ctinfo, unsigned int match_offset, unsigned int match_len, const char *rep_buffer, unsigned int rep_len) { struct iphdr *iph; struct tcphdr *tcph; int oldlen, datalen; if (!skb_make_writable(pskb, (*pskb)->len)) return 0; if (rep_len > match_len && rep_len - match_len > skb_tailroom(*pskb) && !enlarge_skb(pskb, rep_len - match_len)) return 0; SKB_LINEAR_ASSERT(*pskb); iph = (*pskb)->nh.iph; tcph = (void *)iph + iph->ihl*4; oldlen = (*pskb)->len - iph->ihl*4; mangle_contents(*pskb, iph->ihl*4 + tcph->doff*4, match_offset, match_len, rep_buffer, rep_len); datalen = (*pskb)->len - iph->ihl*4; if ((*pskb)->ip_summed != CHECKSUM_PARTIAL) { tcph->check = 0; tcph->check = tcp_v4_check(datalen, iph->saddr, iph->daddr, csum_partial((char *)tcph, datalen, 0)); } else nf_proto_csum_replace2(&tcph->check, *pskb, htons(oldlen), htons(datalen), 1); if (rep_len != match_len) { set_bit(IPS_SEQ_ADJUST_BIT, &ct->status); adjust_tcp_sequence(ntohl(tcph->seq), (int)rep_len - (int)match_len, ct, ctinfo); /* Tell TCP window tracking about seq change */ nf_conntrack_tcp_update(*pskb, (*pskb)->nh.iph->ihl*4, ct, CTINFO2DIR(ctinfo)); } return 1; }
/* TCP sequence number adjustment. Returns 1 on success, 0 on failure */ int nf_nat_seq_adjust(struct sk_buff **pskb, struct nf_conn *ct, enum ip_conntrack_info ctinfo) { struct tcphdr *tcph; int dir; __be32 newseq, newack; struct nf_conn_nat *nat = nfct_nat(ct); struct nf_nat_seq *this_way, *other_way; dir = CTINFO2DIR(ctinfo); this_way = &nat->info.seq[dir]; other_way = &nat->info.seq[!dir]; if (!skb_make_writable(pskb, (*pskb)->nh.iph->ihl*4+sizeof(*tcph))) return 0; tcph = (void *)(*pskb)->data + (*pskb)->nh.iph->ihl*4; if (after(ntohl(tcph->seq), this_way->correction_pos)) newseq = htonl(ntohl(tcph->seq) + this_way->offset_after); else newseq = htonl(ntohl(tcph->seq) + this_way->offset_before); if (after(ntohl(tcph->ack_seq) - other_way->offset_before, other_way->correction_pos)) newack = htonl(ntohl(tcph->ack_seq) - other_way->offset_after); else newack = htonl(ntohl(tcph->ack_seq) - other_way->offset_before); nf_proto_csum_replace4(&tcph->check, *pskb, tcph->seq, newseq, 0); nf_proto_csum_replace4(&tcph->check, *pskb, tcph->ack_seq, newack, 0); DEBUGP("Adjusting sequence number from %u->%u, ack from %u->%u\n", ntohl(tcph->seq), ntohl(newseq), ntohl(tcph->ack_seq), ntohl(newack)); tcph->seq = newseq; tcph->ack_seq = newack; if (!nf_nat_sack_adjust(pskb, tcph, ct, ctinfo)) return 0; nf_conntrack_tcp_update(*pskb, (*pskb)->nh.iph->ihl*4, ct, dir); return 1; }