enum ipsec_rcv_value ipsec_rcv_ipcomp_decomp(struct ipsec_rcv_state *irs) { unsigned int flags = 0; struct ipsec_sa *ipsp = irs->ipsp; struct sk_buff *skb; skb = irs->skb; ipsec_xmit_dmp("ipcomp", skb_transport_header(skb), skb->len); if (ipsp == NULL) return IPSEC_RCV_SAIDNOTFOUND; if (sysctl_ipsec_inbound_policy_check && ((((ntohl(ipsp->ips_said.spi) & 0x0000ffff) != (ntohl(irs->said.spi) & 0x0000ffff)) && (ipsp->ips_encalg != ntohl(irs->said.spi)) /* this is a workaround for peer non-compliance with rfc2393 */ ))) { char sa2[SATOT_BUF]; size_t sa_len2 = 0; sa_len2 = KLIPS_SATOT(debug_rcv, &ipsp->ips_said, 0, sa2, sizeof(sa2)); KLIPS_PRINT(debug_rcv, "klips_debug:ipsec_rcv_ipcomp_decomp: " "Incoming packet with SA(IPCA):%s does not match policy SA(IPCA):%s cpi=%04x cpi->spi=%08x spi=%08x, spi->cpi=%04x for SA grouping, dropped.\n", irs->sa_len ? irs->sa : " (error)", sa_len2 ? sa2 : " (error)", ntohs(irs->protostuff.ipcompstuff.compp->ipcomp_cpi), (__u32)ntohl(irs->said.spi), (__u32)ntohl((ipsp->ips_said.spi)), (__u16)(ntohl(ipsp->ips_said.spi) & 0x0000ffff)); if (irs->stats) irs->stats->rx_dropped++; return IPSEC_RCV_SAIDNOTFOUND; } if (lsw_ip_hdr_version(irs) == 6) ipsp->ips_comp_ratio_cbytes += ntohs(lsw_ip6_hdr(irs)->payload_len) + sizeof(struct ipv6hdr); else ipsp->ips_comp_ratio_cbytes += ntohs(lsw_ip4_hdr(irs)->tot_len); irs->next_header = irs->protostuff.ipcompstuff.compp->ipcomp_nh; #ifdef CONFIG_KLIPS_OCF if (irs->ipsp->ocf_in_use) return ipsec_ocf_rcv(irs); #endif skb = skb_decompress(skb, ipsp, &flags); if (!skb || flags) { KLIPS_PRINT(debug_rcv, "klips_debug:ipsec_rcv_ipcomp_decomp: " "skb_decompress() returned error flags=%x, dropped.\n", flags); if (irs->stats) { if (flags) irs->stats->rx_errors++; else irs->stats->rx_dropped++; } return IPSEC_RCV_IPCOMPFAILED; } /* make sure we update the pointer */ irs->skb = skb; irs->iph = (void *) ip_hdr(skb); if (lsw_ip_hdr_version(irs) == 6) ipsp->ips_comp_ratio_dbytes += ntohs(lsw_ip6_hdr(irs)->payload_len) + sizeof(struct ipv6hdr); else ipsp->ips_comp_ratio_dbytes += ntohs(lsw_ip4_hdr(irs)->tot_len); KLIPS_PRINT(debug_rcv, "klips_debug:ipsec_rcv_ipcomp_decomp: " "packet decompressed SA(IPCA):%s cpi->spi=%08x spi=%08x, spi->cpi=%04x, nh=%d.\n", irs->sa_len ? irs->sa : " (error)", (__u32)ntohl(irs->said.spi), ipsp != NULL ? (__u32)ntohl((ipsp->ips_said.spi)) : 0, ipsp != NULL ? (__u16)(ntohl(ipsp->ips_said.spi) & 0x0000ffff) : 0, irs->next_header); KLIPS_IP_PRINT(debug_rcv & DB_RX_PKTRX, irs->iph); return IPSEC_RCV_OK; }
/* Paul: This seems to be unused dead code */ enum ipsec_xmit_value ipsec_mast_send(struct ipsec_xmit_state*ixs) { /* new route/dst cache code from James Morris */ ixs->skb->dev = ixs->physdev; /*skb_orphan(ixs->skb);*/ if((ixs->error = ip_route_output(&ixs->route, ixs->skb->nh.iph->daddr, ixs->pass ? 0 : ixs->skb->nh.iph->saddr, RT_TOS(ixs->skb->nh.iph->tos), ixs->physdev->ifindex /* rgb: should this be 0? */))) { ixs->stats->tx_errors++; KLIPS_PRINT(debug_mast & DB_MAST_XMIT, "klips_debug:ipsec_mast_send: " "ip_route_output failed with error code %d, dropped\n", ixs->error); return IPSEC_XMIT_ROUTEERR; } if(ixs->dev == ixs->route->u.dst.dev) { ip_rt_put(ixs->route); /* This is recursion, drop it. */ ixs->stats->tx_errors++; KLIPS_PRINT(debug_mast & DB_MAST_XMIT, "klips_debug:ipsec_mast_send: " "suspect recursion, dev=rt->u.dst.dev=%s, dropped\n", ixs->dev->name); return IPSEC_XMIT_RECURSDETECT; } dst_release(skb_dst(ixs->skb)); skb_dst_set(ixs->skb, &ixs->route->u.dst); ixs->stats->tx_bytes += ixs->skb->len; if(ixs->skb->len < ixs->skb->nh.raw - ixs->skb->data) { ixs->stats->tx_errors++; printk(KERN_WARNING "klips_error:ipsec_mast_send: " "tried to __skb_pull nh-data=%ld, %d available. This should never happen, please report.\n", (unsigned long)(ixs->skb->nh.raw - ixs->skb->data), ixs->skb->len); return IPSEC_XMIT_PUSHPULLERR; } __skb_pull(ixs->skb, ixs->skb->nh.raw - ixs->skb->data); ipsec_nf_reset(ixs->skb); KLIPS_PRINT(debug_mast & DB_MAST_XMIT, "klips_debug:ipsec_mast_send: " "...done, calling ip_send() on device:%s\n", ixs->skb->dev ? ixs->skb->dev->name : "NULL"); KLIPS_IP_PRINT(debug_mast & DB_MAST_XMIT, ixs->skb->nh.iph); { int err; err = NF_HOOK(PF_INET, OSW_NF_INET_LOCAL_OUT, ixs->skb, NULL, ixs->route->u.dst.dev, ipsec_mast_xmit2); if(err != NET_XMIT_SUCCESS && err != NET_XMIT_CN) { if(net_ratelimit()) printk(KERN_ERR "klips_error:ipsec_mast_send: " "ip_send() failed, err=%d\n", -err); ixs->stats->tx_errors++; ixs->stats->tx_aborted_errors++; ixs->skb = NULL; return IPSEC_XMIT_IPSENDFAILURE; } } ixs->stats->tx_packets++; ixs->skb = NULL; return IPSEC_XMIT_OK; }
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; }
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; }
enum ipsec_rcv_value ipsec_rcv_ipcomp_decomp(struct ipsec_rcv_state *irs) { unsigned int flags = 0; struct ipsec_sa *ipsp = irs->ipsp; struct sk_buff *skb; skb=irs->skb; ipsec_xmit_dmp("ipcomp", skb->data, skb->len); if(ipsp == NULL) { return IPSEC_RCV_SAIDNOTFOUND; } #if 0 /* we want to check that this wasn't the first SA on the list, because * we don't support bare IPCOMP, for unexplained reasons. MCR */ if (ipsp->ips_onext != NULL) { KLIPS_PRINT(debug_rcv, "klips_debug:ipsec_rcv: " "Incoming packet with outer IPCOMP header SA:%s: not yet supported by KLIPS, dropped\n", irs->sa_len ? irs->sa : " (error)"); if(irs->stats) { irs->stats->rx_dropped++; } return IPSEC_RCV_IPCOMPALONE; } #endif if(sysctl_ipsec_inbound_policy_check && ((((ntohl(ipsp->ips_said.spi) & 0x0000ffff) != ntohl(irs->said.spi)) && (ipsp->ips_encalg != ntohl(irs->said.spi)) /* this is a workaround for peer non-compliance with rfc2393 */ ))) { char sa2[SATOT_BUF]; size_t sa_len2 = 0; sa_len2 = satot(&ipsp->ips_said, 0, sa2, sizeof(sa2)); KLIPS_PRINT(debug_rcv, "klips_debug:ipsec_rcv: " "Incoming packet with SA(IPCA):%s does not match policy SA(IPCA):%s cpi=%04x cpi->spi=%08x spi=%08x, spi->cpi=%04x for SA grouping, dropped.\n", irs->sa_len ? irs->sa : " (error)", ipsp != NULL ? (sa_len2 ? sa2 : " (error)") : "NULL", ntohs(irs->protostuff.ipcompstuff.compp->ipcomp_cpi), (__u32)ntohl(irs->said.spi), ipsp != NULL ? (__u32)ntohl((ipsp->ips_said.spi)) : 0, ipsp != NULL ? (__u16)(ntohl(ipsp->ips_said.spi) & 0x0000ffff) : 0); if(irs->stats) { irs->stats->rx_dropped++; } return IPSEC_RCV_SAIDNOTFOUND; } ipsp->ips_comp_ratio_cbytes += ntohs(irs->ipp->tot_len); irs->next_header = irs->protostuff.ipcompstuff.compp->ipcomp_nh; skb = skb_decompress(skb, ipsp, &flags); if (!skb || flags) { spin_unlock(&tdb_lock); KLIPS_PRINT(debug_rcv, "klips_debug:ipsec_rcv: " "skb_decompress() returned error flags=%x, dropped.\n", flags); if (irs->stats) { if (flags) irs->stats->rx_errors++; else irs->stats->rx_dropped++; } return IPSEC_RCV_IPCOMPFAILED; } /* make sure we update the pointer */ irs->skb = skb; #ifdef NET_21 irs->ipp = skb->nh.iph; #else /* NET_21 */ irs->ipp = skb->ip_hdr; #endif /* NET_21 */ ipsp->ips_comp_ratio_dbytes += ntohs(irs->ipp->tot_len); KLIPS_PRINT(debug_rcv, "klips_debug:ipsec_rcv: " "packet decompressed SA(IPCA):%s cpi->spi=%08x spi=%08x, spi->cpi=%04x, nh=%d.\n", irs->sa_len ? irs->sa : " (error)", (__u32)ntohl(irs->said.spi), ipsp != NULL ? (__u32)ntohl((ipsp->ips_said.spi)) : 0, ipsp != NULL ? (__u16)(ntohl(ipsp->ips_said.spi) & 0x0000ffff) : 0, irs->next_header); KLIPS_IP_PRINT(debug_rcv & DB_RX_PKTRX, irs->ipp); return IPSEC_RCV_OK; }