/* * 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; }
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; }
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; }
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; }