示例#1
0
/*
 * We need to grow the skb to accommodate the expanssion of the ipcomp packet.
 *
 * The following comment comes from the skb_decompress() which does the
 * same...
 *
 * We have no way of knowing the exact length of the resulting
 * decompressed output before we have actually done the decompression.
 * For now, we guess that the packet will not be bigger than the
 * attached ipsec device's mtu or 16260, whichever is biggest.
 * This may be wrong, since the sender's mtu may be bigger yet.
 * XXX This must be dealt with later XXX
 */
static int ipsec_ocf_ipcomp_copy_expand(struct ipsec_rcv_state *irs)
{
	struct sk_buff *nskb;
	unsigned grow_to, grow_by;
	ptrdiff_t ptr_delta;

	if (!irs->skb)
		return IPSEC_RCV_IPCOMPFAILED;

	if (irs->skb->dev) {
		grow_to = irs->skb->dev->mtu <
			  16260 ? 16260 : irs->skb->dev->mtu;
	} else {
		int tot_len;
		if (lsw_ip_hdr_version(irs) == 6)
			tot_len = ntohs(lsw_ip6_hdr(irs)->payload_len) +
				  sizeof(struct ipv6hdr);
		else
			tot_len = ntohs(lsw_ip4_hdr(irs)->tot_len);
		grow_to = 65520 - tot_len;
	}
	grow_by = grow_to - irs->skb->len;
	grow_by -= skb_headroom(irs->skb);
	grow_by -= skb_tailroom(irs->skb);

	/* it's big enough */
	if (!grow_by)
		return IPSEC_RCV_OK;

	nskb = skb_copy_expand(irs->skb, skb_headroom(irs->skb),
			       skb_tailroom(irs->skb) + grow_by, GFP_ATOMIC);
	if (!nskb)
		return IPSEC_RCV_ERRMEMALLOC;

	memcpy(nskb->head, irs->skb->head, skb_headroom(irs->skb));

	skb_set_network_header(nskb,
			       ipsec_skb_offset(irs->skb,
						skb_network_header(irs->skb)));
	skb_set_transport_header(nskb,
				 ipsec_skb_offset(irs->skb,
						  skb_transport_header(
							  irs->skb)));

	/* update all irs pointers */
	ptr_delta = nskb->data - irs->skb->data;
	irs->authenticator = (void*)((char*)irs->authenticator + ptr_delta);
	irs->iph           = (void*)((char*)irs->iph           + ptr_delta);

	/* flip in the large one */
	irs->pre_ipcomp_skb = irs->skb;
	irs->skb = nskb;

	/* move the tail up to the end to let OCF know how big the buffer is */
	if (grow_by > (irs->skb->end - irs->skb->tail))
		grow_by = irs->skb->end - irs->skb->tail;
	skb_put(irs->skb, grow_by);

	return IPSEC_RCV_OK;
}
示例#2
0
enum ipsec_xmit_value ipsec_xmit_ipip_setup(struct ipsec_xmit_state *ixs)
{
	lsw_ip4_hdr(ixs)->version  = 4;

