| /* |
| * |
| * Copyright (C) 2015 Freescale Semiconductor, Inc. |
| * |
| * 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. |
| * |
| * 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. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software |
| * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| */ |
| |
| #include <signal.h> |
| #include <net/if.h> |
| #include <sys/time.h> |
| |
| #include "cmm.h" |
| #include "module_ipsec.h" |
| |
| |
| |
| struct list_head sa_table[SA_HASH_TABLE_SIZE]; |
| pthread_mutex_t sa_lock = PTHREAD_MUTEX_INITIALIZER; |
| |
| |
| static inline unsigned short getSAHash(unsigned short id) |
| { |
| return (id & (SA_HASH_TABLE_SIZE -1)); |
| } |
| |
| |
| int cmmSAShow(struct cli_def * cli, char *command, char *argv[], int argc) |
| { |
| int i, count = 0; |
| struct SATable *pSAEntry; |
| struct list_head *entry; |
| char sbuf[INET6_ADDRSTRLEN], dbuf[INET6_ADDRSTRLEN]; |
| __pthread_mutex_lock(&sa_lock); |
| for (i = 0; i < SA_HASH_TABLE_SIZE; i++) |
| { |
| for(entry = list_first(&sa_table[i]); entry != &sa_table[i]; entry = list_next(entry)) |
| { |
| count++; |
| pSAEntry = container_of(entry, struct SATable, list_by_h); |
| cli_print(cli, "Sagd: %d, SPI:0x%x, sa_type: %d, protocal: %d\n", pSAEntry->SAInfo.sagd, pSAEntry->SAInfo.id.spi, pSAEntry->SAInfo.id.sa_type, pSAEntry->SAInfo.id.proto_family); |
| if(pSAEntry->SAInfo.proto_family != 0) |
| { |
| if(pSAEntry->SAInfo.proto_family == PROTO_FAMILY_IPV4) |
| cli_print(cli, "IPv4 Tunnel Header Source: %s, Destination: %s \n", inet_ntop(AF_INET, &pSAEntry->SAInfo.tunnel.ipv4h.SourceAddress, sbuf, sizeof(sbuf)), inet_ntop(AF_INET, &pSAEntry->SAInfo.tunnel.ipv4h.DestinationAddress, dbuf, sizeof(dbuf))); |
| else |
| cli_print(cli, "IPv6 Tunnel Header Source: %s, Destination: %s \n", inet_ntop(AF_INET6, pSAEntry->SAInfo.tunnel.ipv6h.SourceAddress, sbuf, sizeof(sbuf)), inet_ntop(AF_INET6, pSAEntry->SAInfo.tunnel.ipv6h.DestinationAddress, dbuf, sizeof(dbuf))); |
| } |
| } |
| } |
| __pthread_mutex_unlock(&sa_lock); |
| cli_print(cli, "Total SA count %d\n", count); |
| return CLI_OK; |
| } |
| |
| |
| static struct SATable *__cmmSAFind(unsigned short handle) |
| { |
| unsigned short hash = getSAHash(handle); |
| struct SATable *SAEntry = NULL; |
| struct list_head *entry; |
| |
| for(entry = list_first(&sa_table[hash]); entry != &sa_table[hash]; entry = list_next(entry)) |
| { |
| SAEntry = container_of(entry, struct SATable, list_by_h); |
| if (SAEntry->SAInfo.sagd == handle) |
| return SAEntry; |
| } |
| |
| return NULL; |
| } |
| |
| static struct SATable *__cmmSAAdd(PCommandIPSecCreateSA pSA_info) |
| { |
| struct SATable *newEntry; |
| unsigned short hash; |
| |
| newEntry = (struct SATable*) calloc(1, sizeof(struct SATable)); |
| if (newEntry == NULL) |
| { |
| cmm_print(DEBUG_ERROR, "%s: malloc failed\n", __func__); |
| goto err0; |
| } |
| |
| newEntry->SAInfo.sagd = pSA_info->sagd; |
| hash = getSAHash(newEntry->SAInfo.sagd); |
| memcpy(&newEntry->SAInfo.id, &pSA_info->said, sizeof(newEntry->SAInfo.id)); |
| |
| /* Add it to the hash table */ |
| list_add(&sa_table[hash], &newEntry->list_by_h); |
| |
| err0: |
| return newEntry; |
| } |
| |
| |
| int __cmmSATunnelRegister(FCI_CLIENT *fci_handle, struct SATable* SAEntry) |
| { |
| struct flow flow; |
| CommandIPSecSetTunnelRoute cmd_set_tnl_route; |
| int rc = 0; |
| flow.family = SAEntry->SAInfo.proto_family; |
| |
| if (SAEntry->SAInfo.proto_family == PROTO_FAMILY_IPV4) |
| { |
| flow.sAddr = &SAEntry->SAInfo.tunnel.ipv4h.SourceAddress; |
| flow.dAddr = &SAEntry->SAInfo.tunnel.ipv4h.DestinationAddress; |
| } |
| else |
| { |
| flow.sAddr = SAEntry->SAInfo.tunnel.ipv6h.SourceAddress; |
| flow.dAddr = SAEntry->SAInfo.tunnel.ipv6h.DestinationAddress; |
| } |
| |
| flow.fwmark = 0; |
| /* Eventhough SA is local connection, as the connection will not exist in kernel this is disabled */ |
| flow.local = 0; |
| flow.iifindex = 0; |
| flow.proto = 0; |
| |
| rc = __cmmRouteRegister(&SAEntry->tnl_rt, &flow, 0, "sa"); |
| #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,19,0) |
| /* |
| In 3.19 kernel, neighbor entry in linux neighbor cache is not created during the creation of route entry |
| in linux route cache as was done in previous versions. Consider a scenario where an SA is waiting for a |
| neigbor 'X' and some other connection creates this neigbor entry 'X' in CMM. Now the neigbor creation |
| event received by CMM will be ignored since neighbor entry is already present in CMM and no changes were |
| made to neighbor entry. SA waiting for neighbor will never know the creation of neighbor entry 'X' in CMM. |
| To fix this a dummy entry in created in CMM if the required neighbor entry is not present in linux neighbor cache. |
| */ |
| if(SAEntry->tnl_rt.route && !SAEntry->tnl_rt.route->neighEntry) |
| { |
| SAEntry->tnl_rt.route->neighEntry = __cmmNeighAdd(SAEntry->tnl_rt.route->family, SAEntry->tnl_rt.route->gwAddr, SAEntry->tnl_rt.route->oifindex); |
| SAEntry->tnl_rt.route->neighEntry->count++; |
| } |
| #endif |
| if (rc < 0) |
| goto program; |
| |
| cmm_print(DEBUG_INFO, "%s:Neighor resolved \n", __func__); |
| cmmFeRouteUpdate(fci_handle, ADD | UPDATE, SAEntry->tnl_rt.fpp_route); |
| |
| program: |
| |
| __cmmCheckFPPRouteIdUpdate(&SAEntry->tnl_rt, &SAEntry->flags); |
| cmd_set_tnl_route.sagd = SAEntry->SAInfo.sagd; |
| cmd_set_tnl_route.route_id = SAEntry->tnl_rt.fpp_route_id; |
| |
| /* Send the tunnel command to FPP */ |
| if (SAEntry->flags & FPP_NEEDS_UPDATE) |
| { |
| if (cmmKeyEnginetoIPSec(fci_handle, FPP_CMD_IPSEC_SA_TNL_ROUTE, sizeof(CommandIPSecSetTunnelRoute),(unsigned short*) &cmd_set_tnl_route) < 0) |
| { |
| cmm_print(DEBUG_ERROR, "%s:cmmKeyEnginetoIPSec failed while setting tunnel route:\n", __func__); |
| return -1; |
| } |
| } |
| |
| SAEntry->flags &= ~FPP_NEEDS_UPDATE; |
| |
| return rc; |
| } |
| |
| |
| static void __cmmSARouteUpdate(FCI_CLIENT *fci_handle, struct SATable *s, struct RtEntry *route) |
| { |
| struct ct_route rt = s->tnl_rt; |
| |
| cmm_print(DEBUG_INFO, "%s\n", __func__); |
| |
| if (route->flags & INVALID) |
| { |
| s->tnl_rt.route = NULL; |
| s->tnl_rt.fpp_route = NULL; |
| } |
| else |
| { |
| rt.route = NULL; |
| s->tnl_rt.fpp_route = NULL; |
| } |
| |
| __pthread_mutex_lock(&sa_lock); |
| __cmmSATunnelRegister(fci_handle, s); |
| __pthread_mutex_unlock(&sa_lock); |
| |
| __cmmRouteDeregister(fci_handle, &rt, "sa"); |
| } |
| |
| void __cmmSAUpdateWithRoute(FCI_CLIENT *fci_handle, struct RtEntry *route) |
| { |
| struct SATable *s; |
| struct list_head *entry; |
| int i; |
| |
| |
| for (i = 0; i < SA_HASH_TABLE_SIZE; i++) |
| { |
| for (entry = list_first(&sa_table[i]); entry != &sa_table[i]; entry = list_next(entry)) |
| { |
| s = container_of(entry, struct SATable, list_by_h); |
| |
| if (s->tnl_rt.route == route) |
| __cmmSARouteUpdate(fci_handle, s, route); |
| } |
| } |
| |
| } |
| |
| int __cmmRouteIsSA(int family, const unsigned int* daddr, struct SATable* sa, int prefix_match, int prefix_len) |
| { |
| unsigned int* tunnel_daddr; |
| int addr_len = IPADDRLEN(family); |
| |
| |
| if (sa->tnl_rt.route) |
| goto out; |
| |
| if (sa->SAInfo.proto_family != family) |
| goto out; |
| |
| if (sa->SAInfo.proto_family == PROTO_FAMILY_IPV4) |
| tunnel_daddr = &sa->SAInfo.tunnel.ipv4h.DestinationAddress; |
| else |
| tunnel_daddr = sa->SAInfo.tunnel.ipv6h.DestinationAddress; |
| |
| if (prefix_match) |
| { |
| if (cmmPrefixEqual(tunnel_daddr, daddr, prefix_len)) |
| return 1; |
| } |
| else |
| { |
| if (memcmp(tunnel_daddr, daddr, addr_len) == 0) |
| return 1; |
| } |
| out: |
| return 0; |
| } |
| |
| static int __cmmSARemove(FCI_CLIENT *fci_handle, struct SATable *SAEntry) |
| { |
| unsigned short hash; |
| hash = getSAHash(SAEntry->SAInfo.sagd); |
| |
| __cmmRouteDeregister(fci_handle, &SAEntry->tnl_rt, "sa"); |
| |
| /* Remove it from the hash table */ |
| list_del(&SAEntry->list_by_h); |
| free(SAEntry); |
| |
| return 0; |
| } |
| |
| |
| int cmmSACreate(FCI_CLIENT *fci_handle, unsigned short fcode, unsigned short len, unsigned short *payload) |
| { |
| PCommandIPSecCreateSA pSA_cmd = (PCommandIPSecCreateSA)payload; |
| struct SATable *pSAEntry; |
| int rc = 0; |
| if (len != sizeof(CommandIPSecCreateSA)) |
| { |
| cmm_print(DEBUG_ERROR, "%s: command length doesn't match %d-%d\n", __func__, sizeof(CommandIPSecCreateSA), len); |
| return -1; |
| } |
| |
| cmm_print(DEBUG_INFO, "%s: fcode 0x%x len %d bytes\n", __func__, fcode, len); |
| |
| __pthread_mutex_lock(&sa_lock); |
| pSAEntry = __cmmSAFind(pSA_cmd->sagd); |
| |
| if (pSAEntry) |
| { |
| cmm_print(DEBUG_ERROR, "%s: SA exists :%x \n", __func__, pSA_cmd->sagd); |
| rc = -1; |
| goto out; |
| } |
| |
| pSAEntry = __cmmSAAdd(pSA_cmd); |
| if(!pSAEntry) |
| { |
| rc = -1; |
| goto out; |
| } |
| cmm_print(DEBUG_INFO, "%s: new SA added :%x \n", __func__, pSA_cmd->sagd); |
| |
| out: |
| __pthread_mutex_unlock(&sa_lock); |
| return rc; |
| |
| |
| } |
| |
| int cmmSADelete(FCI_CLIENT *fci_handle, unsigned short fcode, unsigned short len, unsigned short *payload) |
| { |
| PCommandIPSecDeleteSA pSA_cmd = (PCommandIPSecDeleteSA)payload; |
| struct SATable *pSAEntry; |
| int rc = 0; |
| if (len != sizeof(CommandIPSecDeleteSA)) |
| { |
| cmm_print(DEBUG_ERROR, "%s: command length doesn't match %d-%d\n", __func__, sizeof(CommandIPSecDeleteSA), len); |
| return -1; |
| } |
| __pthread_mutex_lock(&sa_lock); |
| __pthread_mutex_lock(&itf_table.lock); |
| __pthread_mutex_lock(&rtMutex); |
| __pthread_mutex_lock(&neighMutex); |
| __pthread_mutex_lock(&flowMutex); |
| |
| pSAEntry = __cmmSAFind(pSA_cmd->sagd); |
| if (!pSAEntry) |
| { |
| cmm_print(DEBUG_ERROR, "%s: SA doesn't exist :%x \n", __func__, pSA_cmd->sagd); |
| rc = -1; |
| goto out; |
| } |
| __cmmSARemove(fci_handle, pSAEntry); |
| |
| out: |
| __pthread_mutex_unlock(&flowMutex); |
| __pthread_mutex_unlock(&neighMutex); |
| __pthread_mutex_unlock(&rtMutex); |
| __pthread_mutex_unlock(&itf_table.lock); |
| __pthread_mutex_unlock(&sa_lock); |
| return rc; |
| } |
| |
| |
| int cmmSAFlush(FCI_CLIENT *fci_handle, unsigned short fcode, unsigned short len, unsigned short *payload) |
| { |
| int i, rc = 0; |
| struct SATable *pSAEntry; |
| struct list_head *entry; |
| |
| __pthread_mutex_lock(&sa_lock); |
| __pthread_mutex_lock(&itf_table.lock); |
| __pthread_mutex_lock(&rtMutex); |
| __pthread_mutex_lock(&neighMutex); |
| __pthread_mutex_lock(&flowMutex); |
| |
| for (i = 0; i < SA_HASH_TABLE_SIZE; i++) |
| { |
| for(entry = list_first(&sa_table[i]); entry != &sa_table[i]; ) |
| { |
| pSAEntry = container_of(entry, struct SATable, list_by_h); |
| entry = list_next(entry); |
| __cmmSARemove(fci_handle, pSAEntry); |
| } |
| } |
| __pthread_mutex_unlock(&flowMutex); |
| __pthread_mutex_unlock(&neighMutex); |
| __pthread_mutex_unlock(&rtMutex); |
| __pthread_mutex_unlock(&itf_table.lock); |
| __pthread_mutex_unlock(&sa_lock); |
| return rc; |
| } |
| |
| int cmmSASetTunnel(FCI_CLIENT *fci_handle, unsigned short fcode, unsigned short len, unsigned short *payload) |
| { |
| PCommandIPSecSetTunnel pSA_cmd = (PCommandIPSecSetTunnel)payload; |
| int rc = 0; |
| struct SATable *pSAEntry; |
| if (len != sizeof(CommandIPSecSetTunnel)) |
| { |
| cmm_print(DEBUG_ERROR, "%s: command length doesn't match %d-%d\n", __func__, sizeof(CommandIPSecSetTunnel), len); |
| return -1; |
| } |
| __pthread_mutex_lock(&sa_lock); |
| __pthread_mutex_lock(&itf_table.lock); |
| __pthread_mutex_lock(&rtMutex); |
| __pthread_mutex_lock(&neighMutex); |
| __pthread_mutex_lock(&flowMutex); |
| pSAEntry = __cmmSAFind(pSA_cmd->sagd); |
| |
| if (!pSAEntry) |
| { |
| cmm_print(DEBUG_ERROR, "%s: SA doesn't exist :%x \n", __func__, pSA_cmd->sagd); |
| rc = -1; |
| goto out; |
| } |
| |
| pSAEntry->SAInfo.proto_family = pSA_cmd->proto_family; |
| if (pSA_cmd->proto_family == PROTO_FAMILY_IPV4) |
| memcpy(&pSAEntry->SAInfo.tunnel.ipv4h, &pSA_cmd->h.ipv4h, IPV4_HDR_SIZE); |
| else |
| memcpy(&pSAEntry->SAInfo.tunnel.ipv6h, &pSA_cmd->h.ipv6h, IPV6_HDR_SIZE); |
| |
| /* Find the route for tunnel and corresponding neighbor here */ |
| rc = __cmmSATunnelRegister(fci_handle, pSAEntry); |
| out: |
| __pthread_mutex_unlock(&flowMutex); |
| __pthread_mutex_unlock(&neighMutex); |
| __pthread_mutex_unlock(&rtMutex); |
| __pthread_mutex_unlock(&itf_table.lock); |
| __pthread_mutex_unlock(&sa_lock); |
| return rc; |
| } |
| |