Ejemplo n.º 1
0
/* Must follow make_writable() since that can move the skb data. */
static void set_tp_port(struct sk_buff *skb, __be16 *port,
			 __be16 new_port, __sum16 *check)
{
	inet_proto_csum_replace2(check, skb, *port, new_port, 0);
	*port = new_port;
	skb->rxhash = 0;
}
Ejemplo n.º 2
0
static void nf_nat_csum(struct sk_buff *skb, const struct iphdr *iph, void *data,
			int datalen, __sum16 *check, int oldlen)
{
	struct rtable *rt = skb_rtable(skb);

	if (skb->ip_summed != CHECKSUM_PARTIAL) {
		if (!(rt->rt_flags & RTCF_LOCAL) &&
		    (!skb->dev || skb->dev->features & NETIF_F_V4_CSUM)) {
			skb->ip_summed = CHECKSUM_PARTIAL;
			skb->csum_start = skb_headroom(skb) +
					  skb_network_offset(skb) +
					  iph->ihl * 4;
			skb->csum_offset = (void *)check - data;
			*check = ~csum_tcpudp_magic(iph->saddr, iph->daddr,
						    datalen, iph->protocol, 0);
		} else {
			*check = 0;
			*check = csum_tcpudp_magic(iph->saddr, iph->daddr,
						   datalen, iph->protocol,
						   csum_partial(data, datalen,
								0));
			if (iph->protocol == IPPROTO_UDP && !*check)
				*check = CSUM_MANGLED_0;
		}
	} else
		inet_proto_csum_replace2(check, skb,
					 htons(oldlen), htons(datalen), 1);
}
Ejemplo n.º 3
0
static void nf_nat_ipv6_csum_recalc(struct sk_buff *skb,
				    u8 proto, void *data, __sum16 *check,
				    int datalen, int oldlen)
{
	const struct ipv6hdr *ipv6h = ipv6_hdr(skb);
	struct rt6_info *rt = (struct rt6_info *)skb_dst(skb);

	if (skb->ip_summed != CHECKSUM_PARTIAL) {
		if (!(rt->rt6i_flags & RTF_LOCAL) &&
		    (!skb->dev || skb->dev->features & NETIF_F_V6_CSUM)) {
			skb->ip_summed = CHECKSUM_PARTIAL;
			skb->csum_start = skb_headroom(skb) +
					  skb_network_offset(skb) +
					  (data - (void *)skb->data);
			skb->csum_offset = (void *)check - data;
			*check = ~csum_ipv6_magic(&ipv6h->saddr, &ipv6h->daddr,
						  datalen, proto, 0);
		} else {
			*check = 0;
			*check = csum_ipv6_magic(&ipv6h->saddr, &ipv6h->daddr,
						 datalen, proto,
						 csum_partial(data, datalen,
							      0));
			if (proto == IPPROTO_UDP && !*check)
				*check = CSUM_MANGLED_0;
		}
	} else
		inet_proto_csum_replace2(check, skb,
					 htons(oldlen), htons(datalen), 1);
}
Ejemplo n.º 4
0
static bool
udplite_manip_pkt(struct sk_buff *skb,
		  const struct nf_nat_l3proto *l3proto,
		  unsigned int iphdroff, unsigned int hdroff,
		  const struct nf_conntrack_tuple *tuple,
		  enum nf_nat_manip_type maniptype)
{
	struct udphdr *hdr;
	__be16 *portptr, newport;

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

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

	if (maniptype == NF_NAT_MANIP_SRC) {
		/* Get rid of source port */
		newport = tuple->src.u.udp.port;
		portptr = &hdr->source;
	} else {
		/* Get rid of dst port */
		newport = tuple->dst.u.udp.port;
		portptr = &hdr->dest;
	}

	l3proto->csum_update(skb, iphdroff, &hdr->check, tuple, maniptype);
	inet_proto_csum_replace2(&hdr->check, skb, *portptr, newport, false);
	if (!hdr->check)
		hdr->check = CSUM_MANGLED_0;

