Exemple #1
0
static void update_ip_l4_checksum(struct sk_buff *skb, struct iphdr *nh,
				  __be32 addr, __be32 new_addr)
{
	int transport_len = skb->len - skb_transport_offset(skb);

	if (nh->frag_off & htons(IP_OFFSET))
		return;

	if (nh->protocol == IPPROTO_TCP) {
		if (likely(transport_len >= sizeof(struct tcphdr)))
			inet_proto_csum_replace4(&tcp_hdr(skb)->check, skb,
						 addr, new_addr, 1);
	} else if (nh->protocol == IPPROTO_UDP) {
		if (likely(transport_len >= sizeof(struct udphdr))) {
			struct udphdr *uh = udp_hdr(skb);

			if (uh->check || skb->ip_summed == CHECKSUM_PARTIAL) {
				inet_proto_csum_replace4(&uh->check, skb,
							 addr, new_addr, 1);
				if (!uh->check)
					uh->check = CSUM_MANGLED_0;
			}
		}
	}
}
Exemple #2
0
static void set_ip_addr(struct sk_buff *skb, struct iphdr *nh,
				__be32 *addr, __be32 new_addr)
{
	int transport_len = skb->len - skb_transport_offset(skb);

	if (nh->protocol == IPPROTO_TCP) {
		if (likely(transport_len >= sizeof(struct tcphdr)))
			inet_proto_csum_replace4(&tcp_hdr(skb)->check, skb,
						 *addr, new_addr, 1);
	} else if (nh->protocol == IPPROTO_UDP) {
		if (likely(transport_len >= sizeof(struct udphdr))) {
			struct udphdr *uh = udp_hdr(skb);

			if (uh->check ||
			    get_ip_summed(skb) == OVS_CSUM_PARTIAL) {
				inet_proto_csum_replace4(&uh->check, skb,
							 *addr, new_addr, 1);
				if (!uh->check)
					uh->check = CSUM_MANGLED_0;
			}
		}
	}

	csum_replace4(&nh->check, *addr, new_addr);
	skb_clear_rxhash(skb);
	*addr = new_addr;
}
int
nf_nat_seq_adjust(struct sk_buff *skb,
		  struct nf_conn *ct,
		  enum ip_conntrack_info ctinfo)
{
	struct tcphdr *tcph;
	int dir;
	__be32 newseq, newack;
	s16 seqoff, ackoff;
	struct nf_conn_nat *nat = nfct_nat(ct);
	struct nf_nat_seq *this_way, *other_way;

	dir = CTINFO2DIR(ctinfo);

	this_way = &nat->seq[dir];
	other_way = &nat->seq[!dir];

#ifdef CONFIG_HTC_NETWORK_MODIFY
	if (IS_ERR(this_way) || (!this_way))
		printk(KERN_ERR "[NET] this_way is NULL in %s!\n", __func__);
#endif

	if (!skb_make_writable(skb, ip_hdrlen(skb) + sizeof(*tcph)))
		return 0;

	tcph = (void *)skb->data + ip_hdrlen(skb);
	if (after(ntohl(tcph->seq), this_way->correction_pos))
		seqoff = this_way->offset_after;
	else
		seqoff = this_way->offset_before;

	if (after(ntohl(tcph->ack_seq) - other_way->offset_before,
		  other_way->correction_pos))
		ackoff = other_way->offset_after;
	else
		ackoff = other_way->offset_before;

	newseq = htonl(ntohl(tcph->seq) + seqoff);
	newack = htonl(ntohl(tcph->ack_seq) - ackoff);

	inet_proto_csum_replace4(&tcph->check, skb, tcph->seq, newseq, 0);
	inet_proto_csum_replace4(&tcph->check, skb, tcph->ack_seq, newack, 0);

	pr_debug("Adjusting sequence number from %u->%u, ack from %u->%u\n",
		 ntohl(tcph->seq), ntohl(newseq), ntohl(tcph->ack_seq),
		 ntohl(newack));

	tcph->seq = newseq;
	tcph->ack_seq = newack;

