📄 ip_conntrack_proto_tcp.c
字号:
struct ip_ct_tcp_state *sender = &conntrack->proto.tcp.seen[dir]; struct ip_ct_tcp_state *receiver = &conntrack->proto.tcp.seen[!dir];#endif end = segment_seq_plus_len(ntohl(tcph->seq), skb->len, iph, tcph); write_lock_bh(&tcp_lock); /* * We have to worry for the ack in the reply packet only... */ if (after(end, conntrack->proto.tcp.seen[dir].td_end)) conntrack->proto.tcp.seen[dir].td_end = end; conntrack->proto.tcp.last_end = end; write_unlock_bh(&tcp_lock); DEBUGP("tcp_update: 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);} #endif#define TH_FIN 0x01#define TH_SYN 0x02#define TH_RST 0x04#define TH_PUSH 0x08#define TH_ACK 0x10#define TH_URG 0x20#define TH_ECE 0x40#define TH_CWR 0x80/* table of valid flag combinations - ECE and CWR are always valid */static const u8 tcp_valid_flags[(TH_FIN|TH_SYN|TH_RST|TH_PUSH|TH_ACK|TH_URG) + 1] ={ [TH_SYN] = 1, [TH_SYN|TH_ACK] = 1, [TH_SYN|TH_PUSH] = 1, [TH_SYN|TH_ACK|TH_PUSH] = 1, [TH_RST] = 1, [TH_RST|TH_ACK] = 1, [TH_RST|TH_ACK|TH_PUSH] = 1, [TH_FIN|TH_ACK] = 1, [TH_ACK] = 1, [TH_ACK|TH_PUSH] = 1, [TH_ACK|TH_URG] = 1, [TH_ACK|TH_URG|TH_PUSH] = 1, [TH_FIN|TH_ACK|TH_PUSH] = 1, [TH_FIN|TH_ACK|TH_URG] = 1, [TH_FIN|TH_ACK|TH_URG|TH_PUSH] = 1,};/* Protect conntrack agaist broken packets. Code taken from ipt_unclean.c. */static int tcp_error(struct sk_buff *skb, enum ip_conntrack_info *ctinfo, unsigned int hooknum){ struct iphdr *iph = skb->nh.iph; struct tcphdr _tcph, *th; unsigned int tcplen = skb->len - iph->ihl * 4; u_int8_t tcpflags; /* Smaller that minimal TCP header? */ th = skb_header_pointer(skb, iph->ihl * 4, sizeof(_tcph), &_tcph); if (th == NULL) { if (LOG_INVALID(IPPROTO_TCP)) nf_log_packet(PF_INET, 0, skb, NULL, NULL, NULL, "ip_ct_tcp: short packet "); return -NF_ACCEPT; } /* Not whole TCP header or malformed packet */ if (th->doff*4 < sizeof(struct tcphdr) || tcplen < th->doff*4) { if (LOG_INVALID(IPPROTO_TCP)) nf_log_packet(PF_INET, 0, skb, NULL, NULL, NULL, "ip_ct_tcp: truncated/malformed packet "); return -NF_ACCEPT; } /* Checksum invalid? Ignore. * We skip checking packets on the outgoing path * because the semantic of CHECKSUM_HW is different there * and moreover root might send raw packets. */ /* FIXME: Source route IP option packets --RR */ if (hooknum == NF_IP_PRE_ROUTING && skb->ip_summed != CHECKSUM_UNNECESSARY && csum_tcpudp_magic(iph->saddr, iph->daddr, tcplen, IPPROTO_TCP, skb->ip_summed == CHECKSUM_HW ? skb->csum : skb_checksum(skb, iph->ihl*4, tcplen, 0))) { if (LOG_INVALID(IPPROTO_TCP)) nf_log_packet(PF_INET, 0, skb, NULL, NULL, NULL, "ip_ct_tcp: bad TCP checksum "); return -NF_ACCEPT; } /* Check TCP flags. */ tcpflags = (((u_int8_t *)th)[13] & ~(TH_ECE|TH_CWR)); if (!tcp_valid_flags[tcpflags]) { if (LOG_INVALID(IPPROTO_TCP)) nf_log_packet(PF_INET, 0, skb, NULL, NULL, NULL, "ip_ct_tcp: invalid TCP flag combination "); return -NF_ACCEPT; } return NF_ACCEPT;}/* Returns verdict for packet, or -1 for invalid. */static int tcp_packet(struct ip_conntrack *conntrack, const struct sk_buff *skb, enum ip_conntrack_info ctinfo){ enum tcp_conntrack new_state, old_state; enum ip_conntrack_dir dir; struct iphdr *iph = skb->nh.iph; struct tcphdr *th, _tcph; unsigned long timeout; unsigned int index; th = skb_header_pointer(skb, iph->ihl * 4, sizeof(_tcph), &_tcph); BUG_ON(th == NULL); write_lock_bh(&tcp_lock); old_state = conntrack->proto.tcp.state; dir = CTINFO2DIR(ctinfo); index = get_conntrack_index(th); new_state = tcp_conntracks[dir][index][old_state]; switch (new_state) { case TCP_CONNTRACK_IGNORE: /* Ignored packets: * * a) SYN in ORIGINAL * b) SYN/ACK in REPLY * c) ACK in reply direction after initial SYN in original. */ if (index == TCP_SYNACK_SET && conntrack->proto.tcp.last_index == TCP_SYN_SET && conntrack->proto.tcp.last_dir != dir && ntohl(th->ack_seq) == conntrack->proto.tcp.last_end) { /* This SYN/ACK acknowledges a SYN that we earlier * ignored as invalid. This means that the client and * the server are both in sync, while the firewall is * not. We kill this session and block the SYN/ACK so * that the client cannot but retransmit its SYN and * thus initiate a clean new session. */ write_unlock_bh(&tcp_lock); if (LOG_INVALID(IPPROTO_TCP)) nf_log_packet(PF_INET, 0, skb, NULL, NULL, NULL, "ip_ct_tcp: " "killing out of sync session "); if (del_timer(&conntrack->timeout)) conntrack->timeout.function((unsigned long) conntrack); return -NF_DROP; } conntrack->proto.tcp.last_index = index; conntrack->proto.tcp.last_dir = dir; conntrack->proto.tcp.last_seq = ntohl(th->seq); conntrack->proto.tcp.last_end = segment_seq_plus_len(ntohl(th->seq), skb->len, iph, th); write_unlock_bh(&tcp_lock); if (LOG_INVALID(IPPROTO_TCP)) nf_log_packet(PF_INET, 0, skb, NULL, NULL, NULL, "ip_ct_tcp: invalid packet ignored "); return NF_ACCEPT; case TCP_CONNTRACK_MAX: /* Invalid packet */ DEBUGP("ip_ct_tcp: Invalid dir=%i index=%u ostate=%u\n", dir, get_conntrack_index(th), old_state); write_unlock_bh(&tcp_lock); if (LOG_INVALID(IPPROTO_TCP)) nf_log_packet(PF_INET, 0, skb, NULL, NULL, NULL, "ip_ct_tcp: invalid state "); return -NF_ACCEPT; case TCP_CONNTRACK_SYN_SENT: if (old_state < TCP_CONNTRACK_TIME_WAIT) break; if ((conntrack->proto.tcp.seen[dir].flags & IP_CT_TCP_FLAG_CLOSE_INIT) || after(ntohl(th->seq), conntrack->proto.tcp.seen[dir].td_end)) { /* Attempt to reopen a closed connection. * Delete this connection and look up again. */ write_unlock_bh(&tcp_lock); if (del_timer(&conntrack->timeout)) conntrack->timeout.function((unsigned long) conntrack); return -NF_REPEAT; } else { write_unlock_bh(&tcp_lock); if (LOG_INVALID(IPPROTO_TCP)) nf_log_packet(PF_INET, 0, skb, NULL, NULL, NULL, "ip_ct_tcp: invalid SYN"); return -NF_ACCEPT; } case TCP_CONNTRACK_CLOSE: if (index == TCP_RST_SET && ((test_bit(IPS_SEEN_REPLY_BIT, &conntrack->status) && conntrack->proto.tcp.last_index == TCP_SYN_SET) || (!test_bit(IPS_ASSURED_BIT, &conntrack->status) && conntrack->proto.tcp.last_index == TCP_ACK_SET)) && ntohl(th->ack_seq) == conntrack->proto.tcp.last_end) { /* RST sent to invalid SYN or ACK we had let trough * at a) and c) above: * * a) SYN was in window then * c) we hold a half-open connection. * * Delete our connection entry. * We skip window checking, because packet might ACK * segments we ignored. */ goto in_window; } /* Just fall trough */ default: /* Keep compilers happy. */ break; } if (!tcp_in_window(&conntrack->proto.tcp, dir, index, skb, iph, th)) { write_unlock_bh(&tcp_lock); return -NF_ACCEPT; } in_window: /* From now on we have got in-window packets */ conntrack->proto.tcp.last_index = index; DEBUGP("tcp_conntracks: src=%u.%u.%u.%u:%hu dst=%u.%u.%u.%u:%hu " "syn=%i ack=%i fin=%i rst=%i old=%i new=%i\n", NIPQUAD(iph->saddr), ntohs(th->source), NIPQUAD(iph->daddr), ntohs(th->dest), (th->syn ? 1 : 0), (th->ack ? 1 : 0), (th->fin ? 1 : 0), (th->rst ? 1 : 0), old_state, new_state); conntrack->proto.tcp.state = new_state; if (old_state != new_state && (new_state == TCP_CONNTRACK_FIN_WAIT || new_state == TCP_CONNTRACK_CLOSE)) conntrack->proto.tcp.seen[dir].flags |= IP_CT_TCP_FLAG_CLOSE_INIT; timeout = conntrack->proto.tcp.retrans >= ip_ct_tcp_max_retrans && *tcp_timeouts[new_state] > ip_ct_tcp_timeout_max_retrans ? ip_ct_tcp_timeout_max_retrans : *tcp_timeouts[new_state]; write_unlock_bh(&tcp_lock); ip_conntrack_event_cache(IPCT_PROTOINFO_VOLATILE, skb); if (new_state != old_state) ip_conntrack_event_cache(IPCT_PROTOINFO, skb); if (!test_bit(IPS_SEEN_REPLY_BIT, &conntrack->status)) { /* If only reply is a RST, we can consider ourselves not to have an established connection: this is a fairly common problem case, so we can delete the conntrack immediately. --RR */ if (th->rst) { if (del_timer(&conntrack->timeout)) conntrack->timeout.function((unsigned long) conntrack); return NF_ACCEPT; } } else if (!test_bit(IPS_ASSURED_BIT, &conntrack->status) && (old_state == TCP_CONNTRACK_SYN_RECV || old_state == TCP_CONNTRACK_ESTABLISHED) && new_state == TCP_CONNTRACK_ESTABLISHED) { /* Set ASSURED if we see see valid ack in ESTABLISHED after SYN_RECV or a valid answer for a picked up connection. */ set_bit(IPS_ASSURED_BIT, &conntrack->status); ip_conntrack_event_cache(IPCT_STATUS, skb); } ip_ct_refresh_acct(conntrack, ctinfo, skb, timeout); return NF_ACCEPT;} /* Called when a new connection for this protocol found. */static int tcp_new(struct ip_conntrack *conntrack, const struct sk_buff *skb){ enum tcp_conntrack new_state; struct iphdr *iph = skb->nh.iph; struct tcphdr *th, _tcph;#ifdef DEBUGP_VARS struct ip_ct_tcp_state *sender = &conntrack->proto.tcp.seen[0]; struct ip_ct_tcp_state *receiver = &conntrack->proto.tcp.seen[1];#endif th = skb_header_pointer(skb, iph->ihl * 4, sizeof(_tcph), &_tcph); BUG_ON(th == NULL); /* Don't need lock here: this conntrack not in circulation yet */ new_state = tcp_conntracks[0][get_conntrack_index(th)] [TCP_CONNTRACK_NONE]; /* Invalid: delete conntrack */ if (new_state >= TCP_CONNTRACK_MAX) { DEBUGP("ip_ct_tcp: invalid new deleting.\n"); return 0; } if (new_state == TCP_CONNTRACK_SYN_SENT) { /* SYN packet */ conntrack->proto.tcp.seen[0].td_end = segment_seq_plus_len(ntohl(th->seq), skb->len, iph, th); conntrack->proto.tcp.seen[0].td_maxwin = ntohs(th->window); if (conntrack->proto.tcp.seen[0].td_maxwin == 0) conntrack->proto.tcp.seen[0].td_maxwin = 1; conntrack->proto.tcp.seen[0].td_maxend = conntrack->proto.tcp.seen[0].td_end; tcp_options(skb, iph, th, &conntrack->proto.tcp.seen[0]); conntrack->proto.tcp.seen[1].flags = 0; conntrack->proto.tcp.seen[0].loose = conntrack->proto.tcp.seen[1].loose = 0; } else if (ip_ct_tcp_loose == 0) { /* Don't try to pick up connections. */ return 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. */ conntrack->proto.tcp.seen[0].td_end = segment_seq_plus_len(ntohl(th->seq), skb->len, iph, th); conntrack->proto.tcp.seen[0].td_maxwin = ntohs(th->window); if (conntrack->proto.tcp.seen[0].td_maxwin == 0) conntrack->proto.tcp.seen[0].td_maxwin = 1; conntrack->proto.tcp.seen[0].td_maxend = conntrack->proto.tcp.seen[0].td_end + conntrack->proto.tcp.seen[0].td_maxwin; conntrack->proto.tcp.seen[0].td_scale = 0; /* We assume SACK. Should we assume window scaling too? */ conntrack->proto.tcp.seen[0].flags = conntrack->proto.tcp.seen[1].flags = IP_CT_TCP_FLAG_SACK_PERM; conntrack->proto.tcp.seen[0].loose = conntrack->proto.tcp.seen[1].loose = ip_ct_tcp_loose; } conntrack->proto.tcp.seen[1].td_end = 0; conntrack->proto.tcp.seen[1].td_maxend = 0; conntrack->proto.tcp.seen[1].td_maxwin = 1; conntrack->proto.tcp.seen[1].td_scale = 0; /* tcp_packet will set them */ conntrack->proto.tcp.state = TCP_CONNTRACK_NONE; conntrack->proto.tcp.last_index = TCP_NONE_SET; DEBUGP("tcp_new: 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); return 1;} struct ip_conntrack_protocol ip_conntrack_protocol_tcp ={ .proto = IPPROTO_TCP, .name = "tcp", .pkt_to_tuple = tcp_pkt_to_tuple, .invert_tuple = tcp_invert_tuple, .print_tuple = tcp_print_tuple, .print_conntrack = tcp_print_conntrack, .packet = tcp_packet, .new = tcp_new, .error = tcp_error,#if defined(CONFIG_IP_NF_CONNTRACK_NETLINK) || \ defined(CONFIG_IP_NF_CONNTRACK_NETLINK_MODULE) .to_nfattr = tcp_to_nfattr, .from_nfattr = nfattr_to_tcp, .tuple_to_nfattr = ip_ct_port_tuple_to_nfattr, .nfattr_to_tuple = ip_ct_port_nfattr_to_tuple,#endif};
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -