예제 #1
0
static int
sctp_conn_schedule(int af, struct sk_buff *skb, struct ip_vs_proto_data *pd,
                   int *verdict, struct ip_vs_conn **cpp)
{
    struct net *net;
    struct ip_vs_service *svc;
    sctp_chunkhdr_t _schunkh, *sch;
    sctp_sctphdr_t *sh, _sctph;
    struct ip_vs_iphdr iph;

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

    sh = skb_header_pointer(skb, iph.len, sizeof(_sctph), &_sctph);
    if (sh == NULL)
        return 0;

    sch = skb_header_pointer(skb, iph.len + sizeof(sctp_sctphdr_t),
                             sizeof(_schunkh), &_schunkh);
    if (sch == NULL)
        return 0;
    net = skb_net(skb);
    if ((sch->type == SCTP_CID_INIT) &&
            (svc = ip_vs_service_get(net, af, skb->mark, iph.protocol,
                                     &iph.daddr, sh->dest))) {
        int ignored;

        if (ip_vs_todrop(net_ipvs(net))) {
            /*
             * 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, pd, &ignored);
        if (!*cpp && ignored <= 0) {
            if (!ignored)
                *verdict = ip_vs_leave(svc, skb, pd);
            else {
                ip_vs_service_put(svc);
                *verdict = NF_DROP;
            }
            return 0;
        }
        ip_vs_service_put(svc);
    }
    /* NF_ACCEPT */
    return 1;
}
예제 #2
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;
}
예제 #3
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;
}
예제 #4
0
/*
 *  Pass or drop the packet.
 *  Called by ip_vs_in, when the virtual service is available but
 *  no destination is available for a new connection.
 */
int ip_vs_leave(struct ip_vs_service *svc, struct sk_buff *skb,
		struct ip_vs_protocol *pp)
{
	__be16 _ports[2], *pptr;
	struct iphdr *iph = ip_hdr(skb);

	pptr = skb_header_pointer(skb, iph->ihl*4,
				  sizeof(_ports), _ports);
	if (pptr == NULL) {
		ip_vs_service_put(svc);
		return NF_DROP;
	}

	/* if it is fwmark-based service, the cache_bypass sysctl is up
	   and the destination is RTN_UNICAST (and not local), then create
	   a cache_bypass connection entry */
	if (sysctl_ip_vs_cache_bypass && svc->fwmark
	    && (inet_addr_type(iph->daddr) == RTN_UNICAST)) {
		int ret, cs;
		struct ip_vs_conn *cp;

		ip_vs_service_put(svc);

		/* create a new connection entry */
		IP_VS_DBG(6, "ip_vs_leave: create a cache_bypass entry\n");
		cp = ip_vs_conn_new(iph->protocol,
				    iph->saddr, pptr[0],
				    iph->daddr, pptr[1],
				    0, 0,
				    IP_VS_CONN_F_BYPASS,
				    NULL);
		if (cp == NULL)
			return NF_DROP;

		/* statistics */
		ip_vs_in_stats(cp, skb);

		/* set state */
		cs = ip_vs_set_state(cp, IP_VS_DIR_INPUT, skb, pp);

		/* transmit the first SYN packet */
		ret = cp->packet_xmit(skb, cp, pp);
		/* do not touch skb anymore */

		atomic_inc(&cp->in_pkts);
		ip_vs_conn_put(cp);
		return ret;
	}

	/*
	 * When the virtual ftp service is presented, packets destined
	 * for other services on the VIP may get here (except services
	 * listed in the ipvs table), pass the packets, because it is
	 * not ipvs job to decide to drop the packets.
	 */
	if ((svc->port == FTPPORT) && (pptr[1] != FTPPORT)) {
		ip_vs_service_put(svc);
		return NF_ACCEPT;
	}

	ip_vs_service_put(svc);

	/*
	 * Notify the client that the destination is unreachable, and
	 * release the socket buffer.
	 * Since it is in IP layer, the TCP socket is not actually
	 * created, the TCP RST packet cannot be sent, instead that
	 * ICMP_PORT_UNREACH is sent here no matter it is TCP/UDP. --WZ
	 */
	icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0);
	return NF_DROP;
}
예제 #5
0
/*
 *  Pass or drop the packet.
 *  Called by ip_vs_in, when the virtual service is available but
 *  no destination is available for a new connection.
 */
