Esempio n. 1
0
static int xfrm4_mode_tunnel_input(struct xfrm_state *x, struct sk_buff *skb)
{
	int err = -EINVAL;

	if (XFRM_MODE_SKB_CB(skb)->protocol != IPPROTO_IPIP)
		goto out;

	if (!pskb_may_pull(skb, sizeof(struct iphdr)))
		goto out;

	if (skb_cloned(skb) &&
	    (err = pskb_expand_head(skb, 0, 0, GFP_ATOMIC)))
		goto out;

	if (x->props.flags & XFRM_STATE_DECAP_DSCP)
		ipv4_copy_dscp(XFRM_MODE_SKB_CB(skb)->tos, ipip_hdr(skb));
	if (!(x->props.flags & XFRM_STATE_NOECN))
		ipip_ecn_decapsulate(skb);

	skb_reset_network_header(skb);
	skb_mac_header_rebuild(skb);
	err = 0;

out:
	return err;
}
static int xfrm4_mode_tunnel_input(struct xfrm_state *x, struct sk_buff *skb)
{
	struct xfrm_tunnel *handler;
	int err = -EINVAL;

	if (XFRM_MODE_SKB_CB(skb)->protocol != IPPROTO_IPIP)
		goto out;

	if (!pskb_may_pull(skb, sizeof(struct iphdr)))
		goto out;

	for_each_input_rcu(rcv_notify_handlers, handler)
		handler->handler(skb);

	err = skb_unclone(skb, GFP_ATOMIC);
	if (err)
		goto out;

	if (x->props.flags & XFRM_STATE_DECAP_DSCP)
		ipv4_copy_dscp(XFRM_MODE_SKB_CB(skb)->tos, ipip_hdr(skb));
	if (!(x->props.flags & XFRM_STATE_NOECN))
		ipip_ecn_decapsulate(skb);

	skb_reset_network_header(skb);
	skb_mac_header_rebuild(skb);

	err = 0;

out:
	return err;
}
static int xfrm4_tunnel_input(struct xfrm_state *x, struct sk_buff *skb)
{
	struct iphdr *iph = skb->nh.iph;
	int err = -EINVAL;

	switch(iph->protocol){
		case IPPROTO_IPIP:
			break;
#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
		case IPPROTO_IPV6:
			break;
#endif
		default:
			goto out;
	}

	if (!pskb_may_pull(skb, sizeof(struct iphdr)))
		goto out;

	if (skb_cloned(skb) &&
	    (err = pskb_expand_head(skb, 0, 0, GFP_ATOMIC)))
		goto out;

	iph = skb->nh.iph;
	if (iph->protocol == IPPROTO_IPIP) {
		if (x->props.flags & XFRM_STATE_DECAP_DSCP)
			ipv4_copy_dscp(iph, skb->h.ipiph);
		if (!(x->props.flags & XFRM_STATE_NOECN))
			ipip_ecn_decapsulate(skb);
	}
#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
	else {
		if (!(x->props.flags & XFRM_STATE_NOECN))
			ipip6_ecn_decapsulate(iph, skb);
		skb->protocol = htons(ETH_P_IPV6);
	}
#endif
	skb->mac.raw = memmove(skb->data - skb->mac_len,
			       skb->mac.raw, skb->mac_len);
	skb->nh.raw = skb->data;
	err = 0;

out:
	return err;
}
int xfrm4_rcv_encap(struct sk_buff *skb, __u16 encap_type)
{
	int err;
	u32 spi;
	struct sec_decap_state xfrm_vec[XFRM_MAX_DEPTH];
	struct xfrm_state *x;
	int xfrm_nr = 0;
	int decaps = 0;

	if ((err = xfrm4_parse_spi(skb, skb->nh.iph->protocol, &spi)) != 0)
		goto drop;

	do {
		struct iphdr *iph = skb->nh.iph;

		if (xfrm_nr == XFRM_MAX_DEPTH)
			goto drop;

		x = xfrm_state_lookup((xfrm_address_t *)&iph->daddr, spi, iph->protocol, AF_INET);
		if (x == NULL)
			goto drop;

		spin_lock(&x->lock);
		if (unlikely(x->km.state != XFRM_STATE_VALID))
			goto drop_unlock;

		if (xfrm_state_check_expire(x))
			goto drop_unlock;

		xfrm_vec[xfrm_nr].decap.decap_type = encap_type;
		if (x->type->input(x, &(xfrm_vec[xfrm_nr].decap), skb))
			goto drop_unlock;

		/* only the first xfrm gets the encap type */
		encap_type = 0;

		x->curlft.bytes += skb->len;
		x->curlft.packets++;

		spin_unlock(&x->lock);

		xfrm_vec[xfrm_nr++].xvec = x;

		iph = skb->nh.iph;

		if (x->props.mode) {
			if (iph->protocol != IPPROTO_IPIP)
				goto drop;
			if (!pskb_may_pull(skb, sizeof(struct iphdr)))
				goto drop;
			if (skb_cloned(skb) &&
			    pskb_expand_head(skb, 0, 0, GFP_ATOMIC))
				goto drop;
			if (x->props.flags & XFRM_STATE_DECAP_DSCP)
				ipv4_copy_dscp(iph, skb->h.ipiph);
			if (!(x->props.flags & XFRM_STATE_NOECN))
				ipip_ecn_decapsulate(skb);
			skb->mac.raw = memmove(skb->data - skb->mac_len,
					       skb->mac.raw, skb->mac_len);
			skb->nh.raw = skb->data;
			memset(&(IPCB(skb)->opt), 0, sizeof(struct ip_options));
			decaps = 1;
			break;
		}

		if ((err = xfrm_parse_spi(skb, skb->nh.iph->protocol, &spi)) < 0)
			goto drop;

	} while (!err);

	/* Allocate new secpath or COW existing one. */

	if (!skb->sp || atomic_read(&skb->sp->refcnt) != 1) {
		struct sec_path *sp;
		sp = secpath_dup(skb->sp);
		if (!sp)
			goto drop;
		if (skb->sp)
			secpath_put(skb->sp);
		skb->sp = sp;
	}
	if (xfrm_nr + skb->sp->len > XFRM_MAX_DEPTH)
		goto drop;

	memcpy(skb->sp->x+skb->sp->len, xfrm_vec, xfrm_nr*sizeof(struct sec_decap_state));
	skb->sp->len += xfrm_nr;

	nf_reset(skb);

	if (decaps) {
		if (!(skb->dev->flags&IFF_LOOPBACK)) {
			dst_release(skb->dst);
			skb->dst = NULL;
		}
		netif_rx(skb);
		return 0;
	} else {
#ifdef CONFIG_NETFILTER
		__skb_push(skb, skb->data - skb->nh.raw);
		skb->nh.iph->tot_len = htons(skb->len);
		ip_send_check(skb->nh.iph);

		NF_HOOK(PF_INET, NF_IP_PRE_ROUTING, skb, skb->dev, NULL,
		        xfrm4_rcv_encap_finish);
		return 0;
#else
		return -skb->nh.iph->protocol;
#endif
	}

drop_unlock:
	spin_unlock(&x->lock);
	xfrm_state_put(x);
drop:
	while (--xfrm_nr >= 0)
		xfrm_state_put(xfrm_vec[xfrm_nr].xvec);

	kfree_skb(skb);
	return 0;
}
int xfrm4_rcv_encap(struct sk_buff *skb, __u16 encap_type)
{
	int err;
	u32 spi, seq;
	struct sec_decap_state xfrm_vec[XFRM_MAX_DEPTH];
	struct xfrm_state *x;
	int xfrm_nr = 0;
	int decaps = 0;
	int offset = 0;

	if ((err = xfrm4_parse_spi(skb, skb->nh.iph->protocol, &spi, &seq)) != 0)
		goto drop;

	do {
		struct iphdr *iph = skb->nh.iph;

		if (xfrm_nr == XFRM_MAX_DEPTH)
			goto drop;

		x = xfrm_state_lookup((xfrm_address_t *)&iph->daddr, spi, iph->protocol, AF_INET);
		if (x == NULL)
			goto drop;

		spin_lock(&x->lock);
		if (unlikely(x->km.state != XFRM_STATE_VALID))
			goto drop_unlock;

#if defined(CONFIG_CAVIUM_OCTEON_IPSEC) && defined(CONFIG_NET_KEY) 
		/*
	  	 * If Octeon IPSEC Acceleration module has been loaded
		 * call it, otherwise, follow the software path
		 */
		if(cavium_ipsec_process)
		{
			if (xfrm_state_check_expire(x))
				goto drop_unlock;

			xfrm_vec[xfrm_nr].decap.decap_type = encap_type;
    
			switch (skb->nh.iph->protocol) {
       			case IPPROTO_AH:
					offset = offsetof(struct ip_auth_hdr, nexthdr);
					break;
				case IPPROTO_ESP:
        			offset = offsetof(struct ip_esp_hdr, spi);
					break;
				default:
					return -skb->nh.iph->protocol;
			}
			offset += (uint64_t)skb->data - (uint64_t)skb->nh.iph;
			/*
		 	 * skb->data points to the start of the esp/ah header
			 * but we require skb->data to point to the start of ip header.
			 */
			skb_push(skb, (unsigned int)((uint64_t)skb->data - (uint64_t)skb->nh.iph));
			if (skb_is_nonlinear(skb) &&
				skb_linearize(skb, GFP_ATOMIC) != 0) {
				err = -ENOMEM;
				goto drop_unlock;
	        }
			if (skb_cloned(skb) &&
				pskb_expand_head(skb, 0, 0, GFP_ATOMIC))
            {
                err = -ENOMEM;
			    goto drop_unlock;
            }

			err = cavium_ipsec_process(x->sa_handle, skb, offset, 0 /*DECRYPT*/);
			if(err) 
				goto drop_unlock;

			/* only the first xfrm gets the encap type */
			encap_type = 0;
			x->curlft.bytes += skb->len;
			x->curlft.packets++;
						
			spin_unlock(&x->lock);
						
			xfrm_vec[xfrm_nr++].xvec = x;

			iph = skb->nh.iph;

			if (x->props.mode) {
#if 0
				if (skb_cloned(skb) &&
					pskb_expand_head(skb, 0, 0, GFP_ATOMIC))
				goto drop;

			/* 
			 * TODO, support ECN and dscp decapsulation
			 */
				if (x->props.flags & XFRM_STATE_DECAP_DSCP)
					ipv4_copy_dscp(iph, skb->h.ipiph);
				if (!(x->props.flags & XFRM_STATE_NOECN))
					ipip_ecn_decapsulate(skb);
#endif
				skb->nh.raw = skb->data;
				memset(&(IPCB(skb)->opt), 0, sizeof(struct ip_options));
				decaps = 1;
				break;
			}
		} else {   /* if (cavium_ipsec_process == NULL) */
			if (x->props.replay_window && xfrm_replay_check(x, seq))
				goto drop_unlock;

			if (xfrm_state_check_expire(x))
				goto drop_unlock;

			xfrm_vec[xfrm_nr].decap.decap_type = encap_type;
			if (x->type->input(x, &(xfrm_vec[xfrm_nr].decap), skb))
				goto drop_unlock;

			/* only the first xfrm gets the encap type */
			encap_type = 0;

			if (x->props.replay_window)
				xfrm_replay_advance(x, seq);
			x->curlft.bytes += skb->len;
			x->curlft.packets++;

			spin_unlock(&x->lock);

			xfrm_vec[xfrm_nr++].xvec = x;

			iph = skb->nh.iph;

			if (x->props.mode) {
				if (iph->protocol != IPPROTO_IPIP)
					goto drop;
				if (!pskb_may_pull(skb, sizeof(struct iphdr)))
					goto drop;
				if (skb_cloned(skb) &&
						pskb_expand_head(skb, 0, 0, GFP_ATOMIC))
					goto drop;
				if (x->props.flags & XFRM_STATE_DECAP_DSCP)
					ipv4_copy_dscp(iph, skb->h.ipiph);
				if (!(x->props.flags & XFRM_STATE_NOECN))
					ipip_ecn_decapsulate(skb);
				skb->mac.raw = memmove(skb->data - skb->mac_len,
				skb->mac.raw, skb->mac_len);
				skb->nh.raw = skb->data;
				memset(&(IPCB(skb)->opt), 0, sizeof(struct ip_options));
				decaps = 1;
				break;
			}
		}  /* if (cavium_ipsec_process) */
#else
		if (x->props.replay_window && xfrm_replay_check(x, seq))
			goto drop_unlock;

		if (xfrm_state_check_expire(x))
			goto drop_unlock;

		xfrm_vec[xfrm_nr].decap.decap_type = encap_type;
		if (x->type->input(x, &(xfrm_vec[xfrm_nr].decap), skb))
			goto drop_unlock;

		/* only the first xfrm gets the encap type */
		encap_type = 0;

		if (x->props.replay_window)
			xfrm_replay_advance(x, seq);
		x->curlft.bytes += skb->len;
		x->curlft.packets++;

		spin_unlock(&x->lock);

		xfrm_vec[xfrm_nr++].xvec = x;

		iph = skb->nh.iph;

		if (x->props.mode) {
			if (iph->protocol != IPPROTO_IPIP)
				goto drop;
			if (!pskb_may_pull(skb, sizeof(struct iphdr)))
				goto drop;
			if (skb_cloned(skb) &&
			    pskb_expand_head(skb, 0, 0, GFP_ATOMIC))
				goto drop;
			if (x->props.flags & XFRM_STATE_DECAP_DSCP)
				ipv4_copy_dscp(iph, skb->h.ipiph);
			if (!(x->props.flags & XFRM_STATE_NOECN))
				ipip_ecn_decapsulate(skb);
			skb->mac.raw = memmove(skb->data - skb->mac_len,
					       skb->mac.raw, skb->mac_len);
			skb->nh.raw = skb->data;
			memset(&(IPCB(skb)->opt), 0, sizeof(struct ip_options));
			decaps = 1;
			break;
		}
#endif

		if ((err = xfrm_parse_spi(skb, skb->nh.iph->protocol, &spi, &seq)) < 0)
			goto drop;
	} while (!err);