Esempio n. 1
0
/*
 * Check and stop ack storm.
 * Return 0 if ack storm is found.
 */
static int syn_proxy_is_ack_storm(struct tcphdr *tcph, struct ip_vs_conn *cp)
{
	/* only for syn-proxy sessions */
	if (!(cp->flags & IP_VS_CONN_F_SYNPROXY) || !tcph->ack)
		return 1;

	if (unlikely(sysctl_ip_vs_synproxy_dup_ack_thresh == 0))
		return 1;

	if (unlikely(tcph->seq == cp->last_seq &&
		     tcph->ack_seq == cp->last_ack_seq)) {
		atomic_inc(&cp->dup_ack_cnt);
		if (atomic_read(&cp->dup_ack_cnt) >=
		    sysctl_ip_vs_synproxy_dup_ack_thresh) {
			atomic_set(&cp->dup_ack_cnt,
				   sysctl_ip_vs_synproxy_dup_ack_thresh);
			/* update statistics */
			IP_VS_INC_ESTATS(ip_vs_esmib, SYNPROXY_ACK_STORM);
			return 0;
		}

		return 1;
	}

	cp->last_seq = tcph->seq;
	cp->last_ack_seq = tcph->ack_seq;
	atomic_set(&cp->dup_ack_cnt, 0);

	return 1;
}
Esempio n. 2
0
int
ip_vs_synproxy_filter_ack(struct sk_buff *skb, struct ip_vs_conn *cp,
			  struct ip_vs_protocol *pp,
			  struct ip_vs_iphdr *iph, int *verdict)
{
	struct tcphdr _tcph, *th;

	th = skb_header_pointer(skb, iph->len, sizeof(_tcph), &_tcph);

	if (unlikely(NULL == th)) {
		IP_VS_ERR_RL("skb has a invalid tcp header\n");
		*verdict = NF_DROP;
		return 0;
	}

	spin_lock(&cp->lock);
	if ((cp->flags & IP_VS_CONN_F_SYNPROXY) &&
	    cp->state == IP_VS_TCP_S_SYN_SENT) {
		/*
		 * Not a ack packet, drop it.
		 */
		if (!th->ack) {
			spin_unlock(&cp->lock);
			*verdict = NF_DROP;
			return 0;
		}

		if (sysctl_ip_vs_synproxy_skb_store_thresh <
		    skb_queue_len(&cp->ack_skb)) {
			spin_unlock(&cp->lock);
			/* update statistics */
			IP_VS_INC_ESTATS(ip_vs_esmib, SYNPROXY_SYNSEND_QLEN);
			*verdict = NF_DROP;
			return 0;
		}

		/*
		 * Still some space left, store it.
		 */
		skb_queue_tail(&cp->ack_skb, skb);
		spin_unlock(&cp->lock);
		*verdict = NF_STOLEN;
		return 0;
	}

	spin_unlock(&cp->lock);
	return 1;
}
Esempio n. 3
0
/*
 * Syn-proxy session reuse function.
 * Update syn_proxy_seq struct and clean syn-proxy related
 * members.
 */
int
ip_vs_synproxy_reuse_conn(int af, struct sk_buff *skb,
			  struct ip_vs_conn *cp,
			  struct ip_vs_protocol *pp,
			  struct ip_vs_iphdr *iph, int *verdict)
{
	struct tcphdr _tcph, *th = NULL;
	struct ip_vs_synproxy_opt opt;
	int res_cookie_check;
	u32 tcp_conn_reuse_states = 0;

	th = skb_header_pointer(skb, iph->len, sizeof(_tcph), &_tcph);
	if (unlikely(NULL == th)) {
		IP_VS_ERR_RL("skb has a invalid tcp header\n");
		*verdict = NF_DROP;
		return 0;
	}

