Пример #1
0
static int
tcp_dnat_handler(struct sk_buff *skb,
		 struct dispatcher_protocol *pp, struct dispatcher_service *svc)
{
	struct tcphdr *tcph;
	unsigned int tcphoff;
	int oldlen;
	struct dispatcher_dest *dest = svc->dest;
	__be16 localport;
	
	localport = htons(GET_REAL_PORT(htons(dest->port), svc));

#ifdef CONFIG_DISPATCHER_IPV6
	if (dest->af == AF_INET6)
		tcphoff = sizeof(struct ipv6hdr);
	else
#endif
		tcphoff = ip_hdrlen(skb);
	oldlen = skb->len - tcphoff;

	/* csum_check requires unshared skb */
	if (!skb_make_writable(skb, tcphoff+sizeof(*tcph))) {
		return 0;
	}

	tcph = (void *)skb_network_header(skb) + tcphoff;
	tcph->dest = localport;

	/*
	 *	Adjust TCP checksums
	 */
	if (skb->ip_summed == CHECKSUM_PARTIAL) {
		tcp_partial_csum_update(dest->af, tcph, &svc->addr, &dest->addr,
					htons(oldlen),
					htons(skb->len - tcphoff));
	} else {
		/* Only port and addr are changed, do fast csum update */
		tcp_fast_csum_update(dest->af, tcph, &svc->addr, &dest->addr,
				     svc->port, localport);
		if (skb->ip_summed == CHECKSUM_COMPLETE)
			skb->ip_summed = CHECKSUM_NONE;
	}
	return 1;
}
Пример #2
0
static int
tcp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
		 struct ip_vs_conn *cp, struct ip_vs_iphdr *iph)
{
	struct tcphdr *tcph;
	unsigned int tcphoff = iph->len;
	int oldlen;
	int payload_csum = 0;

#ifdef CONFIG_IP_VS_IPV6
	if (cp->af == AF_INET6 && iph->fragoffs)
		return 1;
#endif
	oldlen = skb->len - tcphoff;

	/* csum_check requires unshared skb */
	if (!skb_make_writable(skb, tcphoff+sizeof(*tcph)))
		return 0;

	if (unlikely(cp->app != NULL)) {
		int ret;

		/* Some checks before mangling */
		if (pp->csum_check && !pp->csum_check(cp->af, skb, pp))
			return 0;

		/*
		 *	Attempt ip_vs_app call.
		 *	It will fix ip_vs_conn and iph ack_seq stuff
		 */
		if (!(ret = ip_vs_app_pkt_in(cp, skb)))
			return 0;
		/* ret=2: csum update is needed after payload mangling */
		if (ret == 1)
			oldlen = skb->len - tcphoff;
		else
			payload_csum = 1;
	}

	tcph = (void *)skb_network_header(skb) + tcphoff;
	tcph->dest = cp->dport;

	/*
	 *	Adjust TCP checksums
	 */
	if (skb->ip_summed == CHECKSUM_PARTIAL) {
		tcp_partial_csum_update(cp->af, tcph, &cp->vaddr, &cp->daddr,
					htons(oldlen),
					htons(skb->len - tcphoff));
	} else if (!payload_csum) {
		/* Only port and addr are changed, do fast csum update */
		tcp_fast_csum_update(cp->af, tcph, &cp->vaddr, &cp->daddr,
				     cp->vport, cp->dport);
		if (skb->ip_summed == CHECKSUM_COMPLETE)
			skb->ip_summed = (cp->app && pp->csum_check) ?
					 CHECKSUM_UNNECESSARY : CHECKSUM_NONE;
	} else {
		/* full checksum calculation */
		tcph->check = 0;
		skb->csum = skb_checksum(skb, tcphoff, skb->len - tcphoff, 0);
#ifdef CONFIG_IP_VS_IPV6
		if (cp->af == AF_INET6)
			tcph->check = csum_ipv6_magic(&cp->caddr.in6,
						      &cp->daddr.in6,
						      skb->len - tcphoff,
						      cp->protocol, skb->csum);
		else
#endif
			tcph->check = csum_tcpudp_magic(cp->caddr.ip,
							cp->daddr.ip,
							skb->len - tcphoff,
							cp->protocol,
							skb->csum);
		skb->ip_summed = CHECKSUM_UNNECESSARY;
	}
	return 1;
}
Пример #3
0
static int
tcp_fnat_out_handler(struct sk_buff *skb,
		     struct ip_vs_protocol *pp, struct ip_vs_conn *cp)
{
	struct tcphdr *tcph;
	unsigned int tcphoff;
	int oldlen;

#ifdef CONFIG_IP_VS_IPV6
	if (cp->af == AF_INET6)
		tcphoff = sizeof(struct ipv6hdr);
	else
#endif
		tcphoff = ip_hdrlen(skb);
	oldlen = skb->len - tcphoff;