	switch (sysctl_ipsec_tos) {
	case 0:
		lsw_ip4_hdr(ixs)->tos = ip_hdr(ixs->skb)->tos;
		break;
	case 1:
		lsw_ip4_hdr(ixs)->tos = 0;
		break;
	default:
		break;
	}
	lsw_ip4_hdr(ixs)->ttl      = SYSCTL_IPSEC_DEFAULT_TTL;
	lsw_ip4_hdr(ixs)->frag_off = 0;
	lsw_ip4_hdr(ixs)->saddr    =
		((struct sockaddr_in*)(ixs->ipsp->ips_addr_s))->sin_addr.s_addr;
	lsw_ip4_hdr(ixs)->daddr    =
		((struct sockaddr_in*)(ixs->ipsp->ips_addr_d))->sin_addr.s_addr;
	lsw_ip4_hdr(ixs)->protocol = IPPROTO_IPIP;
	lsw_ip4_hdr(ixs)->ihl      = sizeof(struct iphdr) >> 2;

#ifdef NET_21
	printk("THIS CODE IS NEVER CALLED\n");
	skb_set_transport_header(ixs->skb,
				 ipsec_skb_offset(ixs->skb, ip_hdr(ixs->skb)));
#endif  /* NET_21 */
	return IPSEC_XMIT_OK;
}
示例#3
0
static int ipsec_ocf_xmit_cb(struct cryptop *crp)
{
	struct ipsec_xmit_state *ixs =
		(struct ipsec_xmit_state *)crp->crp_opaque;
	struct iphdr *newiph;
	struct ipcomphdr *cmph;
	unsigned orig_len, comp_len;
	struct cryptodesc *crdc = NULL;

	KLIPS_PRINT(debug_tunnel & DB_TN_XMIT,
		    "klips_debug:ipsec_ocf_xmit_cb\n");

	if (ixs == NULL) {
		KLIPS_PRINT(debug_tunnel & DB_TN_XMIT, "klips_debug:ipsec_ocf_xmit_cb: "
			    "NULL ixs in callback\n");
		return 0;
	}

	/*
	 * we must update the state before returning to the state machine.
	 * if we have an error,  terminate the processing by moving to the DONE
	 * state
	 */

	ixs->state = IPSEC_XSM_DONE; /* assume bad xmit */
	if (crp->crp_etype) {
		ptrdiff_t ptr_delta;

		if (crp->crp_etype == EAGAIN) {
			/* Session has been migrated. Store the new session id and retry */
			KLIPS_PRINT(debug_tunnel & DB_TN_XMIT,
				    "klips_debug:ipsec_ocf_xmit_cb: crypto session migrated\n");
			ixs->ipsp->ocf_cryptoid = crp->crp_sid;
			/* resubmit request */
			if (crypto_dispatch(crp) == 0)
				return 0;
			/* resubmit failed */
		}

		KLIPS_PRINT(debug_tunnel & DB_TN_XMIT, "klips_debug:ipsec_ocf_xmit_cb: "
			    "error in processing 0x%x\n",
			    crp->crp_etype);

		switch (ixs->ipsp->ips_said.proto) {
		case IPPROTO_COMP:
			/*
			 * It's ok for compression to fail... we made a clone
			 * of the packet, so we just revert it now...
			 */
			if (!ixs->pre_ipcomp_skb) {
				KLIPS_PRINT(debug_tunnel & DB_TN_XMIT,
					    "klips_debug:ipsec_ocf_xmit_cb: "
					    "IPcomp on %u bytes failed, "
					    "but we have no clone!\n",
					    (unsigned int)
					    (lsw_ip_hdr_version(ixs) == 6 ?
					     (ntohs(lsw_ip6_hdr(ixs)->
						    payload_len) +
					      sizeof(struct ipv6hdr))
					     :
					     ntohs(lsw_ip4_hdr(
							   ixs)->tot_len)) -
					    ixs->iphlen);
				/* this is a fail. */
				break;
			}

			KLIPS_PRINT(debug_tunnel & DB_TN_XMIT,
				    "klips_debug:ipsec_ocf_xmit_cb: "
				    "IPcomp on %u bytes failed, "
				    "using backup clone.\n",
				    (unsigned int)
				    (lsw_ip_hdr_version(ixs) == 6 ?
				     (ntohs(lsw_ip6_hdr(ixs)->payload_len) +
				      sizeof(struct ipv6hdr)) :
				     ntohs(lsw_ip4_hdr(ixs)->tot_len)) -
				    ixs->iphlen);

			ptr_delta = ixs->pre_ipcomp_skb->data - ixs->skb->data;
			ixs->iph           =
				(void*)((char*)ixs->iph + ptr_delta);

			/*
			 * can not free it here, because we are under
			 * IRQ, potentially, so queue it for later
			 */
			kfree_skb(ixs->skb);
			ixs->skb = ixs->pre_ipcomp_skb;
			ixs->pre_ipcomp_skb = NULL;

			skb_set_network_header(ixs->skb,
					       ipsec_skb_offset(ixs->skb,
								((
									 void
									 *)
								 skb_network_header(
									 ixs->
									 skb))
								+
								ptr_delta));
			skb_set_transport_header(ixs->skb,
						 ipsec_skb_offset(ixs->skb,
								  ((
									   void
									   *)
								   skb_transport_header(
									   ixs
									   ->
									   skb))
								  +
								  ptr_delta));
			KLIPS_IP_PRINT(debug_tunnel & DB_TN_XMIT, ixs->iph);

			/* this means we don't compress */
			ixs->state = IPSEC_XSM_CONT;
			break;
		}
		goto bail;
	}

	switch (ixs->ipsp->ips_said.proto) {
	case IPPROTO_ESP:
		/* ESP, nothing to do */
		break;

	case IPPROTO_AH:
		/* AH post processing, put back fields we had to zero */
		if (lsw_ip_hdr_version(ixs) == 4) {
			lsw_ip4_hdr(ixs)->ttl      = ixs->ttl;
			lsw_ip4_hdr(ixs)->check    = ixs->check;
			lsw_ip4_hdr(ixs)->frag_off = ixs->frag_off;
			lsw_ip4_hdr(ixs)->tos      = ixs->tos;
		}
		break;

	case IPPROTO_COMP:
		/* IPcomp fill in the header */
		crdc = crp->crp_desc;

		KLIPS_PRINT(debug_tunnel & DB_TN_XMIT,
			    "klips_debug:ipsec_ocf_xmit_cb: "
			    "after <%s%s%s>, SA:%s:\n",
			    IPS_XFORM_NAME(ixs->ipsp),
			    ixs->sa_len ? ixs->sa_txt : " (error)");
		KLIPS_IP_PRINT(debug_tunnel & DB_TN_XMIT, ixs->iph);

		orig_len = (lsw_ip_hdr_version(ixs) == 6 ?
			    (ntohs(lsw_ip6_hdr(ixs)->payload_len) +
			     sizeof(struct ipv6hdr)) :
			    ntohs(lsw_ip4_hdr(ixs)->tot_len)) -
			   ixs->iphlen;
		comp_len = crp->crp_olen;

		if (sysctl_ipsec_debug_ipcomp && sysctl_ipsec_debug_verbose) {
			ipsec_dmp_block("compress after",
					((unsigned char*)ixs->iph) + ixs->iphlen,
					comp_len);
		}

		newiph =
			(struct iphdr *)((char*)ixs->iph -
					 sizeof(struct ipcomphdr));
		cmph = (struct ipcomphdr *)((char*)newiph + ixs->iphlen);

		/* move the ip header to make room for the new ipcomp header */
		memmove(((unsigned char *) ixs->skb->data) -
			sizeof(struct ipcomphdr),
			ixs->skb->data,
			(((unsigned char *) ixs->iph) + ixs->iphlen) -
			((unsigned char *) ixs->skb->data));
		/* DAVIDM check for head room */
		skb_push(ixs->skb, sizeof(struct ipcomphdr));

		ixs->iph = newiph;
		skb_set_network_header(ixs->skb,
				       ipsec_skb_offset(ixs->skb, newiph));
		skb_set_transport_header(ixs->skb,
					 ipsec_skb_offset(ixs->skb,
							  newiph) +
					 ixs->iphlen);

		/* now we can fill in the ipcomp header */
		cmph->ipcomp_nh = ixs->next_header;
		cmph->ipcomp_flags = 0;
		cmph->ipcomp_cpi =
			htons((__u16)(ntohl(ixs->ipsp->ips_said.spi) &
				      0x0000ffff));

		/* update the ip header to reflect the compression */
		if (lsw_ip_hdr_version(ixs) == 6) {
			lsw_ip6_hdr(ixs)->nexthdr     = IPPROTO_COMP;
			lsw_ip6_hdr(ixs)->payload_len = htons(ixs->iphlen +
							      sizeof(struct
								     ipcomphdr) + comp_len -
							      sizeof(struct
								     ipv6hdr));
		} else {
			lsw_ip4_hdr(ixs)->protocol    = IPPROTO_COMP;
			lsw_ip4_hdr(ixs)->tot_len     = htons(ixs->iphlen +
							      sizeof(struct
								     ipcomphdr) +
							      comp_len);
			lsw_ip4_hdr(ixs)->check       = 0;
			lsw_ip4_hdr(ixs)->check       =
				ip_fast_csum((char *) ixs->iph, lsw_ip4_hdr(
						     ixs)->ihl);
		}

		/* Update skb length/tail by "unputting" the shrinkage */
		safe_skb_put(ixs->skb, comp_len - orig_len);

		ixs->ipsp->ips_comp_adapt_skip = 0;
		ixs->ipsp->ips_comp_adapt_tries = 0;

		/* release the backup copy */
		if (ixs->pre_ipcomp_skb) {
			kfree_skb(ixs->pre_ipcomp_skb);
			ixs->pre_ipcomp_skb = NULL;
		}

		KLIPS_PRINT(debug_tunnel & DB_TN_XMIT,
			    "klips_debug:ipsec_ocf_xmit_cb: "
			    "after <%s%s%s>, SA:%s:\n",
			    IPS_XFORM_NAME(ixs->ipsp),
			    ixs->sa_len ? ixs->sa_txt : " (error)");
		KLIPS_IP_PRINT(debug_tunnel & DB_TN_XMIT, ixs->iph);
		break;
	}

	/* all good */
	ixs->state = IPSEC_XSM_CONT;

bail:
	crypto_freereq(crp);
	crp = NULL;
	ipsec_ocf_queue_task(ipsec_xsm, ixs);
	return 0;
}
示例#4
0
static int ipsec_ocf_rcv_cb(struct cryptop *crp)
{
	struct ipsec_rcv_state *irs =
		(struct ipsec_rcv_state *)crp->crp_opaque;
	struct iphdr *newiph;
	unsigned orig_len, decomp_len;
	struct cryptodesc *crdc = NULL;

	KLIPS_PRINT(debug_rcv, "klips_debug:ipsec_ocf_rcv_cb\n");

	if (irs == NULL) {
		KLIPS_PRINT(debug_rcv, "klips_debug:ipsec_ocf_rcv_cb: "
			    "NULL irs in callback\n");
		return 0;
	}

	/*
	 * we must update the state before returning to the state machine.
	 * if we have an error,  terminate the processing by moving to the DONE
	 * state
	 */

	irs->state = IPSEC_RSM_DONE; /* assume it went badly */

	if (crp->crp_etype) {
		ptrdiff_t ptr_delta;

		if (crp->crp_etype == EAGAIN) {
			/* Session has been migrated. Store the new session id and retry */
			KLIPS_PRINT(debug_rcv,
				    "klips_debug:ipsec_ocf_rcv_cb: crypto session migrated\n");
			irs->ipsp->ocf_cryptoid = crp->crp_sid;
			/* resubmit request */
			if (crypto_dispatch(crp) == 0)
				return 0;
			/* resubmit failed */
		}

		KLIPS_PRINT(debug_rcv, "klips_debug:ipsec_ocf_rcv_cb: "
			    "error in processing 0x%x\n", crp->crp_etype);

		switch (irs->ipsp->ips_said.proto) {
		case IPPROTO_COMP:
			/*
			 * we restore the previous skb on error and pretend nothing
			 * happened, just no compression
			 */
			ptr_delta = irs->pre_ipcomp_skb->data - irs->skb->data;
			irs->authenticator =
				(void*)((char*)irs->authenticator + ptr_delta);
			irs->iph           =
				(void*)((char*)irs->iph           + ptr_delta);

			kfree_skb(irs->skb);
			irs->skb = irs->pre_ipcomp_skb;
			irs->pre_ipcomp_skb = NULL;
			break;
		}

		goto bail;
	}

	switch (irs->ipsp->ips_said.proto) {
	case IPPROTO_ESP:
		/* ESP, process it */
		if (ipsec_rcv_esp_post_decrypt(irs) == IPSEC_RCV_OK) {
			/* this one came up good, set next state */
			irs->state = IPSEC_RSM_DECAP_CONT;
		}
		break;

	case IPPROTO_AH:
		/* AH post processing, put back fields we had to zero */
		if (lsw_ip_hdr_version(irs) == 4) {
			lsw_ip4_hdr(irs)->ttl      = irs->ttl;
			lsw_ip4_hdr(irs)->check    = irs->check;
			lsw_ip4_hdr(irs)->frag_off = irs->frag_off;
			lsw_ip4_hdr(irs)->tos      = irs->tos;
		}
		irs->state         = IPSEC_RSM_AUTH_CHK;

		/* pull up the IP header again after processing */
		skb_pull(irs->skb,
			 ((unsigned char *)irs->protostuff.ahstuff.ahp) -
			 ((unsigned char *)irs->iph));

		break;

	case IPPROTO_COMP:
		crdc = crp->crp_desc;

		KLIPS_PRINT(debug_rcv, "comp before adjustments:\n");
		KLIPS_IP_PRINT(debug_rcv & DB_TN_XMIT, irs->iph);

		orig_len = irs->skb->len - sizeof(struct ipcomphdr);
		decomp_len = crp->crp_olen;

		newiph =
			(struct iphdr*)((char*)irs->iph +
					sizeof(struct ipcomphdr));

		KLIPS_PRINT(debug_rcv,
			    "comp results: olen: %u, inject: %u (len=%d) iph->totlen=%u\n",
			    crp->crp_olen, crdc->crd_inject, decomp_len,
			    ntohs(newiph->tot_len));

		/*
		 * move the ip header to consume room previously taken by
		 * the ipcomp header
		 */
		skb_pull(irs->skb, sizeof(struct ipcomphdr));
		memmove(newiph, irs->iph, irs->iphlen);

		/* adjust the ipp pointer to point to the header we decoded */
		irs->iph = newiph;

		skb_set_network_header(irs->skb, ipsec_skb_offset(irs->skb,
								  ((unsigned
								    char *)
								   skb_network_header(
									   irs
									   ->
									   skb))
								  +
								  sizeof(struct
									 ipcomphdr)));
		skb_set_transport_header(irs->skb, ipsec_skb_offset(irs->skb,
								    ((unsigned
								      char *)
								     skb_transport_header(
									     irs
									     ->
									     skb))
								    +
								    sizeof(
									    struct
									    ipcomphdr)));

		if (lsw_ip_hdr_version(irs) == 6) {
			lsw_ip6_hdr(irs)->nexthdr  = irs->next_header;
		} else {
			lsw_ip4_hdr(irs)->protocol = irs->next_header;
			lsw_ip4_hdr(irs)->tot_len = htons(
				irs->iphlen + decomp_len);
			lsw_ip4_hdr(irs)->check = 0;
			lsw_ip4_hdr(irs)->check = ip_fast_csum(irs->iph, lsw_ip4_hdr(
								       irs)->ihl);
		}

		KLIPS_PRINT(debug_rcv, "comp after len adjustments:\n");
		KLIPS_IP_PRINT(debug_rcv & DB_TN_XMIT, irs->iph);

		/* Update skb length/tail by "putting" the growth */
		safe_skb_put(irs->skb, decomp_len - crp->crp_olen);

		/* set the new header in the skb */
		skb_set_network_header(irs->skb,
				       ipsec_skb_offset(irs->skb, irs->iph));
		KLIPS_IP_PRINT(debug_rcv & DB_RX_PKTRX, ip_hdr(irs->skb));

		/* relese the backup copy */
		if (irs->pre_ipcomp_skb) {
			kfree_skb(irs->pre_ipcomp_skb);
			irs->pre_ipcomp_skb = NULL;
		}

		/* IPcomp finished, continue processing */
		irs->state = IPSEC_RSM_DECAP_CONT;
		break;
	}

bail:
	crypto_freereq(crp);
	crp = NULL;
	ipsec_ocf_queue_task(ipsec_rsm, irs);
	return 0;
}