	tcp_conn_reuse_states =
	    ((sysctl_ip_vs_synproxy_conn_reuse_cl << IP_VS_TCP_S_CLOSE) |
	     (sysctl_ip_vs_synproxy_conn_reuse_tw << IP_VS_TCP_S_TIME_WAIT) |
	     (sysctl_ip_vs_synproxy_conn_reuse_fw << IP_VS_TCP_S_FIN_WAIT) |
	     (sysctl_ip_vs_synproxy_conn_reuse_cw << IP_VS_TCP_S_CLOSE_WAIT) |
	     (sysctl_ip_vs_synproxy_conn_reuse_la << IP_VS_TCP_S_LAST_ACK));

	if (((1 << (cp->state)) & tcp_conn_reuse_states) &&
	    (cp->flags & IP_VS_CONN_F_SYNPROXY) &&
	    (!th->syn && th->ack && !th->rst && !th->fin) &&
	    (cp->syn_proxy_seq.init_seq !=
	     htonl((__u32) ((ntohl(th->ack_seq) - 1))))) {
		/*
		 * Import: set tcp hdr before cookie check, as it
		 * will be used in cookie_check funcs.
		 */
		skb_set_transport_header(skb, iph->len);
#ifdef CONFIG_IP_VS_IPV6
		if (af == AF_INET6) {
			res_cookie_check = ip_vs_synproxy_v6_cookie_check(skb,
									  ntohl
									  (th->
									   ack_seq)
									  - 1,
									  &opt);
		} else
#endif
		{
			res_cookie_check = ip_vs_synproxy_v4_cookie_check(skb,
									  ntohl
									  (th->
									   ack_seq)
									  - 1,
									  &opt);
		}

		if (!res_cookie_check) {
			/* update statistics */
			IP_VS_INC_ESTATS(ip_vs_esmib, SYNPROXY_BAD_ACK);
			/*
			 * Cookie check fail, let it go.
			 */
			return 1;
		}

		/* update statistics */
		IP_VS_INC_ESTATS(ip_vs_esmib, SYNPROXY_OK_ACK);
		IP_VS_INC_ESTATS(ip_vs_esmib, SYNPROXY_CONN_REUSED);
		switch (cp->old_state) {
		case IP_VS_TCP_S_CLOSE:
			IP_VS_INC_ESTATS(ip_vs_esmib,
					 SYNPROXY_CONN_REUSED_CLOSE);
			break;
		case IP_VS_TCP_S_TIME_WAIT:
			IP_VS_INC_ESTATS(ip_vs_esmib,
					 SYNPROXY_CONN_REUSED_TIMEWAIT);
			break;
		case IP_VS_TCP_S_FIN_WAIT:
			IP_VS_INC_ESTATS(ip_vs_esmib,
					 SYNPROXY_CONN_REUSED_FINWAIT);
			break;
		case IP_VS_TCP_S_CLOSE_WAIT:
			IP_VS_INC_ESTATS(ip_vs_esmib,
					 SYNPROXY_CONN_REUSED_CLOSEWAIT);
			break;
		case IP_VS_TCP_S_LAST_ACK:
			IP_VS_INC_ESTATS(ip_vs_esmib,
					 SYNPROXY_CONN_REUSED_LASTACK);
			break;
		}

		spin_lock(&cp->lock);
		__syn_proxy_reuse_conn(cp, skb, th, pp);
		spin_unlock(&cp->lock);

		if (unlikely(!syn_proxy_send_rs_syn(af, th, cp, skb, pp, &opt))) {
			IP_VS_ERR_RL
			    ("syn_proxy_send_rs_syn failed when reuse conn!\n");
			/* release conn immediately */
			spin_lock(&cp->lock);
			cp->timeout = 0;
			spin_unlock(&cp->lock);
		}

		*verdict = NF_STOLEN;
		return 0;
	}

	return 1;
}
Esempio n. 4
0
/*
 * Syn-proxy step 2 logic
 * Receive client's 3-handshakes  Ack packet, do cookie check
 * and then send syn to rs after creating a session.
 *
 */
int
ip_vs_synproxy_ack_rcv(int af, struct sk_buff *skb, struct tcphdr *th,
		       struct ip_vs_protocol *pp, struct ip_vs_conn **cpp,
		       struct ip_vs_iphdr *iph, int *verdict)
{
	struct ip_vs_synproxy_opt opt;
	struct ip_vs_service *svc;
	int res_cookie_check;