	return nf_nat_sack_adjust(skb, tcph, ct, ctinfo);
}
/* TCP sequence number adjustment.  Returns 1 on success, 0 on failure */
int nf_ct_seq_adjust(struct sk_buff *skb,
		     struct nf_conn *ct, enum ip_conntrack_info ctinfo,
		     unsigned int protoff)
{
	enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
	struct tcphdr *tcph;
	__be32 newseq, newack;
	s32 seqoff, ackoff;
	struct nf_conn_seqadj *seqadj = nfct_seqadj(ct);
	struct nf_ct_seqadj *this_way, *other_way;
	int res;

	this_way  = &seqadj->seq[dir];
	other_way = &seqadj->seq[!dir];

	if (!skb_make_writable(skb, protoff + sizeof(*tcph)))
		return 0;

	tcph = (void *)skb->data + protoff;
	spin_lock_bh(&ct->lock);
	if (after(ntohl(tcph->seq), this_way->correction_pos))
		seqoff = this_way->offset_after;
	else
		seqoff = this_way->offset_before;

	if (after(ntohl(tcph->ack_seq) - other_way->offset_before,
		  other_way->correction_pos))
		ackoff = other_way->offset_after;
	else
		ackoff = other_way->offset_before;

	newseq = htonl(ntohl(tcph->seq) + seqoff);
	newack = htonl(ntohl(tcph->ack_seq) - ackoff);

	inet_proto_csum_replace4(&tcph->check, skb, tcph->seq, newseq, 0);
	inet_proto_csum_replace4(&tcph->check, skb, tcph->ack_seq, newack, 0);

	pr_debug("Adjusting sequence number from %u->%u, ack from %u->%u\n",
		 ntohl(tcph->seq), ntohl(newseq), ntohl(tcph->ack_seq),
		 ntohl(newack));

	tcph->seq = newseq;
	tcph->ack_seq = newack;

	res = nf_ct_sack_adjust(skb, protoff, tcph, ct, ctinfo);
	spin_unlock_bh(&ct->lock);

	return res;
}
Exemple #5
0
static inline void update_tp_pseudo_csum_4(struct sk_buff *skb, struct iphdr *ip, const __u32 old_addr, const __u32 new_addr)
{
    const __u8 *l4h = &((__u8*)ip)[ip->ihl << 2];

    if (ip->protocol == IPPROTO_TCP) {
	struct tcphdr *tcp = (struct tcphdr*)l4h;
	inet_proto_csum_replace4(&tcp->check, skb, old_addr, new_addr, 1);
    } else if (ip->protocol == IPPROTO_UDP) {
	struct udphdr *udp = (struct udphdr*)l4h;
	if (udp->check || (skb->ip_summed == CHECKSUM_PARTIAL)) {
	    inet_proto_csum_replace4(&udp->check, skb, old_addr, new_addr, 1);
	    if (! udp->check) {
		udp->check = CSUM_MANGLED_0;
	    }
	}
    }
}
Exemple #6
0
unsigned int synproxy_tstamp_adjust(struct sk_buff *skb,
				    unsigned int protoff,
				    struct tcphdr *th,
				    struct nf_conn *ct,
				    enum ip_conntrack_info ctinfo,
				    const struct nf_conn_synproxy *synproxy)
{
	unsigned int optoff, optend;
	u32 *ptr, old;

	if (synproxy->tsoff == 0)
		return 1;

	optoff = protoff + sizeof(struct tcphdr);
	optend = protoff + th->doff * 4;

	if (!skb_make_writable(skb, optend))
		return 0;

	while (optoff < optend) {
		unsigned char *op = skb->data + optoff;

		switch (op[0]) {
		case TCPOPT_EOL:
			return 1;
		case TCPOPT_NOP:
			optoff++;
			continue;
		default:
			if (optoff + 1 == optend ||
			    optoff + op[1] > optend ||
			    op[1] < 2)
				return 0;
			if (op[0] == TCPOPT_TIMESTAMP &&
			    op[1] == TCPOLEN_TIMESTAMP) {
				if (CTINFO2DIR(ctinfo) == IP_CT_DIR_REPLY) {
					ptr = (u32 *)&op[2];
					old = *ptr;
					*ptr = htonl(ntohl(*ptr) -
						     synproxy->tsoff);
				} else {
					ptr = (u32 *)&op[6];
					old = *ptr;
					*ptr = htonl(ntohl(*ptr) +
						     synproxy->tsoff);
				}
				inet_proto_csum_replace4(&th->check, skb,
							 old, *ptr, 0);
				return 1;
			}
			optoff += op[1];
		}
	}
	return 1;
}
static void
sack_adjust(struct sk_buff *skb,
	    struct tcphdr *tcph,
	    unsigned int sackoff,
	    unsigned int sackend,
	    struct nf_nat_seq *natseq)
{
	while (sackoff < sackend) {
		struct tcp_sack_block_wire *sack;
		__be32 new_start_seq, new_end_seq;

		sack = (void *)skb->data + sackoff;
		if (after(ntohl(sack->start_seq) - natseq->offset_before,
			  natseq->correction_pos))
			new_start_seq = htonl(ntohl(sack->start_seq)
					- natseq->offset_after);
		else
			new_start_seq = htonl(ntohl(sack->start_seq)
					- natseq->offset_before);

		if (after(ntohl(sack->end_seq) - natseq->offset_before,
			  natseq->correction_pos))
			new_end_seq = htonl(ntohl(sack->end_seq)
				      - natseq->offset_after);
		else
			new_end_seq = htonl(ntohl(sack->end_seq)
				      - natseq->offset_before);

		pr_debug("sack_adjust: start_seq: %d->%d, end_seq: %d->%d\n",
			 ntohl(sack->start_seq), new_start_seq,
			 ntohl(sack->end_seq), new_end_seq);

		inet_proto_csum_replace4(&tcph->check, skb,
					 sack->start_seq, new_start_seq, 0);
		inet_proto_csum_replace4(&tcph->check, skb,
					 sack->end_seq, new_end_seq, 0);
		sack->start_seq = new_start_seq;
		sack->end_seq = new_end_seq;
		sackoff += sizeof(*sack);
	}
}
Exemple #8
0
void modisg_snat_oper(struct sk_buff *p_psoSKB, struct iphdr *p_psoIP)
{
  struct tcphdr *psoTCP = tcp_hdr(p_psoSKB);
  __be32 uiSrcAddr;

  if(psoTCP == NULL)
    return;

  /* производим подмену адреса отправителя */
  uiSrcAddr = modisg_dnat_get(p_psoIP->daddr, psoTCP->dest);
  csum_replace4(&p_psoIP->check, p_psoIP->saddr, uiSrcAddr);
  inet_proto_csum_replace4(&psoTCP->check, p_psoSKB, p_psoIP->saddr, uiSrcAddr, 1);
  p_psoIP->saddr = uiSrcAddr;
}
Exemple #9
0
void modisg_dnat_oper(struct sk_buff *p_psoSKB, struct iphdr *p_psoIP)
{
  struct tcphdr *psoTCP = tcp_hdr(p_psoSKB);

  if(psoTCP == NULL)
    return;

  /* сохраняем значение адреса назначения в таблице */
  modisg_dnat_set(p_psoIP->saddr, psoTCP->source, p_psoIP->daddr);
  /* пересчитываем контрольные суммы */
  csum_replace4(&p_psoIP->check, p_psoIP->daddr, L4R_ADDR);
  inet_proto_csum_replace4(&psoTCP->check, p_psoSKB, p_psoIP->daddr, L4R_ADDR, 1);
  p_psoIP->daddr = L4R_ADDR;
}
static bool
tcp_manip_pkt(struct sk_buff *skb,
              unsigned int iphdroff,
              const struct nf_conntrack_tuple *tuple,
              enum nf_nat_manip_type maniptype)
{
    const struct iphdr *iph = (struct iphdr *)(skb->data + iphdroff);
    struct tcphdr *hdr;
    unsigned int hdroff = iphdroff + iph->ihl*4;
    __be32 oldip, newip;
    __be16 *portptr, newport, oldport;
    int hdrsize = 8; /* TCP connection tracking guarantees this much */

