/* Workhorse for destroy_all and error handler for create. `adapter' must not be in the adapter table when this function is called. */ static Boolean ssh_virtual_adapter_destroy(SshInterceptor interceptor, SshVirtualAdapter adapter) { SSH_ASSERT(adapter != NULL); ssh_kernel_mutex_assert_is_locked(interceptor->interceptor_lock); SSH_DEBUG(SSH_D_HIGHOK, ("Destroying virtual adapter %d [%s]", (adapter->dev ? adapter->dev->ifindex : -1), (adapter->dev ? adapter->dev->name : "unknown"))); if (adapter->dev) { /* Detach and destroy net_device. */ ssh_kernel_mutex_unlock(interceptor->interceptor_lock); local_bh_enable(); /* This call will produce an interface event. */ ssh_virtual_adapter_detach_low_level(adapter); local_bh_disable(); ssh_kernel_mutex_lock(interceptor->interceptor_lock); adapter->dev = NULL; } /* Free adapter */ ssh_free(adapter); /* All ok. */ return TRUE; }
void sw_fastpath_release_trd(SshFastpath fastpath, SshUInt32 trd_index) { ssh_kernel_mutex_assert_is_locked(fastpath->engine->flow_control_table_lock); /* Release the lock on the transform table element */ FP_RELEASE_TRD(fastpath, trd_index); }
void engine_audit_new_event(SshEngine engine) { ssh_kernel_mutex_assert_is_locked(engine->flow_control_table_lock); #if SSH_PM_AUDIT_REQUESTS_PER_SECOND == 0 if (!engine->audit_timeout_scheduled) { engine->audit_timeout_scheduled = 1; ssh_kernel_timeout_register(0L, 200000L, engine_audit_request_poll_with_lock, engine); } #endif /* SSH_PM_AUDIT_REQUESTS_PER_SECOND */ }
SshEngineTransformData sw_fastpath_get_trd(SshFastpath fastpath, SshUInt32 trd_index, Boolean ronly, Boolean init) { SshEngineTransformData trd; ssh_kernel_mutex_assert_is_locked(fastpath->engine->flow_control_table_lock); SSH_ASSERT((trd_index & 0xffffff) < fastpath->transform_table_size); /* Get the trd element and lock it */ trd = swi_fastpath_get_trd_lock(fastpath, trd_index, TRUE); if (init) { trd->transform = 0; memset(&trd->spis, 0, sizeof(trd->spis)); memset(&trd->old_spis, 0, sizeof(trd->old_spis)); SSH_IP_UNDEFINE(&trd->own_addr); SSH_IP_UNDEFINE(&trd->gw_addr); } if (!ronly) { fastpath->trd_cache->transform = trd->transform; fastpath->trd_cache->own_addr = trd->own_addr; fastpath->trd_cache->gw_addr = trd->gw_addr; fastpath->trd_cache->remote_port = trd->remote_port; memcpy(fastpath->trd_cache->spis, trd->spis, sizeof(fastpath->trd_cache->spis)); memcpy(fastpath->trd_cache->old_spis, trd->old_spis, sizeof(fastpath->trd_cache->old_spis)); memcpy(fastpath->trd_cache->keymat, trd->keymat, sizeof(fastpath->trd_cache->keymat)); memcpy(fastpath->trd_cache->old_keymat, trd->old_keymat, sizeof(fastpath->trd_cache->old_keymat)); fastpath->trd_cache->is_ipv6 = SSH_IP_IS6(&trd->own_addr); } return trd; }
void sw_fastpath_uninit_trd(SshFastpath fastpath, SshUInt32 trd_index, SshEngineTransformData data) { SshEngineTransformData trd; ssh_kernel_mutex_assert_is_locked(fastpath->engine->flow_control_table_lock); SSH_ASSERT((trd_index & 0xffffff) < fastpath->transform_table_size); /* Fetch the trd element. trd is already locked. */ trd = swi_fastpath_get_trd_lock(fastpath, trd_index, FALSE); SSH_ASSERT(data == trd); #ifdef SSHDIST_IPSEC_TRANSFORM if (fastpath->trd_cache->old_spis[SSH_PME_SPI_AH_IN] != 0 || fastpath->trd_cache->old_spis[SSH_PME_SPI_ESP_IN] != 0) ssh_fastpath_destroy_sa_tc(fastpath, fastpath->trd_cache->transform, fastpath->trd_cache->old_keymat, fastpath->trd_cache-> old_spis[SSH_PME_SPI_AH_IN], fastpath->trd_cache-> old_spis[SSH_PME_SPI_ESP_IN], FALSE, /* for_output */ fastpath->trd_cache->is_ipv6); ssh_fastpath_destroy_sa_tc(fastpath, fastpath->trd_cache->transform, fastpath->trd_cache->keymat, fastpath->trd_cache->spis[SSH_PME_SPI_AH_IN], fastpath->trd_cache->spis[SSH_PME_SPI_ESP_IN], FALSE, /* for_output */ fastpath->trd_cache->is_ipv6); ssh_fastpath_destroy_sa_tc(fastpath, fastpath->trd_cache->transform, fastpath->trd_cache->keymat + (SSH_IPSEC_MAX_KEYMAT_LEN / 2), fastpath->trd_cache->spis[SSH_PME_SPI_AH_OUT], fastpath->trd_cache->spis[SSH_PME_SPI_ESP_OUT], TRUE, /* for_output */ fastpath->trd_cache->is_ipv6); #endif /* SSHDIST_IPSEC_TRANSFORM */ /* Release the lock on the transform table element */ FP_RELEASE_TRD(fastpath, trd_index); }
static inline SshVirtualAdapter ssh_virtual_adapter_ifnum_to_adapter(SshInterceptor interceptor, SshInterceptorIfnum adapter_ifnum) { SshVirtualAdapter adapter = NULL; SshUInt32 i; ssh_kernel_mutex_assert_is_locked(interceptor->interceptor_lock); for (i = 0; i < SSH_LINUX_MAX_VIRTUAL_ADAPTERS; i++) { adapter = interceptor->virtual_adapters[i]; if (adapter && adapter->dev->ifindex == adapter_ifnum) return adapter; } return NULL; }
static SshEngineActionRet engine_packet_handle_pmtu(SshEngine engine, SshEnginePacketContext pc) { const unsigned char *ucp; ssh_kernel_mutex_assert_is_locked(engine->flow_control_table_lock); #if defined (WITH_IPV6) if (pc->pp->protocol == SSH_PROTOCOL_IP6) { if (pc->ipproto == SSH_IPPROTO_IPV6ICMP && pc->icmp_type == SSH_ICMP6_TYPE_TOOBIG && (pc->pp->flags & SSH_ENGINE_P_TOLOCAL)) return ssh_engine_handle_pmtu_icmp(engine, pc); } else #endif /* WITH_IPV6 */ { if (pc->ipproto == SSH_IPPROTO_ICMP) { /* Check if it is an ICMP Unreachable/Fragmentation Needed. */ ucp = ssh_interceptor_packet_pullup_read(pc->pp, pc->hdrlen + 8); if (!ucp) { SSH_DEBUG(SSH_D_FAIL, ("pullup ICMP Unreach/FragNeeded failed")); pc->pp = NULL; return SSH_ENGINE_RET_ERROR; } /* Check if it is a PMTU ICMP packet to our local stack. */ if (SSH_IPH4_PROTO(ucp) == SSH_IPPROTO_ICMP && ucp[pc->hdrlen] == SSH_ICMP_TYPE_UNREACH && ucp[pc->hdrlen + 1] == SSH_ICMP_CODE_UNREACH_NEEDFRAG && (pc->pp->flags & SSH_ENGINE_P_TOLOCAL)) { /* We have an ICMP Unreachable/Fragmentation needed destined to one of our own IP addresses. */ return ssh_engine_handle_pmtu_icmp(engine, pc); } } } return SSH_ENGINE_RET_OK; }
SshUInt32 ssh_fastpath_fragmagic_drop_data(SshFastpath fastpath, SshInterceptorPacket *frag_free_list) { SshFastpathFragEntry fe; SshUInt32 num_frags; ssh_kernel_mutex_assert_is_locked(fastpath->frag_lock); /* Get the oldest entry from the data LRU. */ fe = fastpath->frag_data_lru_tail; if (!fe) return 0; /* Sanity check that it has data. */ SSH_ASSERT(fe->num_frags > 0); /* Clear the entry. */ fe->flags |= SSH_ENGINE_FRAG_REJECT; num_frags = fe->num_frags; ssh_fastpath_fragmagic_remove_data_lru(fastpath, fe); ssh_fastpath_fragmagic_clear_entry(fastpath, fe, frag_free_list); /* "Add" the entry on the data LRU. This does not actually add it, but instead may prepare it for sanity checks. */ ssh_fastpath_fragmagic_add_data_lru(fastpath, fe); return num_frags; }
/* Frees any packets queued in the entry, and adjusts global statistics about fragments. */ void ssh_fastpath_fragmagic_clear_entry(SshFastpath fastpath, SshFastpathFragEntry fe, SshInterceptorPacket *frag_free_list) { SshInterceptorPacket pp; ssh_kernel_mutex_assert_is_locked(fastpath->frag_lock); /* Free all queued packets. */ while (fe->pp_chain) { pp = fe->pp_chain; fe->pp_chain = pp->next; ssh_fastpath_fragmagic_packet_free(pp, frag_free_list); } /* Update global statistics. */ fastpath->frag_num_fragments -= fe->num_frags; fastpath->frag_num_bytes -= fe->total_bytes; fe->num_frags = 0; fe->total_bytes = 0; fe->total_bytes = 0; fe->packet_size = 0; }
void engine_audit_busy(SshEngine engine) { #if SSH_PM_AUDIT_REQUESTS_PER_SECOND == 0 #ifdef SSH_IPSEC_UNIFIED_ADDRESS_SPACE SshEngineAuditPollRequest c; ssh_kernel_mutex_assert_is_locked(engine->flow_control_table_lock); if ((c = ssh_malloc(sizeof(*c))) == NULL) return; c->engine = engine; ssh_engine_record_upcall(engine); ssh_register_timeout(c->timeout, 0L, 0L, engine_audit_request_poll_now, c); #else /* SSH_IPSEC_UNIFIED_ADDRESS_SPACE */ ssh_engine_send(engine, FALSE, TRUE, SSH_ENCODE_UINT32((SshUInt32) 0), SSH_ENCODE_CHAR((unsigned int)SSH_EPA_AUDIT_POLL_REQUEST), SSH_ENCODE_UINT32(0), SSH_FORMAT_END); #endif /* SSH_IPSEC_UNIFIED_ADDRESS_SPACE */ #endif /* SSH_PM_AUDIT_REQUESTS_PER_SECOND */ return; }
/* Performs fragment magic on a fragment. This may queue the packet, reassemble, or process the packet according to a previously defined flow. If `reassemble' is TRUE (significant for first frags only), that indicates that the packet should be reassembled before processing. If `reassemble' is FALSE, that means that the packet should not be reassembled, but that all fragments should be processed as if their flow id was pc->flow_id. This returns SSH_ENGINE_RET_DEINITIALIZE if processing of the packet is now complete, and SSH_ENGINE_RET_RESTART_FLOW_LOOKUP if processing of the packet should continue as a fragment. If this successfully completes reassembly, this returns SSH_ENGINE_RET_RESTART, in which case processing the packet should be restarted (i.e., it should again go through sanity checks). This may also return SSH_ENGINE_RET_ERROR if an error causes pc->pp to be freed, and SSH_ENGINE_RET_DROP if it should be dropped. */ SshEngineActionRet ssh_fastpath_fragmagic(SshFastpath fastpath, SshEnginePacketContext pc, Boolean needs_reassembly) { SshEngine engine = fastpath->engine; SshFastpathFragIdStruct frag_id[1]; SshUInt32 hashvalue, num_frags, chain_len; SshFastpathFragEntry fe; SshInterceptorPacket pp_chain, *ppp, pp, frag_free_list = NULL; SshEnginePacketData pd; SshUInt16 packet_size; SshTime now; SSH_ASSERT(pc->pp != NULL); chain_len = 0; pc->audit.corruption = SSH_PACKET_CORRUPTION_NONE; SSH_DEBUG(SSH_D_MIDOK, ("fragmagic reassemble=%d%s%s", (int)needs_reassembly, (pc->pp->flags & SSH_ENGINE_P_FIRSTFRAG) ? " FIRSTFRAG" : "", (pc->pp->flags & SSH_ENGINE_P_LASTFRAG) ? " LASTFRAG" : "")); SSH_ASSERT(pc->pp->flags & SSH_ENGINE_P_ISFRAG); /* Initialize number of previously queued fragments freed. */ num_frags = 0; /* Compute fragpacketid for the packet. */ if (!ssh_fastpath_fragmagic_compute_id(fastpath, pc, frag_id)) return SSH_ENGINE_RET_ERROR; hashvalue = SSH_ENGINE_FRAG_ID_HASH(frag_id); hashvalue %= SSH_ENGINE_FRAGMENT_HASH_SIZE; ssh_interceptor_get_time(&now, NULL); ssh_kernel_mutex_lock(fastpath->frag_lock); /* If policy forbids fragments. discard them */ if (fastpath->frag_policy == SSH_IPSEC_FRAGS_NO_FRAGS) { ssh_kernel_mutex_unlock(fastpath->frag_lock); return SSH_ENGINE_RET_DROP; } /* Find the fragment entry from the hash table. */ for (fe = fastpath->frag_hash[hashvalue]; fe != NULL && (fe->ifnum != pc->pp->ifnum_in || fe->tunnel_id != pc->tunnel_id || !SSH_ENGINE_FRAG_ID_EQUAL(frag_id, fe->frag_id)); fe = fe->hash_next) ; /* If no such entry exists, create one now. */ if (fe == NULL) { /* Allocate a fragment magic entry. This will take one from the freelist. */ SSH_DEBUG(SSH_D_LOWOK, ("Allocating new fragentry")); fe = ssh_fastpath_fragmagic_get_entry(fastpath, &frag_free_list); memcpy(fe->frag_id, frag_id, sizeof(fe->frag_id)); fe->expiration = now + SSH_ENGINE_FRAGMENT_TIMEOUT; fe->flags = 0; fe->min_packet_size = 0; fe->next_offset = 0; fe->ifnum = pc->pp->ifnum_in; fe->tunnel_id = pc->tunnel_id; ssh_fastpath_fragmagic_add_hash(fastpath, fe); ssh_fastpath_fragmagic_add_all_lru(fastpath, fe); SSH_ASSERT(fe->pp_chain == NULL); } else if ((fe->expiration < engine->run_time) || (fastpath->frag_policy != SSH_IPSEC_FRAGS_STRICT_MONITOR && (fe->flags & SSH_ENGINE_FRAG_SENT_LAST))) { /* The fragment entry has expired. Re-initialize it with new data. If fragment policy is "no policy" then allow fragment contexts to be reused immediately. */ SSH_DEBUG(SSH_D_LOWOK, ("Reusing expired/unnecessary fragentry")); ssh_fastpath_fragmagic_remove_data_lru(fastpath, fe); ssh_fastpath_fragmagic_remove_all_lru(fastpath, fe); ssh_fastpath_fragmagic_clear_entry(fastpath, fe, &frag_free_list); fe->expiration = now + SSH_ENGINE_FRAGMENT_TIMEOUT; fe->flags = 0; fe->min_packet_size = 0; fe->next_offset = 0; fe->ifnum = pc->pp->ifnum_in; fe->tunnel_id = pc->tunnel_id; ssh_fastpath_fragmagic_add_all_lru(fastpath, fe); SSH_ASSERT(fe->pp_chain == NULL); } else { if ((fastpath->frag_policy == SSH_IPSEC_FRAGS_LOOSE_MONITOR) || (fastpath->frag_policy == SSH_IPSEC_FRAGS_STRICT_MONITOR)) { if (fe->flags & SSH_ENGINE_FRAG_REJECT) { SSH_DEBUG(SSH_D_NETGARB, ("fragment id collision within reject time window")); pc->audit.corruption = SSH_PACKET_CORRUPTION_FRAGMENT_ID_COLLISION; goto reject; } if ((fe->flags & SSH_ENGINE_FRAG_SENT_LAST) && (fastpath->frag_policy == SSH_IPSEC_FRAGS_LOOSE_MONITOR)) { SSH_DEBUG(SSH_D_NETGARB, ("discarding late extra fragment")); pc->audit.corruption = SSH_PACKET_CORRUPTION_FRAGMENT_LATE_AND_EXTRA; goto reject; } } } /* If any fragments require reassembly, mark fragmentation context for reassembly */ if (needs_reassembly) fe->flags |= SSH_ENGINE_FRAG_REASSEMBLE; /* If the packet is not sane then mark the whole fragmentation context to be rejected */ if (ssh_fastpath_fragmagic_is_sane(fastpath, fe, pc) == FALSE) goto reject; /* Should we dequeue fragments into the pending packets context from the fragmentation context. (E.g. do we now have enough information to process them better and does the policy allow it). */ if (fe->pp_chain != NULL && ssh_fastpath_fragmagic_is_dequeue(fastpath, fe, pc)) { SSH_DEBUG(SSH_D_NICETOKNOW, ("Dequeing fragments into pending packets")); /* Grab relevant state from the dequeued packet, because a return value of SSH_ENGINE_RET_RESTART_FLOW_LOOKUP signals that the packetcontext can be passed directly. */ ssh_fastpath_fragmagic_sent(fastpath, fe, pc); /* Check if the above function freed pc->pp */ if (pc->pp == NULL) goto error; /* Find the end of the pc->pending_packets list (normally it is empty, unless we have just added something there). */ for (ppp = &pc->pending_packets; *ppp; ppp = &(*ppp)->next) ; /* Move the fragments from the fe chain to the end of the pending_packets chain. */ ssh_fastpath_fragmagic_remove_data_lru(fastpath, fe); *ppp = fe->pp_chain; fe->pp_chain = NULL; /* Update values in each fragment. */ for (pp = *ppp; pp; pp = pp->next) { pd = SSH_INTERCEPTOR_PACKET_DATA(pp, SshEnginePacketData); memcpy(pd->pending_flow_id, fe->flow_id, sizeof(pd->pending_flow_id)); pd->pending_ret = SSH_ENGINE_RET_RESTART_FLOW_LOOKUP; SSH_DEBUG(SSH_D_MIDOK, ("Dequeued pending fragment offset %u len %u " "flags 0x%08x", pd->frag_ofs, pd->frag_len, pd->frag_flags)); } /* Update bookkeeping in the entry. */ fastpath->frag_num_fragments -= fe->num_frags; fastpath->frag_num_bytes -= fe->total_bytes; fe->num_frags = 0; fe->total_bytes = 0; ssh_fastpath_fragmagic_add_data_lru(fastpath, fe); memcpy(pc->flow_id, fe->flow_id, sizeof(pc->flow_id)); ssh_kernel_mutex_unlock(fastpath->frag_lock); ssh_fastpath_fragmagic_free_packet_list(frag_free_list); return SSH_ENGINE_RET_RESTART_FLOW_LOOKUP; } /* Check if pc->pp got freed by ssh_fastpath_fragmagic_is_dequeue() */ if (pc->pp == NULL) goto error; /* Should we pass the fragment in this packet context through without reassembly? */ if (ssh_fastpath_fragmagic_is_dequeue(fastpath, fe, pc)) { /* Grab relevant state from the dequeued packet */ ssh_fastpath_fragmagic_sent(fastpath, fe, pc); if (pc->pp == NULL) goto error; SSH_DEBUG(SSH_D_LOWOK, ("Passing fragment to flow lookup")); SSH_ASSERT(fe->pp_chain == NULL); memcpy(pc->flow_id, fe->flow_id, sizeof(pc->flow_id)); ssh_kernel_mutex_unlock(fastpath->frag_lock); ssh_fastpath_fragmagic_free_packet_list(frag_free_list); return SSH_ENGINE_RET_RESTART_FLOW_LOOKUP; } /* Check if pc->pp got freed by ssh_fastpath_fragmagic_is_dequeue() */ if (pc->pp == NULL) goto error; /* Remove this packet from the LRU lists so that we don't accidentally free it from under us. */ ssh_fastpath_fragmagic_remove_all_lru(fastpath, fe); ssh_fastpath_fragmagic_remove_data_lru(fastpath, fe); SSH_ASSERT(fastpath->frag_num_fragments <= SSH_ENGINE_FRAGMENT_MAX_PACKETS); SSH_ASSERT(fastpath->frag_num_bytes <= SSH_ENGINE_FRAGMENT_MAX_BYTES); /* Drop excess old fragments */ while (fastpath->frag_num_fragments + 1 >= SSH_ENGINE_FRAGMENT_MAX_PACKETS || fastpath->frag_num_bytes + pc->packet_len >= SSH_ENGINE_FRAGMENT_MAX_BYTES) { SshUInt32 frags_dropped; SSH_DEBUG(SSH_D_LOWOK, ("Dropping oldest packet with data")); frags_dropped = ssh_fastpath_fragmagic_drop_data(fastpath, &frag_free_list); if (frags_dropped == 0) { /* If ssh_fastpath_fragmagic_drop_data() could not drop any data, it means that value of either SSH_ENGINE_FRAGMENT_MAX_PACKETS or SSH_ENGINE_FRAGMENT_MAX_BYTES (configured in ipsec_params.h) is too small to contain even this fragmented packet. In that case we can only reject this (current packet). */ ssh_fastpath_fragmagic_add_data_lru(fastpath, fe); ssh_fastpath_fragmagic_add_all_lru(fastpath, fe); goto reject; } num_frags += frags_dropped; } /* Add the fragment into the queue. This will also put it on the data LRU if it is not already there and set flags in fe for _is_dequeue() */ ssh_fastpath_fragmagic_enqueue(fastpath, fe, pc, &frag_free_list); pc->pp = NULL; pp_chain = NULL; chain_len = 0; packet_size = 0; /* If we have a full packet, take the fragment chain from the entry, mark the entry so that it will reject everything, and update global counters. */ if (ssh_fastpath_fragmagic_full_packet(fe)) { packet_size = fe->packet_size; SSH_DEBUG(SSH_D_MIDOK, ("Got full packet, size=%d", (int)packet_size)); pp_chain = fe->pp_chain; chain_len = fe->num_frags; fe->pp_chain = NULL; fe->flags |= SSH_ENGINE_FRAG_REJECT; fastpath->frag_num_bytes -= fe->total_bytes; fastpath->frag_num_fragments -= fe->num_frags; fe->total_bytes = 0; fe->num_frags = 0; fe->packet_size = 0; } /* Re-insert the packet at the head of the LRU lists. */ ssh_fastpath_fragmagic_add_data_lru(fastpath, fe); ssh_fastpath_fragmagic_add_all_lru(fastpath, fe); /* Unlock the fragment data structures. */ ssh_kernel_mutex_unlock(fastpath->frag_lock); if (pp_chain) { /* Merge the fragments into a full chain. This frees pp_chain. */ SSH_DEBUG(SSH_D_LOWOK, ("Merging fragments into full packet")); pc->pp = ssh_fastpath_fragmagic_merge(fastpath, packet_size, pp_chain, &frag_free_list); if (pc->pp == NULL) { #ifdef SSH_IPSEC_STATISTICS SshFastpathGlobalStats stats; ssh_kernel_critical_section_start(fastpath->stats_critical_section); stats = &fastpath->stats[ssh_kernel_get_cpu()]; stats->counters[SSH_ENGINE_STAT_FRAGDROP] += num_frags; stats->counters[SSH_ENGINE_STAT_RESOURCEDROP] += chain_len; ssh_kernel_critical_section_end(fastpath->stats_critical_section); #endif /* SSH_IPSEC_STATISTICS */ SSH_DEBUG(SSH_D_MIDOK, ("Merge failed")); ssh_fastpath_fragmagic_free_packet_list(frag_free_list); return SSH_ENGINE_RET_ERROR; } } ssh_fastpath_fragmagic_free_packet_list(frag_free_list); if (pc->pp) return SSH_ENGINE_RET_RESTART; else return SSH_ENGINE_RET_DEINITIALIZE; reject: /* Reject this packet, free queued packets, and cause this entry to reject any further packets (until it times out). The packet should be on all LRU lists when we come here. */ SSH_DEBUG(SSH_D_MIDOK, ("Fragmentation rejecting packet")); ssh_kernel_mutex_assert_is_locked(fastpath->frag_lock); /* Cause this entry to reject all future packets. */ fe->flags |= SSH_ENGINE_FRAG_REJECT; ssh_fastpath_fragmagic_remove_all_lru(fastpath, fe); ssh_fastpath_fragmagic_remove_data_lru(fastpath, fe); num_frags += fe->num_frags; ssh_fastpath_fragmagic_clear_entry(fastpath, fe, &frag_free_list); ssh_fastpath_fragmagic_add_all_lru(fastpath, fe); ssh_fastpath_fragmagic_add_data_lru(fastpath, fe); ssh_kernel_mutex_unlock(fastpath->frag_lock); SSH_ENGINE_MARK_STAT(pc, SSH_ENGINE_STAT_FRAGDROP); #ifdef SSH_IPSEC_STATISTICS /* Count the previously queued packets that we are dropping. */ ssh_kernel_critical_section_start(fastpath->stats_critical_section); fastpath->stats[ssh_kernel_get_cpu()].counters[SSH_ENGINE_STAT_FRAGDROP] += num_frags; ssh_kernel_critical_section_end(fastpath->stats_critical_section); #endif /* SSH_IPSEC_STATISTICS */ ssh_fastpath_fragmagic_free_packet_list(frag_free_list); return SSH_ENGINE_RET_DROP; error: ssh_kernel_mutex_assert_is_locked(fastpath->frag_lock); ssh_kernel_mutex_unlock(fastpath->frag_lock); #ifdef SSH_IPSEC_STATISTICS ssh_kernel_critical_section_start(fastpath->stats_critical_section); fastpath->stats[ssh_kernel_get_cpu()].counters[SSH_ENGINE_STAT_FRAGDROP] += num_frags; fastpath->stats[ssh_kernel_get_cpu()].counters[SSH_ENGINE_STAT_RESOURCEDROP] += chain_len; ssh_kernel_critical_section_end(fastpath->stats_critical_section); #endif /* SSH_IPSEC_STATISTICS */ ssh_fastpath_fragmagic_free_packet_list(frag_free_list); return SSH_ENGINE_RET_ERROR; }
static void ssh_fastpath_fragmagic_enqueue(SshFastpath fastpath, SshFastpathFragEntry fe, SshEnginePacketContext pc, SshInterceptorPacket *frag_free_list) { const unsigned char *ucp; SshEnginePacketData pd, pd2, prev_pd; SshInterceptorPacket pp, *ppp, prev_pp; SshInterceptorPacket newfrag; ssh_kernel_mutex_assert_is_locked(fastpath->frag_lock); newfrag = pc->pp; #if defined (WITH_IPV6) if (newfrag->protocol == SSH_PROTOCOL_IP6) { pd = SSH_INTERCEPTOR_PACKET_DATA(newfrag, SshEnginePacketData); pd->frag_ofs = pc->fragment_offset; pd->frag_hdrlen = pc->fragh_offset; pd->frag_offset_prevnh = pc->fragh_offset_prevnh; /* The fragment's payload is from after the fragmentation header to the end of the packet. */ pd->frag_len = pc->packet_len - (pc->fragh_offset + SSH_IP6_EXT_FRAGMENT_HDRLEN); } else #endif /* WITH_IPV6 */ { if (fastpath->frag_policy == SSH_IPSEC_FRAGS_NO_FRAGS) { pc->pp = NULL; ssh_fastpath_fragmagic_packet_free(newfrag, frag_free_list); return; } #ifndef SSH_IPSEC_IP_ONLY_INTERCEPTOR SSH_ASSERT(pc->protocol_offset == 0); #endif /* not SSH_IPSEC_IP_ONLY_INTERCEPTOR */ ucp = ssh_interceptor_packet_pullup(newfrag, SSH_IPH4_HDRLEN); if (!ucp) { pc->pp = NULL; return; } SSH_ASSERT(pc->packet_len == SSH_IPH4_LEN(ucp)); pd = SSH_INTERCEPTOR_PACKET_DATA(newfrag, SshEnginePacketData); pd->frag_ofs = 8 * (SSH_IPH4_FRAGOFF(ucp) & SSH_IPH4_FRAGOFF_OFFMASK); pd->frag_hdrlen = pc->hdrlen; pd->frag_len = pc->packet_len - pc->hdrlen; } pd->pending_tunnel_id = pc->tunnel_id; if (newfrag->flags & SSH_ENGINE_P_FIRSTFRAG) pd->frag_flags = SSH_ENGINE_FRAG_QUEUED_FIRST; else if (newfrag->flags & SSH_ENGINE_P_LASTFRAG) pd->frag_flags = SSH_ENGINE_FRAG_QUEUED_LAST; else pd->frag_flags = 0; SSH_DEBUG(SSH_D_NICETOKNOW, ("enqueueing fragment offset %d len %d hdrlen %d flags 0x%08x", pd->frag_ofs, pd->frag_len, pd->frag_hdrlen, pd->frag_flags)); pd->frag_overlap = 0; /* Detach fragment from any operating system context data */ ssh_interceptor_packet_detach(newfrag); /* Remove all fragments which overlap with the current fragment */ for (ppp = &fe->pp_chain; *ppp != NULL;) { pp = *ppp; pd2 = SSH_INTERCEPTOR_PACKET_DATA(pp, SshEnginePacketData); if ((pd2->frag_ofs + pd2->frag_len <= pd->frag_ofs) && ((pd2->frag_flags & SSH_ENGINE_FRAG_QUEUED_LAST) == 0)) { ppp = &pp->next; continue; } if (pd2->frag_ofs >= pd->frag_len + pd->frag_ofs && ((pd->frag_flags & SSH_ENGINE_FRAG_QUEUED_LAST) == 0)) break; fe->flags &= ~pd2->frag_flags; fastpath->frag_num_bytes -= pd2->frag_len; fe->total_bytes -= pd2->frag_len; *ppp = pp->next; ssh_fastpath_fragmagic_packet_free(pp, frag_free_list); } /* Find the place to insert the new packet. */ prev_pp = NULL; prev_pd = NULL; pp = NULL; pd2 = NULL; for (ppp = &fe->pp_chain; *ppp; ppp = &pp->next) { prev_pp = pp; prev_pd = pd2; pp = *ppp; pd2 = SSH_INTERCEPTOR_PACKET_DATA(pp, SshEnginePacketData); if (pd->frag_ofs <= pd2->frag_ofs) break; } /* Just insert the packet */ newfrag->next = *ppp; *ppp = newfrag; fe->total_bytes += pd->frag_len; fastpath->frag_num_bytes += pd->frag_len; fe->num_frags++; fastpath->frag_num_fragments++; if (!fastpath->frag_timeout_scheduled) { SSH_DEBUG(SSH_D_MIDOK, ("Fragmagic timer; scheduled")); fastpath->frag_timeout_scheduled = 1; ssh_kernel_timeout_register(5, 0, ssh_fastpath_fragmagic_timeout, fastpath); } fe->flags |= pd->frag_flags; ssh_fastpath_fragmagic_pullup(fastpath,fe,pc); return; }
Boolean ssh_engine_copy_transform_data(SshEngine engine, SshEnginePacketContext pc) { SshEngineTransformData d_trd; SshEngineFlowData d_flow = NULL; SshEngineFlowControl c_flow; SshEngineTransformControl c_trd; Boolean rv = FALSE; ssh_kernel_mutex_assert_is_locked(engine->flow_control_table_lock); /* Perform some checks to verify that the flow and transform objects belonging to the packet context are still valid. */ if (pc->transform_index != SSH_IPSEC_INVALID_INDEX) { c_trd = SSH_ENGINE_GET_TRD(engine, pc->transform_index); if (c_trd == NULL) { SSH_ENGINE_MARK_STAT(pc, SSH_ENGINE_STAT_ERRORDROP); SSH_DEBUG(SSH_D_FAIL, ("Transform index is not valid anymore")); return FALSE; } } if (pc->flow_index != SSH_IPSEC_INVALID_INDEX) { c_flow = SSH_ENGINE_GET_FLOW(engine, pc->flow_index); if ((c_flow->control_flags & SSH_ENGINE_FLOW_C_VALID) == 0) { SSH_ENGINE_MARK_STAT(pc, SSH_ENGINE_STAT_ERRORDROP); SSH_DEBUG(SSH_D_FAIL, ("Flow disappeared.")); return FALSE; } } if (pc->flow_index != SSH_IPSEC_INVALID_INDEX) { d_flow = FASTPATH_GET_READ_ONLY_FLOW(engine->fastpath, pc->flow_index); /* Check the flow is still valid */ if (d_flow->generation != pc->flow_generation) { SSH_ENGINE_MARK_STAT(pc, SSH_ENGINE_STAT_ERRORDROP); SSH_DEBUG(SSH_D_FAIL, ("Flow disappeared.")); FASTPATH_RELEASE_FLOW(engine->fastpath, pc->flow_index); return FALSE; } } fastpath_copy_flow_data(engine->fastpath, d_flow, pc); if (pc->flow_index != SSH_IPSEC_INVALID_INDEX) FASTPATH_RELEASE_FLOW(engine->fastpath, pc->flow_index); if (pc->transform_index != SSH_IPSEC_INVALID_INDEX) { d_trd = FASTPATH_GET_READ_ONLY_TRD(engine->fastpath, pc->transform_index); if (d_trd == NULL || d_trd->transform == 0) { SSH_ENGINE_MARK_STAT(pc, SSH_ENGINE_STAT_ERRORDROP); SSH_DEBUG(SSH_D_FAIL, ("Transform disappeared.")); FASTPATH_RELEASE_TRD(engine->fastpath, pc->transform_index); return FALSE; } FASTPATH_RELEASE_TRD(engine->fastpath, pc->transform_index); } rv = fastpath_copy_transform_data(engine->fastpath, pc); return rv; }
static SshEngineActionRet engine_packet_handle_flow(SshEngine engine, SshEnginePacketContext pc) { SshEngineFlowControl c_flow; SshEngineFlowData d_flow; SshEnginePolicyRule rule; SshEngineFlowStatus undangle_status; SshEngineActionRet ret; Boolean forward; forward = (pc->flags & SSH_ENGINE_PC_FORWARD) != 0; /* We are now in "non-fastpath context" */ ssh_kernel_mutex_assert_is_locked(engine->flow_control_table_lock); c_flow = SSH_ENGINE_GET_FLOW(engine, pc->flow_index); SSH_ASSERT(c_flow->rule_index != SSH_IPSEC_INVALID_INDEX); d_flow = FASTPATH_GET_FLOW(engine->fastpath, pc->flow_index); /* Check the flow is still valid */ if (d_flow->generation != pc->flow_generation || (c_flow->control_flags & SSH_ENGINE_FLOW_C_VALID) == 0) { SSH_ENGINE_MARK_STAT(pc, SSH_ENGINE_STAT_ERRORDROP); SSH_DEBUG(SSH_D_FAIL, ("Flow disappeared.")); FASTPATH_RELEASE_FLOW(engine->fastpath, pc->flow_index); return SSH_ENGINE_RET_FAIL; } /* Check if flow is in drop mode. */ if (d_flow->data_flags & SSH_ENGINE_FLOW_D_DROP_PKTS) { SSH_ENGINE_MARK_STAT(pc, SSH_ENGINE_STAT_DROP); SSH_DEBUG(SSH_D_NICETOKNOW, ("Flow is in drop mode.")); FASTPATH_RELEASE_FLOW(engine->fastpath, pc->flow_index); return SSH_ENGINE_RET_FAIL; } /* If this is a trigger flow, then it may be that our destination next hop node is still undefined, and it must be defined, before we can undangle this flow. */ if ((c_flow->control_flags & SSH_ENGINE_FLOW_C_TRIGGER) && forward) { SSH_ASSERT((pc->flags & SSH_ENGINE_PC_HIT_TRIGGER) == 0); SSH_DEBUG(SSH_D_NICETOKNOW, ("Packet hit a trigger flow, doing rule execution")); pc->flags |= SSH_ENGINE_PC_HIT_TRIGGER; SSH_ASSERT(pc->rule == NULL); FASTPATH_RELEASE_FLOW(engine->fastpath, pc->flow_index); return SSH_ENGINE_RET_OK; } /* Check if rx_transform_index needs to be updated. */ if (pc->tunnel_id > 1 && (pc->flags & SSH_ENGINE_PC_SKIP_TRD_VERIFY) == 0 && pc->prev_transform_index != SSH_IPSEC_INVALID_INDEX) { int i = 0; Boolean found_match = FALSE; /* Check if packet was decapsulated using transform index in the opposite direction. */ if ((forward && (pc->prev_transform_index == d_flow->reverse_transform_index)) || (!forward && (pc->prev_transform_index == d_flow->forward_transform_index))) found_match = TRUE; /* Check if packet was decapsulated using one of allowed rx transform indexes. */ else { for (i = 0; i < SSH_ENGINE_NUM_RX_TRANSFORMS; i++) { if ((forward && (d_flow->forward_rx_transform_index[i] == SSH_IPSEC_INVALID_INDEX)) || (!forward && (d_flow->reverse_rx_transform_index[i] == SSH_IPSEC_INVALID_INDEX))) break; if ((forward && (pc->prev_transform_index == d_flow->forward_rx_transform_index[i])) || (!forward && (pc->prev_transform_index == d_flow->reverse_rx_transform_index[i]))) { found_match = TRUE; break; } } } /* Packets's prev_transform_index did not match any allowed transform indexes, check packet against rule. */ if (found_match == FALSE) { SSH_DEBUG(SSH_D_LOWOK, ("Packet was decapsulated using a mismatching " "transform index %lx", (unsigned long) pc->prev_transform_index)); ret = engine_packet_handler_verify_sa_selectors(engine, pc); if (ret == SSH_ENGINE_RET_OK) { /* Use an unused slot or move all trd indexes one slot earlier and reuse last slot. */ if (i == SSH_ENGINE_NUM_RX_TRANSFORMS) { for (i = 0; (i + 1) < SSH_ENGINE_NUM_RX_TRANSFORMS; i++) { if (forward) d_flow->forward_rx_transform_index[i] = d_flow->forward_rx_transform_index[i+1]; else d_flow->reverse_rx_transform_index[i] = d_flow->reverse_rx_transform_index[i+1]; } } SSH_ASSERT(i < SSH_ENGINE_NUM_RX_TRANSFORMS); if (forward) d_flow->forward_rx_transform_index[i] = pc->prev_transform_index; else d_flow->reverse_rx_transform_index[i] = pc->prev_transform_index; SSH_DEBUG(SSH_D_LOWOK, ("Inbound %s transform index for flow %d " "updated to %lx", (forward ? "forward" : "reverse"), (int) pc->flow_index, (unsigned long) pc->prev_transform_index)); pc->flags |= SSH_ENGINE_PC_SKIP_TRD_VERIFY; FASTPATH_COMMIT_FLOW(engine->fastpath, pc->flow_index, d_flow); return SSH_ENGINE_RET_RESTART_FLOW_LOOKUP; } FASTPATH_RELEASE_FLOW(engine->fastpath, pc->flow_index); return ret; } } FASTPATH_RELEASE_FLOW(engine->fastpath, pc->flow_index); rule = SSH_ENGINE_GET_RULE(engine, c_flow->rule_index); SSH_DEBUG(SSH_D_NICETOKNOW, ("Packet hit a non-trigger dangling flow!")); /* We should try to undangle the flow and restart the packet */ undangle_status = ssh_engine_flow_undangle(engine, pc->flow_index); switch (undangle_status) { case SSH_ENGINE_FLOW_STATUS_ERROR: SSH_DEBUG(SSH_D_NICETOKNOW, ("Error in undangling flow!")); ssh_engine_free_flow(engine, pc->flow_index); pc->flow_index = SSH_IPSEC_INVALID_INDEX; SSH_ENGINE_MARK_STAT(pc, SSH_ENGINE_STAT_ERRORDROP); return SSH_ENGINE_RET_FAIL; break; case SSH_ENGINE_FLOW_STATUS_REVERSE_TRIGGER: if (!forward) { SSH_DEBUG(SSH_D_FAIL, ("required reverse trigger for tunnel_id=%d", (int) rule->tunnel_id)); /* Fall-through to dangle! */ } case SSH_ENGINE_FLOW_STATUS_DANGLING: SSH_DEBUG(SSH_D_NICETOKNOW, ("Flow still dangling!")); /* Do nothing. Fall-through to trigger generation and THEN drop! */ break; /* The found_flow label requires that flow_table_lock be held. */ case SSH_ENGINE_FLOW_STATUS_WELL_DEFINED: SSH_DEBUG(SSH_D_NICETOKNOW, ("Flow became well-defined. Handling packet!")); /* Pass packet back to fastpath */ engine_packet_continue(pc, SSH_ENGINE_RET_RESTART_FLOW_LOOKUP); return SSH_ENGINE_RET_ASYNC; break; default: SSH_NOTREACHED; } if (rule->type == SSH_ENGINE_RULE_TRIGGER && forward && pc->flow_index != SSH_IPSEC_INVALID_INDEX) { /* This guarantees that we hit a trigger in engine_rule_execute.c */ pc->flags |= SSH_ENGINE_PC_HIT_TRIGGER; /* Ensure that refcounts do not leak. */ SSH_ASSERT(pc->rule == NULL); pc->rule = SSH_ENGINE_GET_RULE(engine, c_flow->rule_index); pc->rule->refcnt++; return SSH_ENGINE_RET_OK; } SSH_ENGINE_MARK_STAT(pc, SSH_ENGINE_STAT_ERRORDROP); return SSH_ENGINE_RET_FAIL; }
static SshEngineActionRet engine_packet_handler_verify_sa_selectors(SshEngine engine, SshEnginePacketContext pc) { unsigned char src_ip[SSH_IP_ADDR_SIZE], dst_ip[SSH_IP_ADDR_SIZE]; SshUInt16 src_port, dst_port; size_t ip_len; SshEngineTransformControl c_trd; SshUInt32 rule_index; SshEnginePolicyRule rule; #ifdef SSHDIST_L2TP SshEngineTransformData d_trd; #endif /* SSHDIST_L2TP */ SSH_ASSERT(pc->pp != NULL); SSH_ASSERT(pc->prev_transform_index != SSH_IPSEC_INVALID_INDEX); ssh_kernel_mutex_assert_is_locked(engine->flow_control_table_lock); SSH_DEBUG(SSH_D_MIDOK, ("Verifying that decapsulated packet matches the SA selectors " "of transform 0x%lx", (unsigned long) pc->prev_transform_index)); /* Fetch transform used for decapsulation. */ c_trd = SSH_ENGINE_GET_TRD(engine, pc->prev_transform_index); if (c_trd == NULL) { SSH_DEBUG(SSH_D_FAIL, ("Transform has 0x%lx disappeared for decapsulated packet", (unsigned long) pc->prev_transform_index)); SSH_ENGINE_MARK_STAT(pc, SSH_ENGINE_STAT_ERRORDROP); return SSH_ENGINE_RET_FAIL; } #ifdef SSHDIST_L2TP d_trd = FASTPATH_GET_READ_ONLY_TRD(engine->fastpath, pc->prev_transform_index); if (d_trd->transform & SSH_PM_IPSEC_L2TP) { FASTPATH_RELEASE_TRD(engine->fastpath, pc->prev_transform_index); SSH_DEBUG(SSH_D_LOWOK, ("Skipping SA selector check for l2tp packet")); return SSH_ENGINE_RET_OK; } FASTPATH_RELEASE_TRD(engine->fastpath, pc->prev_transform_index); #endif /* SSHDIST_L2TP */ engine_packet_handler_lookup_prepare(pc, src_ip, &src_port, dst_ip, &dst_port, &ip_len); /* Find APPLY rule to the transform. */ for (rule_index = c_trd->rules; rule_index != SSH_IPSEC_INVALID_INDEX; rule_index = rule->trd_next) { rule = SSH_ENGINE_GET_RULE(engine, rule_index); SSH_ASSERT(rule != NULL); if (rule->type == SSH_ENGINE_RULE_APPLY) { /* Compare packet 5-tupple to SA selectors in APPLY rule. Note that APPLY rule is for reverse direction, thus packet source/destination addresses and ports must be compared to the opposite rule selector. */ if (rule->protocol != pc->pp->protocol) { SSH_DEBUG(SSH_D_LOWOK, ("Rule %d: protocol mismatch", (int) rule_index)); continue; } if ((rule->selectors & SSH_SELECTOR_SRCIP) && (memcmp(rule->src_ip_low, dst_ip, ip_len) > 0 || memcmp(rule->src_ip_high, dst_ip, ip_len) < 0)) { SSH_DEBUG(SSH_D_LOWOK, ("Rule %d: destination IP mismatch", (int) rule_index)); continue; } if ((rule->selectors & SSH_SELECTOR_DSTIP) && (memcmp(rule->dst_ip_low, src_ip, ip_len) > 0 || memcmp(rule->dst_ip_high, src_ip, ip_len) < 0)) { SSH_DEBUG(SSH_D_LOWOK, ("Rule %d: source IP mismatch", (int) rule_index)); continue; } if ((rule->selectors & SSH_SELECTOR_IPPROTO) && rule->ipproto != pc->ipproto) { SSH_DEBUG(SSH_D_LOWOK, ("Rule %d: IP protocol mismatch", (int) rule_index)); continue; } if ((rule->selectors & SSH_SELECTOR_SRCPORT) && (rule->src_port_low > dst_port || rule->src_port_high < dst_port)) { SSH_DEBUG(SSH_D_LOWOK, ("Rule %d: destination port mismatch", (int) rule_index)); continue; } if (rule->selectors & SSH_SELECTOR_DSTPORT) { if ((rule->selectors & SSH_SELECTOR_ICMPTYPE) && ((rule->dst_port_low & 0xff00) != (dst_port & 0xff00))) { SSH_DEBUG(SSH_D_LOWOK, ("Rule %d: ICMP type mismatch", (int) rule_index)); continue; } if ((rule->selectors & SSH_SELECTOR_ICMPCODE) && ((rule->dst_port_low & 0x00ff) != (dst_port & 0x00ff))) { SSH_DEBUG(SSH_D_LOWOK, ("Rule %d: ICMP code mismatch", (int) rule_index)); continue; } if ((rule->selectors & (SSH_SELECTOR_ICMPTYPE | SSH_SELECTOR_ICMPCODE)) == 0 && (rule->dst_port_low > src_port || rule->dst_port_high < src_port)) { SSH_DEBUG(SSH_D_LOWOK, ("Rule %d: source port mismatch", (int) rule_index)); continue; } } /* Packet fits into SA selectors. */ SSH_DEBUG(SSH_D_MIDOK, ("Rule %d: decapsulated packet matches SA selectors", (int) rule_index)); return SSH_ENGINE_RET_OK; } } SSH_ASSERT(rule_index == SSH_IPSEC_INVALID_INDEX); SSH_DEBUG(SSH_D_MIDOK, ("Decapsulated packet does not match the SA selectors, " "dropping packet.")); pc->audit.corruption = SSH_PACKET_CORRUPTION_IPSEC_INVALID_SELECTORS; return SSH_ENGINE_RET_DROP; }
void sw_fastpath_commit_trd(SshFastpath fastpath, SshUInt32 trd_index, SshEngineTransformData data) { SshEngineTransformData trd; ssh_kernel_mutex_assert_is_locked(fastpath->engine->flow_control_table_lock); trd = swi_fastpath_get_trd_lock(fastpath, trd_index, FALSE); SSH_ASSERT(data == trd); SSH_ASSERT((trd_index & 0xffffff) < fastpath->transform_table_size); #ifdef SSHDIST_IPSEC_TRANSFORM /* SA addresses and/or NAT-T remote port has been updated */ if (SSH_IP_DEFINED(&fastpath->trd_cache->own_addr) && SSH_IP_DEFINED(&fastpath->trd_cache->gw_addr) && (SSH_IP_CMP(&data->own_addr, &fastpath->trd_cache->own_addr) || SSH_IP_CMP(&data->gw_addr, &fastpath->trd_cache->gw_addr) || data->remote_port != fastpath->trd_cache->remote_port )) { ssh_fastpath_update_sa_tc(fastpath, fastpath->trd_cache->transform, fastpath->trd_cache->old_keymat, fastpath->trd_cache-> old_spis[SSH_PME_SPI_AH_IN], fastpath->trd_cache-> old_spis[SSH_PME_SPI_ESP_IN], FALSE, /* for_output */ fastpath->trd_cache->is_ipv6, &data->own_addr, &data->gw_addr, data->remote_port); ssh_fastpath_update_sa_tc(fastpath, fastpath->trd_cache->transform, fastpath->trd_cache->keymat, fastpath->trd_cache->spis[SSH_PME_SPI_AH_IN], fastpath->trd_cache->spis[SSH_PME_SPI_ESP_IN], FALSE, /* for_output */ fastpath->trd_cache->is_ipv6, &data->own_addr, &data->gw_addr, data->remote_port); ssh_fastpath_update_sa_tc(fastpath, fastpath->trd_cache->transform, fastpath->trd_cache->keymat + (SSH_IPSEC_MAX_KEYMAT_LEN / 2), fastpath->trd_cache->spis[SSH_PME_SPI_AH_OUT], fastpath->trd_cache->spis[SSH_PME_SPI_ESP_OUT], TRUE, /* for_output */ fastpath->trd_cache->is_ipv6, &data->own_addr, &data->gw_addr, data->remote_port); } /* Inbound SPI was rekeyed */ if ((data->spis[SSH_PME_SPI_ESP_IN] != fastpath->trd_cache->spis[SSH_PME_SPI_ESP_IN]) || (data->spis[SSH_PME_SPI_AH_IN] != fastpath->trd_cache->spis[SSH_PME_SPI_AH_IN])) { /* If we still have previous old SA around, free any transform contexts relating to it now. */ if (fastpath->trd_cache->old_spis[SSH_PME_SPI_AH_IN] != 0 || fastpath->trd_cache->old_spis[SSH_PME_SPI_ESP_IN] != 0) ssh_fastpath_destroy_sa_tc(fastpath, fastpath->trd_cache->transform, fastpath->trd_cache->old_keymat, fastpath->trd_cache-> old_spis[SSH_PME_SPI_AH_IN], fastpath->trd_cache-> old_spis[SSH_PME_SPI_ESP_IN], FALSE, /* for_output */ fastpath->trd_cache->is_ipv6); } /* Old inbound SPI's are invalidated */ if ((data->old_spis[SSH_PME_SPI_ESP_IN] != fastpath->trd_cache->old_spis[SSH_PME_SPI_ESP_IN] && data->old_spis[SSH_PME_SPI_ESP_IN] == 0) || (data->old_spis[SSH_PME_SPI_AH_IN] != fastpath->trd_cache->old_spis[SSH_PME_SPI_AH_IN] && data->old_spis[SSH_PME_SPI_AH_IN] == 0)) { ssh_fastpath_destroy_sa_tc(fastpath, fastpath->trd_cache->transform, fastpath->trd_cache->old_keymat, fastpath->trd_cache-> old_spis[SSH_PME_SPI_AH_IN], fastpath->trd_cache-> old_spis[SSH_PME_SPI_ESP_IN], FALSE, /* for_output */ fastpath->trd_cache->is_ipv6); } /* Outbound SA was rekeyed */ if ((data->spis[SSH_PME_SPI_ESP_OUT] != fastpath->trd_cache->spis[SSH_PME_SPI_ESP_OUT]) || (data->spis[SSH_PME_SPI_AH_OUT] != fastpath->trd_cache->spis[SSH_PME_SPI_AH_OUT])) { /* Outbound SA was rekeyed. Destroy any old transform contexts for the outbound SA. */ ssh_fastpath_destroy_sa_tc(fastpath, fastpath->trd_cache->transform, fastpath->trd_cache->keymat + (SSH_IPSEC_MAX_KEYMAT_LEN / 2), fastpath->trd_cache->spis[SSH_PME_SPI_AH_OUT], fastpath->trd_cache-> spis[SSH_PME_SPI_ESP_OUT], TRUE, /* for_output */ fastpath->trd_cache->is_ipv6); } #endif /* SSHDIST_IPSEC_TRANSFORM */ /* Release the lock on the transform table element */ FP_COMMIT_TRD(fastpath, trd_index, trd); }