📄 ip_conntrack_proto_tcp.c
字号:
I. Upper bound for valid data: seq + len <= sender.td_maxend II. Lower bound for valid data: seq >= sender.td_end - receiver.td_maxwin III. Upper bound for valid ack: sack <= receiver.td_end IV. Lower bound for valid ack: ack >= receiver.td_end - MAXACKWINDOW where sack is the highest right edge of sack block found in the packet. The upper bound limit for a valid ack is not ignored - we doesn't have to deal with fragments. */static inline __u32 segment_seq_plus_len(__u32 seq, size_t len, struct iphdr *iph, struct tcphdr *tcph) { return (seq + len - (iph->ihl + tcph->doff)*4 + (tcph->syn ? 1 : 0) + (tcph->fin ? 1 : 0));} /* Fixme: what about big packets? */#define MAXACKWINCONST 66000#define MAXACKWINDOW(sender) \ ((sender)->td_maxwin > MAXACKWINCONST ? (sender)->td_maxwin \ : MAXACKWINCONST) /* * Simplified tcp_parse_options routine from tcp_input.c */static void tcp_options(const struct sk_buff *skb, struct iphdr *iph, struct tcphdr *tcph, struct ip_ct_tcp_state *state){ unsigned char buff[(15 * 4) - sizeof(struct tcphdr)]; unsigned char *ptr; int length = (tcph->doff*4) - sizeof(struct tcphdr); if (!length) return; ptr = skb_header_pointer(skb, (iph->ihl * 4) + sizeof(struct tcphdr), length, buff); BUG_ON(ptr == NULL); state->td_scale = state->flags = 0; while (length > 0) { int opcode=*ptr++; int opsize; switch (opcode) { case TCPOPT_EOL: return; case TCPOPT_NOP: /* Ref: RFC 793 section 3.1 */ length--; continue; default: opsize=*ptr++; if (opsize < 2) /* "silly options" */ return; if (opsize > length) break; /* don't parse partial options */ if (opcode == TCPOPT_SACK_PERM && opsize == TCPOLEN_SACK_PERM) state->flags |= IP_CT_TCP_FLAG_SACK_PERM; else if (opcode == TCPOPT_WINDOW && opsize == TCPOLEN_WINDOW) { state->td_scale = *(u_int8_t *)ptr; if (state->td_scale > 14) { /* See RFC1323 */ state->td_scale = 14; } state->flags |= IP_CT_TCP_FLAG_WINDOW_SCALE; } ptr += opsize - 2; length -= opsize; } }}static void tcp_sack(struct tcphdr *tcph, __u32 *sack){ __u32 tmp; unsigned char *ptr; int length = (tcph->doff*4) - sizeof(struct tcphdr); /* Fast path for timestamp-only option */ if (length == TCPOLEN_TSTAMP_ALIGNED*4 && *(__u32 *)(tcph + 1) == __constant_ntohl((TCPOPT_NOP << 24) | (TCPOPT_NOP << 16) | (TCPOPT_TIMESTAMP << 8) | TCPOLEN_TIMESTAMP)) return; ptr = (unsigned char *)(tcph + 1); while (length > 0) { int opcode=*ptr++; int opsize, i; switch (opcode) { case TCPOPT_EOL: return; case TCPOPT_NOP: /* Ref: RFC 793 section 3.1 */ length--; continue; default: opsize=*ptr++; if (opsize < 2) /* "silly options" */ return; if (opsize > length) break; /* don't parse partial options */ if (opcode == TCPOPT_SACK && opsize >= (TCPOLEN_SACK_BASE + TCPOLEN_SACK_PERBLOCK) && !((opsize - TCPOLEN_SACK_BASE) % TCPOLEN_SACK_PERBLOCK)) { for (i = 0; i < (opsize - TCPOLEN_SACK_BASE); i += TCPOLEN_SACK_PERBLOCK) { tmp = ntohl(*((u_int32_t *)(ptr+i)+1)); if (after(tmp, *sack)) *sack = tmp; } return; } ptr += opsize - 2; length -= opsize; } }}static int tcp_in_window(struct ip_ct_tcp *state, enum ip_conntrack_dir dir, unsigned int *index, const struct sk_buff *skb, struct iphdr *iph, struct tcphdr *tcph){ struct ip_ct_tcp_state *sender = &state->seen[dir]; struct ip_ct_tcp_state *receiver = &state->seen[!dir]; __u32 seq, ack, sack, end, win, swin; int res; /* * Get the required data from the packet. */ seq = ntohl(tcph->seq); ack = sack = ntohl(tcph->ack_seq); win = ntohs(tcph->window); end = segment_seq_plus_len(seq, skb->len, iph, tcph); if (receiver->flags & IP_CT_TCP_FLAG_SACK_PERM) tcp_sack(tcph, &sack); DEBUGP("tcp_in_window: START\n"); DEBUGP("tcp_in_window: src=%u.%u.%u.%u:%hu dst=%u.%u.%u.%u:%hu " "seq=%u ack=%u sack=%u win=%u end=%u\n", NIPQUAD(iph->saddr), ntohs(tcph->source), NIPQUAD(iph->daddr), ntohs(tcph->dest), seq, ack, sack, win, end); DEBUGP("tcp_in_window: sender end=%u maxend=%u maxwin=%u scale=%i " "receiver end=%u maxend=%u maxwin=%u scale=%i\n", sender->td_end, sender->td_maxend, sender->td_maxwin, sender->td_scale, receiver->td_end, receiver->td_maxend, receiver->td_maxwin, receiver->td_scale); if (sender->td_end == 0) { /* * Initialize sender data. */ if (tcph->syn && tcph->ack) { /* * Outgoing SYN-ACK in reply to a SYN. */ sender->td_end = sender->td_maxend = end; sender->td_maxwin = (win == 0 ? 1 : win); tcp_options(skb, iph, tcph, sender); /* * RFC 1323: * Both sides must send the Window Scale option * to enable window scaling in either direction. */ if (!(sender->flags & IP_CT_TCP_FLAG_WINDOW_SCALE && receiver->flags & IP_CT_TCP_FLAG_WINDOW_SCALE)) sender->td_scale = receiver->td_scale = 0; } else { /* * We are in the middle of a connection, * its history is lost for us. * Let's try to use the data from the packet. */ sender->td_end = end; sender->td_maxwin = (win == 0 ? 1 : win); sender->td_maxend = end + sender->td_maxwin; } } else if (((state->state == TCP_CONNTRACK_SYN_SENT && dir == IP_CT_DIR_ORIGINAL) || (state->state == TCP_CONNTRACK_SYN_RECV && dir == IP_CT_DIR_REPLY)) && after(end, sender->td_end)) { /* * RFC 793: "if a TCP is reinitialized ... then it need * not wait at all; it must only be sure to use sequence * numbers larger than those recently used." */ sender->td_end = sender->td_maxend = end; sender->td_maxwin = (win == 0 ? 1 : win); tcp_options(skb, iph, tcph, sender); } if (!(tcph->ack)) { /* * If there is no ACK, just pretend it was set and OK. */ ack = sack = receiver->td_end; } else if (((tcp_flag_word(tcph) & (TCP_FLAG_ACK|TCP_FLAG_RST)) == (TCP_FLAG_ACK|TCP_FLAG_RST)) && (ack == 0)) { /* * Broken TCP stacks, that set ACK in RST packets as well * with zero ack value. */ ack = sack = receiver->td_end; } if (seq == end) /* * Packets contains no data: we assume it is valid * and check the ack value only. */ seq = end = sender->td_end; DEBUGP("tcp_in_window: src=%u.%u.%u.%u:%hu dst=%u.%u.%u.%u:%hu " "seq=%u ack=%u sack =%u win=%u end=%u trim=%u\n", NIPQUAD(iph->saddr), ntohs(tcph->source), NIPQUAD(iph->daddr), ntohs(tcph->dest), seq, ack, sack, win, end, after(end, sender->td_maxend) && before(seq, sender->td_maxend) ? sender->td_maxend : end); DEBUGP("tcp_in_window: sender end=%u maxend=%u maxwin=%u scale=%i " "receiver end=%u maxend=%u maxwin=%u scale=%i\n", sender->td_end, sender->td_maxend, sender->td_maxwin, sender->td_scale, receiver->td_end, receiver->td_maxend, receiver->td_maxwin, receiver->td_scale); /* Ignore data over the right edge of the receiver's window. */ if (after(end, sender->td_maxend) && before(seq, sender->td_maxend)) { end = sender->td_maxend; if (*index == TCP_FIN_SET) *index = TCP_ACK_SET; } DEBUGP("tcp_in_window: I=%i II=%i III=%i IV=%i\n", before(end, sender->td_maxend + 1) || before(seq, sender->td_maxend + 1), after(seq, sender->td_end - receiver->td_maxwin - 1) || after(end, sender->td_end - receiver->td_maxwin - 1), before(sack, receiver->td_end + 1), after(ack, receiver->td_end - MAXACKWINDOW(sender))); if (sender->loose || receiver->loose || (before(end, sender->td_maxend + 1) && after(seq, sender->td_end - receiver->td_maxwin - 1) && before(sack, receiver->td_end + 1) && after(ack, receiver->td_end - MAXACKWINDOW(sender)))) { /* * Take into account window scaling (RFC 1323). */ if (!tcph->syn) win <<= sender->td_scale; /* * Update sender data. */ swin = win + (sack - ack); if (sender->td_maxwin < swin) sender->td_maxwin = swin; if (after(end, sender->td_end)) sender->td_end = end; if (after(sack + win, receiver->td_maxend - 1)) { receiver->td_maxend = sack + win; if (win == 0) receiver->td_maxend++; } /* * Check retransmissions. */ if (*index == TCP_ACK_SET) { if (state->last_dir == dir && state->last_seq == seq && state->last_ack == ack && state->last_end == end) state->retrans++; else { state->last_dir = dir; state->last_seq = seq; state->last_ack = ack; state->last_end = end; state->retrans = 0; } } /* * Close the window of disabled window tracking :-) */ if (sender->loose) sender->loose--; res = 1; } else { if (LOG_INVALID(IPPROTO_TCP)) nf_log_packet(PF_INET, 0, skb, NULL, NULL, "ip_ct_tcp: %s ", before(end, sender->td_maxend + 1) ? after(seq, sender->td_end - receiver->td_maxwin - 1) ? before(sack, receiver->td_end + 1) ? after(ack, receiver->td_end - MAXACKWINDOW(sender)) ? "BUG" : "ACK is under the lower bound (possibly overly delayed ACK)" : "ACK is over the upper bound (ACKed data has never seen yet)" : "SEQ is under the lower bound (retransmitted already ACKed data)" : "SEQ is over the upper bound (over the window of the receiver)"); res = ip_ct_tcp_be_liberal && !tcph->rst; } DEBUGP("tcp_in_window: res=%i sender end=%u maxend=%u maxwin=%u " "receiver end=%u maxend=%u maxwin=%u\n", res, sender->td_end, sender->td_maxend, sender->td_maxwin, receiver->td_end, receiver->td_maxend, receiver->td_maxwin); return res;}#ifdef CONFIG_IP_NF_NAT_NEEDED/* Update sender->td_end after NAT successfully mangled the packet */void ip_conntrack_tcp_update(struct sk_buff *skb, struct ip_conntrack *conntrack, enum ip_conntrack_dir dir){ struct iphdr *iph = skb->nh.iph; struct tcphdr *tcph = (void *)skb->nh.iph + skb->nh.iph->ihl*4; __u32 end;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -