コード例 #1
0
ファイル: ip_vs_synproxy.c プロジェクト: tclh123/lvs-tool
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;
}
コード例 #2
0
int
ip_vs_tunnel_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
                     struct ip_vs_protocol *pp)
{
    struct rt6_info *rt;		/* Route to the other host */
    struct in6_addr saddr;		/* Source for tunnel */
    struct net_device *tdev;	/* Device to other host */
    struct ipv6hdr  *old_iph = ipv6_hdr(skb);
    struct ipv6hdr  *iph;		/* Our new IP header */
    unsigned int max_headroom;	/* The extra header space needed */
    int    mtu;
    int ret;

    EnterFunction(10);

    if (!(rt = __ip_vs_get_out_rt_v6(skb, cp->dest, &cp->daddr.in6,
                                     &saddr, 1, 1|2)))
        goto tx_error_icmp;
    if (__ip_vs_is_local_route6(rt)) {
        dst_release(&rt->dst);
        IP_VS_XMIT(NFPROTO_IPV6, skb, cp, 1);
    }

    tdev = rt->dst.dev;

    mtu = dst_mtu(&rt->dst) - sizeof(struct ipv6hdr);
    if (mtu < IPV6_MIN_MTU) {
        IP_VS_DBG_RL("%s(): mtu less than %d\n", __func__,
                     IPV6_MIN_MTU);
        goto tx_error_put;
    }
    if (skb_dst(skb))
        skb_dst(skb)->ops->update_pmtu(skb_dst(skb), mtu);

    if (mtu < ntohs(old_iph->payload_len) + sizeof(struct ipv6hdr) &&
            !skb_is_gso(skb)) {
        if (!skb->dev) {
            struct net *net = dev_net(skb_dst(skb)->dev);

            skb->dev = net->loopback_dev;
        }
        icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu);
        IP_VS_DBG_RL("%s(): frag needed\n", __func__);
        goto tx_error_put;
    }

    /*
     * Okay, now see if we can stuff it in the buffer as-is.
     */
    max_headroom = LL_RESERVED_SPACE(tdev) + sizeof(struct ipv6hdr);

    if (skb_headroom(skb) < max_headroom
            || skb_cloned(skb) || skb_shared(skb)) {
        struct sk_buff *new_skb =
            skb_realloc_headroom(skb, max_headroom);
        if (!new_skb) {
            dst_release(&rt->dst);
            kfree_skb(skb);
            IP_VS_ERR_RL("%s(): no memory\n", __func__);
            return NF_STOLEN;
        }
        kfree_skb(skb);
        skb = new_skb;
        old_iph = ipv6_hdr(skb);
    }

    skb->transport_header = skb->network_header;

    skb_push(skb, sizeof(struct ipv6hdr));
    skb_reset_network_header(skb);
    memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt));

    /* drop old route */
    skb_dst_drop(skb);
    skb_dst_set(skb, &rt->dst);

    /*
     *	Push down and install the IPIP header.
     */
    iph			=	ipv6_hdr(skb);
    iph->version		=	6;
    iph->nexthdr		=	IPPROTO_IPV6;
    iph->payload_len	=	old_iph->payload_len;
    be16_add_cpu(&iph->payload_len, sizeof(*old_iph));
    iph->priority		=	old_iph->priority;
    memset(&iph->flow_lbl, 0, sizeof(iph->flow_lbl));
    ipv6_addr_copy(&iph->daddr, &cp->daddr.in6);
    ipv6_addr_copy(&iph->saddr, &saddr);
    iph->hop_limit		=	old_iph->hop_limit;

    /* Another hack: avoid icmp_send in ip_fragment */
    skb->local_df = 1;

    ret = IP_VS_XMIT_TUNNEL(skb, cp);
    if (ret == NF_ACCEPT)
        ip6_local_out(skb);
    else if (ret == NF_DROP)
        kfree_skb(skb);

    LeaveFunction(10);

    return NF_STOLEN;

tx_error_icmp:
    dst_link_failure(skb);
tx_error:
    kfree_skb(skb);
    LeaveFunction(10);
    return NF_STOLEN;
tx_error_put:
    dst_release(&rt->dst);
    goto tx_error;
}
コード例 #3
0
/*
 *   IP Tunneling transmitter
 *
 *   This function encapsulates the packet in a new IP packet, its
 *   destination will be set to cp->daddr. Most code of this function
 *   is taken from ipip.c.
 *
 *   It is used in VS/TUN cluster. The load balancer selects a real
 *   server from a cluster based on a scheduling algorithm,
 *   encapsulates the request packet and forwards it to the selected
 *   server. For example, all real servers are configured with
 *   "ifconfig tunl0 <Virtual IP Address> up". When the server receives
 *   the encapsulated packet, it will decapsulate the packet, processe
 *   the request and return the response packets directly to the client
 *   without passing the load balancer. This can greatly increase the
 *   scalability of virtual server.
 *
 *   Used for ANY protocol
 */
int
ip_vs_tunnel_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
                  struct ip_vs_protocol *pp)
{
    struct rtable *rt;			/* Route to the other host */
    struct net_device *tdev;		/* Device to other host */
    struct iphdr  *old_iph = ip_hdr(skb);
    u8     tos = old_iph->tos;
    __be16 df = old_iph->frag_off;
    struct iphdr  *iph;			/* Our new IP header */
    unsigned int max_headroom;		/* The extra header space needed */
    int    mtu;
    int ret;

    EnterFunction(10);

    if (!(rt = __ip_vs_get_out_rt(skb, cp->dest, cp->daddr.ip,
                                  RT_TOS(tos), IP_VS_RT_MODE_LOCAL |
                                  IP_VS_RT_MODE_NON_LOCAL)))
        goto tx_error_icmp;
    if (rt->rt_flags & RTCF_LOCAL) {
        ip_rt_put(rt);
        IP_VS_XMIT(NFPROTO_IPV4, skb, cp, 1);
    }

    tdev = rt->dst.dev;

    mtu = dst_mtu(&rt->dst) - sizeof(struct iphdr);
    if (mtu < 68) {
        IP_VS_DBG_RL("%s(): mtu less than 68\n", __func__);
        goto tx_error_put;
    }
    if (skb_dst(skb))
        skb_dst(skb)->ops->update_pmtu(skb_dst(skb), mtu);

    df |= (old_iph->frag_off & htons(IP_DF));

    if ((old_iph->frag_off & htons(IP_DF) &&
            mtu < ntohs(old_iph->tot_len) && !skb_is_gso(skb))) {
        icmp_send(skb, ICMP_DEST_UNREACH,ICMP_FRAG_NEEDED, htonl(mtu));
        IP_VS_DBG_RL("%s(): frag needed\n", __func__);
        goto tx_error_put;
    }

    /*
     * Okay, now see if we can stuff it in the buffer as-is.
     */
    max_headroom = LL_RESERVED_SPACE(tdev) + sizeof(struct iphdr);

    if (skb_headroom(skb) < max_headroom
            || skb_cloned(skb) || skb_shared(skb)) {
        struct sk_buff *new_skb =
            skb_realloc_headroom(skb, max_headroom);
        if (!new_skb) {
            ip_rt_put(rt);
            kfree_skb(skb);
            IP_VS_ERR_RL("%s(): no memory\n", __func__);
            return NF_STOLEN;
        }
        kfree_skb(skb);
        skb = new_skb;
        old_iph = ip_hdr(skb);
    }

    skb->transport_header = skb->network_header;

    /* fix old IP header checksum */
    ip_send_check(old_iph);

    skb_push(skb, sizeof(struct iphdr));
    skb_reset_network_header(skb);
    memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt));

    /* drop old route */
    skb_dst_drop(skb);
    skb_dst_set(skb, &rt->dst);

    /*
     *	Push down and install the IPIP header.
     */
    iph			=	ip_hdr(skb);
    iph->version		=	4;
    iph->ihl		=	sizeof(struct iphdr)>>2;
    iph->frag_off		=	df;
    iph->protocol		=	IPPROTO_IPIP;
    iph->tos		=	tos;
    iph->daddr		=	rt->rt_dst;
    iph->saddr		=	rt->rt_src;
    iph->ttl		=	old_iph->ttl;
    ip_select_ident(iph, &rt->dst, NULL);

    /* Another hack: avoid icmp_send in ip_fragment */
    skb->local_df = 1;

    ret = IP_VS_XMIT_TUNNEL(skb, cp);
    if (ret == NF_ACCEPT)
        ip_local_out(skb);
    else if (ret == NF_DROP)
        kfree_skb(skb);

    LeaveFunction(10);

    return NF_STOLEN;

tx_error_icmp:
    dst_link_failure(skb);
tx_error:
    kfree_skb(skb);
    LeaveFunction(10);
    return NF_STOLEN;
tx_error_put:
    ip_rt_put(rt);
    goto tx_error;
}
コード例 #4
0
ファイル: ip_vs_synproxy.c プロジェクト: tclh123/lvs-tool
/*
 * 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;
}
コード例 #5
0
ファイル: ip_vs_synproxy.c プロジェクト: tclh123/lvs-tool
/*
 * Syn-proxy step 3 logic: receive syn-ack from rs
 * Update syn_proxy_seq.delta and send stored ack skbs
 * to rs.
 */
int
ip_vs_synproxy_synack_rcv(struct sk_buff *skb, struct ip_vs_conn *cp,
			  struct ip_vs_protocol *pp, int ihl, int *verdict)
{
	struct tcphdr _tcph, *th;
	struct sk_buff_head save_skb;
	struct sk_buff *tmp_skb = NULL;
	struct ip_vs_dest *dest = cp->dest;

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

	IP_VS_DBG(6, "in syn_proxy_synack_rcv, "
		  "seq = %u ack_seq = %u %c%c%c cp->is_synproxy = %u cp->state = %u\n",
		  ntohl(th->seq),
		  ntohl(th->ack_seq),
		  (th->syn) ? 'S' : '-',
		  (th->ack) ? 'A' : '-',
		  (th->rst) ? 'R' : '-',
		  cp->flags & IP_VS_CONN_F_SYNPROXY, cp->state);

	skb_queue_head_init(&save_skb);
	spin_lock(&cp->lock);
	if ((th->syn) && (th->ack) && (!th->rst) &&
	    (cp->flags & IP_VS_CONN_F_SYNPROXY) &&
	    cp->state == IP_VS_TCP_S_SYN_SENT) {
		cp->syn_proxy_seq.delta =
		    htonl(cp->syn_proxy_seq.init_seq) - htonl(th->seq);
		cp->timeout = pp->timeout_table[cp->state =
						IP_VS_TCP_S_ESTABLISHED];
		if (dest) {
			atomic_inc(&dest->activeconns);
			atomic_dec(&dest->inactconns);
			cp->flags &= ~IP_VS_CONN_F_INACTIVE;
		}

		/* save tcp sequense for fullnat/nat, INside to OUTside */
		if (sysctl_ip_vs_conn_expire_tcp_rst == 1) {
			cp->rs_end_seq = htonl(ntohl(th->seq) + 1);
			cp->rs_ack_seq = th->ack_seq;
			IP_VS_DBG_RL("packet from RS, seq:%u ack_seq:%u.",
				     ntohl(th->seq), ntohl(th->ack_seq));
			IP_VS_DBG_RL("port:%u->%u", ntohs(th->source),
				     ntohs(th->dest));
		}

		/* First: free stored syn skb */
		if ((tmp_skb = xchg(&cp->syn_skb, NULL)) != NULL) {
			kfree_skb(tmp_skb);
			tmp_skb = NULL;
		}

		if (skb_queue_len(&cp->ack_skb) <= 0) {
			/*
			 * FIXME: maybe a bug here, print err msg and go.
			 * Attention: cp->state has been changed and we
			 * should still DROP the Syn/Ack skb.
			 */
			IP_VS_ERR_RL
			    ("Got ack_skb NULL pointer in syn_proxy_synack_rcv\n");
			spin_unlock(&cp->lock);
			*verdict = NF_DROP;
			return 0;
		}

		while ((tmp_skb = skb_dequeue(&cp->ack_skb)) != NULL) {
			skb_queue_tail(&save_skb, tmp_skb);
		}

		/*
		 * Release the lock, because we don't
		 * touch session any more.
		 */
		spin_unlock(&cp->lock);

		while ((tmp_skb = skb_dequeue(&save_skb)) != NULL) {
			/* If xmit failed, syn_skb will be freed correctly. */
			cp->packet_xmit(tmp_skb, cp, pp);
		}

		*verdict = NF_DROP;
		return 0;
	} else if ((th->rst) &&
		   (cp->flags & IP_VS_CONN_F_SYNPROXY) &&
		   cp->state == IP_VS_TCP_S_SYN_SENT) {
		__u32 temp_seq;
		temp_seq = ntohl(th->seq);
		IP_VS_DBG(6, "get rst from rs, seq = %u ack_seq= %u\n",
			  ntohl(th->seq), ntohl(th->ack_seq));
		/* coute the delta of seq */
		cp->syn_proxy_seq.delta =
		    ntohl(cp->syn_proxy_seq.init_seq) - ntohl(th->seq);
		cp->timeout = pp->timeout_table[cp->state = IP_VS_TCP_S_CLOSE];
		spin_unlock(&cp->lock);
		th->seq = htonl(ntohl(th->seq) + 1);
		syn_proxy_seq_csum_update(th, htonl(temp_seq), th->seq);

		return 1;
	}
	spin_unlock(&cp->lock);

	return 1;
}
コード例 #6
0
ファイル: ip_vs_synproxy.c プロジェクト: tclh123/lvs-tool
/*
 * 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;
}
コード例 #7
0
ファイル: ip_vs_synproxy.c プロジェクト: tclh123/lvs-tool
/*
 * 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;
}
コード例 #8
0
ファイル: ip_vs_synproxy.c プロジェクト: tclh123/lvs-tool
/*
 *  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;
}
コード例 #9
0
/*
 *	Handle ICMP messages in the outside-to-inside direction (incoming)
 *	and sometimes in outgoing direction from ip_vs_forward_icmp.
 *	Find any that might be relevant, check against existing connections,
 *	forward to the right destination host if relevant.
 *	Currently handles error types - unreachable, quench, ttl exceeded.
 */
static int ip_vs_in_icmp(struct sk_buff **skb_p)
{
	struct sk_buff	*skb   = *skb_p;
	struct iphdr    *iph;
	struct icmphdr  *icmph;
	struct iphdr    *ciph;	/* The ip header contained within the ICMP */
	__u16	        *pptr;	/* port numbers from TCP/UDP contained header */
	unsigned short   len;
	unsigned short	clen, csize;
	struct ip_vs_conn *cp;
	struct rtable *rt;			/* Route to the other host */
	int    mtu;

	if (skb_is_nonlinear(skb)) {
		if (skb_linearize(skb, GFP_ATOMIC) != 0)
			return NF_DROP;
	}

	iph = skb->nh.iph;
	ip_send_check(iph);
	icmph = (struct icmphdr *)((char *)iph + (iph->ihl << 2));
	len = ntohs(iph->tot_len) - (iph->ihl<<2);
	if (len < sizeof(struct icmphdr))
		return NF_DROP;

	IP_VS_DBG(12, "icmp in (%d,%d) %u.%u.%u.%u -> %u.%u.%u.%u\n",
		  icmph->type, ntohs(icmp_id(icmph)),
		  NIPQUAD(iph->saddr), NIPQUAD(iph->daddr));

	if ((icmph->type != ICMP_DEST_UNREACH) &&
	    (icmph->type != ICMP_SOURCE_QUENCH) &&
	    (icmph->type != ICMP_TIME_EXCEEDED))
		return NF_ACCEPT;

	/*
	 * If we get here we have an ICMP error of one of the above 3 types
	 * Now find the contained IP header
	 */
	clen = len - sizeof(struct icmphdr);
	if (clen < sizeof(struct iphdr))
		return NF_DROP;
	ciph = (struct iphdr *) (icmph + 1);
	csize = ciph->ihl << 2;
	if (clen < csize)
		return NF_DROP;

	/* We are only interested ICMPs generated from TCP or UDP packets */
	if (ciph->protocol != IPPROTO_UDP && ciph->protocol != IPPROTO_TCP)
		return NF_ACCEPT;

	/* Skip non-first embedded TCP/UDP fragments */
	if (ciph->frag_off & __constant_htons(IP_OFFSET))
		return NF_ACCEPT;

	/* We need at least TCP/UDP ports here */
	if (clen < csize + sizeof(struct udphdr))
		return NF_DROP;

	/* Ensure the checksum is correct */
	if (ip_compute_csum((unsigned char *) icmph, len)) {
		/* Failed checksum! */
		IP_VS_ERR_RL("incoming ICMP: failed checksum from "
			     "%d.%d.%d.%d!\n", NIPQUAD(iph->saddr));
		return NF_DROP;
	}

	pptr = (__u16 *)&(((char *)ciph)[csize]);

	IP_VS_DBG(11, "Handling incoming ICMP for "
		  "%u.%u.%u.%u:%d -> %u.%u.%u.%u:%d\n",
		  NIPQUAD(ciph->saddr), ntohs(pptr[0]),
		  NIPQUAD(ciph->daddr), ntohs(pptr[1]));

	/* This is pretty much what ip_vs_conn_in_get() does,
	   except parameters are in the reverse order */
	cp = ip_vs_conn_in_get(ciph->protocol,
			       ciph->daddr, pptr[1],
			       ciph->saddr, pptr[0]);
	if (cp == NULL)
		return NF_ACCEPT;

	ip_vs_in_stats(cp, skb);

	/* The ICMP packet for VS/TUN, VS/DR and LOCALNODE will be
	   forwarded directly here, because there is no need to
	   translate address/port back */
	if (IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_MASQ) {
		int ret;
		if (cp->packet_xmit)
			ret = cp->packet_xmit(skb, cp);
		else
			ret = NF_ACCEPT;
		atomic_inc(&cp->in_pkts);
		ip_vs_conn_put(cp);
		return ret;
	}

	/*
	 * mangle and send the packet here
	 */
	if (!(rt = __ip_vs_get_out_rt(cp, RT_TOS(iph->tos))))
		goto tx_error_icmp;

	/* MTU checking */
	mtu = rt->u.dst.pmtu;
	if ((skb->len > mtu) && (iph->frag_off&__constant_htons(IP_DF))) {
		ip_rt_put(rt);
		icmp_send(skb, ICMP_DEST_UNREACH,ICMP_FRAG_NEEDED, htonl(mtu));
		IP_VS_DBG_RL("ip_vs_in_icmp(): frag needed\n");
		goto tx_error;
	}

	/* drop old route */
	dst_release(skb->dst);
	skb->dst = &rt->u.dst;

	/* copy-on-write the packet before mangling it */
	if (ip_vs_skb_cow(skb, rt->u.dst.dev->hard_header_len,
			  &iph, (unsigned char**)&icmph)) {
		ip_vs_conn_put(cp);
		return NF_DROP;
	}
	ciph = (struct iphdr *) (icmph + 1);
	pptr = (__u16 *)&(((char *)ciph)[csize]);

	/* The ICMP packet for VS/NAT must be written to correct addresses
	   before being forwarded to the right server */

	/* First change the dest IP address, and recalc checksum */
	iph->daddr = cp->daddr;
	ip_send_check(iph);

	/* Now change the *source* address in the contained IP */
	ciph->saddr = cp->daddr;
	ip_send_check(ciph);

	/* the TCP/UDP source port - cannot redo check */
	pptr[0] = cp->dport;

	/* And finally the ICMP checksum */
	icmph->checksum = 0;
	icmph->checksum = ip_compute_csum((unsigned char *) icmph, len);
	skb->ip_summed = CHECKSUM_UNNECESSARY;

	IP_VS_DBG(11, "Forwarding incoming ICMP to "
		  "%u.%u.%u.%u:%d -> %u.%u.%u.%u:%d\n",
		  NIPQUAD(ciph->saddr), ntohs(pptr[0]),
		  NIPQUAD(ciph->daddr), ntohs(pptr[1]));

#ifdef CONFIG_NETFILTER_DEBUG
	skb->nf_debug = 1 << NF_IP_LOCAL_OUT;
#endif /* CONFIG_NETFILTER_DEBUG */
	ip_send(skb);
	ip_vs_conn_put(cp);
	return NF_STOLEN;

  tx_error_icmp:
	dst_link_failure(skb);
  tx_error:
	dev_kfree_skb(skb);
	ip_vs_conn_put(cp);
	return NF_STOLEN;
}