    /* this could be a inner header returned in icmp packet; in such
       cases we cannot update the checksum field since it is outside of
       the 8 bytes of transport layer headers we are guaranteed */
    if (skb->len >= hdroff + sizeof(struct tcphdr))
        hdrsize = sizeof(struct tcphdr);

    if (!skb_make_writable(skb, hdroff + hdrsize))
        return false;

    iph = (struct iphdr *)(skb->data + iphdroff);
    hdr = (struct tcphdr *)(skb->data + hdroff);

    if (maniptype == NF_NAT_MANIP_SRC) {
        /* Get rid of src ip and src pt */
        oldip = iph->saddr;
        newip = tuple->src.u3.ip;
        newport = tuple->src.u.tcp.port;
        portptr = &hdr->source;
    } else {
        /* Get rid of dst ip and dst pt */
        oldip = iph->daddr;
        newip = tuple->dst.u3.ip;
        newport = tuple->dst.u.tcp.port;
        portptr = &hdr->dest;
    }

    oldport = *portptr;
    *portptr = newport;

    if (hdrsize < sizeof(*hdr))
        return true;

    inet_proto_csum_replace4(&hdr->check, skb, oldip, newip, 1);
    inet_proto_csum_replace2(&hdr->check, skb, oldport, newport, 0);
    return true;
}
static bool
tcp_manip_pkt(struct sk_buff *skb,
	      unsigned int iphdroff,
	      const struct nf_conntrack_tuple *tuple,
	      enum nf_nat_manip_type maniptype)
{
	const struct iphdr *iph = (struct iphdr *)(skb->data + iphdroff);
	struct tcphdr *hdr;
	unsigned int hdroff = iphdroff + iph->ihl*4;
	__be32 oldip, newip;
	__be16 *portptr, newport, oldport;
	int hdrsize = 8; 