static int ip_vs_leave(struct ip_vs_service *svc, struct sk_buff *skb)
{
	struct iphdr *iph = skb->nh.iph;
	__u16 *portp = (__u16 *)&(((char *)iph)[iph->ihl*4]);

	/* if it is fwmark-based service, the cache_bypass sysctl is up
	   and the destination is RTN_UNICAST (and not local), then create
	   a cache_bypass connection entry */
	if (sysctl_ip_vs_cache_bypass && svc->fwmark
	    && (inet_addr_type(iph->daddr) == RTN_UNICAST)) {
		int ret;
		struct ip_vs_conn *cp;

		ip_vs_service_put(svc);

		/* create a new connection entry */
		IP_VS_DBG(6, "ip_vs_leave: create a cache_bypass entry\n");
		cp = ip_vs_conn_new(iph->protocol,
				    iph->saddr, portp[0],
				    iph->daddr, portp[1],
				    0, 0,
				    IP_VS_CONN_F_BYPASS,
				    NULL);
		if (cp == NULL) {
			kfree_skb(skb);
			return NF_STOLEN;
		}

		/* statistics */
		ip_vs_in_stats(cp, skb);

		/* set state */
		ip_vs_set_state(cp, VS_STATE_INPUT, iph, portp);

		/* transmit the first SYN packet */
		ret = cp->packet_xmit(skb, cp);

		atomic_inc(&cp->in_pkts);
		ip_vs_conn_put(cp);
		return ret;
	}

	/*
	 * When the virtual ftp service is presented, packets destined
	 * for other services on the VIP may get here (except services
	 * listed in the ipvs table), pass the packets, because it is
	 * not ipvs job to decide to drop the packets.
	 */
	if ((svc->port == FTPPORT) && (portp[1] != FTPPORT)) {
		ip_vs_service_put(svc);
		return NF_ACCEPT;
	}

	ip_vs_service_put(svc);

	/*
	 * Notify the client that the destination is unreachable, and
	 * release the socket buffer.
	 * Since it is in IP layer, the TCP socket is not actually
	 * created, the TCP RST packet cannot be sent, instead that
	 * ICMP_PORT_UNREACH is sent here no matter it is TCP/UDP. --WZ
	 */
	icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0);
	kfree_skb(skb);
	return NF_STOLEN;
}
예제 #6
0
/*
 *	Check if it's for virtual services, look it up,
 *	and send it on its way...
 */