	*portptr = newport;
	return true;
}
Ejemplo n.º 5
0
/* Return false if there was an error. */
static inline bool
set_ect_tcp(struct sk_buff *skb, const struct ipt_ECN_info *einfo)
{
	struct tcphdr _tcph, *tcph;
	__be16 oldval;

	/* Not enough header? */
	tcph = skb_header_pointer(skb, ip_hdrlen(skb), sizeof(_tcph), &_tcph);
	if (!tcph)
		return false;

	if ((!(einfo->operation & IPT_ECN_OP_SET_ECE) ||
	     tcph->ece == einfo->proto.tcp.ece) &&
	    (!(einfo->operation & IPT_ECN_OP_SET_CWR) ||
	     tcph->cwr == einfo->proto.tcp.cwr))
		return true;

	if (!skb_make_writable(skb, ip_hdrlen(skb) + sizeof(*tcph)))
		return false;
	tcph = (void *)ip_hdr(skb) + ip_hdrlen(skb);

	oldval = ((__be16 *)tcph)[6];
	if (einfo->operation & IPT_ECN_OP_SET_ECE)
		tcph->ece = einfo->proto.tcp.ece;
	if (einfo->operation & IPT_ECN_OP_SET_CWR)
		tcph->cwr = einfo->proto.tcp.cwr;

	inet_proto_csum_replace2(&tcph->check, skb,
				 oldval, ((__be16 *)tcph)[6], 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; /* 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;
}
Ejemplo n.º 7
0
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;
}
Ejemplo n.º 8
0
static void nf_nat_ipv4_csum_recalc(struct sk_buff *skb,
				    u8 proto, void *data, __sum16 *check,
				    int datalen, int oldlen)
{
	if (skb->ip_summed != CHECKSUM_PARTIAL) {
		const struct iphdr *iph = ip_hdr(skb);

		skb->ip_summed = CHECKSUM_PARTIAL;
		skb->csum_start = skb_headroom(skb) + skb_network_offset(skb) +
			ip_hdrlen(skb);
		skb->csum_offset = (void *)check - data;
		*check = ~csum_tcpudp_magic(iph->saddr, iph->daddr, datalen,
					    proto, 0);
	} else
		inet_proto_csum_replace2(check, skb,
					 htons(oldlen), htons(datalen), true);
}
Ejemplo n.º 9
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;
}
Ejemplo n.º 10
0
static bool
icmp_manip_pkt(struct sk_buff *skb,
	       const struct nf_nat_l3proto *l3proto,
	       unsigned int iphdroff, unsigned int hdroff,
	       const struct nf_conntrack_tuple *tuple,
	       enum nf_nat_manip_type maniptype)
{
	struct icmphdr *hdr;

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

	hdr = (struct icmphdr *)(skb->data + hdroff);
	inet_proto_csum_replace2(&hdr->checksum, skb,
				 hdr->un.echo.id, tuple->src.u.icmp.id, false);
	hdr->un.echo.id = tuple->src.u.icmp.id;
	return true;
}
Ejemplo n.º 11
0
static unsigned int
tcpoptstrip_mangle_packet(struct sk_buff *skb,
			  const struct xt_tcpoptstrip_target_info *info,
			  unsigned int tcphoff, unsigned int minlen)
{
	unsigned int optl, i, j;
	struct tcphdr *tcph;
	u_int16_t n, o;
	u_int8_t *opt;

	if (!skb_make_writable(skb, skb->len))
		return NF_DROP;

	tcph = (struct tcphdr *)(skb_network_header(skb) + tcphoff);
	opt  = (u_int8_t *)tcph;

	/*
	 * Walk through all TCP options - if we find some option to remove,
	 * set all octets to %TCPOPT_NOP and adjust checksum.
	 */
	for (i = sizeof(struct tcphdr); i < tcp_hdrlen(skb); i += optl) {
		optl = optlen(opt, i);

		if (i + optl > tcp_hdrlen(skb))
			break;

		if (!tcpoptstrip_test_bit(info->strip_bmap, opt[i]))
			continue;

		for (j = 0; j < optl; ++j) {
			o = opt[i+j];
			n = TCPOPT_NOP;
			if ((i + j) % 2 == 0) {
				o <<= 8;
				n <<= 8;
			}
			inet_proto_csum_replace2(&tcph->check, skb, htons(o),
						 htons(n), 0);
		}
		memset(opt + i, TCPOPT_NOP, optl);
	}

	return XT_CONTINUE;
}
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;
}
Ejemplo n.º 13
0
static bool
dccp_manip_pkt(struct sk_buff *skb,
               const struct nf_nat_l3proto *l3proto,
               unsigned int iphdroff, unsigned int hdroff,
               const struct nf_conntrack_tuple *tuple,
               enum nf_nat_manip_type maniptype)
{
    struct dccp_hdr *hdr;
    __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;

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

    if (maniptype == NF_NAT_MANIP_SRC) {
        newport = tuple->src.u.dccp.port;
        portptr = &hdr->dccph_sport;
    } else {
        newport = tuple->dst.u.dccp.port;
        portptr = &hdr->dccph_dport;
    }

    oldport = *portptr;
    *portptr = newport;

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

