blob: 99c5489ef551d9764e71dfd479630c9304aa2756 [file] [log] [blame]
/*
*
* Copyright (C) 2007 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "cmm.h"
#include "module_l2tp.h"
#include "fpp_private.h"
#include "fpp.h"
#include "cmmd.h"
int l2tp_itf_add(FCI_CLIENT *fci_handle, int request, struct interface *itf)
{
int rc = CMMD_ERR_NOT_CONFIGURED;
fpp_l2tp_itf_add_cmd_t cmd;
cmm_print(DEBUG_INFO, "%s ifindex(%d)\n", __func__, itf->ifindex);
if (!__itf_is_up(itf))
goto out;
if (!(itf->flags & USER_ADDED) || (!itf->l2tp.sock)) /* If USER_ADDED l2tp.sock should never be NULL */
goto out;
switch (request)
{
case ADD:
if ((itf->flags & (FPP_PROGRAMMED | FPP_NEEDS_UPDATE)) == FPP_PROGRAMMED) {
rc = 0;
goto out;
}
if ((itf->flags & (FPP_PROGRAMMED | FPP_NEEDS_UPDATE)) == (FPP_PROGRAMMED | FPP_NEEDS_UPDATE))
{
cmm_print(DEBUG_ERROR, "%s: trying to update L2TP/PPP interface(%d)\n", __func__, itf->ifindex);
goto out;
}
break;
default:
cmm_print(DEBUG_ERROR, "%s: Command not supported\n", __func__);
rc = CMMD_ERR_UNKNOWN_COMMAND;
goto out;
break;
}
memset(&cmd, 0, sizeof(cmd));
cmd.sock_id = itf->l2tp.sock->id;
cmd.local_tun_id = itf->l2tp.local_tun_id;
cmd.peer_tun_id = itf->l2tp.peer_tun_id;
cmd.local_ses_id = itf->l2tp.local_ses_id;
cmd.peer_ses_id = itf->l2tp.peer_ses_id;
cmd.options = itf->l2tp.options;
if (____itf_get_name(itf, cmd.ifname, sizeof(cmd.ifname)) < 0)
{
cmm_print(DEBUG_ERROR, "%s: ____itf_get_name(%d) failed\n", __func__, itf->ifindex);
goto out;
}
cmm_print(DEBUG_COMMAND, "Send FPP_CMD_L2TP_ITF_ADD\n");
rc = fci_write(fci_handle, FPP_CMD_L2TP_ITF_ADD, sizeof(cmd), (unsigned short *) &cmd);
if (rc == FPP_ERR_OK)
{
itf->flags |= FPP_PROGRAMMED;
itf->flags &= ~FPP_NEEDS_UPDATE;
}
else
cmm_print(DEBUG_ERROR, "%s: Error %d while sending CMD_L2TP_ITF_ADD\n", __func__, rc);
out:
return rc;
}
int __l2tp_itf_del(FCI_CLIENT *fci_handle, struct interface *itf)
{
fpp_l2tp_itf_del_cmd_t cmd;
int rc = FPP_ERR_OK;
if (!(itf->flags & FPP_PROGRAMMED))
goto out;
memset(&cmd, 0, sizeof(cmd));
if (____itf_get_name(itf, cmd.ifname, sizeof(cmd.ifname)) < 0)
{
cmm_print(DEBUG_ERROR, "%s: ____itf_get_name(%d) failed\n", __func__, itf->ifindex);
goto out;
}
cmm_print(DEBUG_COMMAND, "Send FPP_CMD_L2TP_ITF_DEL\n");
rc = fci_write(fci_handle, FPP_CMD_L2TP_ITF_DEL, sizeof(cmd), (unsigned short *) &cmd);
if (rc == FPP_ERR_OK)
{
itf->flags &= ~FPP_PROGRAMMED;
itf->flags &= ~FPP_NEEDS_UPDATE;
}
else
cmm_print(DEBUG_ERROR, "%s: Error %d while sending CMD_L2TP_ITF_DEL\n", __func__, rc);
out:
return rc;
}
int l2tp_itf_del(FCI_CLIENT *fci_handle, struct interface *itf)
{
int rc;
struct socket *s;
s = itf->l2tp.sock;
rc = __l2tp_itf_del(fci_handle, itf);
if(itf->l2tp.sock)
{
__socket_close(fci_handle,itf_table.fci_key_handle,s);
itf->l2tp.sock =NULL;
itf->flags &=~USER_ADDED;
itf->itf_flags &= ~ITF_L2TP;
}
return rc;
}
int l2tp_new_session(FCI_CLIENT *fci_handle, cmmd_l2tp_session_t *cmd, u_int16_t *res_buf, u_int16_t *res_len)
{
int ifindex;
struct interface *itf;
int rc = 0;
struct socket *s = NULL;
__pthread_mutex_lock(&itf_table.lock);
__pthread_mutex_lock(&rtMutex);
__pthread_mutex_lock(&neighMutex);
__pthread_mutex_lock(&flowMutex);
__pthread_mutex_lock(&socket_lock);
ifindex = if_nametoindex(cmd->itf_name);
itf = __itf_get(ifindex);
if (!itf)
{
cmm_print(DEBUG_ERROR, "%s: interface %s not found\n", __func__, cmd->itf_name);
res_buf[0] = CMMD_ERR_NOT_FOUND;
goto err;
}
if (itf->flags & FPP_PROGRAMMED)
{
cmm_print(DEBUG_ERROR, "%s: interface %s is already offloaded to PFE\n", __func__, cmd->itf_name);
res_buf[0] = CMMD_ERR_DUPLICATE;
goto err;
}
else if(itf->l2tp.sock) /* This should not happen */
{
/* Close stale socket */
rc = __socket_close(fci_handle,itf_table.fci_key_handle,s);
if(rc == CMMD_ERR_OK)
{
itf->l2tp.sock =NULL;
itf->flags &=~USER_ADDED;
}
else
{
cmm_print(DEBUG_ERROR, "%s: Stale Socket ID %d could not be closed\n", __func__,s->id);
res_buf[0] = rc; /* Should we ignore this error and continue ?*/
goto err;
}
}
itf->itf_flags |= ITF_L2TP;
itf->l2tp.local_ses_id = cmd->local_ses_id;
itf->l2tp.peer_ses_id = cmd->peer_ses_id;
itf->l2tp.local_tun_id = cmd->local_tun_id;
itf->l2tp.peer_tun_id = cmd->peer_tun_id;
s = socket_find_by_addr(cmd->family, cmd->peer_addr, cmd->local_addr, cmd->peer_port, cmd->local_port, IPPROTO_UDP);
if (!s) {
s = malloc(sizeof(struct socket));
if (!s) {
cmm_print(DEBUG_ERROR, "%s: malloc() failed\n", __func__);
goto err;
}
memset(s, 0, sizeof(struct socket));
s->family = cmd->family;
s->id = new_socket_id();
if(!s->id)
{
cmm_print(DEBUG_ERROR, "%s: No Socket ID available \n", __func__);
res_buf[0] = CMMD_ERR_SOCKID_ALREADY_USED;
goto err;
}
s->type = CMMD_SOCKET_TYPE_L2TP;
s->mode = SOCKET_CONNECTED;
memcpy(s->saddr, cmd->peer_addr, IPADDRLEN(s->family));
memcpy(s->daddr, cmd->local_addr, IPADDRLEN(s->family));
s->sport = cmd->peer_port;
s->dport = cmd->local_port;
s->proto = IPPROTO_UDP;
s->dscp = cmd->dscp;
s->fwmark = cmd->fwmark;
s->queue = cmd->queue;
__socket_add(s);
}
else {
/*
* TODO
* In theory we could have multiple L2TP interfaces / sessions on the same socket
* Not likely in CPE...
* Would need ref_count on L2TP socket
*
*/
res_buf[0] = CMMD_ERR_DUPLICATE;
cmm_print(DEBUG_ERROR, "%s: can't offload this L2TP session (%s), it uses the same socket\n", __func__, cmd->itf_name);
}
itf->l2tp.sock = s;
itf->flags |= USER_ADDED;
rc = l2tp_itf_add(fci_handle, ADD, itf);
if(rc != CMMD_ERR_OK) {
itf->flags &= ~USER_ADDED;
goto err;
}
rc = __socket_open(fci_handle, s);
if(rc != CMMD_ERR_OK) {
l2tp_itf_del(fci_handle, itf);
goto err;
}
if (rc >= 0) {
res_buf[0] = rc;
rc = 0;
}
err:
__pthread_mutex_unlock(&socket_lock);
__pthread_mutex_unlock(&flowMutex);
__pthread_mutex_unlock(&neighMutex);
__pthread_mutex_unlock(&rtMutex);
__pthread_mutex_unlock(&itf_table.lock);
*res_len = 2;
return rc;
}
int l2tp_delete_session(FCI_CLIENT *fci_handle, cmmd_l2tp_session_t *cmd, u_int16_t *res_buf, u_int16_t *res_len)
{
int rc = 0;
struct interface *itf;
int ifindex;
__pthread_mutex_lock(&itf_table.lock);
__pthread_mutex_lock(&rtMutex);
__pthread_mutex_lock(&neighMutex);
__pthread_mutex_lock(&flowMutex);
__pthread_mutex_lock(&socket_lock);
ifindex = if_nametoindex(cmd->itf_name);
itf = __itf_get(ifindex);
if (!itf)
{
cmm_print(DEBUG_ERROR, "%s: interface %s not found\n", __func__, cmd->itf_name);
rc = CMMD_ERR_NOT_FOUND;
goto err;
}
if (__itf_is_l2tp(itf))
rc = l2tp_itf_del(itf_table.fci_handle, itf);
err:
__pthread_mutex_unlock(&socket_lock);
__pthread_mutex_unlock(&flowMutex);
__pthread_mutex_unlock(&neighMutex);
__pthread_mutex_unlock(&rtMutex);
__pthread_mutex_unlock(&itf_table.lock);
res_buf[0] = rc;
*res_len = 2;
return rc;
}
int l2tp_daemon(FCI_CLIENT *fci_handle,int command, cmmd_l2tp_session_t *cmd, u_int16_t cmd_len, u_int16_t *res_buf, u_int16_t *res_len)
{
int rc = 0;
cmm_print(DEBUG_INFO, "%s\n", __func__);
res_buf[0] = CMMD_ERR_WRONG_COMMAND_SIZE;
*res_len = 2;
if (cmd_len < sizeof(*cmd))
{
cmm_print(DEBUG_ERROR, "%s: l2tp session command size too small(%d, %d)\n", __func__, cmd_len, sizeof(*cmd));
return rc;
}
if(command == CMMD_CMD_L2TP_SESSION_CREATE)
return l2tp_new_session(fci_handle, cmd, res_buf, res_len);
else if(command == CMMD_CMD_L2TP_SESSION_DESTROY)
return l2tp_delete_session(fci_handle, cmd, res_buf, res_len);
}