	if (skb->len >= hdroff + sizeof(struct tcphdr))
		hdrsize = sizeof(struct tcphdr);

	if (!skb_make_writable(skb, hdroff + hdrsize))
		return false;

	iph = (struct iphdr *)(skb->data + iphdroff);
	hdr = (struct tcphdr *)(skb->data + hdroff);

	if (maniptype == NF_NAT_MANIP_SRC) {
		
		oldip = iph->saddr;
		newip = tuple->src.u3.ip;
		newport = tuple->src.u.tcp.port;
		portptr = &hdr->source;
	} else {
		
		oldip = iph->daddr;
		newip = tuple->dst.u3.ip;
		newport = tuple->dst.u.tcp.port;
		portptr = &hdr->dest;
	}

	oldport = *portptr;
	*portptr = newport;

	if (hdrsize < sizeof(*hdr))
		return true;

	inet_proto_csum_replace4(&hdr->check, skb, oldip, newip, 1);
	inet_proto_csum_replace2(&hdr->check, skb, oldport, newport, 0);
	return true;
}
static void nf_nat_ipv4_csum_update(struct sk_buff *skb,
				    unsigned int iphdroff, __sum16 *check,
				    const struct nf_conntrack_tuple *t,
				    enum nf_nat_manip_type maniptype)
{
	struct iphdr *iph = (struct iphdr *)(skb->data + iphdroff);
	__be32 oldip, newip;

	if (maniptype == NF_NAT_MANIP_SRC) {
		oldip = iph->saddr;
		newip = t->src.u3.ip;
	} else {
		oldip = iph->daddr;
		newip = t->dst.u3.ip;
	}
	inet_proto_csum_replace4(check, skb, oldip, newip, true);
}
Exemple #13
0
static bool
dccp_manip_pkt(struct sk_buff *skb,
           unsigned int iphdroff,
           const struct nf_conntrack_tuple *tuple,
           enum nf_nat_manip_type maniptype)
{
    const struct iphdr *iph = (const void *)(skb->data + iphdroff);
    struct dccp_hdr *hdr;
    unsigned int hdroff = iphdroff + iph->ihl * 4;
    __be32 oldip, newip;
    __be16 *portptr, oldport, newport;
    int hdrsize = 8; /* DCCP connection tracking guarantees this much */