static unsigned int ip_vs_in(unsigned int hooknum,
			     struct sk_buff **skb_p,
			     const struct net_device *in,
			     const struct net_device *out,
			     int (*okfn)(struct sk_buff *))
{
	struct sk_buff	*skb = *skb_p;
	struct iphdr	*iph = skb->nh.iph;
	union ip_vs_tphdr h;
	struct ip_vs_conn *cp;
	struct ip_vs_service *svc;
	int ihl;
	int ret;

	/*
	 *	Big tappo: only PACKET_HOST (nor loopback neither mcasts)
	 *	... don't know why 1st test DOES NOT include 2nd (?)
	 */
	if (skb->pkt_type != PACKET_HOST || skb->dev == &loopback_dev) {
		IP_VS_DBG(12, "packet type=%d proto=%d daddr=%d.%d.%d.%d ignored\n",
			  skb->pkt_type,
			  iph->protocol,
			  NIPQUAD(iph->daddr));
		return NF_ACCEPT;
	}

	if (iph->protocol == IPPROTO_ICMP)
		return ip_vs_in_icmp(skb_p);

	/* let it go if other IP protocols */
	if (iph->protocol != IPPROTO_TCP && iph->protocol != IPPROTO_UDP)
		return NF_ACCEPT;

	/* make sure that protocol header available in skb data area,
	   note that skb data area may be reallocated. */
	ihl = iph->ihl << 2;
	if (ip_vs_header_check(skb, iph->protocol, ihl) == -1)
		return NF_DROP;
	iph = skb->nh.iph;
	h.raw = (char*) iph + ihl;

	/*
	 * Check if the packet belongs to an existing connection entry
	 */
	cp = ip_vs_conn_in_get(iph->protocol, iph->saddr, h.portp[0],
			       iph->daddr, h.portp[1]);

	if (!cp &&
	    (h.th->syn || (iph->protocol!=IPPROTO_TCP)) &&
	    (svc = ip_vs_service_get(skb->nfmark, iph->protocol,
				     iph->daddr, h.portp[1]))) {
		if (ip_vs_todrop()) {
			/*
			 * It seems that we are very loaded.
			 * We have to drop this packet :(
			 */
			ip_vs_service_put(svc);
			return NF_DROP;
		}

		/*
		 * Let the virtual server select a real server for the
		 * incoming connection, and create a connection entry.
		 */
		cp = ip_vs_schedule(svc, iph);
		if (!cp)
			return ip_vs_leave(svc, skb);
		ip_vs_conn_stats(cp, svc);
		ip_vs_service_put(svc);
	}

	if (!cp) {
		/* sorry, all this trouble for a no-hit :) */
		IP_VS_DBG(12, "packet for %s %d.%d.%d.%d:%d continue "
			  "traversal as normal.\n",
			  ip_vs_proto_name(iph->protocol),
			  NIPQUAD(iph->daddr),
			  ntohs(h.portp[1]));
		return NF_ACCEPT;
	}

	IP_VS_DBG(11, "Incoming %s %u.%u.%u.%u:%d->%u.%u.%u.%u:%d\n",
		  ip_vs_proto_name(iph->protocol),
		  NIPQUAD(iph->saddr), ntohs(h.portp[0]),
		  NIPQUAD(iph->daddr), ntohs(h.portp[1]));

	/* Check the server status */
	if (cp->dest && !(cp->dest->flags & IP_VS_DEST_F_AVAILABLE)) {
		/* the destination server is not available */

		if (sysctl_ip_vs_expire_nodest_conn) {
			/* try to expire the connection immediately */
			ip_vs_conn_expire_now(cp);
		} else {
			/* don't restart its timer, and silently
			   drop the packet. */
			__ip_vs_conn_put(cp);
		}
		return NF_DROP;
	}

	ip_vs_in_stats(cp, skb);
	ip_vs_set_state(cp, VS_STATE_INPUT, iph, h.portp);
	if (cp->packet_xmit)
		ret = cp->packet_xmit(skb, cp);
	else {
		IP_VS_DBG_RL("warning: packet_xmit is null");
		ret = NF_ACCEPT;
	}

	/* increase its packet counter and check if it is needed
	   to be synchronized */
	atomic_inc(&cp->in_pkts);
	if (ip_vs_sync_state & IP_VS_STATE_MASTER &&
	    (cp->protocol != IPPROTO_TCP ||
	     cp->state == IP_VS_S_ESTABLISHED) &&
	    (atomic_read(&cp->in_pkts) % 50 == sysctl_ip_vs_sync_threshold))
		ip_vs_sync_conn(cp);

	ip_vs_conn_put(cp);
	return ret;
}
static struct ip_vs_conn *
ip_vs_sched_persist(struct ip_vs_service *svc,
		    struct sk_buff *skb,
		    __be16 src_port, __be16 dst_port, int *ignored)
{
	struct ip_vs_conn *cp = NULL;
	struct ip_vs_iphdr iph;
	struct ip_vs_dest *dest;
	struct ip_vs_conn *ct;
	__be16 dport = 0;		
	unsigned int flags;
	struct ip_vs_conn_param param;
	const union nf_inet_addr fwmark = { .ip = htonl(svc->fwmark) };
	union nf_inet_addr snet;	

	ip_vs_fill_iphdr(svc->af, skb_network_header(skb), &iph);

	
#ifdef CONFIG_IP_VS_IPV6
	if (svc->af == AF_INET6)
		ipv6_addr_prefix(&snet.in6, &iph.saddr.in6, svc->netmask);
	else
#endif
		snet.ip = iph.saddr.ip & svc->netmask;

	IP_VS_DBG_BUF(6, "p-schedule: src %s:%u dest %s:%u "
		      "mnet %s\n",
		      IP_VS_DBG_ADDR(svc->af, &iph.saddr), ntohs(src_port),
		      IP_VS_DBG_ADDR(svc->af, &iph.daddr), ntohs(dst_port),
		      IP_VS_DBG_ADDR(svc->af, &snet));

	{
		int protocol = iph.protocol;
		const union nf_inet_addr *vaddr = &iph.daddr;
		__be16 vport = 0;

		if (dst_port == svc->port) {
			if (svc->port != FTPPORT)
				vport = dst_port;
		} else {
			if (svc->fwmark) {
				protocol = IPPROTO_IP;
				vaddr = &fwmark;
			}
		}
		
		if (ip_vs_conn_fill_param_persist(svc, skb, protocol, &snet, 0,
						  vaddr, vport, &param) < 0) {
			*ignored = -1;
			return NULL;
		}
	}

	
	ct = ip_vs_ct_in_get(&param);
	if (!ct || !ip_vs_check_template(ct)) {
		dest = svc->scheduler->schedule(svc, skb);
		if (!dest) {
			IP_VS_DBG(1, "p-schedule: no dest found.\n");
			kfree(param.pe_data);
			*ignored = 0;
			return NULL;
		}

		if (dst_port == svc->port && svc->port != FTPPORT)
			dport = dest->port;

		ct = ip_vs_conn_new(&param, &dest->addr, dport,
				    IP_VS_CONN_F_TEMPLATE, dest, skb->mark);
		if (ct == NULL) {
			kfree(param.pe_data);
			*ignored = -1;
			return NULL;
		}

		ct->timeout = svc->timeout;
	} else {
		
		dest = ct->dest;
		kfree(param.pe_data);
	}

	dport = dst_port;
	if (dport == svc->port && dest->port)
		dport = dest->port;

	flags = (svc->flags & IP_VS_SVC_F_ONEPACKET
		 && iph.protocol == IPPROTO_UDP)?
		IP_VS_CONN_F_ONE_PACKET : 0;

	ip_vs_conn_fill_param(svc->net, svc->af, iph.protocol, &iph.saddr,
			      src_port, &iph.daddr, dst_port, &param);

	cp = ip_vs_conn_new(&param, &dest->addr, dport, flags, dest, skb->mark);
	if (cp == NULL) {
		ip_vs_conn_put(ct);
		*ignored = -1;
		return NULL;
	}

	ip_vs_control_add(cp, ct);
	ip_vs_conn_put(ct);

	ip_vs_conn_stats(cp, svc);
	return cp;
}