	/*
	 * Don't check svc syn-proxy flag, as it may
	 * be changed after syn-proxy step 1.
	 */
	if (!th->syn && th->ack && !th->rst && !th->fin &&
	    (svc =
	     ip_vs_service_get(af, skb->mark, iph->protocol, &iph->daddr,
			       th->dest))) {
		if (ip_vs_todrop()) {
			/*
			 * It seems that we are very loaded.
			 * We have to drop this packet :(
			 */
			ip_vs_service_put(svc);
			*verdict = NF_DROP;
			return 0;
		}

		if (sysctl_ip_vs_synproxy_defer &&
		    !syn_proxy_ack_has_data(skb, iph, th)) {
			/* update statistics */
			IP_VS_INC_ESTATS(ip_vs_esmib, SYNPROXY_NULL_ACK);
			/*
			 * When expecting ack packet with payload,
			 * we get a pure ack, so have to drop it.
			 */
			ip_vs_service_put(svc);
			*verdict = NF_DROP;
			return 0;
		}

		/*
		 * Import: set tcp hdr before cookie check, as it
		 * will be used in cookie_check funcs.
		 */
		skb_set_transport_header(skb, iph->len);
#ifdef CONFIG_IP_VS_IPV6
		if (af == AF_INET6) {
			res_cookie_check = ip_vs_synproxy_v6_cookie_check(skb,
									  ntohl
									  (th->
									   ack_seq)
									  - 1,
									  &opt);
		} else
#endif
		{
			res_cookie_check = ip_vs_synproxy_v4_cookie_check(skb,
									  ntohl
									  (th->
									   ack_seq)
									  - 1,
									  &opt);
		}

		if (!res_cookie_check) {
			/* update statistics */
			IP_VS_INC_ESTATS(ip_vs_esmib, SYNPROXY_BAD_ACK);
			/*
			 * Cookie check fail, drop it.
			 */
			IP_VS_DBG(6, "syn_cookie check failed seq=%u\n",
				  ntohl(th->ack_seq) - 1);
			ip_vs_service_put(svc);
			*verdict = NF_DROP;
			return 0;
		}

		/* update statistics */
		IP_VS_INC_ESTATS(ip_vs_esmib, SYNPROXY_OK_ACK);

		/*
		 * Let the virtual server select a real server for the
		 * incoming connection, and create a connection entry.
		 */
		*cpp = ip_vs_schedule(svc, skb, 1);
		if (!*cpp) {
			IP_VS_DBG(6, "ip_vs_schedule failed\n");
			*verdict = ip_vs_leave(svc, skb, pp);
			return 0;
		}

		/*
		 * Release service, we don't need it any more.
		 */
		ip_vs_service_put(svc);

		/*
		 * Do anything but print a error msg when fail.
		 * Because session will be correctly freed in ip_vs_conn_expire.
		 */
		if (!syn_proxy_send_rs_syn(af, th, *cpp, skb, pp, &opt)) {
			IP_VS_ERR_RL("syn_proxy_send_rs_syn failed!\n");
		}

		/* count in the ack packet (STOLEN by synproxy) */
		ip_vs_in_stats(*cpp, skb);

		/*
		 * Active sesion timer, and dec refcnt.
		 * Also stole the skb, and let caller return immediately.
		 */
		ip_vs_conn_put(*cpp);
		*verdict = NF_STOLEN;
		return 0;
	}

	return 1;
}
Esempio n. 5
0
/*
 * Create syn packet and send it to rs.
 * ATTENTION: we also store syn skb in cp if syn retransimition
 * is tured on.
 */
static int
syn_proxy_send_rs_syn(int af, const struct tcphdr *th,
		      struct ip_vs_conn *cp, struct sk_buff *skb,
		      struct ip_vs_protocol *pp, struct ip_vs_synproxy_opt *opt)
{
	struct sk_buff *syn_skb;
	int tcp_hdr_size;
	__u8 tcp_flags = TCPCB_FLAG_SYN;
	unsigned int tcphoff;
	struct tcphdr *new_th;

	if (!cp->packet_xmit) {
		IP_VS_ERR_RL("warning: packet_xmit is null");
		return 0;
	}

	syn_skb = alloc_skb(MAX_TCP_HEADER + 15, GFP_ATOMIC);
	if (unlikely(syn_skb == NULL)) {
		IP_VS_ERR_RL("alloc skb failed when send rs syn packet\n");
		return 0;
	}

	/* Reserve space for headers */
	skb_reserve(syn_skb, MAX_TCP_HEADER);
	tcp_hdr_size = (sizeof(struct tcphdr) + TCPOLEN_MSS +
			(opt->tstamp_ok ? TCPOLEN_TSTAMP_ALIGNED : 0) +
			(opt->wscale_ok ? TCPOLEN_WSCALE_ALIGNED : 0) +
			/* SACK_PERM is in the place of NOP NOP of TS */
			((opt->sack_ok
			  && !opt->tstamp_ok) ? TCPOLEN_SACKPERM_ALIGNED : 0));

	new_th = (struct tcphdr *)skb_push(syn_skb, tcp_hdr_size);
	/* Compose tcp header */
	skb_reset_transport_header(syn_skb);
	syn_skb->csum = 0;

	/* Set tcp hdr */
	new_th->source = th->source;
	new_th->dest = th->dest;
	new_th->seq = htonl(ntohl(th->seq) - 1);
	new_th->ack_seq = 0;
	*(((__u16 *) new_th) + 6) =
	    htons(((tcp_hdr_size >> 2) << 12) | tcp_flags);
	/* FIX_ME: what window should we use */
	new_th->window = htons(5000);
	new_th->check = 0;
	new_th->urg_ptr = 0;
	new_th->urg = 0;
	new_th->ece = 0;
	new_th->cwr = 0;

	syn_proxy_syn_build_options((__be32 *) (new_th + 1), opt);

	/*
	 * Set ip hdr
	 * Attention: set source and dest addr to ack skb's.
	 * we rely on packet_xmit func to do NATs thing.
	 */
#ifdef CONFIG_IP_VS_IPV6
	if (af == AF_INET6) {
		struct ipv6hdr *ack_iph = ipv6_hdr(skb);
		struct ipv6hdr *iph =
		    (struct ipv6hdr *)skb_push(syn_skb, sizeof(struct ipv6hdr));

		tcphoff = sizeof(struct ipv6hdr);
		skb_reset_network_header(syn_skb);
		memcpy(&iph->saddr, &ack_iph->saddr, sizeof(struct in6_addr));
		memcpy(&iph->daddr, &ack_iph->daddr, sizeof(struct in6_addr));

		iph->version = 6;
		iph->nexthdr = NEXTHDR_TCP;
		iph->payload_len = htons(tcp_hdr_size);
		iph->hop_limit = IPV6_DEFAULT_HOPLIMIT;

		new_th->check = 0;
		syn_skb->csum =
		    skb_checksum(syn_skb, tcphoff, syn_skb->len - tcphoff, 0);
		new_th->check =
		    csum_ipv6_magic(&iph->saddr, &iph->daddr,
				    syn_skb->len - tcphoff, IPPROTO_TCP,
				    syn_skb->csum);
	} else
#endif
	{
		struct iphdr *ack_iph = ip_hdr(skb);
		u32 rtos = RT_TOS(ack_iph->tos);
		struct iphdr *iph =
		    (struct iphdr *)skb_push(syn_skb, sizeof(struct iphdr));

		tcphoff = sizeof(struct iphdr);
		skb_reset_network_header(syn_skb);
		*((__u16 *) iph) = htons((4 << 12) | (5 << 8) | (rtos & 0xff));
		iph->tot_len = htons(syn_skb->len);
		iph->frag_off = htons(IP_DF);
		/* FIX_ME: what ttl shoule we use */
		iph->ttl = IPDEFTTL;
		iph->protocol = IPPROTO_TCP;
		iph->saddr = ack_iph->saddr;
		iph->daddr = ack_iph->daddr;

		ip_send_check(iph);

		new_th->check = 0;
		syn_skb->csum =
		    skb_checksum(syn_skb, tcphoff, syn_skb->len - tcphoff, 0);
		new_th->check =
		    csum_tcpudp_magic(iph->saddr, iph->daddr,
				      syn_skb->len - tcphoff, IPPROTO_TCP,
				      syn_skb->csum);
	}