    if (skb->len >= hdroff + sizeof(struct dccp_hdr))
        hdrsize = sizeof(struct dccp_hdr);

    if (!skb_make_writable(skb, hdroff + hdrsize))
        return false;

    iph = (struct iphdr *)(skb->data + iphdroff);
    hdr = (struct dccp_hdr *)(skb->data + hdroff);

    if (maniptype == IP_NAT_MANIP_SRC) {
        oldip = iph->saddr;
        newip = tuple->src.u3.ip;
        newport = tuple->src.u.dccp.port;
        portptr = &hdr->dccph_sport;
    } else {
        oldip = iph->daddr;
        newip = tuple->dst.u3.ip;
        newport = tuple->dst.u.dccp.port;
        portptr = &hdr->dccph_dport;
    }

    oldport = *portptr;
    *portptr = newport;

    if (hdrsize < sizeof(*hdr))
        return true;

    inet_proto_csum_replace4(&hdr->dccph_checksum, skb, oldip, newip, 1);
    inet_proto_csum_replace2(&hdr->dccph_checksum, skb, oldport, newport,
                 0);
    return true;
}
static bool
udp_manip_pkt(struct sk_buff *skb,
	      unsigned int iphdroff,
	      const struct nf_conntrack_tuple *tuple,
	      enum nf_nat_manip_type maniptype)
{
	const struct iphdr *iph = (struct iphdr *)(skb->data + iphdroff);
	struct udphdr *hdr;
	unsigned int hdroff = iphdroff + iph->ihl*4;
	__be32 oldip, newip;
	__be16 *portptr, newport;

	if (!skb_make_writable(skb, hdroff + sizeof(*hdr)))
		return false;

	iph = (struct iphdr *)(skb->data + iphdroff);
	hdr = (struct udphdr *)(skb->data + hdroff);

	if (maniptype == NF_NAT_MANIP_SRC) {
		/* Get rid of src ip and src pt */
		oldip = iph->saddr;
		newip = tuple->src.u3.ip;
		newport = tuple->src.u.udp.port;
		portptr = &hdr->source;
	} else {
		/* Get rid of dst ip and dst pt */
		oldip = iph->daddr;
		newip = tuple->dst.u3.ip;
		newport = tuple->dst.u.udp.port;
		portptr = &hdr->dest;
	}
	if (hdr->check || skb->ip_summed == CHECKSUM_PARTIAL) {
		inet_proto_csum_replace4(&hdr->check, skb, oldip, newip, 1);
		inet_proto_csum_replace2(&hdr->check, skb, *portptr, newport,
					 0);
		if (!hdr->check)
			hdr->check = CSUM_MANGLED_0;
	}
	*portptr = newport;
	return true;
}
static int tcf_nat(struct sk_buff *skb, struct tc_action *a,
		   struct tcf_result *res)
{
	struct tcf_nat *p = a->priv;
	struct iphdr *iph;
	__be32 old_addr;
	__be32 new_addr;
	__be32 mask;
	__be32 addr;
	int egress;
	int action;
	int ihl;
	int noff;

	spin_lock(&p->tcf_lock);

	p->tcf_tm.lastuse = jiffies;
	old_addr = p->old_addr;
	new_addr = p->new_addr;
	mask = p->mask;
	egress = p->flags & TCA_NAT_FLAG_EGRESS;
	action = p->tcf_action;

	p->tcf_bstats.bytes += qdisc_pkt_len(skb);
	p->tcf_bstats.packets++;

	spin_unlock(&p->tcf_lock);

	if (unlikely(action == TC_ACT_SHOT))
		goto drop;

	noff = skb_network_offset(skb);
	if (!pskb_may_pull(skb, sizeof(*iph) + noff))
		goto drop;

	iph = ip_hdr(skb);

	if (egress)
		addr = iph->saddr;
	else
		addr = iph->daddr;

