static unsigned int target(struct sk_buff **pskb, const struct net_device *in, const struct net_device *out, unsigned int hooknum, const struct xt_target *target, const void *targinfo) { const struct xt_connmark_target_info *markinfo = targinfo; u_int32_t diff; u_int32_t nfmark; u_int32_t newmark; u_int32_t ctinfo; u_int32_t *ctmark = nf_ct_get_mark(*pskb, &ctinfo); if (ctmark) { switch(markinfo->mode) { case XT_CONNMARK_SET: newmark = (*ctmark & ~markinfo->mask) | markinfo->mark; if (newmark != *ctmark) { *ctmark = newmark; #if defined(CONFIG_IP_NF_CONNTRACK) || defined(CONFIG_IP_NF_CONNTRACK_MODULE) ip_conntrack_event_cache(IPCT_MARK, *pskb); #else nf_conntrack_event_cache(IPCT_MARK, *pskb); #endif } break; case XT_CONNMARK_SAVE: newmark = (*ctmark & ~markinfo->mask) | ((*pskb)->nfmark & markinfo->mask); if (*ctmark != newmark) { *ctmark = newmark; #if defined(CONFIG_IP_NF_CONNTRACK) || defined(CONFIG_IP_NF_CONNTRACK_MODULE) ip_conntrack_event_cache(IPCT_MARK, *pskb); #else nf_conntrack_event_cache(IPCT_MARK, *pskb); #endif } break; case XT_CONNMARK_RESTORE: nfmark = (*pskb)->nfmark; diff = (*ctmark ^ nfmark) & markinfo->mask; if (diff != 0) (*pskb)->nfmark = nfmark ^ diff; break; } } return XT_CONTINUE; }
/* Returns verdict for packet, or -1 for invalid. */ static int icmp_packet(struct ip_conntrack *ct, const struct sk_buff *skb, enum ip_conntrack_info ctinfo) { /* Try to delete connection immediately after all replies: won't actually vanish as we still have skb, and del_timer means this will only run once even if count hits zero twice (theoretically possible with SMP) */ if (CTINFO2DIR(ctinfo) == IP_CT_DIR_REPLY) { if (atomic_dec_and_test(&ct->proto.icmp.count) && del_timer(&ct->timeout)) ct->timeout.function((unsigned long)ct); } else { atomic_inc(&ct->proto.icmp.count); ip_conntrack_event_cache(IPCT_PROTOINFO_VOLATILE, skb); //if (ct->status & IPS_SEEN_REPLY) //TT 20060103 : let ct aging out if no reply in ICMP_TIMEOUT period if ((ct->status & IPS_SEEN_REPLY) || !(ct->timeout.expires)) ip_ct_refresh_acct(ct, ctinfo, skb, ip_ct_icmp_timeout); } 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. */ /* jimmy added 20080616, when SPI enable, block invlaid syn+ack with unexpected sequence */ if(sysctl_spi_enable){ if((index == TCP_SYNACK_SET) && (conntrack->proto.tcp.last_index != TCP_SYN_SET) && //(conntrack->proto.tcp.last_dir != dir) && // Control: 21 <-> random ,Data: random <-> random (ntohl(th->ack_seq) != conntrack->proto.tcp.last_end) ) { if (LOG_INVALID(IPPROTO_TCP)){ nf_log_packet(PF_INET, 0, skb, NULL, NULL, NULL, "ip_ct_tcp: Invalid SynAck packet "); } printk(KERN_NOTICE "Blocked incoming TCP SynAck packet from %u.%u.%u.%u:%hu to %u.%u.%u.%u:%hu with unexpected sequence\n", NIPQUAD(iph->saddr), ntohs(th->source), NIPQUAD(iph->daddr), ntohs(th->dest)); write_unlock_bh(&tcp_lock); return -NF_DROP; } } /* ------------------------------------------------------------- */ 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 (sysctl_spi_enable){ 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; }