	/* Save syn_skb if syn retransmission is on  */
	if (sysctl_ip_vs_synproxy_syn_retry > 0) {
		cp->syn_skb = skb_copy(syn_skb, GFP_ATOMIC);
		atomic_set(&cp->syn_retry_max, sysctl_ip_vs_synproxy_syn_retry);
	}

	/* Save info for fast_response_xmit */
	if(sysctl_ip_vs_fast_xmit && skb->dev &&
				likely(skb->dev->type == ARPHRD_ETHER) &&
				skb_mac_header_was_set(skb)) {
		struct ethhdr *eth = (struct ethhdr *)skb_mac_header(skb);

		if(likely(cp->indev == NULL)) {
			cp->indev = skb->dev;
			dev_hold(cp->indev);
		}

		if (unlikely(cp->indev != skb->dev)) {
			dev_put(cp->indev);
			cp->indev = skb->dev;
			dev_hold(cp->indev);
		}

		memcpy(cp->src_hwaddr, eth->h_source, ETH_ALEN);
		memcpy(cp->dst_hwaddr, eth->h_dest, ETH_ALEN);
		IP_VS_INC_ESTATS(ip_vs_esmib, FAST_XMIT_SYNPROXY_SAVE);
		IP_VS_DBG_RL("syn_proxy_send_rs_syn netdevice:%s\n",
						netdev_name(skb->dev));
	}

	/* count in the syn packet */
	ip_vs_in_stats(cp, skb);

	/* If xmit failed, syn_skb will be freed correctly. */
	cp->packet_xmit(syn_skb, cp, pp);

	return 1;
}
Esempio n. 6
0
/*
 *  syn-proxy step 1 logic:
 *  Check if synproxy is enabled for this skb, and
 *  send Syn/Ack back.
 *
 *  Synproxy is enabled when:
 *  1) skb is a Syn packet.
 *  2) And the service is synproxy-enable.
 *  3) And ip_vs_todrop return false.
 *
 *  @return 0 means the caller should return at once and use
 *   verdict as return value, return 1 for nothing.
 */
int
ip_vs_synproxy_syn_rcv(int af, struct sk_buff *skb,
		       struct ip_vs_iphdr *iph, int *verdict)
{
	struct ip_vs_service *svc = NULL;
	struct tcphdr _tcph, *th;
	struct ip_vs_synproxy_opt tcp_opt;

	th = skb_header_pointer(skb, iph->len, sizeof(_tcph), &_tcph);
	if (unlikely(th == NULL)) {
		goto syn_rcv_out;
	}

	if (th->syn && !th->ack && !th->rst && !th->fin &&
	    (svc =
	     ip_vs_service_get(af, skb->mark, iph->protocol, &iph->daddr,
			       th->dest))
	    && (svc->flags & IP_VS_CONN_F_SYNPROXY)) {
		// release service here, because don't use it any all.
		ip_vs_service_put(svc);

		if (ip_vs_todrop()) {
			/*
			 * It seems that we are very loaded.
			 * We have to drop this packet :(
			 */
			goto syn_rcv_out;
		}
	} else {
		/*
		 * release service.
		 */
		if (svc != NULL) {
			ip_vs_service_put(svc);
		}
		return 1;
	}

	/* update statistics */
	IP_VS_INC_ESTATS(ip_vs_esmib, SYNPROXY_SYN_CNT);

	/* Try to reuse skb if possible */
	if (unlikely(skb_shared(skb) || skb_cloned(skb))) {
		struct sk_buff *new_skb = skb_copy(skb, GFP_ATOMIC);
		if (unlikely(new_skb == NULL)) {
			goto syn_rcv_out;
		}
		/* Drop old skb */
		kfree_skb(skb);
		skb = new_skb;
	}