	if (!((old_addr ^ addr) & mask)) {
		if (skb_cloned(skb) &&
		    !skb_clone_writable(skb, sizeof(*iph) + noff) &&
		    pskb_expand_head(skb, 0, 0, GFP_ATOMIC))
			goto drop;

		new_addr &= mask;
		new_addr |= addr & ~mask;

		/* Rewrite IP header */
		iph = ip_hdr(skb);
		if (egress)
			iph->saddr = new_addr;
		else
			iph->daddr = new_addr;

		csum_replace4(&iph->check, addr, new_addr);
	} else if ((iph->frag_off & htons(IP_OFFSET)) ||
		   iph->protocol != IPPROTO_ICMP) {
		goto out;
	}

	ihl = iph->ihl * 4;

	/* It would be nice to share code with stateful NAT. */
	switch (iph->frag_off & htons(IP_OFFSET) ? 0 : iph->protocol) {
	case IPPROTO_TCP:
	{
		struct tcphdr *tcph;

		if (!pskb_may_pull(skb, ihl + sizeof(*tcph) + noff) ||
		    (skb_cloned(skb) &&
		     !skb_clone_writable(skb, ihl + sizeof(*tcph) + noff) &&
		     pskb_expand_head(skb, 0, 0, GFP_ATOMIC)))
			goto drop;

		tcph = (void *)(skb_network_header(skb) + ihl);
		inet_proto_csum_replace4(&tcph->check, skb, addr, new_addr, 1);
		break;
	}
	case IPPROTO_UDP:
	{
		struct udphdr *udph;

		if (!pskb_may_pull(skb, ihl + sizeof(*udph) + noff) ||
		    (skb_cloned(skb) &&
		     !skb_clone_writable(skb, ihl + sizeof(*udph) + noff) &&
		     pskb_expand_head(skb, 0, 0, GFP_ATOMIC)))
			goto drop;

		udph = (void *)(skb_network_header(skb) + ihl);
		if (udph->check || skb->ip_summed == CHECKSUM_PARTIAL) {
			inet_proto_csum_replace4(&udph->check, skb, addr,
						 new_addr, 1);
			if (!udph->check)
				udph->check = CSUM_MANGLED_0;
		}
		break;
	}
	case IPPROTO_ICMP:
	{
		struct icmphdr *icmph;

		if (!pskb_may_pull(skb, ihl + sizeof(*icmph) + noff))
			goto drop;

		icmph = (void *)(skb_network_header(skb) + ihl);

		if ((icmph->type != ICMP_DEST_UNREACH) &&
		    (icmph->type != ICMP_TIME_EXCEEDED) &&
		    (icmph->type != ICMP_PARAMETERPROB))
			break;

		if (!pskb_may_pull(skb, ihl + sizeof(*icmph) + sizeof(*iph) +
					noff))
			goto drop;

		icmph = (void *)(skb_network_header(skb) + ihl);
		iph = (void *)(icmph + 1);
		if (egress)
			addr = iph->daddr;
		else
			addr = iph->saddr;

		if ((old_addr ^ addr) & mask)
			break;

		if (skb_cloned(skb) &&
		    !skb_clone_writable(skb, ihl + sizeof(*icmph) +
					     sizeof(*iph) + noff) &&
		    pskb_expand_head(skb, 0, 0, GFP_ATOMIC))
			goto drop;

		icmph = (void *)(skb_network_header(skb) + ihl);
		iph = (void *)(icmph + 1);

		new_addr &= mask;
		new_addr |= addr & ~mask;

		/* XXX Fix up the inner checksums. */
		if (egress)
			iph->daddr = new_addr;
		else
			iph->saddr = new_addr;

		inet_proto_csum_replace4(&icmph->checksum, skb, addr, new_addr,
					 0);
		break;
	}
	default:
		break;
	}

out:
	return action;

drop:
	spin_lock(&p->tcf_lock);
	p->tcf_qstats.drops++;
	spin_unlock(&p->tcf_lock);
	return TC_ACT_SHOT;
}