struct ip_vs_conn *
ip_vs_schedule(struct ip_vs_service *svc, struct sk_buff *skb,
	       struct ip_vs_proto_data *pd, int *ignored)
{
	struct ip_vs_protocol *pp = pd->pp;
	struct ip_vs_conn *cp = NULL;
	struct ip_vs_iphdr iph;
	struct ip_vs_dest *dest;
	__be16 _ports[2], *pptr;
	unsigned int flags;

	*ignored = 1;
	ip_vs_fill_iphdr(svc->af, skb_network_header(skb), &iph);
	pptr = skb_header_pointer(skb, iph.len, sizeof(_ports), _ports);
	if (pptr == NULL)
		return NULL;

	if (pptr[0] == FTPDATA) {
		IP_VS_DBG_PKT(12, svc->af, pp, skb, 0,
			      "Not scheduling FTPDATA");
		return NULL;
	}

	if ((!skb->dev || skb->dev->flags & IFF_LOOPBACK) &&
	    (cp = pp->conn_in_get(svc->af, skb, &iph, iph.len, 1))) {
		IP_VS_DBG_PKT(12, svc->af, pp, skb, 0,
			      "Not scheduling reply for existing connection");
		__ip_vs_conn_put(cp);
		return NULL;
	}

	if (svc->flags & IP_VS_SVC_F_PERSISTENT)
		return ip_vs_sched_persist(svc, skb, pptr[0], pptr[1], ignored);

	*ignored = 0;