	/* reuse skb here: deal with tcp options, exchage ip, port. */
	syn_proxy_reuse_skb(af, skb, &tcp_opt);

	if (unlikely(skb->dev == NULL)) {
		IP_VS_ERR_RL("%s: skb->dev is null !!!\n", __func__);
		goto syn_rcv_out;
	}

	/* Send the packet out */
	if (likely(skb->dev->type == ARPHRD_ETHER)) {
		unsigned char t_hwaddr[ETH_ALEN];

		/* Move the data pointer to point to the link layer header */
		struct ethhdr *eth = (struct ethhdr *)skb_mac_header(skb);
		skb->data = (unsigned char *)skb_mac_header(skb);
		skb->len += ETH_HLEN;	//sizeof(skb->mac.ethernet);

		memcpy(t_hwaddr, (eth->h_dest), ETH_ALEN);
		memcpy((eth->h_dest), (eth->h_source), ETH_ALEN);
		memcpy((eth->h_source), t_hwaddr, ETH_ALEN);
		skb->pkt_type = PACKET_OUTGOING;
	} else if (skb->dev->type == ARPHRD_LOOPBACK) {
		/* set link layer */
		if (likely(skb_mac_header_was_set(skb))) {
			skb->data = skb_mac_header(skb);
			skb->len += sizeof(struct ethhdr);
		} else {
			skb_push(skb, sizeof(struct ethhdr));
			skb_reset_mac_header(skb);
		}
	}

	dev_queue_xmit(skb);
	*verdict = NF_STOLEN;
	return 0;
syn_rcv_out:
	/* Drop the packet when all things are right also,
	 * then we needn't to kfree_skb() */
	*verdict = NF_DROP;
	return 0;
}
Esempio n. 7
0
static int
tcp_conn_schedule(int af, struct sk_buff *skb, struct ip_vs_protocol *pp,
		  int *verdict, struct ip_vs_conn **cpp)
{
	struct ip_vs_service *svc;
	struct tcphdr _tcph, *th;
	struct ip_vs_iphdr iph;

	ip_vs_fill_iphdr(af, skb_network_header(skb), &iph);

	th = skb_header_pointer(skb, iph.len, sizeof(_tcph), &_tcph);
	if (th == NULL) {
		*verdict = NF_DROP;
		return 0;
	}

	/*
	 * Syn-proxy step 2 logic: receive client's
	 * 3-handshake Ack packet
	 */
	if (ip_vs_synproxy_ack_rcv(af, skb, th, pp, cpp, &iph, verdict) == 0) {
		return 0;
	}

	if (th->syn && !th->ack && !th->fin && !th->rst &&
	    (svc = ip_vs_service_get(af, skb->mark, iph.protocol, &iph.daddr,
				     th->dest))) {
		if (ip_vs_todrop()) {
			/*
			 * It seems that we are very loaded.
			 * We have to drop this packet :(
			 */
			ip_vs_service_put(svc);
			*verdict = NF_DROP;
			return 0;
		}

		/*
		 * Let the virtual server select a real server for the
		 * incoming connection, and create a connection entry.
		 */
		*cpp = ip_vs_schedule(svc, skb, 0);
		if (!*cpp) {
			*verdict = ip_vs_leave(svc, skb, pp);
			return 0;
		}

		/*
		 * Set private establish state timeout into cp from svc,
		 * due cp may use its user establish state timeout
		 * different from sysctl_ip_vs_tcp_timeouts
		 */
		(*cpp)->est_timeout = svc->est_timeout;

		ip_vs_service_put(svc);
		return 1;
	}

	/* drop tcp packet which send to vip and !vport */
	if (sysctl_ip_vs_tcp_drop_entry &&
	    (svc = ip_vs_lookup_vip(af, iph.protocol, &iph.daddr))) {
		IP_VS_INC_ESTATS(ip_vs_esmib, DEFENCE_TCP_DROP);
		*verdict = NF_DROP;
		return 0;
	}

	return 1;
}