blob: cb07aca813d936e622ecdbb77d67045b4a69ff7a [file] [log] [blame]
/*
*
* 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;
}