    l3proto->csum_update(skb, iphdroff, &hdr->dccph_checksum,
                         tuple, maniptype);
    inet_proto_csum_replace2(&hdr->dccph_checksum, skb, oldport, newport,
                             0);
    return true;
}
Ejemplo n.º 14
0
static int
tcpmss_mangle_packet(struct sk_buff *skb,
		     const struct xt_tcpmss_info *info,
		     unsigned int in_mtu,
		     unsigned int tcphoff,
		     unsigned int minlen)
{
	struct tcphdr *tcph;
	unsigned int tcplen, i;
	__be16 oldval;
	u16 newmss;
	u8 *opt;

	if (!skb_make_writable(skb, skb->len))
		return -1;

	tcplen = skb->len - tcphoff;
	tcph = (struct tcphdr *)(skb_network_header(skb) + tcphoff);

	/* Header cannot be larger than the packet */
	if (tcplen < tcph->doff*4)
		return -1;

	if (info->mss == XT_TCPMSS_CLAMP_PMTU) {
		if (dst_mtu(skb_dst(skb)) <= minlen) {
			if (net_ratelimit())
				pr_err("unknown or invalid path-MTU (%u)\n",
				       dst_mtu(skb_dst(skb)));
			return -1;
		}
		if (in_mtu <= minlen) {
			if (net_ratelimit())
				pr_err("unknown or invalid path-MTU (%u)\n",
				       in_mtu);
			return -1;
		}
		newmss = min(dst_mtu(skb_dst(skb)), in_mtu) - minlen;
	} else
		newmss = info->mss;

	opt = (u_int8_t *)tcph;
	for (i = sizeof(struct tcphdr); i < tcph->doff*4; i += optlen(opt, i)) {
		if (opt[i] == TCPOPT_MSS && tcph->doff*4 - i >= TCPOLEN_MSS &&
		    opt[i+1] == TCPOLEN_MSS) {
			u_int16_t oldmss;

			oldmss = (opt[i+2] << 8) | opt[i+3];

			/* Never increase MSS, even when setting it, as
			 * doing so results in problems for hosts that rely
			 * on MSS being set correctly.
			 */
			if (oldmss <= newmss)
				return 0;

			opt[i+2] = (newmss & 0xff00) >> 8;
			opt[i+3] = newmss & 0x00ff;

			inet_proto_csum_replace2(&tcph->check, skb,
						 htons(oldmss), htons(newmss),
						 0);
			return 0;
		}
	}
Ejemplo n.º 15
0
static int
tcpmss_mangle_packet(struct sk_buff *skb,
		     const struct xt_action_param *par,
		     unsigned int family,
		     unsigned int tcphoff,
		     unsigned int minlen)
{
	const struct xt_tcpmss_info *info = par->targinfo;
	struct tcphdr *tcph;
	int len, tcp_hdrlen;
	unsigned int i;
	__be16 oldval;
	u16 newmss;
	u8 *opt;

	/* This is a fragment, no TCP header is available */
	if (par->fragoff != 0)
		return 0;

	if (!skb_make_writable(skb, skb->len))
		return -1;

	len = skb->len - tcphoff;
	if (len < (int)sizeof(struct tcphdr))
		return -1;

	tcph = (struct tcphdr *)(skb_network_header(skb) + tcphoff);
	tcp_hdrlen = tcph->doff * 4;

	if (len < tcp_hdrlen)
		return -1;

	if (info->mss == XT_TCPMSS_CLAMP_PMTU) {
		struct net *net = dev_net(par->in ? par->in : par->out);
		unsigned int in_mtu = tcpmss_reverse_mtu(net, skb, family);

		if (dst_mtu(skb_dst(skb)) <= minlen) {
			net_err_ratelimited("unknown or invalid path-MTU (%u)\n",
					    dst_mtu(skb_dst(skb)));
			return -1;
		}
		if (in_mtu <= minlen) {
			net_err_ratelimited("unknown or invalid path-MTU (%u)\n",
					    in_mtu);
			return -1;
		}
		newmss = min(dst_mtu(skb_dst(skb)), in_mtu) - minlen;
	} else
		newmss = info->mss;

	opt = (u_int8_t *)tcph;
	for (i = sizeof(struct tcphdr); i <= tcp_hdrlen - TCPOLEN_MSS; i += optlen(opt, i)) {
		if (opt[i] == TCPOPT_MSS && opt[i+1] == TCPOLEN_MSS) {
			u_int16_t oldmss;

			oldmss = (opt[i+2] << 8) | opt[i+3];

			/* Never increase MSS, even when setting it, as
			 * doing so results in problems for hosts that rely
			 * on MSS being set correctly.
			 */
			if (oldmss <= newmss)
				return 0;

			opt[i+2] = (newmss & 0xff00) >> 8;
			opt[i+3] = newmss & 0x00ff;

			inet_proto_csum_replace2(&tcph->check, skb,
						 htons(oldmss), htons(newmss),
						 false);
			return 0;
		}
	}