	if (!svc->fwmark && pptr[1] != svc->port) {
		if (!svc->port)
			pr_err("Schedule: port zero only supported "
			       "in persistent services, "
			       "check your ipvs configuration\n");
		return NULL;
	}

	dest = svc->scheduler->schedule(svc, skb);
	if (dest == NULL) {
		IP_VS_DBG(1, "Schedule: no dest found.\n");
		return NULL;
	}

	flags = (svc->flags & IP_VS_SVC_F_ONEPACKET
		 && iph.protocol == IPPROTO_UDP)?
		IP_VS_CONN_F_ONE_PACKET : 0;

	{
		struct ip_vs_conn_param p;

		ip_vs_conn_fill_param(svc->net, svc->af, iph.protocol,
				      &iph.saddr, pptr[0], &iph.daddr, pptr[1],
				      &p);
		cp = ip_vs_conn_new(&p, &dest->addr,
				    dest->port ? dest->port : pptr[1],
				    flags, dest, skb->mark);
		if (!cp) {
			*ignored = -1;
			return NULL;
		}
	}

	IP_VS_DBG_BUF(6, "Schedule fwd:%c c:%s:%u v:%s:%u "
		      "d:%s:%u conn->flags:%X conn->refcnt:%d\n",
		      ip_vs_fwd_tag(cp),
		      IP_VS_DBG_ADDR(svc->af, &cp->caddr), ntohs(cp->cport),
		      IP_VS_DBG_ADDR(svc->af, &cp->vaddr), ntohs(cp->vport),
		      IP_VS_DBG_ADDR(svc->af, &cp->daddr), ntohs(cp->dport),
		      cp->flags, atomic_read(&cp->refcnt));

	ip_vs_conn_stats(cp, svc);
	return cp;
}


int ip_vs_leave(struct ip_vs_service *svc, struct sk_buff *skb,
		struct ip_vs_proto_data *pd)
{
	__be16 _ports[2], *pptr;
	struct ip_vs_iphdr iph;
#ifdef CONFIG_SYSCTL
	struct net *net;
	struct netns_ipvs *ipvs;
	int unicast;
#endif

	ip_vs_fill_iphdr(svc->af, skb_network_header(skb), &iph);

	pptr = skb_header_pointer(skb, iph.len, sizeof(_ports), _ports);
	if (pptr == NULL) {
		ip_vs_service_put(svc);
		return NF_DROP;
	}

#ifdef CONFIG_SYSCTL
	net = skb_net(skb);

#ifdef CONFIG_IP_VS_IPV6
	if (svc->af == AF_INET6)
		unicast = ipv6_addr_type(&iph.daddr.in6) & IPV6_ADDR_UNICAST;
	else
#endif
		unicast = (inet_addr_type(net, iph.daddr.ip) == RTN_UNICAST);

	ipvs = net_ipvs(net);
	if (ipvs->sysctl_cache_bypass && svc->fwmark && unicast) {
		int ret;
		struct ip_vs_conn *cp;
		unsigned int flags = (svc->flags & IP_VS_SVC_F_ONEPACKET &&
				      iph.protocol == IPPROTO_UDP)?
				      IP_VS_CONN_F_ONE_PACKET : 0;
		union nf_inet_addr daddr =  { .all = { 0, 0, 0, 0 } };

		ip_vs_service_put(svc);

		
		IP_VS_DBG(6, "%s(): create a cache_bypass entry\n", __func__);
		{
			struct ip_vs_conn_param p;
			ip_vs_conn_fill_param(svc->net, svc->af, iph.protocol,
					      &iph.saddr, pptr[0],
					      &iph.daddr, pptr[1], &p);
			cp = ip_vs_conn_new(&p, &daddr, 0,
					    IP_VS_CONN_F_BYPASS | flags,
					    NULL, skb->mark);
			if (!cp)
				return NF_DROP;
		}

		
		ip_vs_in_stats(cp, skb);

		
		ip_vs_set_state(cp, IP_VS_DIR_INPUT, skb, pd);

		
		ret = cp->packet_xmit(skb, cp, pd->pp);
		

		atomic_inc(&cp->in_pkts);
		ip_vs_conn_put(cp);
		return ret;
	}
예제 #8
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;
}