/* * receive code * Copyright (C) 1996, 1997 John Ioannidis. * Copyright (C) 1998-2003 Richard Guy Briggs. * Copyright (C) 2004-2007 Michael Richardson * Copyright (C) 2007-2008 Paul Wouters * Copyright (C) 2010 David McCullough * Copyright (C) 2011 Bart Trojanowski * Copyright (C) 2012 Paul Wouters * Copyright (C) 2011-2012 David McCullough * * OCF/receive state machine written by * David McCullough * Copyright (C) 2004-2005 Intel Corporation. All Rights Reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. See . * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. */ #include #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 38) && \ !defined(AUTOCONF_INCLUDED) #include #endif #define __NO_VERSION__ #include #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0) && LINUX_VERSION_CODE >= \ KERNEL_VERSION(2, 4, 26) # include #endif #include /* printk() */ #include "libreswan/ipsec_param.h" #include /* kmalloc() */ #include /* error codes */ #include /* size_t */ #include /* mark_bh */ #include /* struct device, and other headers */ #include /* eth_type_trans */ #include /* struct iphdr */ #include #include #ifdef NET_26 # include #endif #include #include #include /* *lock* */ #ifdef NEED_SPINLOCK_TYPES # include #endif #include #include "libreswan/ipsec_kversion.h" #include "libreswan/radij.h" #include "libreswan/ipsec_encap.h" #include "libreswan/ipsec_sa.h" #include "libreswan/ipsec_radij.h" #include "libreswan/ipsec_xform.h" #include "libreswan/ipsec_tunnel.h" #include "libreswan/ipsec_mast.h" #include "libreswan/ipsec_rcv.h" #include "libreswan/ipsec_auth.h" #include "libreswan/ipsec_esp.h" #ifdef CONFIG_KLIPS_AH #include "libreswan/ipsec_ah.h" #endif /* CONFIG_KLIPS_AH */ #ifdef CONFIG_KLIPS_IPCOMP #include "libreswan/ipsec_ipcomp.h" #endif /* CONFIG_KLIPS_COMP */ #include #include #include "libreswan/ipsec_proto.h" #include "libreswan/ipsec_alg.h" #ifdef CONFIG_KLIPS_OCF #include "ipsec_ocf.h" #endif #ifdef CONFIG_IPSEC_NAT_TRAVERSAL #include #endif /* This is a private use protocol, and AT&T should be ashamed. They should have * used protocol # 59, which is "no next header" instead of 0xFE. */ #ifndef IPPROTO_ATT_HEARTBEAT #define IPPROTO_ATT_HEARTBEAT 0xFE #endif /* management of buffers */ static struct ipsec_rcv_state *ipsec_rcv_state_new(void); static void ipsec_rcv_state_delete(struct ipsec_rcv_state *irs); /* * Check-replay-window routine, adapted from the original * by J. Hughes, from draft-ietf-ipsec-esp-des-md5-03.txt * * This is a routine that implements a 64 packet window. This is intend- * ed on being an implementation sample. */ int ipsec_replaywin_override = -1; module_param(ipsec_replaywin_override, int, 0644); MODULE_PARM_DESC(ipsec_replaywin_override, "override replay window (-1=no change, 0=disable, N=override value"); DEBUG_NO_STATIC int ipsec_checkreplaywindow(struct ipsec_sa*ipsp, __u32 seq) { __u32 diff; if (ipsec_replaywin_override >= 0) ipsp->ips_replaywin = ipsec_replaywin_override; if (ipsp->ips_replaywin == 0) /* replay shut off */ return 1; if (seq == 0) return 0; /* first == 0 or wrapped */ /* new larger sequence number */ if (seq > ipsp->ips_replaywin_lastseq) return 1; /* larger is good */ diff = ipsp->ips_replaywin_lastseq - seq; /* too old or wrapped */ /* if wrapped, kill off SA? */ if (diff >= ipsp->ips_replaywin) return 0; /* this packet already seen */ if (ipsp->ips_replaywin_bitmap & (1 << diff)) return 0; return 1; /* out of order but good */ } DEBUG_NO_STATIC int ipsec_updatereplaywindow(struct ipsec_sa*ipsp, __u32 seq) { __u32 diff; if (ipsec_replaywin_override >= 0) ipsp->ips_replaywin = ipsec_replaywin_override; if (ipsp->ips_replaywin == 0) /* replay shut off */ return 1; if (seq == 0) return 0; /* first == 0 or wrapped */ /* new larger sequence number */ if (seq > ipsp->ips_replaywin_lastseq) { diff = seq - ipsp->ips_replaywin_lastseq; /* In win, set bit for this pkt */ if (diff < ipsp->ips_replaywin) ipsp->ips_replaywin_bitmap = (ipsp->ips_replaywin_bitmap << diff) | 1; else /* This packet has way larger seq num */ ipsp->ips_replaywin_bitmap = 1; if (seq - ipsp->ips_replaywin_lastseq - 1 > ipsp->ips_replaywin_maxdiff) { ipsp->ips_replaywin_maxdiff = seq - ipsp-> ips_replaywin_lastseq - 1; } ipsp->ips_replaywin_lastseq = seq; return 1; /* larger is good */ } diff = ipsp->ips_replaywin_lastseq - seq; /* too old or wrapped */ /* if wrapped, kill off SA? */ if (diff >= ipsp->ips_replaywin) { /* if(seq < 0.25*max && ipsp->ips_replaywin_lastseq > 0.75*max) { ipsec_sa_delchain(ipsp); } */ return 0; } /* this packet already seen */ if (ipsp->ips_replaywin_bitmap & (1 << diff)) return 0; ipsp->ips_replaywin_bitmap |= (1 << diff); /* mark as seen */ return 1; /* out of order but good */ } #ifdef CONFIG_KLIPS_AUTH_HMAC_MD5 struct auth_alg ipsec_rcv_md5[] = { { osMD5Init, osMD5Update, osMD5Final, AHMD596_ALEN } }; #endif /* CONFIG_KLIPS_AUTH_HMAC_MD5 */ #ifdef CONFIG_KLIPS_AUTH_HMAC_SHA1 struct auth_alg ipsec_rcv_sha1[] = { { SHA1Init, SHA1Update, SHA1Final, AHSHA196_ALEN } }; #endif /* CONFIG_KLIPS_AUTH_HMAC_MD5 */ static inline void ipsec_rcv_redodebug(struct ipsec_rcv_state *irs) { if (lsw_ip_hdr_version(irs) == 4) { inet_addrtot(AF_INET, &lsw_ip4_hdr(irs)->saddr, 0, irs->ipsaddr_txt, sizeof(irs->ipsaddr_txt)); inet_addrtot(AF_INET, &lsw_ip4_hdr(irs)->daddr, 0, irs->ipdaddr_txt, sizeof(irs->ipdaddr_txt)); } else if (lsw_ip_hdr_version(irs) == 6) { inet_addrtot(AF_INET6, &lsw_ip6_hdr(irs)->saddr, 0, irs->ipsaddr_txt, sizeof(irs->ipsaddr_txt)); inet_addrtot(AF_INET6, &lsw_ip6_hdr(irs)->daddr, 0, irs->ipdaddr_txt, sizeof(irs->ipdaddr_txt)); } else { snprintf(irs->ipsaddr_txt, sizeof(irs->ipsaddr_txt), ""); snprintf(irs->ipdaddr_txt, sizeof(irs->ipdaddr_txt), ""); } } DEBUG_NO_STATIC char *ipsec_rcv_err(int err) { static char tmp[32]; switch ((int) err) { case IPSEC_RCV_PENDING: return "IPSEC_RCV_PENDING"; case IPSEC_RCV_LASTPROTO: return "IPSEC_RCV_LASTPROTO"; case IPSEC_RCV_OK: return "IPSEC_RCV_OK"; case IPSEC_RCV_BADPROTO: return "IPSEC_RCV_BADPROTO"; case IPSEC_RCV_BADLEN: return "IPSEC_RCV_BADLEN"; case IPSEC_RCV_ESP_BADALG: return "IPSEC_RCV_ESP_BADALG"; case IPSEC_RCV_3DES_BADBLOCKING: return "IPSEC_RCV_3DES_BADBLOCKING"; case IPSEC_RCV_ESP_DECAPFAIL: return "IPSEC_RCV_ESP_DECAPFAIL"; case IPSEC_RCV_DECAPFAIL: return "IPSEC_RCV_DECAPFAIL"; case IPSEC_RCV_SAIDNOTFOUND: return "IPSEC_RCV_SAIDNOTFOUND"; case IPSEC_RCV_IPCOMPALONE: return "IPSEC_RCV_IPCOMPALONE"; case IPSEC_RCV_IPCOMPFAILED: return "IPSEC_RCV_IPCOMPFAILED"; case IPSEC_RCV_SAIDNOTLIVE: return "IPSEC_RCV_SAIDNOTLIVE"; case IPSEC_RCV_FAILEDINBOUND: return "IPSEC_RCV_FAILEDINBOUND"; case IPSEC_RCV_LIFETIMEFAILED: return "IPSEC_RCV_LIFETIMEFAILED"; case IPSEC_RCV_BADAUTH: return "IPSEC_RCV_BADAUTH"; case IPSEC_RCV_REPLAYFAILED: return "IPSEC_RCV_REPLAYFAILED"; case IPSEC_RCV_AUTHFAILED: return "IPSEC_RCV_AUTHFAILED"; case IPSEC_RCV_REPLAYROLLED: return "IPSEC_RCV_REPLAYROLLED"; case IPSEC_RCV_BAD_DECRYPT: return "IPSEC_RCV_BAD_DECRYPT"; case IPSEC_RCV_REALLYBAD: return "IPSEC_RCV_REALLYBAD"; case IPSEC_RCV_ERRMEMALLOC: return "IPSEC_RCV_ERRMEMALLOC"; } snprintf(tmp, sizeof(tmp), "%d", err); return tmp; } /* * here is a state machine to handle receiving ipsec packets. * basically we keep getting re-entered until processing is * complete. For the simple case we step down the states and finish. * each state is ideally some logical part of the process. If a state * can pend (ie., require async processing to complete), then this * should be the part of last action before it returns IPSEC_RCV_PENDING * * Any particular action may alter the next_state in irs to move us to * a state other than the preferred "next_state", but this is the * exception and is highlighted when it is done. * * prototypes for state action */ static enum ipsec_rcv_value ipsec_rcv_init(struct ipsec_rcv_state *irs); static enum ipsec_rcv_value ipsec_rcv_decap_init(struct ipsec_rcv_state *irs); static enum ipsec_rcv_value ipsec_rcv_decap_lookup(struct ipsec_rcv_state *irs); static enum ipsec_rcv_value ipsec_rcv_auth_init(struct ipsec_rcv_state *irs); static enum ipsec_rcv_value ipsec_rcv_auth_decap(struct ipsec_rcv_state *irs); static enum ipsec_rcv_value ipsec_rcv_auth_calc(struct ipsec_rcv_state *irs); static enum ipsec_rcv_value ipsec_rcv_auth_chk(struct ipsec_rcv_state *irs); static enum ipsec_rcv_value ipsec_rcv_decrypt(struct ipsec_rcv_state *irs); static enum ipsec_rcv_value ipsec_rcv_decap_cont(struct ipsec_rcv_state *irs); static enum ipsec_rcv_value ipsec_rcv_cleanup(struct ipsec_rcv_state *irs); static enum ipsec_rcv_value ipsec_rcv_complete(struct ipsec_rcv_state *irs); /* * the state table and each action */ static struct { enum ipsec_rcv_value (*action)(struct ipsec_rcv_state *irs); int next_state; } rcv_state_table[] = { [IPSEC_RSM_INIT] = { ipsec_rcv_init, IPSEC_RSM_DECAP_INIT }, [IPSEC_RSM_DECAP_INIT] = { ipsec_rcv_decap_init, IPSEC_RSM_DECAP_LOOKUP }, [IPSEC_RSM_DECAP_LOOKUP] = { ipsec_rcv_decap_lookup, IPSEC_RSM_AUTH_INIT }, [IPSEC_RSM_AUTH_INIT] = { ipsec_rcv_auth_init, IPSEC_RSM_AUTH_DECAP }, [IPSEC_RSM_AUTH_DECAP] = { ipsec_rcv_auth_decap, IPSEC_RSM_AUTH_CALC }, [IPSEC_RSM_AUTH_CALC] = { ipsec_rcv_auth_calc, IPSEC_RSM_AUTH_CHK }, [IPSEC_RSM_AUTH_CHK] = { ipsec_rcv_auth_chk, IPSEC_RSM_DECRYPT }, [IPSEC_RSM_DECRYPT] = { ipsec_rcv_decrypt, IPSEC_RSM_DECAP_CONT }, [IPSEC_RSM_DECAP_CONT] = { ipsec_rcv_decap_cont, IPSEC_RSM_CLEANUP }, [IPSEC_RSM_CLEANUP] = { ipsec_rcv_cleanup, IPSEC_RSM_COMPLETE }, [IPSEC_RSM_COMPLETE] = { ipsec_rcv_complete, IPSEC_RSM_DONE }, [IPSEC_RSM_DONE] = { NULL, IPSEC_RSM_DONE }, }; struct sk_buff *ipsec_rcv_unclone(struct sk_buff *skb, struct ipsec_rcv_state *irs) { /* if skb was cloned (most likely due to a packet sniffer such as tcpdump being momentarily attached to the interface), make a copy of our own to modify */ if (skb_cloned(skb)) { /* include any mac header while copying.. */ if (skb_headroom(skb) < irs->hard_header_len) { printk(KERN_WARNING "klips_error:ipsec_rcv_unclone: " "tried to skb_push hhlen=%d, %d available. This should never happen, please report(1).\n", irs->hard_header_len, skb_headroom(skb)); return NULL; } skb_push(skb, irs->hard_header_len); if (skb_cow(skb, skb_headroom(skb)) != 0) return NULL; if (skb->len < irs->hard_header_len) { printk(KERN_WARNING "klips_error:ipsec_rcv_unclone: " "tried to skb_pull hhlen=%d, %d available. This should never happen, please report(2).\n", irs->hard_header_len, skb->len); return NULL; } skb_pull(skb, irs->hard_header_len); } return skb; } #if !defined(NET_26) && defined(CONFIG_IPSEC_NAT_TRAVERSAL) /* * decapsulate a UDP encapsulated ESP packet */ struct sk_buff *ipsec_rcv_natt_decap(struct sk_buff *skb, struct ipsec_rcv_state *irs, int *udp_decap_ret_p) { *udp_decap_ret_p = 0; if (skb->sk && ip_hdr(skb) && ip_hdr(skb)->protocol == IPPROTO_UDP) { /** * Packet comes from udp_queue_rcv_skb so it is already defrag, * checksum verified, ... (ie safe to use) * * If the packet is not for us, return -1 and udp_queue_rcv_skb * will continue to handle it (do not kfree skb !!). */ #ifndef UDP_OPT_IN_SOCK struct udp_opt { __u32 esp_in_udp; }; struct udp_opt *tp = (struct udp_opt *)&(skb->sk->tp_pinfo.af_tcp); #else struct udp_opt *tp = &(skb->sk->tp_pinfo.af_udp); #endif struct iphdr *ip = ip_hdr(skb); struct udphdr *udp = (struct udphdr *)((__u32 *)ip + ip->ihl); __u8 *udpdata = (__u8 *)udp + sizeof(struct udphdr); __u32 *udpdata32 = (__u32 *)udpdata; irs->natt_sport = ntohs(udp->source); irs->natt_dport = ntohs(udp->dest); KLIPS_PRINT(debug_rcv, "klips_debug:ipsec_rcv_natt_decap: " "suspected ESPinUDP packet (NAT-Traversal) [%d].\n", tp->esp_in_udp); KLIPS_IP_PRINT(debug_rcv, ip); if (udpdata < skb->tail) { unsigned int len = skb->tail - udpdata; if ((len == 1) && (udpdata[0] == 0xff)) { KLIPS_PRINT(debug_rcv, "klips_debug:ipsec_rcv_natt_decap: " /* not IPv6 compliant message */ "NAT-keepalive from %pI4.\n", &ip->saddr); *udp_decap_ret_p = 0; return NULL; } else if (tp->esp_in_udp == ESPINUDP_WITH_NON_IKE && len > 2 * sizeof(__u32) + sizeof(struct esphdr) && udpdata32[0] == 0 && udpdata32[1] == 0) { /* ESP Packet with Non-IKE header */ KLIPS_PRINT(debug_rcv, "klips_debug:ipsec_rcv_natt_decap: " "ESPinUDP pkt with Non-IKE - spi=0x%x\n", ntohl(udpdata32[2])); irs->natt_type = ESPINUDP_WITH_NON_IKE; irs->natt_len = sizeof(struct udphdr) + (2 * sizeof(__u32)); } else if (tp->esp_in_udp == ESPINUDP_WITH_NON_ESP && len > sizeof(struct esphdr) && udpdata32[0] != 0) { /* ESP Packet without Non-ESP header */ irs->natt_type = ESPINUDP_WITH_NON_ESP; irs->natt_len = sizeof(struct udphdr); KLIPS_PRINT(debug_rcv, "klips_debug:ipsec_rcv_natt_decap: " "ESPinUDP pkt without Non-ESP - spi=0x%x\n", ntohl(udpdata32[0])); } else { KLIPS_PRINT(debug_rcv, "klips_debug:ipsec_rcv_natt_decap: " "IKE packet - not handled here\n"); *udp_decap_ret_p = -1; return NULL; } } else { return NULL; } } return skb; } #endif void ipsec_rcv_setoutif(struct ipsec_rcv_state *irs) { struct sk_buff *skb = irs->skb; if (skb != NULL && irs->ipsp->ips_out) { if (skb->dev != irs->ipsp->ips_out) { KLIPS_PRINT(debug_rcv, "changing originating interface from %s to %s\n", skb->dev->name, irs->ipsp->ips_out->name); } skb->dev = irs->ipsp->ips_out; #ifdef USE_NETDEV_OPS if (skb->dev && skb->dev->netdev_ops->ndo_get_stats) { struct net_device_stats *stats = skb->dev->netdev_ops->ndo_get_stats(skb->dev); irs->stats = stats; } #else if (skb->dev && skb->dev->get_stats) { struct net_device_stats *stats = skb->dev->get_stats( skb->dev); irs->stats = stats; } #endif } } static enum ipsec_rcv_value ipsec_rcv_decap_ipip(struct ipsec_rcv_state *irs) { struct ipsec_sa *ipsp; struct ipsec_sa* ipsnext = NULL; struct sk_buff *skb; struct iphdr *ipp = lsw_ip4_hdr(irs); #ifdef CONFIG_KLIPS_IPV6 struct ipv6hdr *ipp6 = lsw_ip6_hdr(irs); #endif /* CONFIG_KLIPS_IPV6 */ int failed_inbound_check = 0; enum ipsec_rcv_value result = IPSEC_RCV_DECAPFAIL; ipsp = irs->ipsp; skb = irs->skb; irs->sa_len = KLIPS_SATOT(debug_rcv, &irs->said, 0, irs->sa, sizeof(irs->sa)); if (ipp->version == 4 && ipp->protocol != IPPROTO_IPIP && ipp->protocol != IPPROTO_IPV6 && ipp->protocol != IPPROTO_ATT_HEARTBEAT) { /* AT&T heartbeats to SIG/GIG */ KLIPS_PRINT(debug_rcv, "klips_debug:ipsec_rcv_decap_ipip: " "SA:%s, Hey! How did this get through? Dropped.\n", irs->sa_len ? irs->sa : " (error)"); if (irs->stats) irs->stats->rx_dropped++; goto rcvleave; } if (sysctl_ipsec_inbound_policy_check) { struct sockaddr_in *psin = NULL; struct sockaddr_in6 *psin6 = NULL; if ((ipsnext = ipsp->ips_next)) { char sa2[SATOT_BUF]; size_t sa_len2; sa_len2 = KLIPS_SATOT(debug_rcv, &ipsnext->ips_said, 0, sa2, sizeof(sa2)); KLIPS_PRINT(debug_rcv, "klips_debug:ipsec_rcv_decap_ipip: " "unexpected SA:%s after IPIP SA:%s\n", sa_len2 ? sa2 : " (error)", irs->sa_len ? irs->sa : " (error)"); if (irs->stats) irs->stats->rx_dropped++; goto rcvleave; } if (((struct sockaddr_in6*)(ipsp->ips_addr_s))->sin6_family == AF_INET6) psin6 = (struct sockaddr_in6*)(ipsp->ips_addr_s); else if (((struct sockaddr_in*)(ipsp->ips_addr_s))->sin_family == AF_INET6) psin = (struct sockaddr_in*)(ipsp->ips_addr_s); if ((psin && ipp->saddr != psin->sin_addr.s_addr) #ifdef CONFIG_KLIPS_IPV6 || (psin6 && memcmp(&ipp6->saddr, &psin6->sin6_addr, sizeof(ipp6->saddr)) != 0) #endif ) { char saddr1[SUBNETTOA_BUF], saddr2[SUBNETTOA_BUF]; *saddr1 = 0; #ifdef CONFIG_KLIPS_IPV6 if (ipp->version == 6) inet_addrtot(AF_INET6, &ipp6->saddr, 0, saddr1, sizeof(saddr1)); else #endif inet_addrtot(AF_INET, &ipp->saddr, 0, saddr1, sizeof(saddr1)); *saddr2 = 0; sin_addrtot(&psin->sin_addr, 0, saddr2, sizeof(saddr2)); KLIPS_PRINT(debug_rcv, "klips_debug:ipsec_rcv_decap_ipip: " "SA:%s, src=%s(%s) does match expected %s.\n", irs->sa_len ? irs->sa : " (error)", irs->ipsaddr_txt, saddr1, saddr2); if (irs->stats) irs->stats->rx_dropped++; goto rcvleave; } } ipsec_rcv_setoutif(irs); /* added to support AT&T heartbeats to SIG/GIG */ if ((ipp->version == 4 || ipp->version == 6) && (irs->proto == IPPROTO_IPIP || irs->proto == IPPROTO_IPV6)) { /* * XXX this needs to be locked from when it was first looked * up in the decapsulation loop. Perhaps it is better to put * the IPIP decap inside the loop. */ ipsp->ips_life.ipl_bytes.ipl_count += skb->len; ipsp->ips_life.ipl_bytes.ipl_last = skb->len; if (!ipsp->ips_life.ipl_usetime.ipl_count) ipsp->ips_life.ipl_usetime.ipl_count = jiffies / HZ; ipsp->ips_life.ipl_usetime.ipl_last = jiffies / HZ; ipsp->ips_life.ipl_packets.ipl_count += 1; /* new L3 header is where L4 payload was */ skb_set_network_header(skb, ipsec_skb_offset(skb, skb_transport_header( skb))); /* now setup new L4 payload location */ ipp = (struct iphdr *)skb_network_header(skb); #ifdef CONFIG_KLIPS_IPV6 ipp6 = (struct ipv6hdr *) ipp; if (ipp->version == 6) { IPSEC_FRAG_OFF_DECL(frag_off) unsigned char nexthdr = ipp6->nexthdr; int nexthdroff; nexthdroff = ipsec_ipv6_skip_exthdr(skb, ipsec_skb_offset( skb, ipp6 + 1), &nexthdr, &frag_off); skb_set_transport_header(skb, ipsec_skb_offset(skb, (void*)skb-> data + nexthdroff)); irs->iphlen = nexthdroff - ipsec_skb_offset(skb, ipp6); } else #endif /* CONFIG_KLIPS_IPV6 */ { skb_set_transport_header(skb, ipsec_skb_offset(skb, skb_network_header( skb) + (ipp->ihl << 2))); irs->iphlen = ipp->ihl << 2; } if (skb->len < irs->iphlen) { printk(KERN_WARNING "klips_debug:ipsec_rcv_decap_ipip: " "tried to skb_pull iphlen=%d, %d available. This should never happen, please report(3).\n", irs->iphlen, (int)(skb->len)); goto rcvleave; } /* * we need to pull up by size of the new IP header, * + options, but also by any UDP/ESP encap there might * have been, and this deals with all cases. */ skb_pull(skb, (skb_transport_header(skb) - skb_network_header(skb))); /* remove any saved options that we might have, * since we have a new IP header. */ memset(&(IPCB(skb)->opt), 0, sizeof(struct ip_options)); #if 0 KLIPS_PRINT(debug_rcv, "csum: %d\n", ip_fast_csum((u8 *)ipp, ipp->ihl)); #endif /* re-do any strings for debugging */ irs->iph = (void*)ipp; if (debug_rcv) ipsec_rcv_redodebug(irs); if (lsw_ip_hdr_version(irs) == 6) skb->protocol = htons(ETH_P_IPV6); else skb->protocol = htons(ETH_P_IP); skb->ip_summed = 0; KLIPS_PRINT(debug_rcv & DB_RX_PKTRX, "klips_debug:ipsec_rcv_decap_ipip: " "IPIP tunnel stripped.\n"); KLIPS_IP_PRINT(debug_rcv & DB_RX_PKTRX, ipp); } if (sysctl_ipsec_inbound_policy_check) { /* Note: "xor" (^) logically replaces "not equal" (!=) and "bitwise or" (|) logically replaces "boolean or" (||). This is done to speed up execution by doing only bitwise operations and no branch operations */ if (ipp->version == 4) { if (ip_address_family(&ipsp->ips_flow_s) != AF_INET) failed_inbound_check = 1; else if (ip_address_family(&ipsp->ips_flow_d) != AF_INET) failed_inbound_check = 1; else if (((ipp->saddr & ipsp->ips_mask_s.u.v4.sin_addr.s_addr) ^ ipsp->ips_flow_s.u.v4.sin_addr.s_addr) | ((ipp->daddr & ipsp->ips_mask_d.u.v4.sin_addr.s_addr) ^ ipsp->ips_flow_d.u.v4.sin_addr.s_addr)) failed_inbound_check = 1; } #ifdef CONFIG_KLIPS_IPV6 else if (ipp->version == 6) { /* boilerplate macro for Bad Bits in address */ #define BB(end, i) ((ipp6->end##addr.s6_addr32[0] & \ ipsp->ips_mask_##end.u.v6.sin6_addr.s6_addr32[i]) ^ \ ipsp->ips_flow_##end.u.v6.sin6_addr.s6_addr32[i]) if (ip_address_family(&ipsp->ips_flow_s) != AF_INET6) failed_inbound_check = 1; else if (ip_address_family(&ipsp->ips_flow_d) != AF_INET6) failed_inbound_check = 1; else if (BB(s, 0) | BB(d, 0) | BB(s, 1) | BB(d, 1) | BB(s, 2) | BB(d, 2) | BB(s, 3) | BB(d, 3)) failed_inbound_check = 1; #undef BB } #endif /* CONFIG_KLIPS_IPV6 */ } if (failed_inbound_check) { char sflow_txt[SUBNETTOA_BUF], dflow_txt[SUBNETTOA_BUF]; #ifdef CONFIG_KLIPS_IPV6 if (ipsp->ips_flow_s.u.v4.sin_family == AF_INET6) { subnet6toa(&ipsp->ips_flow_s.u.v6.sin6_addr, &ipsp->ips_mask_s.u.v6.sin6_addr, 0, sflow_txt, sizeof(sflow_txt)); subnet6toa(&ipsp->ips_flow_d.u.v6.sin6_addr, &ipsp->ips_mask_d.u.v6.sin6_addr, 0, dflow_txt, sizeof(dflow_txt)); } else #endif { subnettoa(ipsp->ips_flow_s.u.v4.sin_addr, ipsp->ips_mask_s.u.v4.sin_addr, 0, sflow_txt, sizeof(sflow_txt)); subnettoa(ipsp->ips_flow_d.u.v4.sin_addr, ipsp->ips_mask_d.u.v4.sin_addr, 0, dflow_txt, sizeof(dflow_txt)); } KLIPS_PRINT(debug_rcv, "klips_debug:ipsec_rcv_decap_ipip: " "SA:%s, inner tunnel policy [%s -> %s] does not agree with pkt contents [%s -> %s].\n", irs->sa_len ? irs->sa : " (error)", sflow_txt, dflow_txt, irs->ipsaddr_txt, irs->ipdaddr_txt); if (irs->stats) irs->stats->rx_dropped++; goto rcvleave; } #if 0 if (sysctl_ipsec_inbound_policy_check) { char sflow_txt[SUBNETTOA_BUF], dflow_txt[SUBNETTOA_BUF]; subnettoa(ipsp->ips_flow_s.u.v4.sin_addr, ipsp->ips_mask_s.u.v4.sin_addr, 0, sflow_txt, sizeof(sflow_txt)); subnettoa(ipsp->ips_flow_d.u.v4.sin_addr, ipsp->ips_mask_d.u.v4.sin_addr, 0, dflow_txt, sizeof(dflow_txt)); KLIPS_PRINT(debug_mast, "klips_debug:ipsec_rcv_decap_ipip: " "SA:%s, inner tunnel policy [%s -> %s] agrees with pkt contents [%s -> %s].\n", irs->sa_len ? irs->sa : " (error)", sflow_txt, dflow_txt, irs->ipsaddr_txt, irs->ipdaddr_txt); } #endif #if defined(CONFIG_NETFILTER) skb->nfmark = IPSEC_NFMARK_IS_SAREF_BIT | (skb->nfmark & (~(IPsecSAref2NFmark(IPSEC_SA_REF_MASK)))) | IPsecSAref2NFmark(IPsecSA2SAref(ipsp)); KLIPS_PRINT(debug_rcv & DB_RX_PKTRX, "klips_debug:ipsec_rcv_decap_ipip: " "IPIP SA sets skb->nfmark=0x%x.\n", (unsigned)skb->nfmark); #endif /* CONFIG_NETFILTER */ result = IPSEC_RCV_OK; rcvleave: return result; } /* * get all the initial checking and setup done. None of this can be off * loaded by any currently support hardware * * the following things should be setup when we exit this function. * * irs->stats == stats structure (or NULL) * irs->ipp = IP header. * irs->len = total length of packet * skb->nh.iph = ipp; * skb->h.raw = start of payload * irs->ipsp = NULL. * irs->iphlen = N/A = is recalculated. * irs->ilen = 0; * irs->authlen = 0; * irs->authfuncs = NULL; * irs->skb = the skb; * * proto_funcs should be from ipsec_esp.c, ipsec_ah.c or ipsec_ipcomp.c. * */ static enum ipsec_rcv_value ipsec_rcv_init(struct ipsec_rcv_state *irs) { struct net_device_stats *stats = NULL; /* This device's statistics */ int i; struct sk_buff *skb; KLIPS_PRINT(debug_rcv, "klips_debug: %s(st=%d,nxt=%d)\n", __FUNCTION__, irs->state, irs->next_state); if (irs == NULL) { KLIPS_PRINT(debug_rcv, "klips_debug:ipsec_rcv_init: NULL irs."); return IPSEC_RCV_REALLYBAD; } skb = irs->skb; if (!skb) { KLIPS_PRINT(debug_rcv, "klips_debug:ipsec_rcv_init: NULL skb."); return IPSEC_RCV_REALLYBAD; } if (skb->data == NULL) { KLIPS_PRINT(debug_rcv, "klips_debug:ipsec_rcv_init: " "NULL skb->data passed in, packet is bogus, dropping.\n"); return IPSEC_RCV_REALLYBAD; } /* dev->hard_header_len is unreliable and should not be used */ /* klips26_rcv_encap will have already set hard_header_len for us */ if (irs->hard_header_len == 0) { irs->hard_header_len = skb_mac_header(skb) ? (skb_network_header(skb) - skb_mac_header(skb)) : 0; if ((irs->hard_header_len < 0) || (irs->hard_header_len > skb_headroom(skb))) irs->hard_header_len = 0; } skb = ipsec_rcv_unclone(skb, irs); if (skb == NULL) return IPSEC_RCV_REALLYBAD; /* In Linux >2.4.4, we may have to reassemble fragments. They are not assembled automatically to save TCP from having to copy twice. */ if (skb_is_nonlinear(skb)) { #ifdef HAVE_NEW_SKB_LINEARIZE if (skb_linearize_cow(skb) != 0) #else if (skb_linearize(skb, GFP_ATOMIC) != 0) #endif return IPSEC_RCV_REALLYBAD; } irs->iph = (void *)ip_hdr(skb); #if defined(CONFIG_IPSEC_NAT_TRAVERSAL) && !defined(NET_26) if (irs->natt_len) { /** * Now, we are sure packet is ESPinUDP, and we have a private * copy that has been linearized, remove natt_len bytes * from packet and modify protocol to ESP. */ if (((unsigned char *)skb->data > (unsigned char *)ip_hdr(skb)) && ((unsigned char *)ip_hdr(skb) > (unsigned char *)skb->head)) { unsigned int _len = (unsigned char *)skb->data - (unsigned char *)ip_hdr(skb); KLIPS_PRINT(debug_rcv, "klips_debug:ipsec_rcv_init: adjusting skb: skb_push(%u)\n", _len); skb_push(skb, _len); } KLIPS_PRINT(debug_rcv, "klips_debug:ipsec_rcv_init: " "removing %d bytes from ESPinUDP packet\n", irs->natt_len); irs->iph = (struct iphdr *)skb->data; irs->iphlen = lsw_ip4_hdr(irs)->ihl << 2; lsw_ip4_hdr(irs)->tot_len = htons(ntohs(lsw_ip4_hdr(irs)->tot_len) - irs->natt_len); if (skb->len < irs->iphlen + irs->natt_len) { printk(KERN_WARNING "klips_error:ipsec_rcv_init: " "ESPinUDP packet is too small (%d < %d+%d). " "This should never happen, please report.\n", (int)(skb->len), irs->iphlen, irs->natt_len); return IPSEC_RCV_REALLYBAD; } /* advance payload pointer to point past the UDP header */ skb->h.raw = skb->h.raw + irs->natt_len; /* modify protocol */ lsw_ip4_hdr(irs)->protocol = IPPROTO_ESP; skb->sk = NULL; KLIPS_IP_PRINT(debug_rcv, ip_hdr(skb)); } #endif if (debug_rcv) ipsec_rcv_redodebug(irs); #ifdef CONFIG_KLIPS_IPV6 if (lsw_ip_hdr_version(irs) == 6) { IPSEC_FRAG_OFF_DECL(frag_off) int nexthdroff; irs->proto = lsw_ip6_hdr(irs)->nexthdr; nexthdroff = ipsec_ipv6_skip_exthdr(irs->skb, ((void *)(lsw_ip6_hdr( irs) + 1)) - (void*)irs->skb->data, &irs->proto, &frag_off); irs->iphlen = nexthdroff - (irs->iph - (void*)irs->skb->data); } else #endif /* CONFIG_KLIPS_IPV6 */ { irs->iphlen = lsw_ip4_hdr(irs)->ihl << 2; irs->proto = lsw_ip4_hdr(irs)->protocol; } KLIPS_PRINT(debug_rcv, "klips_debug:ipsec_rcv_init: " "<<< Info -- "); KLIPS_PRINTMORE(debug_rcv && skb->dev, "skb->dev=%s ", skb->dev->name ? skb->dev->name : "NULL"); KLIPS_PRINTMORE(debug_rcv, "\n"); if ( (irs->proto != IPPROTO_AH) && #ifdef CONFIG_KLIPS_IPCOMP_disabled_until_we_register_IPCOMP_HANDLER (irs->proto != IPPROTO_COMP) && #endif /* CONFIG_KLIPS_IPCOMP */ (irs->proto != IPPROTO_ESP) ) { KLIPS_PRINT(debug_rcv & DB_RX_IPSA, "klips_debug:ipsec_rcv_init: Why the hell is someone " "passing me a non-ipsec irs->proto = %d packet? -- dropped.\n", irs->proto); return IPSEC_RCV_REALLYBAD; } /* * if there is an attached ipsec device, then use that device for * stats until we know better. */ if (skb->dev) { struct ipsecpriv *prvdev = NULL; struct net_device *ipsecdev = NULL; for (i = 0; i <= ipsecdevices_max; i++) { if (ipsecdevices[i] == NULL) continue; if (!netdev_priv(ipsecdevices[i])) continue; prvdev = netdev_to_ipsecpriv(ipsecdevices[i]); if (prvdev->dev == skb->dev) { ipsecdev = ipsecdevices[i]; break; } } if (ipsecdev) { KLIPS_PRINT(debug_rcv, "klips_debug:ipsec_rcv_init: " "assigning packet ownership to " "virtual device %s from " "physical device %s.\n", ipsecdev->name, skb->dev->name); skb->dev = ipsecdev; } else { KLIPS_PRINT(debug_rcv, "klips_debug:ipsec_rcv_init: " "assigning packet ownership to " "virtual device mast0 from " "physical device %s.\n", skb->dev->name); skb->dev = ipsec_mast_get_device(0); /* ipsec_mast_get takes the device */ if (skb->dev) dev_put(skb->dev); } if (prvdev) stats = (struct net_device_stats *) &(prvdev->mystats); } else { KLIPS_PRINT(debug_rcv, "klips_debug:ipsec_rcv_init: " "device supplied with skb is NULL\n"); } if (stats) stats->rx_packets++; KLIPS_IP_PRINT(debug_rcv, irs->iph); /* set up for decap */ irs->stats = stats; irs->ipsp = NULL; irs->lastipsp = NULL; irs->ilen = 0; irs->authlen = 0; irs->authfuncs = NULL; irs->skb = skb; return IPSEC_RCV_OK; } static enum ipsec_rcv_value ipsec_rcv_decap_init(struct ipsec_rcv_state *irs) { KLIPS_PRINT(debug_rcv, "klips_debug: %s(st=%d,nxt=%d)\n", __FUNCTION__, irs->state, irs->next_state); switch (irs->proto) { case IPPROTO_ESP: irs->proto_funcs = esp_xform_funcs; break; #ifdef CONFIG_KLIPS_AH case IPPROTO_AH: irs->proto_funcs = ah_xform_funcs; break; #endif /* !CONFIG_KLIPS_AH */ #ifdef CONFIG_KLIPS_IPCOMP case IPPROTO_COMP: irs->proto_funcs = ipcomp_xform_funcs; break; #endif /* !CONFIG_KLIPS_IPCOMP */ default: if (irs->stats) irs->stats->rx_errors++; return IPSEC_RCV_BADPROTO; } return IPSEC_RCV_OK; } static enum ipsec_rcv_value ipsec_rcv_decap_lookup(struct ipsec_rcv_state *irs) { struct sk_buff *skb; KLIPS_PRINT(debug_rcv, "klips_debug: %s(st=%d,nxt=%d)\n", __FUNCTION__, irs->state, irs->next_state); irs->replay = 0; #ifdef CONFIG_KLIPS_ALG irs->ixt_a = NULL; #endif /* CONFIG_KLIPS_ALG */ skb = irs->skb; irs->len = skb->len; if (debug_rcv) ipsec_rcv_redodebug(irs); if (lsw_ip_hdr_version(irs) == 6) { irs->said.dst.u.v6.sin6_addr = lsw_ip6_hdr(irs)->daddr; irs->said.dst.u.v6.sin6_family = AF_INET6; } else { irs->said.dst.u.v4.sin_addr.s_addr = lsw_ip4_hdr(irs)->daddr; irs->said.dst.u.v4.sin_family = AF_INET; } /* note: rcv_checks set up the said.spi value, if appropriate */ if (irs->proto_funcs->rcv_checks) return (*irs->proto_funcs->rcv_checks)(irs, irs->skb); return IPSEC_RCV_OK; } static enum ipsec_rcv_value ipsec_rcv_auth_init(struct ipsec_rcv_state *irs) { struct ipsec_sa *newipsp; KLIPS_PRINT(debug_rcv, "klips_debug: %s(st=%d,nxt=%d)\n", __FUNCTION__, irs->state, irs->next_state); irs->said.proto = irs->proto; irs->sa_len = KLIPS_SATOT(debug_rcv, &irs->said, 0, irs->sa, sizeof(irs->sa)); newipsp = ipsec_sa_getbyid(&irs->said, IPSEC_REFRX); if (newipsp == NULL) { KLIPS_PRINT(debug_rcv, "klips_debug:ipsec_rcv_auth_init: " "no ipsec_sa for SA:%s: incoming packet with no SA dropped\n", irs->sa_len ? irs->sa : " (error)"); if (irs->stats) irs->stats->rx_dropped++; return IPSEC_RCV_SAIDNOTFOUND; } /* If it is in larval state, drop the packet, we cannot process yet. */ if (newipsp->ips_state == K_SADB_SASTATE_LARVAL) { KLIPS_PRINT(debug_rcv, "klips_debug:ipsec_rcv_auth_init: " "ipsec_sa in larval state, cannot be used yet, dropping packet.\n"); if (irs->stats) irs->stats->rx_dropped++; ipsec_sa_put(newipsp, IPSEC_REFRX); return IPSEC_RCV_SAIDNOTLIVE; } if (newipsp->ips_state == K_SADB_SASTATE_DEAD) { KLIPS_PRINT(debug_rcv, "klips_debug:ipsec_rcv_auth_init: " "ipsec_sa in dead state, cannot be used any more, dropping packet.\n"); if (irs->stats) irs->stats->rx_dropped++; ipsec_sa_put(newipsp, IPSEC_REFRX); return IPSEC_RCV_SAIDNOTLIVE; } if (sysctl_ipsec_inbound_policy_check) { struct sockaddr_in *psin = NULL; struct sockaddr_in6 *psin6 = NULL; char sa_saddr_txt[ADDRTOA_BUF] = { 0, }; if (((struct sockaddr_in6*)(newipsp->ips_addr_s))->sin6_family == AF_INET6) psin6 = (struct sockaddr_in6*)(newipsp->ips_addr_s); else if (((struct sockaddr_in*)(newipsp->ips_addr_s))-> sin_family == AF_INET6) psin = (struct sockaddr_in*)(newipsp->ips_addr_s); if ((psin && lsw_ip4_hdr(irs)->saddr != psin->sin_addr.s_addr) || (psin6 && memcmp(&lsw_ip6_hdr(irs)->saddr, &psin6->sin6_addr, sizeof(lsw_ip6_hdr(irs)->saddr)) != 0)) { if (debug_rcv) { /* generate SA->saddr */ if (psin) { inet_addrtot(AF_INET, &psin->sin_addr.s_addr, 0, sa_saddr_txt, sizeof(sa_saddr_txt)); } else if (psin6) { inet_addrtot(AF_INET6, &psin6->sin6_addr.s6_addr, 0, sa_saddr_txt, sizeof(sa_saddr_txt)); } /* generate ipsaddr_txt */ if (!(*irs->ipsaddr_txt)) ipsec_rcv_redodebug(irs); /* regenerate sa_len */ if (!irs->sa_len) { irs->sa_len = KLIPS_SATOT(debug_rcv, &irs->said, 0, irs->sa, sizeof(irs-> sa)); } } KLIPS_ERROR(debug_rcv, "klips_debug:ipsec_rcv_auth_init: " "SA:%s, src=%s of pkt does not agree with expected SA source address policy (%s).\n", irs->sa_len ? irs->sa : " (error)", irs->ipsaddr_txt, sa_saddr_txt); if (irs->stats) irs->stats->rx_dropped++; ipsec_sa_put(newipsp, IPSEC_REFRX); return IPSEC_RCV_FAILEDINBOUND; } KLIPS_PRINT(debug_rcv, "klips_debug:ipsec_rcv_auth_init: " "SA:%s, src=%s of pkt agrees with expected SA source address policy.\n", irs->sa_len ? irs->sa : " (error)", irs->ipsaddr_txt); /* * at this point, we have looked up a new SA, and we want to * make sure that if this isn't the first SA in the list, * that the previous SA actually points at this one. */ if (irs->ipsp) { if (irs->ipsp->ips_next != newipsp) { KLIPS_ERROR(debug_rcv, "klips_debug:ipsec_rcv_auth_init: " "unexpected SA:%s: does not agree with ips->inext policy, dropped\n", irs->sa_len ? irs->sa : " (error)"); if (irs->stats) irs->stats->rx_dropped++; ipsec_sa_put(newipsp, IPSEC_REFRX); return IPSEC_RCV_FAILEDINBOUND; } KLIPS_PRINT(debug_rcv, "klips_debug:ipsec_rcv_auth_init: " "SA:%s grouping from previous SA is OK.\n", irs->sa_len ? irs->sa : " (error)"); } else { KLIPS_PRINT(debug_rcv, "klips_debug:ipsec_rcv_auth_init: " "SA:%s First SA in group.\n", irs->sa_len ? irs->sa : " (error)"); } if (irs->proto == IPPROTO_ESP) { KLIPS_PRINT(debug_rcv, "klips_debug:ipsec_rcv_auth_init: " "natt_type=%u tdbp->ips_natt_type=%u : %s\n", irs->natt_type, newipsp->ips_natt_type, (irs->natt_type == newipsp->ips_natt_type) ? "ok" : "bad"); if (irs->natt_type != newipsp->ips_natt_type) { KLIPS_PRINT(debug_rcv, "klips_debug:ipsec_rcv_auth_init: " "SA:%s does not agree with expected NAT-T policy.\n", irs->sa_len ? irs->sa : " (error)"); if (irs->stats) irs->stats->rx_dropped++; ipsec_sa_put(newipsp, IPSEC_REFRX); return IPSEC_RCV_FAILEDINBOUND; } } } if (newipsp != irs->ipsp) { if (irs->lastipsp) ipsec_sa_put(irs->lastipsp, IPSEC_REFRX); irs->lastipsp = irs->ipsp; irs->ipsp = newipsp; } else { /* we already have a refcount for it */ ipsec_sa_put(newipsp, IPSEC_REFRX); } return IPSEC_RCV_OK; } static enum ipsec_rcv_value ipsec_rcv_auth_decap(struct ipsec_rcv_state *irs) { ipsec_rcv_setoutif(irs); irs->proto_funcs = irs->ipsp->ips_xformfuncs; if (irs->proto_funcs == NULL) return IPSEC_RCV_BADPROTO; if (irs->proto_funcs->protocol != irs->proto) { if (irs->proto_funcs->protocol == IPPROTO_COMP) { /* looks like an IPCOMP that we can skip */ struct ipsec_sa *newipsp; newipsp = irs->ipsp->ips_next; if (newipsp) ipsec_sa_get(newipsp, IPSEC_REFRX); if (irs->lastipsp) ipsec_sa_put(irs->lastipsp, IPSEC_REFRX); irs->lastipsp = irs->ipsp; irs->ipsp = newipsp; /* come back into here with the next transform */ irs->next_state = IPSEC_RSM_DECAP_INIT; return IPSEC_RCV_OK; } KLIPS_PRINT(debug_rcv, "klips_debug:ipsec_rcv_auth_decap: " "packet with protocol %d!=%d dropped, last successful SA:%s.\n", irs->proto_funcs->protocol, irs->proto, irs->sa_len ? irs->sa : " (error)"); if (irs->stats) irs->stats->rx_errors++; return IPSEC_RCV_FAILEDINBOUND; } if (debug_rcv) ipsec_rcv_redodebug(irs); /* now check the lifetimes */ if (ipsec_lifetime_check(&irs->ipsp->ips_life.ipl_bytes, "bytes", irs->sa, ipsec_life_countbased, ipsec_incoming, irs->ipsp) == ipsec_life_harddied || ipsec_lifetime_check(&irs->ipsp->ips_life.ipl_addtime, "addtime", irs->sa, ipsec_life_timebased, ipsec_incoming, irs->ipsp) == ipsec_life_harddied || ipsec_lifetime_check(&irs->ipsp->ips_life.ipl_addtime, "usetime", irs->sa, ipsec_life_timebased, ipsec_incoming, irs->ipsp) == ipsec_life_harddied || ipsec_lifetime_check(&irs->ipsp->ips_life.ipl_packets, "packets", irs->sa, ipsec_life_countbased, ipsec_incoming, irs->ipsp) == ipsec_life_harddied) { /* * disconnect SA from the hash table, so it cannot be * found again. */ ipsec_sa_rm(irs->ipsp); if (irs->stats) irs->stats->rx_dropped++; KLIPS_PRINT(debug_rcv, "klips_debug:ipsec_rcv_auth_decap: " "decap (%d) failed lifetime check\n", irs->proto); return IPSEC_RCV_LIFETIMEFAILED; } #if 0 /* * This is removed for some reasons: * 1) it needs to happen *after* authentication. * 2) do we really care, if it authenticates, if it came * from the wrong location? * 3) the NAT_KA messages in IKE will also get to pluto * and it will figure out that stuff has moved. * 4) the 2.6 udp-esp encap function does not pass us * the originating port number, and I can't tell * if skb->sk is guaranteed to be valid here. * 2005-04-16: mcr@xelerance.com */ /* * * XXX we should ONLY update pluto if the SA passes all checks, * which we clearly do not now. */ if (irs->natt_type != 0 && (irs->ipp->saddr != ((struct sockaddr_in*)(newipsp->ips_addr_s))->sin_addr.s_addr || irs->natt_sport != newipsp->ips_natt_sport)) { struct sockaddr sipaddr; struct sockaddr_in *psin = (struct sockaddr_in*)(newipsp->ips_addr_s); /** Advertise NAT-T addr change to pluto **/ sipaddr.sa_family = AF_INET; ((struct sockaddr_in*)&sipaddr)->sin_addr.s_addr = irs->ipp->saddr; ((struct sockaddr_in*)&sipaddr)->sin_port = htons( irs->natt_sport); pfkey_nat_t_new_mapping(newipsp, &sipaddr, irs->natt_sport); /** * Then allow or block packet depending on * sysctl_ipsec_inbound_policy_check. * * In all cases, pluto will update SA if new mapping is * accepted. */ if (sysctl_ipsec_inbound_policy_check) { KLIPS_PRINT(debug_rcv, "klips_debug:ipsec_rcv_auth_decap: " "SA:%s, src=%s:%u of pkt does not agree with expected " "SA source address [%08x:%u] (notifying pluto of change).\n", irs->sa_len ? irs->sa : " (error)", irs->ipsaddr_txt, irs->natt_sport, psin->sin_addr.s_addr, newipsp->ips_natt_sport); if (irs->stats) irs->stats->rx_dropped++; ipsec_sa_put(newipsp); return IPSEC_RCV_FAILEDINBOUND; } } #endif irs->authfuncs = NULL; /* authenticate, if required */ #ifdef CONFIG_KLIPS_OCF if (irs->ipsp->ocf_in_use) { irs->authlen = AHHMAC_HASHLEN; irs->authfuncs = NULL; irs->ictx = NULL; irs->octx = NULL; irs->ictx_len = 0; irs->octx_len = 0; } else #endif /* CONFIG_KLIPS_OCF */ #ifdef CONFIG_KLIPS_ALG /* authenticate, if required */ if ((irs->ixt_a = irs->ipsp->ips_alg_auth)) { irs->authlen = AHHMAC_HASHLEN; irs->authfuncs = NULL; irs->ictx = NULL; irs->octx = NULL; irs->ictx_len = 0; irs->octx_len = 0; KLIPS_PRINT(debug_rcv, "klips_debug:ipsec_rcv_auth_decap: " "authalg=%d authlen=%d\n", irs->ipsp->ips_authalg, irs->authlen); } else #endif /* CONFIG_KLIPS_ALG */ switch (irs->ipsp->ips_authalg) { #ifdef CONFIG_KLIPS_AUTH_HMAC_MD5 case AH_MD5: irs->authlen = AHHMAC_HASHLEN; irs->authfuncs = ipsec_rcv_md5; irs->ictx = (void *) &((struct md5_ctx*)(irs->ipsp->ips_key_a))->ictx; irs->octx = (void *) &((struct md5_ctx*)(irs->ipsp->ips_key_a))->octx; irs->ictx_len = sizeof(((struct md5_ctx*)(irs->ipsp->ips_key_a))->ictx); irs->octx_len = sizeof(((struct md5_ctx*)(irs->ipsp->ips_key_a))->octx); break; #endif /* CONFIG_KLIPS_AUTH_HMAC_MD5 */ #ifdef CONFIG_KLIPS_AUTH_HMAC_SHA1 case AH_SHA: irs->authlen = AHHMAC_HASHLEN; irs->authfuncs = ipsec_rcv_sha1; irs->ictx = (void *) &((struct sha1_ctx*)(irs->ipsp->ips_key_a))->ictx; irs->octx = (void *) &((struct sha1_ctx*)(irs->ipsp->ips_key_a))->octx; irs->ictx_len = sizeof(((struct sha1_ctx*)(irs->ipsp->ips_key_a))->ictx); irs->octx_len = sizeof(((struct sha1_ctx*)(irs->ipsp->ips_key_a))->octx); break; #endif /* CONFIG_KLIPS_AUTH_HMAC_SHA1 */ case AH_NONE: irs->authlen = 0; irs->authfuncs = NULL; irs->ictx = NULL; irs->octx = NULL; irs->ictx_len = 0; irs->octx_len = 0; break; default: irs->ipsp->ips_errs.ips_alg_errs += 1; if (irs->stats) irs->stats->rx_errors++; return IPSEC_RCV_BADAUTH; } /* ilen counts number of bytes in ESP portion */ irs->ilen = ((irs->skb->data + irs->skb->len) - skb_transport_header(irs->skb)) - irs->authlen; if (irs->ilen <= 0) { KLIPS_PRINT(debug_rcv, "klips_debug:ipsec_rcv_auth_decap: " "runt %s packet with no data, dropping.\n", (irs->proto == IPPROTO_ESP ? "esp" : "ah")); if (irs->stats) irs->stats->rx_dropped++; return IPSEC_RCV_BADLEN; } if (irs->authfuncs || #ifdef CONFIG_KLIPS_OCF irs->ipsp->ocf_in_use || #endif #ifdef CONFIG_KLIPS_ALG irs->ixt_a || #endif 0) { if (irs->proto_funcs->rcv_setup_auth) { return (*irs->proto_funcs->rcv_setup_auth)(irs, irs->skb, &irs->replay, &irs-> authenticator); } } return IPSEC_RCV_OK; } static enum ipsec_rcv_value ipsec_rcv_auth_calc(struct ipsec_rcv_state *irs) { KLIPS_PRINT(debug_rcv, "klips_debug: %s(st=%d,nxt=%d)\n", __FUNCTION__, irs->state, irs->next_state); if (irs->authfuncs || #ifdef CONFIG_KLIPS_OCF (irs->ipsp->ocf_in_use && irs->ipsp->ips_authalg) || #endif #ifdef CONFIG_KLIPS_ALG irs->ixt_a || #endif 0) { if (!irs->authenticator) { irs->ipsp->ips_errs.ips_auth_errs += 1; if (irs->stats) irs->stats->rx_dropped++; return IPSEC_RCV_BADAUTH; } if (!ipsec_checkreplaywindow(irs->ipsp, irs->replay)) { irs->ipsp->ips_errs.ips_replaywin_errs += 1; KLIPS_PRINT(debug_rcv & DB_RX_REPLAY, "klips_debug:ipsec_rcv_auth_calc: " "duplicate frame from %s, packet dropped\n", irs->ipsaddr_txt); if (irs->stats) irs->stats->rx_dropped++; return IPSEC_RCV_REPLAYFAILED; } /* * verify authenticator */ KLIPS_PRINT(debug_rcv, "klips_debug:ipsec_rcv_auth_calc: " "encalg = %d, authalg = %d.\n", irs->ipsp->ips_encalg, irs->ipsp->ips_authalg); /* calculate authenticator */ if (irs->proto_funcs->rcv_calc_auth == NULL) return IPSEC_RCV_BADAUTH; return (*irs->proto_funcs->rcv_calc_auth)(irs, irs->skb); } return IPSEC_RCV_OK; } static enum ipsec_rcv_value ipsec_rcv_auth_chk(struct ipsec_rcv_state *irs) { KLIPS_PRINT(debug_rcv, "klips_debug: %s(st=%d,nxt=%d) - %s\n", __FUNCTION__, irs->state, irs->next_state, irs->auth_checked ? "already checked" : "will check"); if (irs->auth_checked) return IPSEC_RCV_OK; if (irs->authfuncs || #ifdef CONFIG_KLIPS_OCF (irs->ipsp->ocf_in_use && irs->ipsp->ips_authalg) || #endif #ifdef CONFIG_KLIPS_ALG irs->ixt_a || #endif 0) { if (memcmp(irs->hash, irs->authenticator, irs->authlen)) { irs->ipsp->ips_errs.ips_auth_errs += 1; KLIPS_ERROR(debug_rcv & DB_RX_INAU, "klips_debug:ipsec_rcv_auth_chk: " "auth failed on incoming packet from %s (replay=%d): calculated hash=%08x%08x%08x received hash=%08x%08x%08x, dropped\n", irs->ipsaddr_txt, irs->replay, ntohl(*(__u32*)&irs->hash[0]), ntohl(*(__u32*)&irs->hash[4]), ntohl(*(__u32*)&irs->hash[8]), ntohl(*(__u32*)irs->authenticator), ntohl(*((__u32*)irs->authenticator + 1)), ntohl(*((__u32*)irs->authenticator + 2))); if (irs->stats) irs->stats->rx_dropped++; return IPSEC_RCV_AUTHFAILED; } else { KLIPS_PRINT(debug_rcv, "klips_debug:ipsec_rcv_auth_chk: " "authentication successful.\n"); } /* Crypto hygiene: clear memory used to calculate autheticator. * The length varies with the algorithm. */ memset(irs->hash, 0, irs->authlen); /* If the sequence number == 0, expire SA, it had rolled */ if (irs->ipsp->ips_replaywin && !irs->replay /* !irs->ipsp->ips_replaywin_lastseq */) { /* we need to remove it from the sadb hash, so that it can't be found again */ ipsec_sa_rm(irs->ipsp); KLIPS_ERROR(debug_rcv, "klips_debug:ipsec_rcv_auth_chk: " "replay window counter rolled, expiring SA.\n"); if (irs->stats) irs->stats->rx_dropped++; return IPSEC_RCV_REPLAYROLLED; } /* now update the replay counter */ if (!ipsec_updatereplaywindow(irs->ipsp, irs->replay)) { irs->ipsp->ips_errs.ips_replaywin_errs += 1; KLIPS_ERROR(debug_rcv & DB_RX_REPLAY, "klips_debug:ipsec_rcv_auth_chk: " "duplicate frame from %s, packet dropped\n", irs->ipsaddr_txt); if (irs->stats) irs->stats->rx_dropped++; return IPSEC_RCV_REPLAYROLLED; } irs->auth_checked = 1; } return IPSEC_RCV_OK; } static enum ipsec_rcv_value ipsec_rcv_decrypt(struct ipsec_rcv_state *irs) { KLIPS_PRINT(debug_rcv, "klips_debug: %s(st=%d,nxt=%d)\n", __FUNCTION__, irs->state, irs->next_state); if (irs->proto_funcs->rcv_decrypt) return (*irs->proto_funcs->rcv_decrypt)(irs); return IPSEC_RCV_OK; } /* * here we decide if there is more decapsulating required and * change the next state appropriately */ static enum ipsec_rcv_value ipsec_rcv_decap_cont(struct ipsec_rcv_state *irs) { struct sk_buff *skb; struct ipsec_sa *ipsnext = NULL; /* next SA towards inside of packet */ enum ipsec_rcv_value rv; KLIPS_PRINT(debug_rcv, "klips_debug: %s(st=%d,nxt=%d)\n", __FUNCTION__, irs->state, irs->next_state); /* * if we haven't checked the auth values yet, do it now. * This is needed for the case where drivers do crypt+hash * in one operation. */ rv = ipsec_rcv_auth_chk(irs); if (rv != IPSEC_RCV_OK) return rv; /* * Adjust pointers after decrypt */ skb = irs->skb; irs->len = skb->len; irs->iph = (void *) ip_hdr(skb); #ifdef CONFIG_KLIPS_IPV6 if (lsw_ip_hdr_version(irs) == 6) { IPSEC_FRAG_OFF_DECL(frag_off) unsigned char nexthdr = lsw_ip6_hdr(irs)->nexthdr; int nexthdroff; nexthdroff = ipsec_ipv6_skip_exthdr(irs->skb, (void *)(lsw_ip6_hdr(irs) + 1) - (void *)irs->skb->data, &nexthdr, &frag_off); irs->iphlen = nexthdroff - (irs->iph - (void*)irs->skb->data); skb_set_transport_header(skb, ipsec_skb_offset(skb, skb_network_header( skb) + irs->iphlen)); } else #endif /* CONFIG_KLIPS_IPV6 */ { irs->iphlen = lsw_ip4_hdr(irs)->ihl << 2; skb_set_transport_header(skb, ipsec_skb_offset(skb, skb_network_header( skb) + irs->iphlen)); } /* zero any options that there might be */ memset(&(IPCB(skb)->opt), 0, sizeof(struct ip_options)); if (debug_rcv) ipsec_rcv_redodebug(irs); /* * Discard the original ESP/AH header */ if (lsw_ip_hdr_version(irs) == 6) { lsw_ip6_hdr(irs)->nexthdr = irs->next_header; skb->protocol = htons(ETH_P_IPV6); } else { lsw_ip4_hdr(irs)->protocol = irs->next_header; lsw_ip4_hdr(irs)->check = 0; /* NOTE: this will be included in checksum */ lsw_ip4_hdr(irs)->check = ip_fast_csum((unsigned char *)ip_hdr(skb), irs->iphlen >> 2); skb->protocol = htons(ETH_P_IP); } irs->proto = irs->next_header; /* needed for decap_init recursion */ KLIPS_PRINT(debug_rcv & DB_RX_PKTRX, "klips_debug:ipsec_rcv_decap_cont: " "after <%s%s%s>, SA:%s:\n", IPS_XFORM_NAME(irs->ipsp), irs->sa_len ? irs->sa : " (error)"); KLIPS_IP_PRINT(debug_rcv & DB_RX_PKTRX, irs->iph); skb->ip_summed = 0; ipsnext = irs->ipsp->ips_next; if (sysctl_ipsec_inbound_policy_check) { if (ipsnext) { if (irs->next_header != IPPROTO_AH && irs->next_header != IPPROTO_ESP && #ifdef CONFIG_KLIPS_IPCOMP irs->next_header != IPPROTO_COMP && (ipsnext->ips_said.proto != IPPROTO_COMP || ipsnext->ips_next) && #endif /* CONFIG_KLIPS_IPCOMP */ irs->next_header != IPPROTO_IPIP && irs->next_header != IPPROTO_IPV6 && irs->next_header != IPPROTO_ATT_HEARTBEAT /* heartbeats to AT&T SIG/GIG */ ) { KLIPS_PRINT(debug_rcv, "packet with incomplete policy dropped, last successful SA:%s.\n", irs->sa_len ? irs->sa : " (error)"); if (irs->stats) irs->stats->rx_dropped++; return IPSEC_RCV_FAILEDINBOUND; } KLIPS_PRINT(debug_rcv, "klips_debug:ipsec_rcv_decap_cont: " "SA:%s, Another IPSEC header to process.\n", irs->sa_len ? irs->sa : " (error)"); } else { KLIPS_PRINT(debug_rcv, "klips_debug:ipsec_rcv_decap_cont: " "No ips_inext from this SA:%s.\n", irs->sa_len ? irs->sa : " (error)"); } } #ifdef CONFIG_KLIPS_IPCOMP /* update ipcomp ratio counters, even if no ipcomp packet is present */ if (ipsnext && ipsnext->ips_said.proto == IPPROTO_COMP && irs->next_header != IPPROTO_COMP) { 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); ipsnext->ips_comp_ratio_cbytes += tot_len; ipsnext->ips_comp_ratio_dbytes += tot_len; } #endif /* CONFIG_KLIPS_IPCOMP */ irs->ipsp->ips_life.ipl_bytes.ipl_count += irs->len; irs->ipsp->ips_life.ipl_bytes.ipl_last = irs->len; if (!irs->ipsp->ips_life.ipl_usetime.ipl_count) irs->ipsp->ips_life.ipl_usetime.ipl_count = jiffies / HZ; irs->ipsp->ips_life.ipl_usetime.ipl_last = jiffies / HZ; irs->ipsp->ips_life.ipl_packets.ipl_count += 1; #if defined(CONFIG_NETFILTER) if (irs->proto == IPPROTO_ESP || irs->proto == IPPROTO_AH) { skb->nfmark = IPSEC_NFMARK_IS_SAREF_BIT | (skb->nfmark & (~(IPsecSAref2NFmark(IPSEC_SA_REF_MASK)))) | IPsecSAref2NFmark(IPsecSA2SAref(irs->ipsp)); KLIPS_PRINT(debug_rcv & DB_RX_PKTRX, "klips_debug:ipsec_rcv_decap_cont: " "%s SA sets skb->nfmark=0x%x.\n", irs->proto == IPPROTO_ESP ? "ESP" : "AH", (unsigned)skb->nfmark); } #endif /* CONFIG_NETFILTER */ /* do we need to do more decapsulation */ if ((irs->proto == IPPROTO_ESP || irs->proto == IPPROTO_AH || #ifdef CONFIG_KLIPS_IPCOMP irs->proto == IPPROTO_COMP || #endif /* CONFIG_KLIPS_IPCOMP */ 0) && irs->ipsp != NULL) irs->next_state = IPSEC_RSM_DECAP_INIT; return IPSEC_RCV_OK; } static enum ipsec_rcv_value ipsec_rcv_cleanup(struct ipsec_rcv_state *irs) { struct sk_buff *skb; KLIPS_PRINT(debug_rcv, "klips_debug: %s(st=%d,nxt=%d)\n", __FUNCTION__, irs->state, irs->next_state); /* set up for cleanup */ skb = irs->skb; if ((irs->natt_type) && (irs->proto != IPPROTO_IPIP)) { /* * NAT-Traversal and Transport Mode: * we need to correct TCP/UDP checksum * If we've got NAT-OA, we can fix checksum without recalculation. */ __u32 natt_oa = (irs->ipsp && irs->ipsp->ips_natt_oa) ? ((struct sockaddr_in*)(irs->ipsp->ips_natt_oa)) ->sin_addr.s_addr : 0; if (natt_oa != 0) { /* reset source address to what it was before NAT */ lsw_ip4_hdr(irs)->saddr = natt_oa; lsw_ip4_hdr(irs)->check = 0; lsw_ip4_hdr(irs)->check = ip_fast_csum( (unsigned char *)irs->iph, lsw_ip4_hdr( irs)->ihl); KLIPS_PRINT(debug_rcv, "csum: %04x\n", lsw_ip4_hdr(irs)->check); } /* * This fails when both systems are behind NAT * mcr: So, either the ports passed in by the NAT-OA mechanism for the fixup * are wrong, and so the checksum fixup is wrong * Not very harmful since the checksum is useless and the crypto hash will * be used anyway. */ #ifdef DISABLE_UDP_CHECKSUM /* see https://bugs.libreswan.org/issues/601 */ if (lsw_ip4_hdr(irs)->protocol == IPPROTO_UDP) { udp_hdr(skb)->check = 0; KLIPS_PRINT(debug_rcv, "UDP checksum for NAT packet disabled at compile time\n"); } #endif } /* okay, acted on all SA's, so free the last SA, and move to the next */ if (irs->ipsp) { struct ipsec_sa *newipsp; newipsp = irs->ipsp->ips_next; if (newipsp) ipsec_sa_get(newipsp, IPSEC_REFRX); if (irs->lastipsp) ipsec_sa_put(irs->lastipsp, IPSEC_REFRX); irs->lastipsp = irs->ipsp; irs->ipsp = newipsp; } #ifdef CONFIG_KLIPS_IPCOMP /* if there is an IPCOMP, but we don't have an IPPROTO_COMP, * then we can just skip it */ if (irs->ipsp && irs->ipsp->ips_said.proto == IPPROTO_COMP) { struct ipsec_sa *newipsp = NULL; newipsp = irs->ipsp->ips_next; if (newipsp) ipsec_sa_get(newipsp, IPSEC_REFRX); if (irs->lastipsp) ipsec_sa_put(irs->lastipsp, IPSEC_REFRX); irs->lastipsp = irs->ipsp; irs->ipsp = newipsp; irs->sa_len = 0; } #endif /* CONFIG_KLIPS_IPCOMP */ /* * the SA is still locked from the loop */ if (irs->ipsp && irs->ipsp->ips_xformfuncs->protocol == IPPROTO_IPIP) { enum ipsec_rcv_value decap_stat; decap_stat = ipsec_rcv_decap_ipip(irs); if (decap_stat != IPSEC_RCV_OK) return decap_stat; } if (irs->stats) irs->stats->rx_bytes += skb->len; /* * if we are supposed to return the packet directly to the transport * layer, then dump it out correctly. */ if (unlikely(!irs->lastipsp)) printk("%s,%d: %s lastipsp should never be NULL\n", __FILE__, __LINE__, __FUNCTION__); if (irs->lastipsp->ips_transport_direct) { KLIPS_PRINT(debug_rcv, "receiving packet as transport direct\n"); skb->ip_summed = CHECKSUM_UNNECESSARY; /* STUFF */ } #ifdef CONFIG_INET_IPSEC_SAREF if (skb->sp) secpath_put(skb->sp); skb->sp = secpath_dup(NULL); if (skb->sp) skb->sp->ref = irs->lastipsp->ips_ref; else return IPSEC_RCV_REALLYBAD; #endif return IPSEC_RCV_OK; } static enum ipsec_rcv_value ipsec_rcv_complete(struct ipsec_rcv_state *irs) { KLIPS_PRINT(debug_rcv, "klips_debug: %s(st=%d,nxt=%d)\n", __FUNCTION__, irs->state, irs->next_state); /* * make sure that data now starts at IP header, since we are going * to pass this back to ip_input (aka netif_rx). Rules for what the * pointers wind up a different for 2.6 vs 2.4, so we just fudge it here. */ #ifdef NET_26 irs->skb->data = skb_push(irs->skb, skb_transport_header(irs->skb) - skb_network_header(irs->skb)); #else irs->skb->data = skb_network_header(irs->skb); { int len; struct iphdr *iph = ip_hdr(irs->skb); #ifdef CONFIG_KLIPS_IPV6 struct ipv6hdr *ip6h = (struct ipv6hdr *) iph; if (iph->version == 6) len = ntohs(iph->payload_len) + sizeof(*ip6h); else #endif /* CONFIG_KLIPS_IPV6 */ len = ntohs(iph->tot_len); irs->skb->len = len; } #endif /* * release the dst that was attached, since we have likely * changed the actual destination of the packet. */ skb_dst_drop(irs->skb); irs->skb->pkt_type = PACKET_HOST; if (irs->hard_header_len && (skb_mac_header(irs->skb) != (skb_network_header(irs->skb) - irs->hard_header_len)) && (irs->hard_header_len <= skb_headroom(irs->skb))) { /* copy back original MAC header */ memmove(skb_network_header(irs->skb) - irs->hard_header_len, skb_mac_header(irs->skb), irs->hard_header_len); skb_set_mac_header(irs->skb, ipsec_skb_offset(irs->skb, skb_network_header(irs->skb) - irs->hard_header_len)); /* we have to fix the protocol, it's not right with IP4/IP6 or * IP6/IP4 tunnels */ if (irs->hard_header_len == ETH_HLEN) eth_hdr(irs->skb)->h_proto = irs->skb->protocol; } ipsec_nf_reset(irs->skb); if (irs->skb->dev == NULL) { /* the physical dev went down and is no longer attached */ KLIPS_PRINT(debug_rcv & DB_RX_PKTRX, "klips_debug:ipsec_rcv_complete: interface for skb is detached.\n"); return IPSEC_RCV_REALLYBAD; } KLIPS_PRINT(debug_rcv & DB_RX_PKTRX, "klips_debug:ipsec_rcv_complete: " "netif_rx(%s) called.\n", irs->skb->dev->name); netif_rx(irs->skb); irs->skb = NULL; return IPSEC_RCV_OK; } /* * ipsec_rsm is responsible for walking us through the state machine * it is the only entry point into the receive processing and does * appropriate checks and state changes for us. */ void ipsec_rsm(struct ipsec_rcv_state *irs) { if (irs == NULL) { KLIPS_PRINT(debug_rcv, "klips_debug:ipsec_rsm: " "irs == NULL.\n"); return; } /* * make sure nothing is removed from underneath us */ spin_lock_bh(&tdb_lock); /* * if we have a valid said, then we must check it here to ensure it * hasn't gone away while we were waiting for a task to complete */ if (irs->said.proto) { struct ipsec_sa *ipsp; ipsp = ipsec_sa_getbyid(&irs->said, IPSEC_REFRX); if (ipsp == NULL) { KLIPS_PRINT(debug_rcv, "klips_debug:ipsec_rsm: " "no ipsec_sa for SA:%s: " "incoming packet with no SA dropped\n", irs->sa_len ? irs->sa : " (error)"); if (irs->stats) irs->stats->rx_dropped++; /* drop through and cleanup */ irs->state = IPSEC_RSM_DONE; } else { /* set the refcount back */ ipsec_sa_put(ipsp, IPSEC_REFRX); } } while (irs->state != IPSEC_RSM_DONE) { int rc; irs->next_state = rcv_state_table[irs->state].next_state; rc = rcv_state_table[irs->state].action(irs); if (rc == IPSEC_RCV_OK) { /* some functions change the next state, see the state table */ irs->state = irs->next_state; } else if (rc == IPSEC_RCV_PENDING) { /* * things are on hold until we return here in the next/new state * we check our SA is valid when we return */ spin_unlock_bh(&tdb_lock); return; } else { /* bad result, force state change to done */ KLIPS_PRINT(debug_rcv, "klips_debug:ipsec_rsm: " "processing completed due to %s.\n", ipsec_rcv_err(rc)); irs->state = IPSEC_RSM_DONE; } } if (irs->lastipsp) { ipsec_sa_put(irs->lastipsp, IPSEC_REFRX); irs->lastipsp = NULL; } if (irs->ipsp) { ipsec_sa_put(irs->ipsp, IPSEC_REFRX); irs->ipsp = NULL; } /* * all done with anything needing locks */ spin_unlock_bh(&tdb_lock); if (irs->skb) { ipsec_kfree_skb(irs->skb); irs->skb = NULL; } if (irs->pre_ipcomp_skb) { ipsec_kfree_skb(irs->pre_ipcomp_skb); irs->pre_ipcomp_skb = NULL; } ipsec_rcv_state_delete(irs); KLIPS_DEC_USE; /* once less packet using the driver */ } int ipsec_rcv(struct sk_buff *skb ) { struct ipsec_rcv_state *irs = NULL; /* Don't unlink in the middle of a turnaround */ KLIPS_INC_USE; if (skb == NULL) { KLIPS_PRINT(debug_rcv, "klips_debug:ipsec_rcv: " "NULL skb passed in.\n"); goto rcvleave; } if (skb->data == NULL) { KLIPS_PRINT(debug_rcv, "klips_debug:ipsec_rcv: " "NULL skb->data passed in, packet is bogus, dropping.\n"); goto rcvleave; } irs = ipsec_rcv_state_new(); if (unlikely(!irs)) { KLIPS_PRINT(debug_rcv, "klips_debug:ipsec_rcv: " "failled to allocate a rcv state object\n"); goto rcvleave; } #if defined(CONFIG_IPSEC_NAT_TRAVERSAL) && !defined(NET_26) { /* NET_26 NAT-T is handled by seperate function */ struct sk_buff *nskb; int udp_decap_ret = 0; nskb = ipsec_rcv_natt_decap(skb, irs, &udp_decap_ret); if (nskb == NULL) { /* return with non-zero, because UDP.c code * need to send it upstream. */ if (skb && udp_decap_ret == 0) ipsec_kfree_skb(skb); if (irs) ipsec_rcv_state_delete(irs); KLIPS_DEC_USE; return udp_decap_ret; } skb = nskb; } #endif /* NAT_T */ irs->skb = skb; /* * we hand off real early to the state machine because we just cannot * know how much processing it is off-loading */ ipsec_rsm(irs); return 0; rcvleave: if (irs) ipsec_rcv_state_delete(irs); if (skb) ipsec_kfree_skb(skb); KLIPS_DEC_USE; return 0; } #ifdef NET_26 /* * this entry point is not a protocol entry point, so the entry * is a bit different. * * skb->iph->tot_len has been byte-swapped, and reduced by the size of * the IP header (and options). * * skb->h.raw has been pulled up the ESP header. * * skb->iph->protocol = 50 IPPROTO_ESP; * */ int klips26_udp_encap_rcv(struct sock *sk, struct sk_buff *skb) { struct udp_sock *up = udp_sk(sk); struct udphdr *uh; struct iphdr *iph; int iphlen, len; int ret; __u8 *udpdata; __be32 *udpdata32; __u16 encap_type = up->encap_type; /* if this is not encapsulated socket, then just return now */ if (!encap_type) return 1; /* If this is a paged skb, make sure we pull up * whatever data we need to look at. */ len = skb->len - sizeof(struct udphdr); if (!pskb_may_pull(skb, sizeof(struct udphdr) + min(len, 8))) return 1; /* Now we can get the pointers */ uh = udp_hdr(skb); udpdata = (__u8 *)(uh + 1); udpdata32 = (__be32 *)udpdata; switch (encap_type) { default: case UDP_ENCAP_ESPINUDP: KLIPS_PRINT(debug_rcv, "UDP_ENCAP_ESPINUDP_NON_IKE: len=%d 0x%x\n", len, udpdata32[0]); /* Check if this is a keepalive packet. If so, eat it. */ if (len == 1 && udpdata[0] == 0xff) { KLIPS_PRINT(debug_rcv, "UDP_ENCAP_ESPINUDP: keepalive packet detected\n"); goto drop; } else if (len > sizeof(struct ip_esp_hdr) && udpdata32[0] != 0) { KLIPS_PRINT(debug_rcv, "UDP_ENCAP_ESPINUDP: ESP IN UDP packet detected\n"); /* ESP Packet without Non-ESP header */ len = sizeof(struct udphdr); } else { /* Must be an IKE packet.. pass it through */ KLIPS_PRINT(debug_rcv, "UDP_ENCAP_ESPINUDP: IKE packet detected\n"); return 1; } break; case UDP_ENCAP_ESPINUDP_NON_IKE: KLIPS_PRINT(debug_rcv, "UDP_ENCAP_ESPINUDP_NON_IKE: len=%d 0x%x\n", len, udpdata32[0]); /* Check if this is a keepalive packet. If so, eat it. */ if (len == 1 && udpdata[0] == 0xff) { KLIPS_PRINT(debug_rcv, "UDP_ENCAP_ESPINUDP_NON_IKE: keepalive packet detected\n"); goto drop; } else if (len > 2 * sizeof(u32) + sizeof(struct ip_esp_hdr) && udpdata32[0] == 0 && udpdata32[1] == 0) { KLIPS_PRINT(debug_rcv, "UDP_ENCAP_ESPINUDP_NON_IKE: ESP IN UDP NON IKE packet detected\n"); /* ESP Packet with Non-IKE marker */ len = sizeof(struct udphdr) + 2 * sizeof(u32); } else { /* Must be an IKE packet.. pass it through */ KLIPS_PRINT(debug_rcv, "UDP_ENCAP_ESPINUDP_NON_IKE: IKE packet detected\n"); return 1; } break; } /* At this point we are sure that this is an ESPinUDP packet, * so we need to remove 'len' bytes from the packet (the UDP * header and optional ESP marker bytes) and then modify the * protocol to ESP, and then call into the transform receiver. */ if (skb_cloned(skb) && pskb_expand_head(skb, 0, 0, GFP_ATOMIC)) { KLIPS_PRINT(debug_rcv, "clone or expand problem\n"); goto drop; } /* Now we can update and verify the packet length... */ iph = ip_hdr(skb); iphlen = iph->ihl << 2; iph->tot_len = htons(ntohs(iph->tot_len) - len); if (skb->len < iphlen + len) { /* packet is too small!?! */ KLIPS_PRINT(debug_rcv, "packet too small\n"); goto drop; } /* pull the data buffer up to the ESP header and set the * transport header to point to ESP. Keep UDP on the stack * for later. */ __skb_pull(skb, len); skb_reset_transport_header(skb); /* modify the protocol (it's ESP!) */ iph->protocol = IPPROTO_ESP; /* process ESP */ KLIPS_PRINT(debug_rcv, "starting processing ESP packet\n"); ret = klips26_rcv_encap(skb, encap_type); return ret; drop: kfree_skb(skb); return 0; } int klips26_rcv_encap(struct sk_buff *skb, __u16 encap_type) { struct ipsec_rcv_state *irs = NULL; /* Don't unlink in the middle of a turnaround */ KLIPS_INC_USE; if (skb == NULL) { KLIPS_PRINT(debug_rcv, "klips_debug:klips26_rcv_encap: " "NULL skb passed in.\n"); goto rcvleave; } if (skb->data == NULL) { KLIPS_PRINT(debug_rcv, "klips_debug:klips26_rcv_encap: " "NULL skb->data passed in, packet is bogus, dropping.\n"); goto rcvleave; } irs = ipsec_rcv_state_new(); if (unlikely(!irs)) { KLIPS_PRINT(debug_rcv, "klips_debug:klips26_rcv_encap: " "failled to allocate a rcv state object\n"); goto rcvleave; } irs->hard_header_len = skb->dev->hard_header_len; switch (encap_type) { case UDP_ENCAP_ESPINUDP: irs->natt_type = ESPINUDP_WITH_NON_ESP; break; case UDP_ENCAP_ESPINUDP_NON_IKE: irs->natt_type = ESPINUDP_WITH_NON_IKE; break; default: if (printk_ratelimit()) { printk(KERN_INFO "KLIPS received unknown UDP-ESP encap type %u\n", encap_type); } goto rcvleave; } irs->skb = skb; /* * we hand off real early to the state machine because we just cannot * know how much processing it is off-loading */ ipsec_rsm(irs); return 0; rcvleave: if (irs) ipsec_rcv_state_delete(irs); if (skb) ipsec_kfree_skb(skb); KLIPS_DEC_USE; return 0; } #endif /* ------------------------------------------------------------------------ * this handles creating and managing state for recv path */ static DEFINE_SPINLOCK(irs_cache_lock); #ifdef HAVE_KMEM_CACHE_MACRO static struct kmem_cache *irs_cache_allocator = NULL; #else static kmem_cache_t *irs_cache_allocator = NULL; #endif static unsigned irs_cache_allocated_count = 0; int ipsec_irs_cache_allocated_max = 1000; module_param(ipsec_irs_cache_allocated_max, int, 0644); MODULE_PARM_DESC(ipsec_irs_cache_allocated_max, "Maximum outstanding receive packets (before they are dropped)"); int ipsec_rcv_state_cache_init(void) { if (irs_cache_allocator) return -EBUSY; spin_lock_init(&irs_cache_lock); #ifdef HAVE_KMEM_CACHE_MACRO irs_cache_allocator = KMEM_CACHE(ipsec_rcv_state, 0); #else irs_cache_allocator = kmem_cache_create("ipsec_irs", sizeof(struct ipsec_rcv_state), 0, 0, NULL, NULL); #endif if (!irs_cache_allocator) return -ENOMEM; return 0; } void ipsec_rcv_state_cache_cleanup(void) { if (unlikely(irs_cache_allocated_count)) printk("ipsec: deleting ipsec_irs kmem_cache while in use\n"); if (irs_cache_allocator) { kmem_cache_destroy(irs_cache_allocator); irs_cache_allocator = NULL; } irs_cache_allocated_count = 0; } static struct ipsec_rcv_state *ipsec_rcv_state_new(void) { struct ipsec_rcv_state *irs; spin_lock_bh(&irs_cache_lock); if (irs_cache_allocated_count >= ipsec_irs_cache_allocated_max) { spin_unlock_bh(&irs_cache_lock); KLIPS_PRINT(debug_rcv, "klips_debug:ipsec_rcv_state_new: " "exceeded maximum outstanding RX packet cnt %d\n", irs_cache_allocated_count); return NULL; } irs = kmem_cache_alloc(irs_cache_allocator, GFP_ATOMIC); if (likely(irs != NULL)) irs_cache_allocated_count++; spin_unlock_bh(&irs_cache_lock); if (unlikely(NULL == irs)) goto bail; /* initialize the object */ #if 1 memset((caddr_t)irs, 0, sizeof(*irs)); #else /* optimised to only clear the essentials */ irs->state = 0; irs->next_state = 0; irs->auth_checked = 0; irs->stats = NULL; irs->authenticator = NULL; irs->said.proto = 0; irs->hard_header_len = 0; irs->natt_type = 0; irs->natt_len = 0; irs->lastipsp = NULL; irs->ipsp = NULL; #endif bail: return irs; } static void ipsec_rcv_state_delete(struct ipsec_rcv_state *irs) { if (unlikely(!irs)) return; spin_lock_bh(&irs_cache_lock); irs_cache_allocated_count--; kmem_cache_free(irs_cache_allocator, irs); spin_unlock_bh(&irs_cache_lock); }