	/* csum_check requires unshared skb */
	if (!skb_make_writable(skb, tcphoff + sizeof(*tcph)))
		return 0;

	if (unlikely(cp->app != NULL)) {
		/* Some checks before mangling */
		if (pp->csum_check && !pp->csum_check(cp->af, skb, pp))
			return 0;

		/* Call application helper if needed */
		if (!ip_vs_app_pkt_out(cp, skb))
			return 0;
	}

	tcph = (void *)skb_network_header(skb) + tcphoff;
	tcp_save_out_seq(skb, cp, tcph, tcphoff);
	tcph->source = cp->vport;
	tcph->dest = cp->cport;

	/*
	 * for syn_ack
	 * 1. adjust tcp opt mss in rs->client
	 */
	if (tcph->syn && tcph->ack) {
		tcp_opt_adjust_mss(cp->af, tcph);
	}

	/* adjust tcp ack/sack sequence */
	if (tcp_out_adjust_seq(cp, tcph) == 0) {
		return 0;
	}

	/*
	 * for syn_ack
	 * 2. init sequence
	 */
	if (tcph->syn && tcph->ack) {
		tcp_out_init_seq(cp, tcph);
	}

	/* do csum later */
	if (sysctl_ip_vs_csum_offload) {
		skb->csum_start = skb_network_header(skb) -
				skb->head + (ip_hdr(skb)->ihl << 2);
		skb->csum_offset = offsetof(struct tcphdr, check);
		skb->ip_summed = CHECKSUM_PARTIAL;
	}

	/* Adjust TCP checksums */
	if (skb->ip_summed == CHECKSUM_PARTIAL) {
		tcp_partial_csum_reset(cp->af, (skb->len - tcphoff),
					tcph, &cp->vaddr, &cp->caddr);
	} else if (!cp->app) {
		/* Only port and addr are changed, do fast csum update */
		tcp_fast_csum_update(cp->af, tcph, &cp->daddr, &cp->vaddr,
				     cp->dport, cp->vport);
		tcp_fast_csum_update(cp->af, tcph, &cp->laddr, &cp->caddr,
				     cp->lport, cp->cport);
		if (skb->ip_summed == CHECKSUM_COMPLETE)
			skb->ip_summed = CHECKSUM_NONE;
	} else {
		/* full checksum calculation */
		tcph->check = 0;
		skb->csum = skb_checksum(skb, tcphoff, skb->len - tcphoff, 0);
#ifdef CONFIG_IP_VS_IPV6
		if (cp->af == AF_INET6)
			tcph->check = csum_ipv6_magic(&cp->vaddr.in6,
						      &cp->caddr.in6,
						      skb->len - tcphoff,
						      cp->protocol, skb->csum);
		else
#endif
			tcph->check = csum_tcpudp_magic(cp->vaddr.ip,
							cp->caddr.ip,
							skb->len - tcphoff,
							cp->protocol, skb->csum);

		IP_VS_DBG(11, "O-pkt: %s O-csum=%d (+%zd)\n",
			pp->name, tcph->check,
			(char *)&(tcph->check) - (char *)tcph);
	}
	return 1;
}