blob: b01d67fe4fd65ea6f7598ba049ddf2128a659557 [file] [log] [blame]
/*
* Copyright (c) 2009 Mindspeed Technologies, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*
*/
#include "module_socket.h"
#ifdef COMCERTO_1000
#include "module_msp.h"
#endif
#if defined(COMCERTO_2000)
#include "control_common.h"
PVOID CLASS_PE_LMEM_SH2(sock4_cache)[NUM_SOCK_ENTRIES] __attribute__((aligned(32)));
PVOID CLASS_PE_LMEM_SH2(sock6_cache)[NUM_SOCK_ENTRIES] __attribute__((aligned(32)));
PVOID CLASS_PE_LMEM_SH2(sockid_cache)[NUM_SOCK_ENTRIES] __attribute__((aligned(32)));
PVOID UTIL_DMEM_SH2(sock4_cache)[NUM_SOCK_ENTRIES] __attribute__((aligned(32)));
PVOID UTIL_DMEM_SH2(sock6_cache)[NUM_SOCK_ENTRIES] __attribute__((aligned(32)));
PVOID UTIL_DMEM_SH2(sockid_cache)[NUM_SOCK_ENTRIES] __attribute__((aligned(32)));
struct dma_pool *socket_dma_pool;
extern TIMER_ENTRY socket_timer;
struct dlist_head hw_sock_removal_list; /* common list to maintain hw socket under delayed removal state */
struct dlist_head hw_sock4_active_list[NUM_SOCK_ENTRIES]; /* list to maintain active ipv4 hw sockets */
struct dlist_head hw_sock6_active_list[NUM_SOCK_ENTRIES]; /* list to maintain active ipv6 hw sockets */
struct dlist_head hw_sock_active_list_id[NUM_SOCK_ENTRIES]; /* list to maintain active hw sockets by socket's ID */
#endif
static void SOCKET4_delete_route(PSockEntry pSocket)
{
L2_route_put(pSocket->pRtEntry);
pSocket->pRtEntry = NULL;
}
static void SOCKET6_delete_route(PSock6Entry pSocket)
{
L2_route_put(pSocket->pRtEntry);
pSocket->pRtEntry = NULL;
}
BOOL SOCKET4_check_route(PSockEntry pSocket)
{
PRouteEntry pRtEntry;
pRtEntry = L2_route_get(pSocket->route_id);
if (pRtEntry == NULL)
return FALSE;
pSocket->pRtEntry = pRtEntry;
return TRUE;
}
BOOL SOCKET6_check_route(PSock6Entry pSocket)
{
PRouteEntry pRtEntry;
pRtEntry = L2_route_get(pSocket->route_id);
if (pRtEntry == NULL)
return FALSE;
pSocket->pRtEntry = pRtEntry;
return TRUE;
}
#if defined(COMCERTO_2000)
/** Update PE's internal memory socket cache tables with the HW entry's DDR address
*
* @entry_ddr DDR physical address of the hw socket entry
*
*/
static void socket_add_to_class(U32 host_addr, U32 *pe_addr)
{
class_bus_write(host_addr, virt_to_class_pe_lmem(pe_addr), 4);
}
static void socket_add_to_util(U32 host_addr, U32 *pe_addr)
{
pe_dmem_write(UTIL_ID, host_addr, virt_to_util_dmem(pe_addr), 4);
}
static void socket_update_pe_sockid_cache(U32 host_addr, U32 hash)
{
U32 *pe_addr;
pe_addr = (U32*)&class_sockid_cache[hash];
socket_add_to_class(host_addr, pe_addr);
pe_addr = (U32*)&util_sockid_cache[hash];
socket_add_to_util(host_addr, pe_addr);
}
static void socket_update_pe_sock4_cache(U32 host_addr, U32 hash)
{
U32 *pe_addr;
pe_addr = (U32*)&class_sock4_cache[hash];
socket_add_to_class(host_addr, pe_addr);
pe_addr = (U32*)&util_sock4_cache[hash];
socket_add_to_util(host_addr, pe_addr);
}
static void socket_update_pe_sock6_cache(U32 host_addr, U32 hash)
{
U32 *pe_addr;
pe_addr = (U32*)&class_sock6_cache[hash];
socket_add_to_class(host_addr, pe_addr);
pe_addr = (U32*)&util_sock6_cache[hash];
socket_add_to_util(host_addr, pe_addr);
}
void socket4_update(PSockEntry pSocket, u8 event)
{
struct _thw_sock *hw_sock = pSocket->hw_sock;
int i;
if(!pSocket->hw_sock)
return;
if(!pSocket->pRtEntry)
SOCKET4_check_route(pSocket);
hw_entry_set_flags(&hw_sock->flags, SOCK_UPDATING | SOCK_VALID);
if(event == SOCKET_CREATE)
{
hw_sock->initial_takeover_done = pSocket->initial_takeover_done;
}
hw_sock->queue = cpu_to_be16(pSocket->queue);
hw_sock->dscp = cpu_to_be16(pSocket->dscp);
hw_sock->SocketType = pSocket->SocketType;
hw_sock->SocketFamily = pSocket->SocketFamily;
hw_sock->SocketID = cpu_to_be16(pSocket->SocketID);
hw_sock->owner = pSocket->owner;
hw_sock->owner_type = cpu_to_be16(pSocket->owner_type);
hw_sock->Sport = pSocket->Sport;
hw_sock->Dport = pSocket->Dport;
hw_sock->proto = pSocket->proto;
hw_sock->connected = pSocket->connected;
hw_sock->Saddr_v4 = pSocket->Saddr_v4;
hw_sock->Daddr_v4 = pSocket->Daddr_v4;
hw_sock->hash = cpu_to_be16(pSocket->hash);
hw_sock->hash_by_id = cpu_to_be16(pSocket->hash_by_id);
hw_sock->secure = cpu_to_be16(pSocket->secure);
hw_sock->SA_nr_rx = cpu_to_be16(pSocket->SA_nr_rx);
for (i = 0; i < pSocket->SA_nr_rx; i++)
hw_sock->SA_handle_rx[i] = cpu_to_be16(pSocket->SA_handle_rx[i]);
hw_sock->SA_nr_tx = cpu_to_be16(pSocket->SA_nr_tx);
for (i = 0; i < pSocket->SA_nr_tx; i++)
hw_sock->SA_handle_tx[i] = cpu_to_be16(pSocket->SA_handle_tx[i]);
if(pSocket->pRtEntry)
{
hw_sock->route.itf = cpu_to_be32(virt_to_class(pSocket->pRtEntry->itf));
memcpy(hw_sock->route.dstmac, pSocket->pRtEntry->dstmac, ETHER_ADDR_LEN);
hw_sock->route.mtu = cpu_to_be16(pSocket->pRtEntry->mtu);
hw_sock->route.Daddr_v4 = pSocket->pRtEntry->Daddr_v4;
}
else
{
/* mark route as not valid */
hw_sock->route.itf = 0xffffffff;
}
hw_entry_set_flags(&hw_sock->flags, SOCK_VALID);
}
void socket6_update(PSock6Entry pSocket, u8 event)
{
struct _thw_sock *hw_sock = pSocket->hw_sock;
int i;
if(!pSocket->hw_sock)
return;
if(!pSocket->pRtEntry)
SOCKET6_check_route(pSocket);
hw_entry_set_flags(&hw_sock->flags, SOCK_UPDATING | SOCK_VALID);
if(event == SOCKET_CREATE)
{
hw_sock->initial_takeover_done = pSocket->initial_takeover_done;
}
hw_sock->queue = cpu_to_be16(pSocket->queue);
hw_sock->dscp = cpu_to_be16(pSocket->dscp);
hw_sock->SocketType = pSocket->SocketType;
hw_sock->SocketFamily = pSocket->SocketFamily;
hw_sock->SocketID = cpu_to_be16(pSocket->SocketID);
hw_sock->owner = pSocket->owner;
hw_sock->owner_type = cpu_to_be16(pSocket->owner_type);
hw_sock->Sport = pSocket->Sport;
hw_sock->Dport = pSocket->Dport;
hw_sock->proto = pSocket->proto;
hw_sock->connected = pSocket->connected;
hw_sock->hash = cpu_to_be16(pSocket->hash);
hw_sock->hash_by_id = cpu_to_be16(pSocket->hash_by_id);
hw_sock->secure = cpu_to_be16(pSocket->secure);
hw_sock->SA_nr_rx = cpu_to_be16(pSocket->SA_nr_rx);
for (i = 0; i < pSocket->SA_nr_rx; i++)
hw_sock->SA_handle_rx[i] = cpu_to_be16(pSocket->SA_handle_rx[i]);
hw_sock->SA_nr_tx = cpu_to_be16(pSocket->SA_nr_tx);
for (i = 0; i < pSocket->SA_nr_tx; i++)
hw_sock->SA_handle_tx[i] = cpu_to_be16(pSocket->SA_handle_tx[i]);
SFL_memcpy(hw_sock->Saddr_v6, pSocket->Saddr_v6, IPV6_ADDRESS_LENGTH);
SFL_memcpy(hw_sock->Daddr_v6, pSocket->Daddr_v6, IPV6_ADDRESS_LENGTH);
if(pSocket->pRtEntry)
{
hw_sock->route.itf = cpu_to_be32(virt_to_class(pSocket->pRtEntry->itf));
memcpy(hw_sock->route.dstmac, pSocket->pRtEntry->dstmac, ETHER_ADDR_LEN);
hw_sock->route.mtu = cpu_to_be16(pSocket->pRtEntry->mtu);
hw_sock->route.Daddr_v4 = pSocket->pRtEntry->Daddr_v4;
}
else
{
/* mark route as not valid */
hw_sock->route.itf = 0xffffffff;
}
hw_entry_set_flags(&hw_sock->flags, SOCK_VALID);
}
/** Schedules an hardware socket entry for removal.
* The entry is added to a removal list and it will be free later from a timer.
* The removal time must be bigger than the worst case PE processing time for tens of packets.
*
* @param hw_sock pointer to the hardware socket entry
*
*/
static void hw_socket_schedule_remove(struct _thw_sock *hw_sock)
{
hw_sock->removal_time = jiffies + 2;
dlist_add(&hw_sock_removal_list, &hw_sock->list);
}
/** Processes hardware socket delayed removal list.
* Free all hardware socket in the removal list that have reached their removal time.
*
*
*/
static void hw_socket_delayed_remove(void)
{
struct dlist_head *entry;
struct _thw_sock *hw_sock;
dlist_for_each_safe(hw_sock, entry, &hw_sock_removal_list, list)
{
if (!time_after(jiffies, hw_sock->removal_time))
continue;
dlist_remove(&hw_sock->list);
dma_pool_free(socket_dma_pool, hw_sock, be32_to_cpu(hw_sock->dma_addr));
}
}
PSockEntry socket4_alloc(void)
{
return pfe_kzalloc(sizeof(SockEntry), GFP_KERNEL);
}
void socket4_free(PSockEntry pSocket)
{
pfe_kfree(pSocket);
}
/** Adds a software socket entry to the local hash and hardware hash (making it visible to PFE).
*
* @param pEntry pointer to the software socket
* @param hash hash index where to add the socket
*
* @return NO_ERR in case of success, ERR_xxx in case of error
*/
int socket4_add(PSockEntry pSocket)
{
struct _thw_sock *hw_sock;
struct _thw_sock *hw_sock_first;
int rc;
dma_addr_t dma_addr;
/* Allocate hardware entry */
hw_sock = dma_pool_alloc(socket_dma_pool, GFP_ATOMIC, &dma_addr);
if (!hw_sock)
{
rc = ERR_NOT_ENOUGH_MEMORY;
goto err;
}
memset(hw_sock, 0, sizeof(struct _thw_sock));
hw_sock->dma_addr = cpu_to_be32(dma_addr);
/* Link software conntrack to hardware conntrack */
pSocket->hw_sock = hw_sock;
hw_sock->sw_sock = pSocket;
/* add hw entry to active list and update next pointer */
if(!dlist_empty(&hw_sock4_active_list[pSocket->hash]))
{
/* list is not empty, and we'll be added at head, so current first will become our next pointer */
hw_sock_first = container_of(dlist_first(&hw_sock4_active_list[pSocket->hash]), typeof(struct _thw_sock), list);
hw_entry_set_field(&hw_sock->next, hw_entry_get_field(&hw_sock_first->dma_addr));
}
else
{
/* entry is empty, so we'll be the first and only one entry */
hw_entry_set_field(&hw_sock->next, 0);
}
dlist_add(&hw_sock4_active_list[pSocket->hash], &hw_sock->list);
/* idem for the by id list */
if(!dlist_empty(&hw_sock_active_list_id[pSocket->hash_by_id]))
{
hw_sock_first = container_of(dlist_first(&hw_sock_active_list_id[pSocket->hash_by_id]), typeof(struct _thw_sock), list_id);
hw_entry_set_field(&hw_sock->nextid, hw_entry_get_field(&hw_sock_first->dma_addr));
}
else
{
hw_entry_set_field(&hw_sock->nextid, 0);
}
dlist_add(&hw_sock_active_list_id[pSocket->hash_by_id], &hw_sock->list_id);
/* fill in the hw entry */
socket4_update(pSocket, SOCKET_CREATE);
/* Update PE's internal memory socket cache tables with the HW entry's DDR address */
socket_update_pe_sock4_cache(hw_sock->dma_addr, pSocket->hash);
socket_update_pe_sockid_cache(hw_sock->dma_addr, pSocket->hash_by_id);
/* Add software entry to local hash */
slist_add(&sock4_cache[pSocket->hash], &pSocket->list);
slist_add(&sockid_cache[pSocket->hash_by_id], &pSocket->list_id);
return NO_ERR;
err:
socket4_free(pSocket);
return rc;
}
/** Removes a software socket entry from the local hash and hardware hash
* The hardware socket is marked invalid/in use and scheduled to delayed removal.
* The software socket is removed immediately from the local hash.
*
* @param pEntry pointer to the software socket
* @param hash hash index where to remove the socket
*
*/
void socket4_remove(PSockEntry pSocket, U32 hash, U32 hash_by_id)
{
struct _thw_sock *hw_sock;
struct _thw_sock *hw_sock_prev;
/* Check if there is a hardware socket */
if ((hw_sock = pSocket->hw_sock))
{
/* detach from software socket */
pSocket->hw_sock = NULL;
/* if the removed entry is first in hash slot then only PE dmem hash need to be updated */
if (&hw_sock->list == dlist_first(&hw_sock4_active_list[hash]))
{
socket_update_pe_sock4_cache(hw_entry_get_field(&hw_sock->next), hash);
}
else
{
hw_sock_prev = container_of(hw_sock->list.prev, typeof(struct _thw_sock), list);
hw_entry_set_field(&hw_sock_prev->next, hw_entry_get_field(&hw_sock->next));
}
dlist_remove(&hw_sock->list);
if (&hw_sock->list_id == dlist_first(&hw_sock_active_list_id[hash_by_id]))
{
socket_update_pe_sockid_cache(hw_entry_get_field(&hw_sock->nextid), hash_by_id);
}
else
{
hw_sock_prev = container_of(hw_sock->list_id.prev, typeof(struct _thw_sock), list_id);
hw_entry_set_field(&hw_sock_prev->nextid, hw_entry_get_field(&hw_sock->nextid));
}
dlist_remove(&hw_sock->list_id);
/* now switching hw entry from active to delayed removal list */
hw_socket_schedule_remove(hw_sock);
}
/* destroy sw socket entry */
SOCKET4_delete_route(pSocket);
/* Unlink from software list */
slist_remove(&sock4_cache[hash], &pSocket->list);
slist_remove(&sockid_cache[hash_by_id], &pSocket->list_id);
socket4_free(pSocket);
}
PSock6Entry socket6_alloc(void)
{
return pfe_kzalloc(sizeof(Sock6Entry), GFP_KERNEL);
}
void socket6_free(PSock6Entry pSocket)
{
pfe_kfree(pSocket);
}
int socket6_add(PSock6Entry pSocket)
{
struct _thw_sock *hw_sock;
struct _thw_sock *hw_sock_first;
int rc;
dma_addr_t dma_addr;
/* Allocate hardware entry */
hw_sock = dma_pool_alloc(socket_dma_pool, GFP_ATOMIC, &dma_addr);
if (!hw_sock)
{
rc = ERR_NOT_ENOUGH_MEMORY;
goto err;
}
memset(hw_sock, 0, sizeof(struct _thw_sock));
hw_sock->dma_addr = cpu_to_be32(dma_addr);
/* Link software conntrack to hardware conntrack */
pSocket->hw_sock = hw_sock;
hw_sock->sw_sock = pSocket;
/* add hw entry to active list and update next pointer */
if(!dlist_empty(&hw_sock6_active_list[pSocket->hash]))
{
/* list is not empty, and we'll be added at head, so current first will become our next pointer */
hw_sock_first = container_of(dlist_first(&hw_sock6_active_list[pSocket->hash]), typeof(struct _thw_sock), list);
hw_entry_set_field(&hw_sock->next, hw_entry_get_field(&hw_sock_first->dma_addr));
}
else
{
/* entry is empty, so we'll be the first and only one entry */
hw_entry_set_field(&hw_sock->next, 0);
}
dlist_add(&hw_sock6_active_list[pSocket->hash], &hw_sock->list);
/* idem for the by id list */
if(!dlist_empty(&hw_sock_active_list_id[pSocket->hash_by_id]))
{
hw_sock_first = container_of(dlist_first(&hw_sock_active_list_id[pSocket->hash_by_id]), typeof(struct _thw_sock), list_id);
hw_entry_set_field(&hw_sock->nextid, hw_entry_get_field(&hw_sock_first->dma_addr));
}
else
{
hw_entry_set_field(&hw_sock->nextid, 0);
}
dlist_add(&hw_sock_active_list_id[pSocket->hash_by_id], &hw_sock->list_id);
/* fill in the hw entry */
socket6_update(pSocket, SOCKET_CREATE);
/* Update PE's internal memory socket cache tables with the HW entry's DDR address */
socket_update_pe_sock6_cache(hw_sock->dma_addr, pSocket->hash);
socket_update_pe_sockid_cache(hw_sock->dma_addr, pSocket->hash_by_id);
slist_add(&sock6_cache[pSocket->hash], &pSocket->list);
slist_add(&sockid_cache[pSocket->hash_by_id], &pSocket->list_id);
return NO_ERR;
err:
socket6_free(pSocket);
return rc;
}
/** Removes a software socket entry from the local hash and hardware hash
* The hardware socket is marked invalid/in use and scheduled to delayed removal.
* The software socket is removed immediately from the local hash.
*
* @param pEntry pointer to the software socket
* @param hash hash index where to remove the socket
*
*/
void socket6_remove(PSock6Entry pSocket, U32 hash, U32 hash_by_id)
{
struct _thw_sock *hw_sock;
struct _thw_sock *hw_sock_prev;
/* Check if there is a hardware socket */
if ((hw_sock = pSocket->hw_sock))
{
/* detach from software socket */
pSocket->hw_sock = NULL;
/* if the removed entry is first in hash slot then only PE dmem hash need to be updated */
if (&hw_sock->list == dlist_first(&hw_sock6_active_list[hash]))
{
socket_update_pe_sock6_cache(hw_entry_get_field(&hw_sock->next), hash);
}
else
{
hw_sock_prev = container_of(hw_sock->list.prev, typeof(struct _thw_sock), list);
hw_entry_set_field(&hw_sock_prev->next, hw_entry_get_field(&hw_sock->next));
}
if (&hw_sock->list_id == dlist_first(&hw_sock_active_list_id[hash_by_id]))
{
socket_update_pe_sockid_cache(hw_entry_get_field(&hw_sock->nextid), hash_by_id);
}
else
{
hw_sock_prev = container_of(hw_sock->list_id.prev, typeof(struct _thw_sock), list_id);
hw_entry_set_field(&hw_sock_prev->nextid, hw_entry_get_field(&hw_sock->nextid));
}
/* Unlink from hardware lists */
dlist_remove(&hw_sock->list);
dlist_remove(&hw_sock->list_id);
/* now switching hw entry from active to delayed removal list */
hw_socket_schedule_remove((struct _thw_sock *)hw_sock);
}
SOCKET6_delete_route(pSocket);
/* Unlink from software list */
slist_remove(&sock6_cache[hash], &pSocket->list);
slist_remove(&sockid_cache[hash_by_id], &pSocket->list_id);
/* Free local entry */
socket6_free(pSocket);
}
#else
PSockEntry socket4_alloc(void)
{
PSockEntry sock = Heap_Alloc(sizeof(SockEntry));
if (sock)
memset(sock,0, sizeof(SockEntry));
return (sock);
}
void socket4_free(PSockEntry pSocket)
{
Heap_Free((PVOID)pSocket);
}
int socket4_add(PSockEntry pSocket)
{
slist_add(&sock4_cache[pSocket->hash], &pSocket->list);
slist_add(&sockid_cache[pSocket->hash_by_id], &pSocket->list_id);
return NO_ERR;
}
void socket4_remove(PSockEntry pSocket, U32 hash, U32 hash_by_id)
{
SOCKET4_delete_route(pSocket);
slist_remove(&sock4_cache[hash], &pSocket->list);
slist_remove(&sockid_cache[hash_by_id], &pSocket->list_id);
socket4_free(pSocket);
}
void socket4_update(PSockEntry pSocket, u8 event)
{
}
PSock6Entry socket6_alloc(void)
{
PSock6Entry sock = Heap_Alloc(sizeof(Sock6Entry));
if (sock)
memset(sock,0,sizeof(Sock6Entry));
return (sock);
}
void socket6_free(PSock6Entry pSocket)
{
Heap_Free((PVOID)pSocket);
}
int socket6_add(PSock6Entry pSocket)
{
slist_add(&sock6_cache[pSocket->hash], &pSocket->list);
slist_add(&sockid_cache[pSocket->hash_by_id], &pSocket->list_id);
return NO_ERR;
}
void socket6_remove(PSock6Entry pSocket, U32 hash, U32 hash_by_id)
{
SOCKET6_delete_route(pSocket);
slist_remove(&sock6_cache[hash], &pSocket->list);
slist_remove(&sockid_cache[hash_by_id], &pSocket->list_id);
socket6_free(pSocket);
}
void socket6_update(PSock6Entry pSocket, U8 event)
{
}
#endif
PSockEntry SOCKET_bind(U16 socketID, PVOID owner, U16 owner_type)
{
PSockEntry pSocket = SOCKET_find_entry_by_id(socketID);
if (pSocket) {
pSocket->owner = owner;
pSocket->owner_type = owner_type;
/* update hardware socket */
if(pSocket->SocketFamily == PROTO_IPV6)
socket6_update((PSock6Entry)pSocket, SOCKET_BIND);
else
socket4_update(pSocket, SOCKET_BIND);
}
return pSocket;
}
PSockEntry SOCKET_unbind(U16 socketID)
{
PSockEntry pSocket = SOCKET_find_entry_by_id(socketID);
if (pSocket) {
pSocket->owner = NULL;
pSocket->owner_type = SOCK_OWNER_NONE;
/* update hardware socket */
if(pSocket->SocketFamily == PROTO_IPV6)
socket6_update((PSock6Entry)pSocket, SOCKET_UNBIND);
else
socket4_update(pSocket, SOCKET_UNBIND);
}
return pSocket;
}
PSockEntry SOCKET_find_entry_by_id(U16 socketID)
{
PSockEntry pEntry;
PSockEntry pSocket = NULL;
struct slist_entry *entry;
int hash;
hash = HASH_SOCKID(socketID);
slist_for_each(pEntry, entry, &sockid_cache[hash], list_id)
{
if (pEntry->SocketID == socketID)
pSocket = pEntry;
}
return pSocket;
}
PSockEntry SOCKET4_find_entry(U32 saddr, U16 sport, U32 daddr, U16 dport, U16 proto)
{
PSockEntry pEntry;
PSockEntry pSock3 = NULL;
struct slist_entry *entry;
U32 hash = HASH_SOCK(daddr, dport, proto);
slist_for_each(pEntry, entry, &sock4_cache[hash], list)
{
if (pEntry->Daddr_v4 == daddr && pEntry->Dport == dport && pEntry->proto == proto) {
if (pEntry->connected) {
// check 5-tuples for connected sockets
if (pEntry->Saddr_v4 == saddr && pEntry->Sport == sport) {
return pEntry;
}
}
else // remind last 3 tuples match (should be unique)
pSock3 = pEntry;
}
}
return pSock3;
}
PSock6Entry SOCKET6_find_entry(U32 *saddr, U16 sport, U32 *daddr, U16 dport, U16 proto)
{
PSock6Entry pEntry;
PSock6Entry pSock3 = NULL;
struct slist_entry *entry;
U32 hash;
U32 daddr_lo;
daddr_lo = READ_UNALIGNED_INT(daddr[IP6_LO_ADDR]);
hash = HASH_SOCK6(daddr_lo, dport, proto);
slist_for_each(pEntry, entry, &sock6_cache[hash], list)
{
if (!IPV6_CMP(pEntry->Daddr_v6, daddr) && (pEntry->Dport == dport) && (pEntry->proto == proto))
{
// 3-tuples match
if (pEntry->connected) {
// check 5-tuples for connected sockets
if (!IPV6_CMP(pEntry->Saddr_v6, saddr) && (pEntry->Sport == sport))
return pEntry;
}
else // remind last 3 tuples match (should be unique)
pSock3 = pEntry;
}
}
return pSock3;
}
/* free IPv4 sockets entries */
void SOCKET4_free_entries(void)
{
int i;
U32 hash_by_id;
PSockEntry pSock;
for(i = 0; i < NUM_SOCK_ENTRIES; i++)
{
struct slist_entry *entry;
slist_for_each_safe(pSock, entry, &sock4_cache[i], list)
{
hash_by_id = HASH_SOCKID(pSock->SocketID);
socket4_remove(pSock, i, hash_by_id);
}
}
}
int SOCKET4_HandleIP_Socket_Open (U16 *p, U16 Length)
{
SockOpenCommand SocketCmd;
PSockEntry pEntry;
// Check length
if (Length != sizeof(SockOpenCommand))
return ERR_WRONG_COMMAND_SIZE;
// Ensure alignment
SFL_memcpy((U8*)&SocketCmd, (U8*)p, sizeof(SockOpenCommand));
if (!SocketCmd.SockID)
return ERR_WRONG_SOCKID;
pEntry = SOCKET4_find_entry(SocketCmd.Saddr, SocketCmd.Sport, SocketCmd.Daddr, SocketCmd.Dport, SocketCmd.proto);
if ((pEntry) && (pEntry->connected == SocketCmd.mode)) {
if (pEntry->SocketID != SocketCmd.SockID)
return ERR_SOCK_ALREADY_OPENED_WITH_OTHER_ID;
else
return ERR_SOCK_ALREADY_OPEN;
}
if (SOCKET_find_entry_by_id(SocketCmd.SockID) != NULL)
return ERR_SOCKID_ALREADY_USED;
#if defined(COMCERTO_2000)
/* FIXME, if MSP support was not compiled in we should return error */
#elif defined(COMCERTO_1000)
if ((SocketCmd.SockType == SOCKET_TYPE_MSP) && (!mspItfInfo))
return ERR_MSP_NOT_READY;
#elif defined(COMCERTO_100)
if (SocketCmd.SockType == SOCKET_TYPE_MSP)
return ERR_WRONG_SOCK_TYPE;
#endif
if ((pEntry = (struct _tSockEntry*)socket4_alloc()) == NULL)
return ERR_NOT_ENOUGH_MEMORY;
memset(pEntry, 0, sizeof (SockEntry));
pEntry->SocketFamily = PROTO_IPV4;
pEntry->Daddr_v4 = SocketCmd.Daddr;
pEntry->Saddr_v4 = SocketCmd.Saddr;
pEntry->Dport = SocketCmd.Dport;
pEntry->Sport = SocketCmd.Sport;
pEntry->proto = SocketCmd.proto;
pEntry->SocketID = SocketCmd.SockID;
pEntry->queue = SocketCmd.queue;
pEntry->dscp = SocketCmd.dscp;
pEntry->SocketType = SocketCmd.SockType;
pEntry->connected = SocketCmd.mode;
pEntry->route_id = SocketCmd.route_id;
pEntry->initial_takeover_done = FALSE;
pEntry->hash = HASH_SOCK(pEntry->Daddr_v4, pEntry->Dport, pEntry->proto);
pEntry->hash_by_id = HASH_SOCKID(pEntry->SocketID);
#if defined(COMCERTO_2000)
{
int i;
pEntry->secure = SocketCmd.secure;
pEntry->SA_nr_rx = SocketCmd.SA_nr_rx;
for (i = 0; i < SocketCmd.SA_nr_rx; i++)
pEntry->SA_handle_rx[i] = SocketCmd.SA_handle_rx[i];
pEntry->SA_nr_tx = SocketCmd.SA_nr_tx;
for (i = 0; i < SocketCmd.SA_nr_tx; i++)
pEntry->SA_handle_tx[i] = SocketCmd.SA_handle_tx[i];
}
#endif
if (pEntry->SocketType == SOCKET_TYPE_L2TP)
pEntry->owner_type = SOCK_OWNER_L2TP;
/* Add software and hardware entry to local and packet engine hash */
return socket4_add(pEntry);
}
int SOCKET4_HandleIP_Socket_Update (U16 *p, U16 Length)
{
SockUpdateCommand SocketCmd;
PSockEntry pEntry;
int rc = NO_ERR;
// Check length
if (Length != sizeof(SockUpdateCommand))
return ERR_WRONG_COMMAND_SIZE;
// Ensure alignment
SFL_memcpy((U8*)&SocketCmd, (U8*)p, sizeof(SockUpdateCommand));
pEntry = SOCKET_find_entry_by_id(SocketCmd.SockID);
if (pEntry == NULL)
return ERR_SOCKID_UNKNOWN;
if (pEntry->SocketFamily != PROTO_IPV4)
return ERR_WRONG_SOCK_FAMILY;
if (pEntry->connected &&
(((SocketCmd.Saddr != 0xFFFFFFFF) && (SocketCmd.Saddr != pEntry->Saddr_v4)) ||
((SocketCmd.Sport != 0xffff) && (SocketCmd.Sport != pEntry->Sport))))
return ERR_SOCK_ALREADY_OPEN;
if ((SocketCmd.Saddr != 0xFFFFFFFF) && (SocketCmd.Saddr != pEntry->Saddr_v4)) {
SOCKET4_delete_route(pEntry);
pEntry->Saddr_v4 = SocketCmd.Saddr;
}
if (pEntry->route_id != SocketCmd.route_id) {
SOCKET4_delete_route(pEntry);
pEntry->route_id = SocketCmd.route_id;
}
if (SocketCmd.Sport != 0xffff)
pEntry->Sport = SocketCmd.Sport;
if (SocketCmd.queue != 0xff)
pEntry->queue = SocketCmd.queue;
if (SocketCmd.dscp != 0xffff)
pEntry->dscp = SocketCmd.dscp;
#if defined(COMCERTO_2000)
if (SocketCmd.secure != 0xffff)
{
int i;
pEntry->secure = SocketCmd.secure;
pEntry->SA_nr_rx = SocketCmd.SA_nr_rx;
for (i = 0; i < SocketCmd.SA_nr_rx; i++)
pEntry->SA_handle_rx[i] = SocketCmd.SA_handle_rx[i];
pEntry->SA_nr_tx = SocketCmd.SA_nr_tx;
for (i = 0; i < SocketCmd.SA_nr_tx; i++)
pEntry->SA_handle_tx[i] = SocketCmd.SA_handle_tx[i];
}
#endif
socket4_update(pEntry, SOCKET_UPDATE);
return rc;
}
int SOCKET4_HandleIP_Socket_Close (U16 *p, U16 Length)
{
SockCloseCommand SocketCmd;
PSockEntry pEntry;
U32 hash, hash_by_id;
// Check length
if (Length != sizeof(SockCloseCommand))
return ERR_WRONG_COMMAND_SIZE;
// Ensure alignment
SFL_memcpy((U8*)&SocketCmd, (U8*)p, sizeof(SockCloseCommand));
pEntry = SOCKET_find_entry_by_id(SocketCmd.SockID);
if (pEntry == NULL)
return ERR_SOCKID_UNKNOWN;
hash = HASH_SOCK(pEntry->Daddr_v4, pEntry->Dport, pEntry->proto);
hash_by_id = HASH_SOCKID(pEntry->SocketID);
/* destroy hw socket entry */
socket4_remove(pEntry, hash, hash_by_id);
return NO_ERR;
}
/* free IPv6 sockets entries */
void SOCKET6_free_entries(void)
{
int i;
U32 hash_by_id;
PSock6Entry pSock;
for (i = 0; i < NUM_SOCK_ENTRIES; i++)
{
struct slist_entry *entry;
slist_for_each_safe(pSock, entry, &sock6_cache[i], list)
{
hash_by_id = HASH_SOCKID(pSock->SocketID);
socket6_remove(pSock, i, hash_by_id);
}
}
}
int SOCKET6_HandleIP_Socket_Open(U16 *p, U16 Length)
{
Sock6OpenCommand SocketCmd;
PSock6Entry pEntry;
// Check length
if (Length != sizeof(Sock6OpenCommand))
return ERR_WRONG_COMMAND_SIZE;
// Ensure alignment
SFL_memcpy((U8*)&SocketCmd, (U8*)p, sizeof(Sock6OpenCommand));
if (!SocketCmd.SockID)
return ERR_WRONG_SOCKID;
pEntry = SOCKET6_find_entry(SocketCmd.Saddr, SocketCmd.Sport, SocketCmd.Daddr, SocketCmd.Dport, SocketCmd.proto);
if ((pEntry) && (pEntry->connected == SocketCmd.mode)) {
if (pEntry->SocketID != SocketCmd.SockID)
return ERR_SOCK_ALREADY_OPENED_WITH_OTHER_ID;
else
return ERR_SOCK_ALREADY_OPEN;
}
if (SOCKET_find_entry_by_id(SocketCmd.SockID) != NULL)
return ERR_SOCKID_ALREADY_USED;
if(SocketCmd.SockType == SOCKET_TYPE_MSP)
return ERR_WRONG_SOCK_TYPE;
if ((pEntry = socket6_alloc()) == NULL)
return ERR_NOT_ENOUGH_MEMORY;
memset(pEntry, 0, sizeof (Sock6Entry));
pEntry->SocketFamily = PROTO_IPV6;
SFL_memcpy(pEntry->Daddr_v6, SocketCmd.Daddr, IPV6_ADDRESS_LENGTH);
SFL_memcpy(pEntry->Saddr_v6, SocketCmd.Saddr, IPV6_ADDRESS_LENGTH);
pEntry->Dport = SocketCmd.Dport;
pEntry->Sport = SocketCmd.Sport;
pEntry->proto = SocketCmd.proto;
pEntry->SocketID = SocketCmd.SockID;
pEntry->queue = SocketCmd.queue;
pEntry->dscp = SocketCmd.dscp;
pEntry->connected = SocketCmd.mode;
pEntry->SocketType = SocketCmd.SockType;
pEntry->route_id = SocketCmd.route_id;
pEntry->hash = HASH_SOCK6(pEntry->Daddr_v6[IP6_LO_ADDR], pEntry->Dport, pEntry->proto);
pEntry->hash_by_id = HASH_SOCKID(pEntry->SocketID);
if (pEntry->SocketType == SOCKET_TYPE_L2TP)
pEntry->owner_type = SOCK_OWNER_L2TP;
#if defined(COMCERTO_2000)
{
int i;
pEntry->secure = SocketCmd.secure;
pEntry->SA_nr_rx = SocketCmd.SA_nr_rx;
for (i = 0; i < SocketCmd.SA_nr_rx; i++)
pEntry->SA_handle_rx[i] = SocketCmd.SA_handle_rx[i];
pEntry->SA_nr_tx = SocketCmd.SA_nr_tx;
for (i = 0; i < SocketCmd.SA_nr_tx; i++)
pEntry->SA_handle_tx[i] = SocketCmd.SA_handle_tx[i];
}
#endif
return socket6_add(pEntry);
}
int SOCKET6_HandleIP_Socket_Update(U16 *p, U16 Length)
{
Sock6UpdateCommand SocketCmd;
PSock6Entry pEntry;
int rc = NO_ERR;
U32 nulladdr[4] = {0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff};
// Check length
if (Length != sizeof(Sock6UpdateCommand))
return ERR_WRONG_COMMAND_SIZE;
// Ensure alignment
SFL_memcpy((U8*)&SocketCmd, (U8*)p, sizeof(Sock6UpdateCommand));
pEntry = (PSock6Entry)SOCKET_find_entry_by_id(SocketCmd.SockID);
if (pEntry == NULL)
return ERR_SOCKID_UNKNOWN;
if (pEntry->SocketFamily != PROTO_IPV6)
return ERR_WRONG_SOCK_FAMILY;
/* For connected sockets don't allow 5-tuple to change */
if (pEntry->connected &&
((IPV6_CMP(SocketCmd.Saddr, nulladdr) && IPV6_CMP(SocketCmd.Saddr, pEntry->Saddr_v6)) ||
((SocketCmd.Sport != 0xffff) && (SocketCmd.Sport != pEntry->Sport))))
return ERR_SOCK_ALREADY_OPEN;
if (IPV6_CMP(SocketCmd.Saddr, nulladdr) && IPV6_CMP(SocketCmd.Saddr, pEntry->Saddr_v6))
{
// Route has changed -> delete it
SOCKET6_delete_route(pEntry);
SFL_memcpy(pEntry->Saddr_v6, SocketCmd.Saddr, IPV6_ADDRESS_LENGTH);
}
if (pEntry->route_id != SocketCmd.route_id)
{
// Route has changed -> delete it
SOCKET6_delete_route(pEntry);
pEntry->route_id = SocketCmd.route_id;
}
if (SocketCmd.Sport != 0xffff)
pEntry->Sport = SocketCmd.Sport;
if (SocketCmd.queue != 0xff)
pEntry->queue = SocketCmd.queue;
if (SocketCmd.dscp != 0xffff)
pEntry->dscp = SocketCmd.dscp;
#if defined(COMCERTO_2000)
{
int i;
pEntry->secure = SocketCmd.secure;
pEntry->SA_nr_rx = SocketCmd.SA_nr_rx;
for (i = 0; i < SocketCmd.SA_nr_rx; i++)
pEntry->SA_handle_rx[i] = SocketCmd.SA_handle_rx[i];
pEntry->SA_nr_tx = SocketCmd.SA_nr_tx;
for (i = 0; i < SocketCmd.SA_nr_tx; i++)
pEntry->SA_handle_tx[i] = SocketCmd.SA_handle_tx[i];
}
#endif
socket6_update(pEntry, SOCKET_UPDATE);
return rc;
}
int SOCKET6_HandleIP_Socket_Close(U16 *p, U16 Length)
{
Sock6CloseCommand SocketCmd;
PSock6Entry pEntry;
U32 hash, hash_by_id;
// Check length
if (Length != sizeof(Sock6CloseCommand))
return ERR_WRONG_COMMAND_SIZE;
// Ensure alignment
SFL_memcpy((U8*)&SocketCmd, (U8*)p, sizeof(Sock6CloseCommand));
pEntry = (PSock6Entry)SOCKET_find_entry_by_id(SocketCmd.SockID);
if (pEntry == NULL)
return ERR_SOCKID_UNKNOWN;
hash = HASH_SOCK6(pEntry->Daddr_v6[IP6_LO_ADDR], pEntry->Dport, pEntry->proto);
hash_by_id = HASH_SOCKID(pEntry->SocketID);
socket6_remove(pEntry, hash, hash_by_id);
return NO_ERR;
}
#if defined (COMCERTO_2000)
BOOL socket_init(void)
{
int i;
struct pfe_ctrl *ctrl = &pfe->ctrl;
socket_dma_pool = ctrl->dma_pool_512;
for (i = 0; i < NUM_SOCK_ENTRIES; i++)
{
slist_head_init(&sock4_cache[i]);
slist_head_init(&sock6_cache[i]);
slist_head_init(&sockid_cache[i]);
dlist_head_init(&hw_sock4_active_list[i]);
dlist_head_init(&hw_sock6_active_list[i]);
dlist_head_init(&hw_sock_active_list_id[i]);
}
dlist_head_init(&hw_sock_removal_list);
timer_init(&socket_timer, hw_socket_delayed_remove);
timer_add(&socket_timer, CT_TIMER_INTERVAL);
return 0;
}
void socket_exit(void)
{
struct dlist_head *entry;
struct _thw_sock *hw_sock;
timer_del(&socket_timer);
SOCKET4_free_entries();
SOCKET6_free_entries();
dlist_for_each_safe(hw_sock, entry, &hw_sock_removal_list, list)
{
dlist_remove(&hw_sock->list);
dma_pool_free(socket_dma_pool, hw_sock, be32_to_cpu(hw_sock->dma_addr));
}
}
#endif