blob: 3eedf53d72c3c036f6db20615fdda107d06a8737 [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 <signal.h>
#include <net/if.h>
#include <sys/time.h>
#include "cmm.h"
#include "itf.h"
#include "ffbridge.h"
#include "cmmd.h"
struct list_head ct_table[CONNTRACK_HASH_TABLE_SIZE];
struct list_head ct_table_by_rep[CONNTRACK_HASH_TABLE_SIZE];
struct list_head ct_table_by_orig_route[2 * ROUTE_HASH_TABLE_SIZE];
struct list_head ct_table_by_rep_route[2 * ROUTE_HASH_TABLE_SIZE];
struct list_head ct_table_by_orig_tunnel_route[2 * ROUTE_HASH_TABLE_SIZE];
struct list_head ct_table_by_rep_tunnel_route[2 * ROUTE_HASH_TABLE_SIZE];
struct conntrack_stats ct_stats;
pthread_mutex_t ctMutex = PTHREAD_MUTEX_INITIALIZER; /*mutex to prevent race condition on the conntrack table*/
const unsigned char null_mac[ETH_ALEN] = {0, };
const unsigned int null_ip[4] = {0, };
extern void cmmDPDIPsecSAUpdate(struct cmm_ct *ctx);
extern int cmmNeighSendSolicit(void);
void * cb_data;
int cb_status;
/*****************************************************************
* cmmCtSetTimeout
*
*
******************************************************************/
static void cmmCtSetPermanent(struct nfct_handle * handler, struct ctTable * ctEntry)
{
char buf[nfct_maxsize()] __attribute__ ((aligned (4)));
struct nf_conntrack *ctTemp = (struct nf_conntrack *)buf;
struct nf_conntrack *ct = ctEntry->ct;
const unsigned int *Saddr, *Daddr, *SaddrReply, *DaddrReply;
unsigned short Sport, Dport, SportReply, DportReply;
int family, proto, status;
u_int32_t id;
status = nfct_get_attr_u32(ct, ATTR_STATUS);
if(((ctEntry->flags & (FPP_PROGRAMMED))
&& ((status & IPS_PERMANENT) == 0)))
{
memset(ctTemp, 0, nfct_maxsize());
family = nfct_get_attr_u8(ct, ATTR_ORIG_L3PROTO);
proto = nfct_get_attr_u8(ct, ATTR_ORIG_L4PROTO);
nfct_set_attr_u8(ctTemp, ATTR_ORIG_L3PROTO, family);
nfct_set_attr_u8(ctTemp, ATTR_ORIG_L4PROTO, proto);
nfct_set_attr_u8(ctTemp, ATTR_REPL_L3PROTO, family);
nfct_set_attr_u8(ctTemp, ATTR_REPL_L4PROTO, proto);
if (family == AF_INET)
{
Saddr = nfct_get_attr(ct, ATTR_ORIG_IPV4_SRC);
Daddr = nfct_get_attr(ct, ATTR_ORIG_IPV4_DST);
SaddrReply = nfct_get_attr(ct, ATTR_REPL_IPV4_SRC);
DaddrReply = nfct_get_attr(ct, ATTR_REPL_IPV4_DST);
nfct_set_attr(ctTemp, ATTR_ORIG_IPV4_SRC, Saddr);
nfct_set_attr(ctTemp, ATTR_ORIG_IPV4_DST, Daddr);
nfct_set_attr(ctTemp, ATTR_REPL_IPV4_SRC, SaddrReply);
nfct_set_attr(ctTemp, ATTR_REPL_IPV4_DST, DaddrReply);
}
else
{
Saddr = nfct_get_attr(ct, ATTR_ORIG_IPV6_SRC);
Daddr = nfct_get_attr(ct, ATTR_ORIG_IPV6_DST);
SaddrReply = nfct_get_attr(ct, ATTR_REPL_IPV6_SRC);
DaddrReply = nfct_get_attr(ct, ATTR_REPL_IPV6_DST);
nfct_set_attr(ctTemp, ATTR_ORIG_IPV6_SRC, Saddr);
nfct_set_attr(ctTemp, ATTR_ORIG_IPV6_DST, Daddr);
nfct_set_attr(ctTemp, ATTR_REPL_IPV6_SRC, SaddrReply);
nfct_set_attr(ctTemp, ATTR_REPL_IPV6_DST, DaddrReply);
}
Sport = nfct_get_attr_u16(ct, ATTR_ORIG_PORT_SRC);
Dport = nfct_get_attr_u16(ct, ATTR_ORIG_PORT_DST);
SportReply = nfct_get_attr_u16(ct, ATTR_REPL_PORT_SRC);
DportReply = nfct_get_attr_u16(ct, ATTR_REPL_PORT_DST);
status |= IPS_PERMANENT;
/* Set the current ct to avoid useless re-setting in the same pass */
nfct_set_attr_u32(ct, ATTR_STATUS, status);
nfct_set_attr_u32(ctTemp, ATTR_STATUS, status);
nfct_set_attr_u16(ctTemp, ATTR_ORIG_PORT_SRC, Sport);
nfct_set_attr_u16(ctTemp, ATTR_ORIG_PORT_DST, Dport);
nfct_set_attr_u16(ctTemp, ATTR_REPL_PORT_SRC, SportReply);
nfct_set_attr_u16(ctTemp, ATTR_REPL_PORT_DST, DportReply);
id = nfct_get_attr_u32(ct, ATTR_ID);
nfct_set_attr_u32(ctTemp, ATTR_ID, id);
if (nfct_query(handler, NFCT_Q_UPDATE, (void*)ctTemp) < 0) {
if (errno != ENOENT)
cmm_print(DEBUG_ERROR, "%s: ATTR_STATUS update, nfct_query(NFCT_Q_UPDATE) %s\n", __func__, strerror(errno));
}
else
cmm_print(DEBUG_INFO, "%s: Ct status set to IPS_PERMANENT\n", __func__);
}
}
/*****************************************************************
* cmmCtForceUpdate
*
* This function will trigger kernel to resend an update for the given
* entry that needs up-to-date information.
******************************************************************/
static void cmmCtForceUpdate(struct nfct_handle * handler, struct ctTable * ctEntry)
{
char buf[nfct_maxsize()] __attribute__ ((aligned (4)));
struct nf_conntrack *ctTemp = (struct nf_conntrack *)buf;
struct nf_conntrack *ct = ctEntry->ct;
const unsigned int *Saddr, *Daddr, *SaddrReply, *DaddrReply;
unsigned short Sport, Dport, SportReply, DportReply;
int family, proto;
memset(ctTemp, 0, nfct_maxsize());
family = nfct_get_attr_u8(ct, ATTR_ORIG_L3PROTO);
proto = nfct_get_attr_u8(ct, ATTR_ORIG_L4PROTO);
nfct_set_attr_u8(ctTemp, ATTR_ORIG_L3PROTO, family);
nfct_set_attr_u8(ctTemp, ATTR_ORIG_L4PROTO, proto);
nfct_set_attr_u8(ctTemp, ATTR_REPL_L3PROTO, family);
nfct_set_attr_u8(ctTemp, ATTR_REPL_L4PROTO, proto);
if (family == AF_INET)
{
Saddr = nfct_get_attr(ct, ATTR_ORIG_IPV4_SRC);
Daddr = nfct_get_attr(ct, ATTR_ORIG_IPV4_DST);
SaddrReply = nfct_get_attr(ct, ATTR_REPL_IPV4_SRC);
DaddrReply = nfct_get_attr(ct, ATTR_REPL_IPV4_DST);
nfct_set_attr(ctTemp, ATTR_ORIG_IPV4_SRC, Saddr);
nfct_set_attr(ctTemp, ATTR_ORIG_IPV4_DST, Daddr);
nfct_set_attr(ctTemp, ATTR_REPL_IPV4_SRC, SaddrReply);
nfct_set_attr(ctTemp, ATTR_REPL_IPV4_DST, DaddrReply);
}
else
{
Saddr = nfct_get_attr(ct, ATTR_ORIG_IPV6_SRC);
Daddr = nfct_get_attr(ct, ATTR_ORIG_IPV6_DST);
SaddrReply = nfct_get_attr(ct, ATTR_REPL_IPV6_SRC);
DaddrReply = nfct_get_attr(ct, ATTR_REPL_IPV6_DST);
nfct_set_attr(ctTemp, ATTR_ORIG_IPV6_SRC, Saddr);
nfct_set_attr(ctTemp, ATTR_ORIG_IPV6_DST, Daddr);
nfct_set_attr(ctTemp, ATTR_REPL_IPV6_SRC, SaddrReply);
nfct_set_attr(ctTemp, ATTR_REPL_IPV6_DST, DaddrReply);
}
Sport = nfct_get_attr_u16(ct, ATTR_ORIG_PORT_SRC);
Dport = nfct_get_attr_u16(ct, ATTR_ORIG_PORT_DST);
SportReply = nfct_get_attr_u16(ct, ATTR_REPL_PORT_SRC);
DportReply = nfct_get_attr_u16(ct, ATTR_REPL_PORT_DST);
nfct_set_attr_u16(ctTemp, ATTR_ORIG_PORT_SRC, Sport);
nfct_set_attr_u16(ctTemp, ATTR_ORIG_PORT_DST, Dport);
nfct_set_attr_u16(ctTemp, ATTR_REPL_PORT_SRC, SportReply);
nfct_set_attr_u16(ctTemp, ATTR_REPL_PORT_DST, DportReply);
if (nfct_query(handler, NFCT_Q_UPDATE, (void*)ctTemp) < 0) {
if (errno != ENOENT)
cmm_print(DEBUG_ERROR, "%s: ATTR_STATUS update, nfct_query(NFCT_Q_UPDATE) %s\n", __func__, strerror(errno));
}
}
/*****************************************************************
* cmmCtNetlinkRemove
*
*
******************************************************************/
int cmmCtNetlinkRemove(struct nfct_handle * handler, struct nf_conntrack *ct)
{
int rc;
/*Remove the conntrack*/
rc = nfct_query(handler, NFCT_Q_DESTROY, (void*)ct);
if (rc < 0) {
if (errno != ENOENT)
cmm_print(DEBUG_ERROR, "%s: nfct_query(NFCT_Q_DESTROY) %s\n", __func__, strerror(errno));
return -1;
}
return 0;
}
/*****************************************************************
* cmmCtShow
*
*
******************************************************************/
int cmmCtShow(struct cli_def * cli, char *command, char *argv[], int argc)
{
struct ctTable * temp;
struct list_head *entry;
char buf[1024];
int i, cpt = 0, nb_mult_ids = 0;
int len;
unsigned int timeout = 0, orig_timeout;
for (i = 0 ; i < CONNTRACK_HASH_TABLE_SIZE; i++)
{
__pthread_mutex_lock(&ctMutex);
__pthread_mutex_lock(&flowMutex);
for(entry = list_first(&ct_table[i]); entry != &ct_table[i]; entry = list_next(entry))
{
temp = container_of(entry, struct ctTable, list);
cpt++;
/* Keep track of original timeout */
orig_timeout = nfct_get_attr_u32(temp->ct, ATTR_TIMEOUT);
if (temp->flags & FPP_PROGRAMMED)
cmmFeGetTimeout(globalConf.cli.fci_handle, temp, &timeout);
else
timeout = 0;
nfct_set_attr_u32(temp->ct, ATTR_TIMEOUT, timeout);
len = nfct_snprintf(buf, 1024, temp->ct, NFCT_T_UNKNOWN, NFCT_O_PLAIN, NFCT_OF_SHOW_LAYER3);
nfct_set_attr_u32(temp->ct, ATTR_TIMEOUT, orig_timeout);
if (temp->fEntryOrig || temp->fEntryRep)
{
len += snprintf(buf + len, 1024 - len, " IpSec:");
if (temp->fEntryOrig)
len += snprintf(buf + len, 1024 - len, " O(sa_nr:%d H0:%04x)", temp->fEntryOrig->sa_nr, temp->fEntryOrig->sa_handle[0]);
if (temp->fEntryRep)
len += snprintf(buf + len, 1024 - len, " R(sa_nr:%d H0:%04x)", temp->fEntryRep->sa_nr, temp->fEntryRep->sa_handle[0]);
}
cli_print(cli, "%s, Flags: %x, n_id: %d", buf, temp->flags, temp->n_id);
if (temp->n_id > 1)
nb_mult_ids++;
}
__pthread_mutex_unlock(&flowMutex);
__pthread_mutex_unlock(&ctMutex);
/* Give a chance to other processes waiting for the lock */
if (!(i % 100))
sched_yield();
}
cli_print(cli, "%d connections printed", cpt);
cli_print(cli, "%d connections with > 1 ids", nb_mult_ids);
return CLI_OK;
}
/*****************************************************************
* __cmmCtIsInv
*
* To be used on already matched conntracks (but that can be inverted)
******************************************************************/
static int __cmmCtIsInv(struct nf_conntrack *ct1, struct nf_conntrack *ct2)
{
int family = nfct_get_attr_u8(ct1, ATTR_ORIG_L3PROTO);
if (family == AF_INET) {
if (nfct_get_attr_u32(ct1, ATTR_ORIG_IPV4_SRC) != nfct_get_attr_u32(ct2, ATTR_ORIG_IPV4_SRC))
return 1;
}
else {
if (memcmp(nfct_get_attr(ct1, ATTR_ORIG_IPV6_SRC), nfct_get_attr(ct2, ATTR_ORIG_IPV6_SRC), 16))
return 1;
}
return 0;
}
/*****************************************************************
* __cmmCtFindId
*
*
******************************************************************/
static int __cmmCtFindId(struct ctTable *ctEntry, u_int32_t id)
{
int i;
for (i = 0; i < MAX_CT_ID; i++) {
if (id == ctEntry->ids[i])
return 1;
}
return 0;
}
/*****************************************************************
* __cmmCtAddId
*
*
******************************************************************/
static int __cmmCtAddId(struct ctTable *ctEntry, u_int32_t id)
{
int i;
for (i = 0; i < MAX_CT_ID; i++) {
if (!ctEntry->ids[i]) {
ctEntry->ids[i]= id;
ctEntry->n_id++;
return 0;
}
}
cmm_print(DEBUG_ERROR, "%s: ctEntry id_array is full. This can lead to synchronisation issues with kernel conntrack table\n", __func__);
return -1;
}
/*****************************************************************
* __cmmCtDelId
*
*
******************************************************************/
static int __cmmCtDelId(struct ctTable *ctEntry, u_int32_t id)
{
int i;
for (i = 0; i < MAX_CT_ID; i++) {
if (id == ctEntry->ids[i]) {
ctEntry->ids[i] = 0;
ctEntry->n_id--;
return 0;
}
}
return -1;
}
/*****************************************************************
* __cmmCtAdd
*
*
******************************************************************/
static struct ctTable *__cmmCtAdd(struct nf_conntrack *ct)
{
struct ctTable *newEntry;
int key;
const unsigned int *Saddr, *Daddr, *SaddrReply, *DaddrReply;
//Add the Conntrack to the local table
if (ct_stats.current >= CONNTRACK_MAX)
{
cmm_print(DEBUG_WARNING, "%s: maximum allowed conntracks %d reached\n", __func__, ct_stats.current);
goto err0;
}
newEntry = (struct ctTable*) malloc(sizeof(struct ctTable));
if (newEntry == NULL)
{
cmm_print(DEBUG_ERROR, "%s: malloc failed\n", __func__);
goto err0;
}
memset(newEntry, 0, sizeof(struct ctTable));
newEntry->ct = ct;
newEntry->family = nfct_get_attr_u8(ct, ATTR_ORIG_L3PROTO);
__cmmCtAddId(newEntry, nfct_get_attr_u32(ct, ATTR_ID));
if (newEntry->family == AF_INET)
{
Saddr = nfct_get_attr(ct, ATTR_ORIG_IPV4_SRC);
Daddr = nfct_get_attr(ct, ATTR_ORIG_IPV4_DST);
SaddrReply = nfct_get_attr(ct, ATTR_REPL_IPV4_SRC);
DaddrReply = nfct_get_attr(ct, ATTR_REPL_IPV4_DST);
}
else
{
Saddr = nfct_get_attr(ct, ATTR_ORIG_IPV6_SRC);
Daddr = nfct_get_attr(ct, ATTR_ORIG_IPV6_DST);
SaddrReply = nfct_get_attr(ct, ATTR_REPL_IPV6_SRC);
DaddrReply = nfct_get_attr(ct, ATTR_REPL_IPV6_DST);
}
//Add the Conntrack to the local table
key = HASH_CT(newEntry->family, Saddr,
Daddr,
nfct_get_attr_u16(ct, ATTR_ORIG_PORT_SRC),
nfct_get_attr_u16(ct, ATTR_ORIG_PORT_DST),
nfct_get_attr_u8(ct, ATTR_ORIG_L4PROTO));
list_add(&ct_table[key], &newEntry->list);
//Add the Conntrack to the local by replier table (used for flow cache <-> ct lookup)
key = HASH_CT(newEntry->family, SaddrReply,
DaddrReply,
nfct_get_attr_u16(ct, ATTR_REPL_PORT_SRC),
nfct_get_attr_u16(ct, ATTR_REPL_PORT_DST),
nfct_get_attr_u8(ct, ATTR_ORIG_L4PROTO));
list_add(&ct_table_by_rep[key], &newEntry->list_by_rep);
key = HASH_RT(newEntry->family, Saddr, SaddrReply);
list_add(&ct_table_by_orig_route[key], &newEntry->list_by_orig_route);
key = HASH_RT(newEntry->family, SaddrReply, Saddr);
list_add(&ct_table_by_rep_route[key], &newEntry->list_by_rep_route);
ct_stats.current++;
newEntry->timeout = 0;
newEntry->tlast = -1;
return newEntry;
err0:
return NULL;
}
/*****************************************************************
* __cmmCtRemove
*
*
******************************************************************/
void __cmmCtRemove(struct ctTable *ctEntry)
{
ct_stats.current--;
list_del(&ctEntry->list);
list_del(&ctEntry->list_by_rep);
list_del(&ctEntry->list_by_orig_route);
list_del(&ctEntry->list_by_rep_route);
nfct_destroy(ctEntry->ct);
free(ctEntry);
}
/*****************************************************************
* __cmmCtFindFromFlow
*
*
******************************************************************/
struct ctTable *__cmmCtFindFromFlow(int family, unsigned int *saddr, unsigned int *daddr, unsigned short sport, unsigned short dport, unsigned char proto, char *orig)
{
struct ctTable *track_entry;
struct list_head *entry;
int key, key2;
const unsigned int *Saddr, *Daddr, *SaddrReply, *DaddrReply;
int ipAddrLen = IPADDRLEN(family);
key = HASH_CT(family, saddr, daddr, sport, dport, proto);
key2 = 0xffffffff;
second_pass_ct:
entry = list_first(&ct_table[key]);
while (entry != &ct_table[key])
{
track_entry = container_of(entry, struct ctTable, list);
if (nfct_get_attr_u8(track_entry->ct, ATTR_ORIG_L3PROTO) != family)
goto next_orig;
if (family == AF_INET)
{
Saddr = nfct_get_attr(track_entry->ct, ATTR_ORIG_IPV4_SRC);
Daddr = nfct_get_attr(track_entry->ct, ATTR_ORIG_IPV4_DST);
}
else
{
Saddr = nfct_get_attr(track_entry->ct, ATTR_ORIG_IPV6_SRC);
Daddr = nfct_get_attr(track_entry->ct, ATTR_ORIG_IPV6_DST);
}
if ( /*IP addresses*/
(!memcmp(Saddr, saddr, ipAddrLen)) &&
(!memcmp(Daddr, daddr, ipAddrLen)) &&
/*Port*/
(nfct_get_attr_u16(track_entry->ct, ATTR_ORIG_PORT_SRC) == sport) &&
(nfct_get_attr_u16(track_entry->ct, ATTR_ORIG_PORT_DST) == dport) &&
/*Protocol*/
(nfct_get_attr_u8(track_entry->ct, ATTR_ORIG_L4PROTO) == proto)
)
{
//Entry found
*orig = 1;
goto end;
}
if ( /*IP addresses*/
(!memcmp(Saddr, daddr, ipAddrLen)) &&
(!memcmp(Daddr, saddr, ipAddrLen)) &&
/*Port*/
(nfct_get_attr_u16(track_entry->ct, ATTR_ORIG_PORT_SRC) == dport) &&
(nfct_get_attr_u16(track_entry->ct, ATTR_ORIG_PORT_DST) == sport) &&
/*Protocol*/
(nfct_get_attr_u8(track_entry->ct, ATTR_ORIG_L4PROTO) == proto)
)
{
//Entry found
*orig = 0;
goto end;
}
next_orig:
entry = list_next(entry);
}
entry = list_first(&ct_table_by_rep[key]);
while (entry != &ct_table_by_rep[key])
{
track_entry = container_of(entry, struct ctTable, list_by_rep);
if (nfct_get_attr_u8(track_entry->ct, ATTR_ORIG_L3PROTO) != family)
goto next_rep;
if (family == AF_INET)
{
SaddrReply = nfct_get_attr(track_entry->ct, ATTR_REPL_IPV4_SRC);
DaddrReply = nfct_get_attr(track_entry->ct, ATTR_REPL_IPV4_DST);
}
else
{
SaddrReply = nfct_get_attr(track_entry->ct, ATTR_REPL_IPV6_SRC);
DaddrReply = nfct_get_attr(track_entry->ct, ATTR_REPL_IPV6_DST);
}
if ( /*IP addresses*/
(!memcmp(SaddrReply, saddr, ipAddrLen)) &&
(!memcmp(DaddrReply, daddr, ipAddrLen)) &&
/*Port*/
(nfct_get_attr_u16(track_entry->ct, ATTR_REPL_PORT_SRC) == sport) &&
(nfct_get_attr_u16(track_entry->ct, ATTR_REPL_PORT_DST) == dport) &&
/*Protocol*/
(nfct_get_attr_u8(track_entry->ct, ATTR_ORIG_L4PROTO) == proto)
)
{
//Entry found
*orig = 0;
goto end;
}
if ( /*IP addresses*/
(!memcmp(SaddrReply, daddr, ipAddrLen)) &&
(!memcmp(DaddrReply, saddr, ipAddrLen)) &&
/*Port*/
(nfct_get_attr_u16(track_entry->ct, ATTR_REPL_PORT_SRC) == dport) &&
(nfct_get_attr_u16(track_entry->ct, ATTR_REPL_PORT_DST) == sport) &&
/*Protocol*/
(nfct_get_attr_u8(track_entry->ct, ATTR_ORIG_L4PROTO) == proto)
)
{
//Entry found
*orig = 1;
goto end;
}
next_rep:
entry = list_next(entry);
}
track_entry = NULL;
if (key2 == 0xffffffff)
{
// HASH_CT is treating source and dest addr assymetrically.
// Need to search second time because conntrack could be registered for opposite direction
key2 = HASH_CT(family, daddr, saddr, dport, sport, proto);
if (key != key2)
{
key = key2;
goto second_pass_ct;
}
}
end:
return track_entry;
}
/*****************************************************************
* cmmCtCompare
*
*
******************************************************************/
static int cmmCtCompare(struct nf_conntrack *ct1, struct nf_conntrack *ct2)
{
int family;
if ((family = nfct_get_attr_u8(ct1, ATTR_ORIG_L3PROTO)) != nfct_get_attr_u8(ct2, ATTR_ORIG_L3PROTO))
return 0;
if (family == AF_INET)
{
if ( /*IP addresses*/
nfct_get_attr_u32(ct1, ATTR_ORIG_IPV4_SRC) != nfct_get_attr_u32(ct2, ATTR_ORIG_IPV4_SRC) ||
nfct_get_attr_u32(ct1, ATTR_ORIG_IPV4_DST) != nfct_get_attr_u32(ct2, ATTR_ORIG_IPV4_DST) ||
/*Reply IP addresses*/
nfct_get_attr_u32(ct1, ATTR_REPL_IPV4_SRC) != nfct_get_attr_u32(ct2, ATTR_REPL_IPV4_SRC) ||
nfct_get_attr_u32(ct1, ATTR_REPL_IPV4_DST) != nfct_get_attr_u32(ct2, ATTR_REPL_IPV4_DST))
return 0;
}
else
{
if ( /*IP addresses*/
memcmp(nfct_get_attr(ct1, ATTR_ORIG_IPV6_SRC), nfct_get_attr(ct2, ATTR_ORIG_IPV6_SRC), 16) ||
memcmp(nfct_get_attr(ct1, ATTR_ORIG_IPV6_DST), nfct_get_attr(ct2, ATTR_ORIG_IPV6_DST), 16) ||
/*Reply IP addresses*/
memcmp(nfct_get_attr(ct1, ATTR_REPL_IPV6_SRC), nfct_get_attr(ct2, ATTR_REPL_IPV6_SRC), 16) ||
memcmp(nfct_get_attr(ct1, ATTR_REPL_IPV6_DST), nfct_get_attr(ct2, ATTR_REPL_IPV6_DST), 16))
return 0;
}
if(/*Port*/
nfct_get_attr_u16(ct1, ATTR_ORIG_PORT_SRC) != nfct_get_attr_u16(ct2, ATTR_ORIG_PORT_SRC) ||
nfct_get_attr_u16(ct1, ATTR_ORIG_PORT_DST) != nfct_get_attr_u16(ct2, ATTR_ORIG_PORT_DST) ||
/*Reply Port*/
nfct_get_attr_u16(ct1, ATTR_REPL_PORT_SRC) != nfct_get_attr_u16(ct2, ATTR_REPL_PORT_SRC) ||
nfct_get_attr_u16(ct1, ATTR_REPL_PORT_DST) != nfct_get_attr_u16(ct2, ATTR_REPL_PORT_DST) ||
/*Protocol*/
nfct_get_attr_u8(ct1, ATTR_ORIG_L4PROTO) != nfct_get_attr_u8(ct2, ATTR_ORIG_L4PROTO)
)
return 0;
return 1;
}
/*****************************************************************
* cmmCtCompare
*
*
******************************************************************/
static int cmmCtCompareInv(struct nf_conntrack *ct1, struct nf_conntrack *ct2)
{
int family;
if ((family = nfct_get_attr_u8(ct1, ATTR_ORIG_L3PROTO)) != nfct_get_attr_u8(ct2, ATTR_REPL_L3PROTO))
return 0;
if (family == AF_INET)
{
if ( /*IP addresses*/
nfct_get_attr_u32(ct1, ATTR_ORIG_IPV4_SRC) != nfct_get_attr_u32(ct2, ATTR_REPL_IPV4_SRC) ||
nfct_get_attr_u32(ct1, ATTR_ORIG_IPV4_DST) != nfct_get_attr_u32(ct2, ATTR_REPL_IPV4_DST) ||
/*Reply IP addresses*/
nfct_get_attr_u32(ct1, ATTR_REPL_IPV4_SRC) != nfct_get_attr_u32(ct2, ATTR_ORIG_IPV4_SRC) ||
nfct_get_attr_u32(ct1, ATTR_REPL_IPV4_DST) != nfct_get_attr_u32(ct2, ATTR_ORIG_IPV4_DST))
return 0;
}
else
{
if ( /*IP addresses*/
memcmp(nfct_get_attr(ct1, ATTR_ORIG_IPV6_SRC), nfct_get_attr(ct2, ATTR_REPL_IPV6_SRC), 16) ||
memcmp(nfct_get_attr(ct1, ATTR_ORIG_IPV6_DST), nfct_get_attr(ct2, ATTR_REPL_IPV6_DST), 16) ||
/*Reply IP addresses*/
memcmp(nfct_get_attr(ct1, ATTR_REPL_IPV6_SRC), nfct_get_attr(ct2, ATTR_ORIG_IPV6_SRC), 16) ||
memcmp(nfct_get_attr(ct1, ATTR_REPL_IPV6_DST), nfct_get_attr(ct2, ATTR_ORIG_IPV6_DST), 16))
return 0;
}
if(/*Port*/
nfct_get_attr_u16(ct1, ATTR_ORIG_PORT_SRC) != nfct_get_attr_u16(ct2, ATTR_REPL_PORT_SRC) ||
nfct_get_attr_u16(ct1, ATTR_ORIG_PORT_DST) != nfct_get_attr_u16(ct2, ATTR_REPL_PORT_DST) ||
/*Reply Port*/
nfct_get_attr_u16(ct1, ATTR_REPL_PORT_SRC) != nfct_get_attr_u16(ct2, ATTR_ORIG_PORT_SRC) ||
nfct_get_attr_u16(ct1, ATTR_REPL_PORT_DST) != nfct_get_attr_u16(ct2, ATTR_ORIG_PORT_DST) ||
/*Protocol*/
nfct_get_attr_u8(ct1, ATTR_ORIG_L4PROTO) != nfct_get_attr_u8(ct2, ATTR_REPL_L4PROTO)
)
return 0;
return 1;
}
/*****************************************************************
* __cmmCtFind
*
*
******************************************************************/
struct ctTable *__cmmCtFind(struct nf_conntrack *ctTemp)
{
struct ctTable *ctEntry;
struct list_head *entry;
int family;
int key;
const void *dAddr, *sAddr;
u_int16_t sport, dport;
char saddr_buf[INET6_ADDRSTRLEN], daddr_buf[INET6_ADDRSTRLEN];
family = nfct_get_attr_u8(ctTemp, ATTR_ORIG_L3PROTO);
if (family == AF_INET)
{
sAddr = nfct_get_attr(ctTemp, ATTR_ORIG_IPV4_SRC);
dAddr = nfct_get_attr(ctTemp, ATTR_ORIG_IPV4_DST);
}
else
{
sAddr = nfct_get_attr(ctTemp, ATTR_ORIG_IPV6_SRC);
dAddr = nfct_get_attr(ctTemp, ATTR_ORIG_IPV6_DST);
}
sport = nfct_get_attr_u16(ctTemp, ATTR_ORIG_PORT_SRC);
dport = nfct_get_attr_u16(ctTemp, ATTR_ORIG_PORT_DST);
key = HASH_CT(family, sAddr, dAddr, sport, dport,
nfct_get_attr_u8(ctTemp, ATTR_ORIG_L4PROTO));
entry = list_first(&ct_table[key]);
while (entry != &ct_table[key])
{
ctEntry = container_of(entry, struct ctTable, list);
if (cmmCtCompare(ctEntry->ct, ctTemp))
//Entry found
goto found;
entry = list_next(entry);
}
/* Search for inverted entry */
entry = list_first(&ct_table_by_rep[key]);
while (entry != &ct_table_by_rep[key])
{
ctEntry = container_of(entry, struct ctTable, list_by_rep);
if (cmmCtCompareInv(ctEntry->ct, ctTemp))
goto found;
entry = list_next(entry);
}
ctEntry = NULL;
found:
cmm_print(DEBUG_INFO, "%s: conntrack %sfound src:%s dst:%s sport:%d dport:%d\n", __func__,
ctEntry == NULL ? "not " : "",
inet_ntop(family, sAddr, saddr_buf, INET6_ADDRSTRLEN),
inet_ntop(family, dAddr, daddr_buf, INET6_ADDRSTRLEN),
ntohs(sport), ntohs(dport));
return ctEntry;
}
/*****************************************************************
* __cmmCtItfRegister
*
*
******************************************************************/
static int __cmmCtItfRegister(struct RtEntry *route, const char *dir)
{
char ifname[IFNAMSIZ];
int rc;
if (!route->phys_oifindex || (route->flags & CHECK_BRIDGE_PORT))
{
rc = __itf_is_bridge(route->oifindex);
if (rc < 0)
{
cmm_print(DEBUG_ERROR, "%s: route %s output interface lookup failed\n", __func__, dir);
goto err;
}
if (rc)
{
rc = cmmBrToFF(route);
if (rc < 0)
{
cmm_print(DEBUG_ERROR, "%s: route %s output interface lookup failed\n", __func__, dir);
goto err;
}
}
else
route->phys_oifindex = route->oifindex;
route->flags &= ~CHECK_BRIDGE_PORT;
}
rc = __itf_is_programmed(route->phys_oifindex);
if (rc <= 0)
{
cmm_print(DEBUG_ERROR, "%s: route %s output interface %s is not programmed in FPP\n", __func__, dir, if_indextoname(route->phys_oifindex, ifname));
goto err;
}
return 0;
err:
return -1;
}
/*****************************************************************
* __cmmNeighRegister
*
*
******************************************************************/
static int __cmmNeighRegister(struct RtEntry *route, const char *dir)
{
char addr_buf[INET6_ADDRSTRLEN];
int rc;
if ((rc = __itf_is_noarp(route->oifindex)) < 0)
{
cmm_print(DEBUG_ERROR, "%s: itf_is_noarp() failed for %s entry\n", __func__, dir);
goto err;
}
if (rc)
goto out;
if (!route->neighEntry)
{
route->neighEntry = __cmmNeighGet(route->family, route->gwAddr, route->oifindex);
if (!route->neighEntry)
{
cmm_print(DEBUG_ERROR, "%s: arp %s entry add failed\n", __func__, dir);
goto err;
}
}
if (!(route->neighEntry->state & NUD_VALID))
{
cmm_print(DEBUG_INFO, "%s: %s unresolved mac address for %s entry\n",
__func__, inet_ntop(route->family, route->neighEntry->ipAddr, addr_buf, sizeof(addr_buf)), dir);
goto err;
}
out:
return 0;
err:
return -1;
}
/*****************************************************************
* __cmmFPPRouteRegister
*
*
******************************************************************/
static int __cmmFPPRouteRegister(struct ct_route *rt, int is_floating_tunnel, const char *dir)
{
const unsigned char *dst_mac;
if (rt->fpp_route)
goto out;
if (rt->route->neighEntry)
dst_mac = rt->route->neighEntry->macAddr;
else
dst_mac = null_mac;
if (is_floating_tunnel)
rt->fpp_route = __cmmFPPRouteGet(rt->route->phys_oifindex, dst_mac, rt->route->mtu, rt->route->dAddr, IPADDRLEN(rt->route->family));
else
rt->fpp_route = __cmmFPPRouteGet(rt->route->phys_oifindex, dst_mac, rt->route->mtu, NULL, 0);
if (!rt->fpp_route)
{
goto err;
}
out:
return 0;
err:
return -1;
}
/*****************************************************************
* __cmmRouteRegister
*
*
******************************************************************/
int __cmmRouteRegister(struct ct_route *rt, struct flow *flow, int is_floating_tunnel, const char *dir)
{
if (!rt->route)
{
rt->route = __cmmRouteGet(flow);
if (!rt->route)
{
cmm_print(DEBUG_ERROR, "%s: route %s entry add failed\n", __func__, dir);
goto err;
}
}
if (__cmmNeighRegister(rt->route, dir) < 0)
{
goto err;
}
if (__cmmCtItfRegister(rt->route, dir) < 0)
{
goto err;
}
if (__cmmFPPRouteRegister(rt, is_floating_tunnel, dir) < 0)
{
goto err;
}
return 0;
err:
return -1;
}
/*****************************************************************
* __cmmCtTunnelRouteRegister
*
*
******************************************************************/
static int __cmmCtTunnelRouteRegister(struct ct_route *rt, struct ct_route *tunnel_rt, unsigned int Daddr4o6,
unsigned int Dport4o6, const char *dir)
{
struct interface *itf;
unsigned int sAddr[4], dAddr[4];
struct flow flow;
memset(sAddr , 0 , sizeof(sAddr));
memset(dAddr , 0 , sizeof(dAddr));
itf = __itf_get(rt->route->oifindex);
if (!itf)
goto err0;
if (!__itf_is_tunnel(itf))
goto out;
if(itf->tunnel_family == AF_INET)
{
if (!____itf_is_floating_sit_tunnel(itf))
goto out;
sAddr[0] = itf->tunnel_parm4.iph.saddr;
dAddr[0] = tunnel_get_ipv4_dst(rt->route, itf);
if (!dAddr[0])
goto err0;
}
else if(itf->tunnel_family == AF_INET6)
{
if (!____itf_is_4o6_tunnel(itf))
goto out;
memcpy(sAddr, itf->tunnel_parm6.laddr.s6_addr, 16);
if(getTunnel4rdAddress(itf, dAddr, Daddr4o6,Dport4o6) < 0)
goto err0;
cmm_print(DEBUG_ERROR, "%s: V6 Tunnel :s :%x:%x:%x:%x - d:%x:%x:%x:%x\n", __func__, sAddr[0],sAddr[1],sAddr[2],sAddr[3],dAddr[0],dAddr[1],dAddr[2],dAddr[3]);
}
else
goto out;
flow.family = itf->tunnel_family;
flow.sAddr = &sAddr[0];
flow.dAddr = &dAddr[0];
flow.iifindex = 0;
flow.fwmark = 0;
if (__cmmRouteRegister(tunnel_rt, &flow, 1, dir) < 0)
goto err0;
out:
__itf_put(itf);
return 0;
err0:
__itf_put(itf);
return -1;
}
/*****************************************************************
* __cmmCheckFPPRouteIdUpdate
*
*
******************************************************************/
void __cmmCheckFPPRouteIdUpdate(struct ct_route *rt, int *flags)
{
if (rt->fpp_route)
{
if (rt->fpp_route_id != rt->fpp_route->id)
{
*flags |= FPP_NEEDS_UPDATE;
rt->fpp_route_id = rt->fpp_route->id;
}
}
else if (rt->fpp_route_id)
{
*flags |= FPP_NEEDS_UPDATE;
rt->fpp_route_id = 0;
}
}
/*****************************************************************
* ____cmmCtRegister
*
*
******************************************************************/
int ____cmmCtRegister(FCI_CLIENT *fci_handle, struct ctTable *ctEntry)
{
struct nf_conntrack *ct = ctEntry->ct;
int dir = ctEntry->dir;
const unsigned int *dAddrOrig, *dAddrRepl, *sAddrOrig, *sAddrRepl;
unsigned short dPortOrig, dPortRepl, sPortOrig, sPortRepl;
struct flow flow;
unsigned char proto;
void *tmp;
int key;
int rc;
int iif;
struct interface *itf,*out_itf;
struct RtEntry *route;
proto = nfct_get_attr_u8(ct, ATTR_ORIG_L4PROTO);
if (ctEntry->family == AF_INET)
{
sAddrOrig = nfct_get_attr(ct, ATTR_ORIG_IPV4_SRC);
sAddrRepl = nfct_get_attr(ct, ATTR_REPL_IPV4_SRC);
dAddrOrig = nfct_get_attr(ct, ATTR_ORIG_IPV4_DST);
dAddrRepl = nfct_get_attr(ct, ATTR_REPL_IPV4_DST);
}
else
{
sAddrOrig = nfct_get_attr(ct, ATTR_ORIG_IPV6_SRC);
sAddrRepl = nfct_get_attr(ct, ATTR_REPL_IPV6_SRC);
dAddrOrig = nfct_get_attr(ct, ATTR_ORIG_IPV6_DST);
dAddrRepl = nfct_get_attr(ct, ATTR_REPL_IPV6_DST);
}
sPortOrig = nfct_get_attr_u16(ct, ATTR_ORIG_PORT_SRC);
sPortRepl = nfct_get_attr_u16(ct, ATTR_REPL_PORT_SRC);
dPortOrig = nfct_get_attr_u16(ct, ATTR_ORIG_PORT_DST);
dPortRepl = nfct_get_attr_u16(ct, ATTR_REPL_PORT_DST);
if (dir & ORIGINATOR)
{
/* Check if conntrack is between two fpp interfaces */
if (!__itf_is_programmed(nfct_get_attr_u32(ct, ATTR_ORIG_COMCERTO_FP_IIF)))
{
dir &= ~ORIGINATOR;
goto replier;
}
// Is this CT secure ?
if (!ctEntry->fEntryOrig)
ctEntry->fEntryOrig = __cmmFlowGet(ctEntry->family, sAddrOrig, dAddrOrig, sPortOrig, dPortOrig, proto);
if (!ctEntry->fEntryOrig)
ctEntry->fEntryOrig = __cmmFlowGet(ctEntry->family, dAddrRepl, sAddrRepl, dPortRepl, sPortRepl, proto);
flow.family = ctEntry->family;
flow.sAddr = sAddrOrig;
flow.dAddr = sAddrRepl;
flow.iifindex = nfct_get_attr_u32(ct, ATTR_ORIG_COMCERTO_FP_IFINDEX);
flow.fwmark = nfct_get_attr_u32(ct, ATTR_ORIG_COMCERTO_FP_MARK);
if (__cmmRouteRegister(&ctEntry->orig, &flow, 0, "originator") < 0)
{
dir &= ~ORIGINATOR;
goto replier;
}
if(ctEntry->dir_filter & ORIGINATOR) {
/*check if inbound interface is LAN and outbound interface WLAN and vice-versa if so forward normally*/
itf = __itf_find(iif = nfct_get_attr_u32(ct, ATTR_ORIG_COMCERTO_FP_IIF));
route = ctEntry->orig.route;
out_itf = __itf_find(route->oifindex);
#ifdef WIFI_ENABLE
if( !(__itf_is_wifi(out_itf) && (!is_wan_port_ifindex(iif))) &&
!((!is_wan_port_ifindex(route->oifindex)) && __itf_is_wifi(itf))) {
dir &= ~ORIGINATOR;
goto replier;
}
#endif
}
tmp = ctEntry->orig_tunnel.route;
rc = __cmmCtTunnelRouteRegister(&ctEntry->orig, &ctEntry->orig_tunnel,dAddrOrig[0], dPortOrig, "originator tunnel");
if (ctEntry->orig_tunnel.route && !tmp)
{
key = HASH_RT(ctEntry->orig_tunnel.route->family, ctEntry->orig_tunnel.route->sAddr, ctEntry->orig_tunnel.route->dAddr);
list_add(&ct_table_by_orig_tunnel_route[key], &ctEntry->list_by_orig_tunnel_route);
ctEntry->flags |= FPP_NEEDS_UPDATE;
}
if (rc < 0)
{
dir &= ~ORIGINATOR;
goto replier;
}
}
replier:
if (dir & REPLIER)
{
/* Check if conntrack is between two fpp interfaces */
if (!__itf_is_programmed(nfct_get_attr_u32(ct, ATTR_REPL_COMCERTO_FP_IIF)))
{
dir &= ~REPLIER;
goto program;
}
// Is this CT secure ?
if (!ctEntry->fEntryRep)
ctEntry->fEntryRep = __cmmFlowGet(ctEntry->family, sAddrRepl, dAddrRepl, sPortRepl, dPortRepl, proto);
if (!ctEntry->fEntryRep)
ctEntry->fEntryRep = __cmmFlowGet(ctEntry->family, dAddrOrig, sAddrOrig, dPortOrig, sPortOrig, proto);
flow.family = ctEntry->family;
flow.sAddr = sAddrRepl;
flow.dAddr = sAddrOrig;
flow.iifindex = nfct_get_attr_u32(ct, ATTR_REPL_COMCERTO_FP_IFINDEX);
flow.fwmark = nfct_get_attr_u32(ct, ATTR_REPL_COMCERTO_FP_MARK);
if (__cmmRouteRegister(&ctEntry->rep, &flow, 0, "replier") < 0)
{
dir &= ~REPLIER;
goto program;
}
tmp = ctEntry->rep_tunnel.route;
rc = __cmmCtTunnelRouteRegister(&ctEntry->rep, &ctEntry->rep_tunnel, dAddrRepl[0], dPortRepl, "replier tunnel");
if (ctEntry->rep_tunnel.route && !tmp)
{
key = HASH_RT(ctEntry->rep_tunnel.route->family, ctEntry->rep_tunnel.route->sAddr, ctEntry->rep_tunnel.route->dAddr);
list_add(&ct_table_by_rep_tunnel_route[key], &ctEntry->list_by_rep_tunnel_route);
ctEntry->flags |= FPP_NEEDS_UPDATE;
}
if (rc < 0)
{
dir &= ~REPLIER;
goto program;
}
}
program:
if (dir & ORIGINATOR)
{
rc = cmmFeRouteUpdate(fci_handle, ADD | UPDATE, ctEntry->orig.fpp_route);
if (rc < 0)
{
dir &= ~ORIGINATOR;
goto program_replier;
}
if (ctEntry->orig_tunnel.fpp_route)
{
rc = cmmFeRouteUpdate(fci_handle, ADD | UPDATE, ctEntry->orig_tunnel.fpp_route);
if (rc < 0)
{
dir &= ~ORIGINATOR;
goto program_replier;
}
}
}
program_replier:
if (dir & REPLIER)
{
rc = cmmFeRouteUpdate(fci_handle, ADD | UPDATE, ctEntry->rep.fpp_route);
if (rc < 0)
{
dir &= ~REPLIER;
goto program_ct;
}
if (ctEntry->rep_tunnel.fpp_route)
{
rc = cmmFeRouteUpdate(fci_handle, ADD | UPDATE, ctEntry->rep_tunnel.fpp_route);
if (rc < 0)
{
dir &= ~REPLIER;
goto program_ct;
}
}
}
program_ct:
if (ctEntry->fpp_dir != dir)
{
ctEntry->flags |= FPP_NEEDS_UPDATE;
ctEntry->fpp_dir = dir;
}
__cmmCheckFPPRouteIdUpdate(&ctEntry->orig, &ctEntry->flags);
__cmmCheckFPPRouteIdUpdate(&ctEntry->orig_tunnel, &ctEntry->flags);
__cmmCheckFPPRouteIdUpdate(&ctEntry->rep, &ctEntry->flags);
__cmmCheckFPPRouteIdUpdate(&ctEntry->rep_tunnel, &ctEntry->flags);
cmm_third_part_update(ctEntry, dir);
if (dir)
rc = cmmFeCtUpdate(fci_handle, ADD | UPDATE, ctEntry);
else
rc = cmmFeCtUpdate(fci_handle, UPDATE, ctEntry);
if (rc == 0)
{
/* Use of globalConf.nf_conntrack_handle must be protected by ctMutex */
cmmCtSetPermanent(globalConf.nf_conntrack_handle, ctEntry);
cmm_print(DEBUG_INFO, "%s: CtAdd Success\n", __func__);
goto out;
}
cmm_print(DEBUG_ERROR, "%s: CtAdd failed\n", __func__);
return -1;
out:
return 0;
}
/*****************************************************************
* __cmmCtUpdateWithRoute
*
*
******************************************************************/
void __cmmCtUpdateWithRoute(FCI_CLIENT *fci_handle, struct RtEntry *route)
{
struct ctTable *ctEntry;
struct list_head *entry;
struct fpp_rt *fpp_route;
int key;
cmm_print(DEBUG_INFO, "%s\n", __func__);
key = HASH_RT(route->family, route->sAddr, route->dAddr);
entry = list_first(&ct_table_by_orig_route[key]);
while (entry != &ct_table_by_orig_route[key])
{
ctEntry = container_of(entry, struct ctTable, list_by_orig_route);
entry = list_next(entry);
if (ctEntry->orig.route == route)
{
fpp_route = ctEntry->orig.fpp_route;
ctEntry->orig.fpp_route = NULL;
____cmmCtRegister(fci_handle, ctEntry);
__cmmFPPRouteDeregister(fci_handle, fpp_route, "originator");
}
}
entry = list_first(&ct_table_by_rep_route[key]);
while (entry != &ct_table_by_rep_route[key])
{
ctEntry = container_of(entry, struct ctTable, list_by_rep_route);
entry = list_next(entry);
if (ctEntry->rep.route == route)
{
fpp_route = ctEntry->rep.fpp_route;
ctEntry->rep.fpp_route = NULL;
____cmmCtRegister(fci_handle, ctEntry);
__cmmFPPRouteDeregister(fci_handle, fpp_route, "replier");
}
}
entry = list_first(&ct_table_by_orig_tunnel_route[key]);
while (entry != &ct_table_by_orig_tunnel_route[key])
{
ctEntry = container_of(entry, struct ctTable, list_by_orig_tunnel_route);
entry = list_next(entry);
if (ctEntry->orig_tunnel.route == route)
{
fpp_route = ctEntry->orig_tunnel.fpp_route;
ctEntry->orig_tunnel.fpp_route = NULL;
____cmmCtRegister(fci_handle, ctEntry);
__cmmFPPRouteDeregister(fci_handle, fpp_route, "originator tunnel");
}
}
entry = list_first(&ct_table_by_rep_tunnel_route[key]);
while (entry != &ct_table_by_rep_tunnel_route[key])
{
ctEntry = container_of(entry, struct ctTable, list_by_rep_tunnel_route);
entry = list_next(entry);
if (ctEntry->rep_tunnel.route == route)
{
fpp_route = ctEntry->rep_tunnel.fpp_route;
ctEntry->rep_tunnel.fpp_route = NULL;
____cmmCtRegister(fci_handle, ctEntry);
__cmmFPPRouteDeregister(fci_handle, fpp_route, "replier tunnel");
}
}
}
/*****************************************************************
* __cmmCtRegister
*
*
******************************************************************/
static int __cmmCtRegister(FCI_CLIENT *fci_handle, struct nfct_handle *handle, struct nf_conntrack *ct, struct ctTable *ctEntry, int dir)
{
struct FlowEntry *fEntryOrig, *fEntryRep;
struct RtEntry *rtEntryOrig = NULL;
struct RtEntry *rtEntryRep = NULL;
const unsigned int *dAddrOrig, *dAddrRepl, *sAddrOrig, *sAddrRepl;
unsigned short dPortOrig, dPortRepl, sPortOrig, sPortRepl;
int family;
unsigned char proto;
cmm_print(DEBUG_INFO, "%s\n", __func__);
family = nfct_get_attr_u8(ct, ATTR_ORIG_L3PROTO);
proto = nfct_get_attr_u8(ct, ATTR_ORIG_L4PROTO);
if (family == AF_INET)
{
sAddrOrig = nfct_get_attr(ct, ATTR_ORIG_IPV4_SRC);
sAddrRepl = nfct_get_attr(ct, ATTR_REPL_IPV4_SRC);
dAddrOrig = nfct_get_attr(ct, ATTR_ORIG_IPV4_DST);
dAddrRepl = nfct_get_attr(ct, ATTR_REPL_IPV4_DST);
}
else
{
sAddrOrig = nfct_get_attr(ct, ATTR_ORIG_IPV6_SRC);
sAddrRepl = nfct_get_attr(ct, ATTR_REPL_IPV6_SRC);
dAddrOrig = nfct_get_attr(ct, ATTR_ORIG_IPV6_DST);
dAddrRepl = nfct_get_attr(ct, ATTR_REPL_IPV6_DST);
}
sPortOrig = nfct_get_attr_u16(ct, ATTR_ORIG_PORT_SRC);
sPortRepl = nfct_get_attr_u16(ct, ATTR_REPL_PORT_SRC);
dPortOrig = nfct_get_attr_u16(ct, ATTR_ORIG_PORT_DST);
dPortRepl = nfct_get_attr_u16(ct, ATTR_REPL_PORT_DST);
__pthread_mutex_lock(&rtMutex);
if (!ctEntry)
{
if (!cmmFcIsConntrackAllowed(fci_handle, ct, &rtEntryOrig))
{
cmm_print(DEBUG_INFO, "%s: conntrack not allowed\n", __func__);
/* // A socket could also use this flow, so don't delete it as yet.
// Wait for garbage collector to handle this.
__pthread_mutex_lock(&flowMutex);
if ((fEntryRep = __cmmFlowGet(family, sAddrRepl, dAddrRepl, sPortRepl, dPortRepl, proto)))
__cmmFlowPut(fEntryRep);
if ((fEntryOrig = __cmmFlowGet(family, sAddrOrig, dAddrOrig, sPortOrig, dPortOrig, proto)))
__cmmFlowPut(fEntryOrig);
if ((fEntryRep = __cmmFlowGet(family, dAddrOrig, sAddrOrig, dPortOrig, sPortOrig, proto)))
__cmmFlowPut(fEntryRep);
if ((fEntryOrig = __cmmFlowGet(family, dAddrRepl, sAddrRepl, dPortRepl, sPortRepl, proto)))
__cmmFlowPut(fEntryOrig);
__pthread_mutex_unlock(&flowMutex);
*/
goto fail0;
}
if (family == AF_INET)
{
struct RtEntry *rtEntryPolicy;
rtEntryPolicy = cmmPolicyRouting(sAddrOrig[0], sAddrRepl[0], proto, sPortOrig, sPortRepl);
if (rtEntryPolicy)
{
if (rtEntryOrig)
____cmmRouteDeregister(rtEntryOrig, "originator");
rtEntryOrig = rtEntryPolicy;
}
rtEntryPolicy = cmmPolicyRouting(sAddrRepl[0], sAddrOrig[0], proto, sPortRepl, sPortOrig);
if (rtEntryPolicy)
{
if (rtEntryRep)
____cmmRouteDeregister(rtEntryRep, "replier");
rtEntryRep = rtEntryPolicy;
}
}
ctEntry = __cmmCtAdd(ct);
if (!ctEntry)
{
cmm_print(DEBUG_ERROR, "%s: conntrack add failed\n", __func__);
goto fail0;
}
ct_stats.created++;
if (globalConf.asymff_enable) {
if(cmmFcIsConntrackAsymFastForwarded(ct))
ctEntry->dir_filter = ORIGINATOR;
}
if (rtEntryOrig)
ctEntry->orig.route = rtEntryOrig;
if (rtEntryRep)
ctEntry->rep.route = rtEntryRep;
}
ctEntry->dir = dir;
__pthread_mutex_lock(&neighMutex);
__pthread_mutex_lock(&flowMutex);
____cmmCtRegister(fci_handle, ctEntry);
__pthread_mutex_unlock(&flowMutex);
__pthread_mutex_unlock(&neighMutex);
__pthread_mutex_unlock(&rtMutex);
/* since we keep the conntrack in the local cache don't return NFCT_CB_CONTINUE */
return NFCT_CB_STOLEN;
fail0:
if (rtEntryOrig)
____cmmRouteDeregister(rtEntryOrig, "originator");
if (rtEntryRep)
____cmmRouteDeregister(rtEntryRep, "replier");
__pthread_mutex_unlock(&rtMutex);
return NFCT_CB_CONTINUE;
}
/*****************************************************************
* __cmmCtUpdate
*
*
******************************************************************/
static void __cmmCtUpdate(struct nf_conntrack *ct, struct nfct_handle *handle, struct ctTable *ctEntry)
{
cmm_print(DEBUG_INFO, "%s\n", __func__);
nfct_destroy(ctEntry->ct);
ctEntry->ct = ct;
cmmCtSetPermanent(handle, ctEntry);
}
/*****************************************************************
* __cmmFPPRouteDeregister
*
*
******************************************************************/
void __cmmFPPRouteDeregister(FCI_CLIENT *fci_handle, struct fpp_rt *fpp_route, const char *dir)
{
int rc = 0;
if (!fpp_route)
return;
cmm_print(DEBUG_INFO, "%s: removing %s route entry\n", __func__, dir);
if (fpp_route->count == 1)
{
rc = cmmFeRouteUpdate(fci_handle, REMOVE, fpp_route);
}
/* In case of a deregister error don't free the route entry, we still need to track the fpp state */
if (rc < 0)
fpp_route->count--;
else
__cmmFPPRoutePut(fpp_route);
}
/*****************************************************************
* ____cmmRouteDeregister
*
*
******************************************************************/
void ____cmmRouteDeregister(struct RtEntry *route, const char *dir)
{
cmm_print(DEBUG_INFO, "%s: removing %s route entry\n", __func__, dir);
if (route->count == 1)
{
//Try to remove arp entries
if (route->neighEntry)
{
__cmmNeighPut(route->neighEntry);
route->neighEntry = NULL;
}
else
{
cmm_print(DEBUG_WARNING, "%s: %s ARP/Neighbor entry not found\n", __func__, dir);
}
}
__cmmRoutePut(route);
}
/*****************************************************************
* __cmmRouteDeregister
*
*
******************************************************************/
void __cmmRouteDeregister(FCI_CLIENT *fci_handle, struct ct_route *rt, const char *dir)
{
if (rt->fpp_route)
{
__cmmFPPRouteDeregister(fci_handle, rt->fpp_route, dir);
rt->fpp_route = NULL;
}
if (rt->route)
{
____cmmRouteDeregister(rt->route, dir);
rt->route = NULL;
}
else
{
cmm_print(DEBUG_WARNING, "%s: %s route entry not found\n", __func__, dir);
}
}
/*****************************************************************
* ____cmmCtDeregister
*
*
******************************************************************/
void ____cmmCtDeregister(FCI_CLIENT *fci_handle, FCI_CLIENT *fci_key_handle, struct ctTable *ctEntry)
{
int rc;
rc = cmmFeCtUpdate(fci_handle, REMOVE, ctEntry);
__pthread_mutex_lock(&rtMutex);
__pthread_mutex_lock(&neighMutex);
//Try to remove route entries
__cmmRouteDeregister(fci_handle, &ctEntry->rep, "replier");
__cmmRouteDeregister(fci_handle, &ctEntry->orig, "originator");
if (ctEntry->rep_tunnel.route)
{
__cmmRouteDeregister(fci_handle, &ctEntry->rep_tunnel, "replier tunnel");
list_del(&ctEntry->list_by_rep_tunnel_route);
}
if (ctEntry->orig_tunnel.route)
{
__cmmRouteDeregister(fci_handle, &ctEntry->orig_tunnel, "originator tunnel");
list_del(&ctEntry->list_by_orig_tunnel_route);
}
__pthread_mutex_unlock(&neighMutex);
__pthread_mutex_unlock(&rtMutex);
__pthread_mutex_lock(&flowMutex);
// Flow cache entry remove
if (ctEntry->fEntryOrig)
if (!cmmFlowKeyEngineRemove(fci_key_handle, ctEntry->fEntryOrig))
{
__cmmFlowPut(ctEntry->fEntryOrig);
ctEntry->fEntryOrig = NULL;
}
if (ctEntry->fEntryRep)
if (!cmmFlowKeyEngineRemove(fci_key_handle, ctEntry->fEntryRep))
{
__cmmFlowPut(ctEntry->fEntryRep);
ctEntry->fEntryRep = NULL;
}
__pthread_mutex_unlock(&flowMutex);
if (!rc)
__cmmCtRemove(ctEntry);
else
cmm_print(DEBUG_ERROR, "%s: DeRegister failed\n", __func__);
}
/*****************************************************************
* __cmmCtDeregister
*
*
******************************************************************/
static void __cmmCtDeregister(struct cmm_ct *ctx, struct nf_conntrack *ct)
{
struct ctTable *ctEntry;
u_int32_t id1, id2;
cmm_print(DEBUG_INFO, "%s\n", __func__);
ctEntry = __cmmCtFind(ct);
if (!ctEntry) {
cmm_print(DEBUG_INFO, "%s: conntrack entry not found\n", __func__);
goto out;
}
/* Make sure we're destroying the correct ctEntry (bz46186) */
id1 = nfct_get_attr_u32(ct, ATTR_ID);
id2 = nfct_get_attr_u32(ctEntry->ct, ATTR_ID);
if (id1 != 0 && id2 != 0 && id1 != id2)
{
cmm_print(DEBUG_WARNING, "%s: ID mismatch (%d, %d)\n", __func__, id1, id2);
goto out;
}
ct_stats.destroyed++;
____cmmCtDeregister(ctx->fci_handle, ctx->fci_key_handle, ctEntry);
out:
return;
}
static const char *conntrack_event_type(enum nf_conntrack_msg_type type)
{
if (type & NFCT_T_UPDATE)
return "UPDATE";
if (type & NFCT_T_DESTROY)
return "DESTROY";
if (type & NFCT_T_NEW)
return "NEW";
return "unsupported type";
}
static const char *conntrack_status(u_int32_t status)
{
if (status & IPS_ASSURED)
return "ASSURED";
if ((status & (IPS_CONFIRMED | IPS_SEEN_REPLY)) == IPS_CONFIRMED)
return "CONFIRMED";
if ((status & (IPS_CONFIRMED | IPS_SEEN_REPLY)) == (IPS_CONFIRMED | IPS_SEEN_REPLY))
return "CONFIRMED/SEEN_REPLY";
return "unsupported status";
}
static const char *conntrack_tcp_state(u_int8_t state)
{
static const char *tcp_state[] = {
[TCP_CONNTRACK_NONE] = "NONE",
[TCP_CONNTRACK_SYN_SENT] = "SYN_SENT",
[TCP_CONNTRACK_SYN_RECV] = "SYN_RECV",
[TCP_CONNTRACK_ESTABLISHED] = "ESTABLISHED",
[TCP_CONNTRACK_FIN_WAIT] = "FIN_WAIT",
[TCP_CONNTRACK_CLOSE_WAIT] = "CLOSE_WAIT",
[TCP_CONNTRACK_LAST_ACK] = "LAST_ACK",
[TCP_CONNTRACK_TIME_WAIT] = "TIME_WAIT",
[TCP_CONNTRACK_CLOSE] = "CLOSE",
[TCP_CONNTRACK_LISTEN] = "LISTEN",
[TCP_CONNTRACK_SYN_SENT2] = "SYN_SENT2",
[TCP_CONNTRACK_MAX] = "MAX"
};
if (state >= TCP_CONNTRACK_MAX)
return "unknown state";
return tcp_state[state];
}
static int cmmCtCheckCtCb(enum nf_conntrack_msg_type type,
struct nf_conntrack *ct,
void *data)
{
struct nf_conntrack *ct_local = (struct nf_conntrack *) cb_data;
cb_status = 0;
if((nfct_get_attr_u32(ct, ATTR_ID) != nfct_get_attr_u32(ct_local, ATTR_ID))
|| __cmmCtIsInv(ct, ct_local))
cb_status = -1;
return NFCT_CB_CONTINUE;
}
static int cmmCheckUpdateEvent(struct nfct_handle *handle, struct nf_conntrack *ct)
{
int rc = 0;
cb_data = ct;
if (nfct_query(handle, NFCT_Q_GET, (void*)ct) < 0) {
if (errno == ENOENT) {
rc = -1;
goto exit;
}
else
cmm_print(DEBUG_ERROR, "%s: nfct_query(NFCT_Q_GET) %s %d\n", __func__, strerror(errno), errno);
}
else if (cb_status < 0)
rc = -1;
exit:
return rc;
}
static int cmmCheckEvent(struct cmm_ct *ctx, struct nf_conntrack *ct, struct ctTable **ctEntry, enum nf_conntrack_msg_type type, u_int32_t id)
{
/* First pass on entries not already tracked in CMM */
switch (type) {
case NFCT_T_UPDATE:
/* Racy update event, need to check if entry
* does really exist
*/
if (!*ctEntry || !__cmmCtFindId(*ctEntry, id))
if (cmmCheckUpdateEvent(ctx->get_handle, ct) < 0)
return -1;
break;
case NFCT_T_DESTROY:
if (!*ctEntry)
return -1;
break;
default:
break;
}
/* Second pass on tracked entries */
if (*ctEntry)
switch (type) {
case NFCT_T_NEW:
/* Do not process a new event
* which arrives after an update
*/
if (*ctEntry && __cmmCtFindId(*ctEntry, id))
return -1;
case NFCT_T_UPDATE:
if (!__cmmCtFindId(*ctEntry, id))
__cmmCtAddId(*ctEntry, id);
/* Do not process multiple ids entries */
if ((*ctEntry)->n_id > 1)
return -1;
break;
case NFCT_T_DESTROY:
/* If after __cmmCtDelId
* n_id > 1 : keep the conntrack but do not unregister it, need to wait.
* Still in the middle of an out-of-sync period
* n_id = 1 : coming back from an out-of-sync period, ask kernel for an update
* but still destroy the current entry as its state is not in sync anymore
* n_id = 0 : regular case, deregister
*/
if (!__cmmCtDelId(*ctEntry, id)) {
if ((*ctEntry)->n_id > 1) {
return -1;
}
else if (((*ctEntry)->n_id) == 1)
cmmCtForceUpdate(ctx->handle, *ctEntry);
}
else
return -1;
break;
default:
break;
}
/* Success, event will be processed */
return 0;
}
/*****************************************************************
* __cmmCtCatch()
*
* This function is called by libnetfilter_conntrack library
* when an event occurs on conntrack table
*
******************************************************************/
static int __cmmCtCatch(struct cmm_ct *ctx, enum nf_conntrack_msg_type type, struct nf_conntrack *ct)
{
u_int8_t l4proto;
u_int8_t l3proto;
u_int8_t state;
int rc = NFCT_CB_CONTINUE;
u_int32_t status;
struct ctTable *ctEntry = NULL;
u_int32_t id;
// If Forward Engine programmation is forbidden, don't do anything
if (globalConf.enable == 0)
goto exit;
#ifdef C2000_DPI
status = nfct_get_attr_u32(ct, ATTR_STATUS);
if ((globalConf.dpi_enable) && ((status & IPS_DPI_ALLOWED) != IPS_DPI_ALLOWED))
{
goto exit;
}
#endif
l3proto = nfct_get_attr_u8(ct, ATTR_ORIG_L3PROTO);
if ((l3proto != AF_INET6) && (l3proto != AF_INET))
{
cmm_print(DEBUG_INFO, "%s: unsupported L3 Proto: %#x\n", __func__, l3proto);
goto exit;
}
// In case of TCP we should check that the connection is established
l4proto = nfct_get_attr_u8(ct, ATTR_ORIG_L4PROTO);
id = nfct_get_attr_u32(ct, ATTR_ID);
if (!id)
cmm_print(DEBUG_ERROR, "%s: Conntrack event with NULL id\n", __func__);
if ((l4proto == IPPROTO_UDP) || (l4proto == IPPROTO_IPIP))
{
ctEntry = __cmmCtFind(ct);
if (cmmCheckEvent(ctx, ct, &ctEntry, type, id) < 0)
goto exit;
switch (type) {
case NFCT_T_DESTROY:
cmm_print(DEBUG_INFO, "%s: proto %d connection %s(%#x)\n", __func__, l4proto, conntrack_event_type(type), type);
if (ctEntry) {
ct_stats.destroyed++;
____cmmCtDeregister(ctx->fci_handle, ctx->fci_key_handle, ctEntry);
}
break;
case NFCT_T_NEW:
case NFCT_T_UPDATE:
status = nfct_get_attr_u32(ct, ATTR_STATUS);
if (ctEntry) {
__cmmCtUpdate(ct, ctx->handle, ctEntry);
rc = NFCT_CB_STOLEN;
}
cmm_print(DEBUG_INFO, "%s: proto %d connection %s(%#x) %s(%#x)\n", __func__,
l4proto, conntrack_event_type(type), type,
conntrack_status(status), status);
if (l4proto == IPPROTO_UDP) {
if (status & IPS_ASSURED) {
rc = __cmmCtRegister(ctx->fci_handle, ctx->handle, ct, ctEntry, ORIGINATOR | REPLIER);
}
else if (status & IPS_CONFIRMED) {
rc = __cmmCtRegister(ctx->fci_handle, ctx->handle, ct, ctEntry, ORIGINATOR);
}
else {
goto exit;
}
}
else if (l4proto == IPPROTO_IPIP) {
if ((status & (IPS_CONFIRMED | IPS_SEEN_REPLY)) == IPS_CONFIRMED) {
rc = __cmmCtRegister(ctx->fci_handle, ctx->handle, ct, ctEntry, ORIGINATOR);
}
/* Actually IPIP connections will never reach ASSURED state, so both directions are
* programmed when packets have been seen in opposite direction */
else if ((status & (IPS_CONFIRMED | IPS_SEEN_REPLY)) == (IPS_CONFIRMED | IPS_SEEN_REPLY)) {
rc = __cmmCtRegister(ctx->fci_handle, ctx->handle, ct, ctEntry, ORIGINATOR | REPLIER);
}
else {
goto exit;
}
}
break;
default:
cmm_print(DEBUG_INFO, "%s: proto %d connection %s(%#x)\n", __func__,
l4proto, conntrack_event_type(type), type);
break;
}
}
else if (l4proto == IPPROTO_TCP)
{
switch (type) {
case NFCT_T_DESTROY:
cmm_print(DEBUG_INFO, "%s: TCP connection %s(%#x)\n", __func__, conntrack_event_type(type), type);
__cmmCtDeregister(ctx, ct);
break;
case NFCT_T_NEW:
case NFCT_T_UPDATE:
status = nfct_get_attr_u32(ct, ATTR_STATUS);
ctEntry = __cmmCtFind(ct);
if (ctEntry) {
__cmmCtUpdate(ct, ctx->handle, ctEntry);
rc = NFCT_CB_STOLEN;
}
if (nfct_attr_is_set(ct, ATTR_TCP_STATE))
{
state = nfct_get_attr_u8(ct, ATTR_TCP_STATE);
cmm_print(DEBUG_INFO, "%s: TCP connection %s(%#x) %s(%#x) %s(%#x)\n", __func__,
conntrack_event_type(type), type,
conntrack_status(status), status,
conntrack_tcp_state(state), state);
if (state == TCP_CONNTRACK_ESTABLISHED)
rc = __cmmCtRegister(ctx->fci_handle, ctx->handle, ct, ctEntry, ORIGINATOR | REPLIER);
else
if (ctEntry)
{
ct_stats.destroyed++;
____cmmCtDeregister(ctx->fci_handle, ctx->fci_key_handle, ctEntry);
}
}
else
{
cmm_print(DEBUG_INFO, "%s: TCP connection %s(%#x) %s(%#x) missing state attribute\n", __func__,
conntrack_event_type(type), type,
conntrack_status(status), status);
goto exit;
}
break;
default:
cmm_print(DEBUG_INFO, "%s: TCP connection %s(%#x)\n", __func__,
conntrack_event_type(type), type);
break;
}
}
exit:
return rc;
}
static int count = 0;
/*****************************************************************
* cmmCtCatch()
*
* This function is called by libnetfilter_conntrack library
* when an event occurs on conntrack table
*
******************************************************************/
static int cmmCtCatch(enum nf_conntrack_msg_type type, struct nf_conntrack *ct, void *data)
{
struct cmm_ct *ctx = data;
int rc;
__pthread_mutex_lock(&itf_table.lock);
__pthread_mutex_lock(&ctMutex);
rc = __cmmCtCatch(ctx, type, ct);
__pthread_mutex_unlock(&ctMutex);
__pthread_mutex_unlock(&itf_table.lock);
count++;
return rc;
}
/*****************************************************************
* file_write
*
*
******************************************************************/
static int file_write(const char *filename, const void *buf, int size)
{
int fd;
if ((fd = open(filename, O_WRONLY)) < 0)
{
cmm_print(DEBUG_ERROR, "%s: open(%s) failed, %s\n", __func__, filename, strerror(errno));
goto err0;
}
if (write(fd, buf, size) != size)
{
cmm_print(DEBUG_ERROR, "%s: write() failed, %s\n", __func__, strerror(errno));
goto err1;
}
close(fd);
return 0;
err1:
close(fd);
err0:
return -1;
}
/*****************************************************************
* cmmCtKernelModuleInit
*
*
******************************************************************/
static int cmmCtKernelModuleInit()
{
cmm_print(DEBUG_INFO, "%s:\n", __func__);
return file_write(TCP_BE_LIBERAL_PATH, "1", 1);
}
/*****************************************************************
* cmmCtKernelModuleUnInit
*
*
******************************************************************/
static void cmmCtKernelModuleUnInit()
{
cmm_print(DEBUG_INFO, "%s:\n", __func__);
file_write(TCP_BE_LIBERAL_PATH, "0", 1);
}
#ifdef APP_SOLICIT
/*****************************************************************
* cmmNeighborKernelModuleInit
*
*
******************************************************************/
static int cmmRtnlKernelModuleInit()
{
cmm_print(DEBUG_INFO, "%s:\n", __func__);
/*Prepare the neighbor code to be in a good configuration*/
if (file_write(APP_SOLICIT_IPV4_PATH, "1", 1) < 0)
{
goto err0;
}
if (file_write(APP_SOLICIT_IPV4_WAN_PATH, "1", 1) < 0)
{
goto err1;
}
if (file_write(APP_SOLICIT_IPV4_LAN_PATH, "1", 1) < 0)
{
goto err2;
}
if (file_write(APP_SOLICIT_IPV6_PATH, "1", 1) < 0)
{
goto err3;
}
if (file_write(APP_SOLICIT_IPV6_WAN_PATH, "1", 1) < 0)
{
goto err4;
}
if (file_write(APP_SOLICIT_IPV6_LAN_PATH, "1", 1) < 0)
{
goto err5;
}
return 0;
err5:
file_write(APP_SOLICIT_IPV6_WAN_PATH, "0", 1);
err4:
file_write(APP_SOLICIT_IPV6_PATH, "0", 1);
err3:
file_write(APP_SOLICIT_IPV4_LAN_PATH, "0", 1);
err2:
file_write(APP_SOLICIT_IPV4_WAN_PATH, "0", 1);
err1:
file_write(APP_SOLICIT_IPV4_PATH, "0", 1);
err0:
return -1;
}
/*****************************************************************
* cmmNeighborKernelModuleUnInit
*
*
******************************************************************/
static void cmmRtnlKernelModuleUnInit()
{
cmm_print(DEBUG_INFO, "%s:\n", __func__);
file_write(APP_SOLICIT_IPV4_PATH, "0", 1);
file_write(APP_SOLICIT_IPV4_WAN_PATH, "0", 1);
file_write(APP_SOLICIT_IPV4_LAN_PATH, "0", 1);
file_write(APP_SOLICIT_IPV6_PATH, "0", 1);
file_write(APP_SOLICIT_IPV6_WAN_PATH, "0", 1);
file_write(APP_SOLICIT_IPV6_LAN_PATH, "0", 1);
}
#endif
/*****************************************************************
* cmmCtResync
*
*
******************************************************************/
static int cmmCtResync(struct cmm_ct *ctx)
{
struct ctTable *ctEntry;
struct list_head *entry, *next;
int family;
int i;
int queried = 0;
int destroyed = 0;
int len, read = 0;
int mult_ids_dest = 0;
int entry_exist = 0;
unsigned char buf[64 * 1024];
cmm_print(DEBUG_ERROR, "%s: start %d\n", __func__, ct_stats.current);
/* Flush all state events in the netlink socket */
while (read < 2 * NFNL_SOCK_SIZE)
{
len = nfnl_recv(nfct_nfnlh(ctx->catch_handle), buf, 64 * 1024);
if (len <= 0)
{
/* Buffer is empty, exit */
if (errno == EAGAIN)
break;
cmm_print(DEBUG_ERROR, "%s: nfnl_recv() %s\n", __func__, strerror(errno));
/* Lost events again, try to re-resync later */
if (errno == ENOBUFS)
goto err;
continue;
}
read += len;
}
cmm_print(DEBUG_ERROR, "%s: flushed %d bytes\n", __func__, read);
__pthread_mutex_lock(&ctMutex);
/* Go through all connections and determine which no longer exist in the kernel, then remove them */
for (i = 0; i < CONNTRACK_HASH_TABLE_SIZE; i++)
{
for (entry = list_first(&ct_table[i]); next = list_next(entry), entry != &ct_table[i]; entry = next)
{
ctEntry = container_of(entry, struct ctTable, list);
queried++;
if (ctEntry->n_id > 1) {
mult_ids_dest++;
goto destroy;
}
cb_data = ctEntry->ct;
if (nfct_query(ctx->get_handle, NFCT_Q_GET, (void*)ctEntry->ct) < 0) {
if (errno != ENOENT)
cmm_print(DEBUG_ERROR, "%s: nfct_query(NFCT_Q_GET) %s\n", __func__, strerror(errno));
else
goto destroy;
}
else if (cb_status < 0) {
destroy:
destroyed++;
ct_stats.destroyed++;
____cmmCtDeregister(ctx->fci_handle, ctx->fci_key_handle, ctEntry);
}
}
}
cmm_print(DEBUG_ERROR, "%s: end %d %d %d %d %d\n", __func__, ct_stats.current, queried, destroyed, mult_ids_dest, entry_exist);
__pthread_mutex_unlock(&ctMutex);
/* Now dump all existing conntracks */
family = AF_UNSPEC;
if (nfct_query(ctx->catch_handle, NFCT_Q_DUMP, (void *)&family) < 0)
{
cmm_print(DEBUG_ERROR, "%s: nfct_query(NFCT_Q_DUMP) %s\n", __func__, strerror(errno));
goto err;
}
return 0;
err:
cmm_print(DEBUG_ERROR, "%s: end %d %d %d\n", __func__, ct_stats.current, queried, destroyed);
return -1;
}
sig_atomic_t timer_expired = 0;
static void sig_alarm_hdlr(int signum)
{
timer_expired = 1;
}
/*****************************************************************
* cmmCtThread
*
* Function that initializes Conntrack event catching
*
******************************************************************/
static void *cmmCtThread(void *data)
{
struct cmm_ct *ctx = data;
fd_set set;
int fd_ct, fd_fci;
#if !defined(IPSEC_SUPPORT_DISABLED)
int fd_key;
#endif
int fd_neigh, fd_link, fd_ifaddr, fd_route, fd_rule, fd_abm;
struct timeval timeout;
struct itimerval itimer;
struct sigaction action;
int need_resync = 0;
int rc;
cmm_print(DEBUG_INFO, "%s: pid %d\n", __func__, getpid());
#if !defined(IPSEC_SUPPORT_DISABLED)
fd_key = fci_fd(ctx->fci_key_catch_handle);
#endif
fd_fci = fci_fd(ctx->fci_catch_handle);
fd_ct = nfct_fd(ctx->catch_handle);
/* Dump all conntracks */
cmmCtResync(ctx);
action.sa_handler = sig_alarm_hdlr;
sigemptyset(&action.sa_mask);
action.sa_flags = 0;
if (sigaction(SIGALRM, &action, NULL) < 0)
{
cmm_print(DEBUG_ERROR, "%s: sigaction() failed %s\n", __func__, strerror(errno));
goto out;
}
/* Set periodic timer, every one second */
itimer.it_value.tv_sec = 1;
itimer.it_value.tv_usec = 0;
itimer.it_interval.tv_sec = 1;
itimer.it_interval.tv_usec = 0;
if (setitimer(ITIMER_REAL, &itimer, NULL) < 0)
{
cmm_print(DEBUG_ERROR, "%s: setitimer() failed %s\n", __func__, strerror(errno));
goto out;
}
fd_neigh = cmm_rtnl_fd(&ctx->rth_neigh);
fd_link = cmm_rtnl_fd(&ctx->rth_link);
fd_ifaddr = cmm_rtnl_fd(&ctx->rth_ifaddr);
fd_route = cmm_rtnl_fd(&ctx->rth_route);
fd_rule = cmm_rtnl_fd(&ctx->rth_rule);
fd_abm= cmm_rtnl_fd(&ctx->rth_abm);
while (1)
{
if (timer_expired)
{
#if PPPOE_AUTO_ENABLE
cmmPPPoEAutoKeepAlive();
#endif
cmmDPDIPsecSAUpdate(ctx);
/* Resync if needed and system is idle */
if (need_resync && !count)
{
if (!cmmCtResync(ctx))
need_resync = 0;
}
cmmNeighSendSolicit();
count = 0;
timer_expired = 0;
}
FD_ZERO (&set);
FD_SET (fd_ct, &set);
#if !defined(IPSEC_SUPPORT_DISABLED)
FD_SET (fd_key, &set);
#endif
FD_SET (fd_fci, &set);
FD_SET (fd_neigh, &set);
FD_SET (fd_link, &set);
FD_SET (fd_ifaddr, &set);
FD_SET (fd_route, &set);
FD_SET (fd_rule, &set);
if(globalConf.auto_bridge)
FD_SET (fd_abm, &set);
timeout.tv_sec = 1;
timeout.tv_usec = 0;
rc = select(FD_SETSIZE, &set, NULL, NULL, &timeout);
if (rc < 0)
{
if (errno == EINTR)
continue;
cmm_print(DEBUG_ERROR, "%s: select() failed %s\n", __func__, strerror(errno));
goto out;
}
#if !defined(IPSEC_SUPPORT_DISABLED)
if (FD_ISSET(fd_key, &set))
{
rc = fci_catch(ctx->fci_key_catch_handle);
if (rc < 0)
{
if (errno != EAGAIN)
{
cmm_print(DEBUG_ERROR, "%s: fci_catch() failed %s\n", __func__, strerror(errno));
}
}
}
#endif
if (FD_ISSET(fd_ct, &set))
{
rc = nfct_catch(ctx->catch_handle);
if (rc < 0)
{
if (errno != EAGAIN)
{
cmm_print(DEBUG_ERROR, "%s: nfct_catch() %s\n", __func__, strerror(errno));
if (errno == ENOBUFS)
{
/* At this point we lost some conntrack events,
try to resync later when system is idle */
need_resync = 1;
}
}
}
}
if (FD_ISSET(fd_fci, &set))
{
rc = fci_catch(ctx->fci_catch_handle);
if (rc < 0)
{
if (errno != EAGAIN)
{
cmm_print(DEBUG_ERROR, "%s: fci_catch() failed %s\n", __func__, strerror(errno));
}
}
}
if (FD_ISSET(fd_neigh, &set))
{
rc = cmm_rtnl_listen(&ctx->rth_neigh, cmmRtnlNeigh, ctx);
if (rc < 0)
{
if (errno != EAGAIN)
{
cmm_print(DEBUG_ERROR, "%s: cmm_rtnl_listen() failed %s\n", __func__, strerror(errno));
if (errno == ENOBUFS)
{
/* If the above function exits, there was some error, try to dump all ARP/Neighbor entries in case an event was lost */
cmm_rtnl_neigh_dump_request(&ctx->rth_neigh, AF_INET);
cmm_rtnl_neigh_dump_request(&ctx->rth_neigh, AF_INET6);
}
}
}
}
if (FD_ISSET(fd_link, &set))
{
rc = cmm_rtnl_listen(&ctx->rth_link, cmmRtnlLink, &itf_table);
if (rc < 0)
{
if (errno != EAGAIN)
{
cmm_print(DEBUG_ERROR, "%s: cmm_rtnl_listen() failed %s\n", __func__, strerror(errno));
}
}
}
if (FD_ISSET(fd_ifaddr, &set))
{
rc = cmm_rtnl_listen(&ctx->rth_ifaddr, cmmRtnlIfAddr, &itf_table);
if (rc < 0)
{
if (errno != EAGAIN)
{
cmm_print(DEBUG_ERROR, "%s: cmm_rtnl_listen() failed %s\n", __func__, strerror(errno));
}
}
}
if (FD_ISSET(fd_route, &set))
{
rc = cmm_rtnl_listen(&ctx->rth_route, cmmRtnlRoute, ctx);
if (rc < 0)
{
if (errno != EAGAIN)
{
cmm_print(DEBUG_ERROR, "%s: cmm_rtnl_listen() failed %s\n", __func__, strerror(errno));
}
}
}
if (FD_ISSET(fd_rule, &set))
{
rc = cmm_rtnl_listen(&ctx->rth_rule, cmmRtnlRule, ctx);
if (rc < 0)
{
if (errno != EAGAIN)
{
cmm_print(DEBUG_ERROR, "%s: cmm_rtnl_listen() failed %s\n", __func__, strerror(errno));
}
}
}
if(globalConf.auto_bridge){
if (FD_ISSET(fd_abm, &set))
{
rc = cmm_rtnl_listen(&ctx->rth_abm, cmm_l2flow_netlink_rcv, ctx);
if (rc < 0)
{
if (errno != EAGAIN)
{
cmm_print(DEBUG_ERROR, "%s: cmm_rtnl_listen() failed %s\n", __func__, strerror(errno));
}
}
}
}
}
out:
cmm_print(DEBUG_INFO, "%s: exiting\n", __func__);
kill (0, SIGTERM);
pthread_exit(NULL);
return NULL;
}
int cmmCtInit(struct cmm_ct *ctx)
{
int fd;
int i;
int size;
socklen_t socklen = sizeof(size);
cmm_print(DEBUG_INFO, "%s\n", __func__);
for (i = 0; i < CONNTRACK_HASH_TABLE_SIZE; i++)
{
list_head_init(&ct_table[i]);
list_head_init(&ct_table_by_rep[i]);
}
for (i = 0; i < FLOW_HASH_TABLE_SIZE; i++)
list_head_init(&flow_table[i]);
for (i = 0; i < 2 * NEIGHBOR_HASH_TABLE_SIZE; i++)
{
list_head_init(&rt_table_by_gw_ip[i]);
list_head_init(&neigh_table[i]);
}
list_head_init(&neigh_state_table);
for (i = 0; i < NEIGHBOR_HASH_TABLE_SIZE; i++)
list_head_init(&neigh_table_by_mac[i]);
for (i = 0; i < ROUTE_HASH_TABLE_SIZE; i++)
list_head_init(&fpp_rt_table[i]);
for (i = 0; i < 2 * ROUTE_HASH_TABLE_SIZE; i++)
{
list_head_init(&rt_table[i]);
list_head_init(&ct_table_by_orig_route[i]);
list_head_init(&ct_table_by_rep_route[i]);
list_head_init(&ct_table_by_orig_tunnel_route[i]);
list_head_init(&ct_table_by_rep_tunnel_route[i]);
}
for (i = 0; i < HASH_SOCKET_SIZE ; i++) {
list_head_init(&socket_table[i]);
list_head_init(&socket_table_by_addr[i]);
}
for (i = 0; i < MC_NUM_HASH_ENTRIES ; i++)
list_head_init(&mc_table[i]);
for (i = 0; i < L2FLOW_HASH_TABLE_SIZE; i++)
list_head_init(&l2flow_table[i]);
memset(&ct_stats, 0, sizeof(struct conntrack_stats));
if (cmmCtKernelModuleInit() < 0)
{
goto err0;
}
#ifdef APP_SOLICIT
if (cmmRtnlKernelModuleInit() < 0)
{
goto err1;
}
#endif
ctx->fci_catch_handle = fci_open(FCILIB_FF_TYPE, NL_FF_GROUP);
if (!ctx->fci_catch_handle)
{
cmm_print(DEBUG_CRIT, "%s: fci_open() failed, %s\n", __func__, strerror(errno));
goto err2;
}
ctx->fci_handle = fci_open(FCILIB_FF_TYPE, 0);
if (!ctx->fci_handle)
{
cmm_print(DEBUG_CRIT, "%s: fci_open() failed, %s\n", __func__, strerror(errno));
goto err3;
}
#if !defined(IPSEC_SUPPORT_DISABLED)
ctx->fci_key_catch_handle = fci_open(FCILIB_KEY_TYPE, NL_KEY_ALL_GROUP);
if (!ctx->fci_key_catch_handle)
{
cmm_print(DEBUG_CRIT, "%s: fci_open() failed, %s\n", __func__, strerror(errno));
goto err4;
}
ctx->fci_key_handle = fci_open(FCILIB_KEY_TYPE, 0);
if (!ctx->fci_key_handle)
{
cmm_print(DEBUG_CRIT, "%s: fci_open() failed, %s\n", __func__, strerror(errno));
goto err5;
}
#endif
// Open a Conntrack socket
ctx->catch_handle = nfct_open(CONNTRACK, NFCT_ALL_CT_GROUPS);
if (!ctx->catch_handle)
{
cmm_print(DEBUG_CRIT, "%s: nfct_open() failed, %s\n", __func__, strerror(errno));
goto err6;
}
// Open a Netfilter socket
ctx->handle = nfct_open(CONNTRACK, 0);
if (!ctx->handle)
{
cmm_print(DEBUG_CRIT, "%s: nfct_open()failed, %s\n", __func__, strerror(errno));
goto err7;
}
// Open a Netfilter socket
ctx->get_handle = nfct_open(CONNTRACK, 0);
if (!ctx->get_handle)
{
cmm_print(DEBUG_CRIT, "%s: nfct_open()failed, %s\n", __func__, strerror(errno));
goto err8;
}
/* Open a Netlink socket and register to the Multicast groups*/
if (cmm_rtnl_open(&ctx->rth_neigh, RTMGRP_NEIGH) < 0)
{
cmm_print(DEBUG_STDERR, "%s: cmm_rtnl_open(NEIGHBOR) failed\n", __func__);
goto err9;
}
if (cmm_rtnl_open(&ctx->rth_link, RTMGRP_LINK) < 0)
{
cmm_print(DEBUG_STDERR, "%s: cmm_rtnl_open(LINK) failed\n", __func__);
goto err10;
}
if (cmm_rtnl_open(&ctx->rth_ifaddr, RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR) < 0)
{
cmm_print(DEBUG_STDERR, "%s: cmm_rtnl_open(IFADDR) failed\n", __func__);
goto err11;
}
if (cmm_rtnl_open(&ctx->rth_route, RTMGRP_IPV4_ROUTE | RTMGRP_IPV6_ROUTE) < 0)
{
cmm_print(DEBUG_STDERR, "%s: cmm_rtnl_open(ROUTE) failed\n", __func__);
goto err12;
}
if (cmm_rtnl_open(&ctx->rth_rule, RTMGRP_IPV4_RULE) < 0)
{
cmm_print(DEBUG_STDERR, "%s: cmm_rtnl_open(RULE) failed\n", __func__);
goto err13;
}
if(cmmBridgeInit(ctx))
goto err14;
fd = fci_fd(ctx->fci_catch_handle);
if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0)
{
cmm_print(DEBUG_ERROR, "%s: fcntl(%d) failed %s\n", __func__, fd, strerror(errno));
goto err14;
}
#if !defined(IPSEC_SUPPORT_DISABLED)
fd = fci_fd(ctx->fci_key_catch_handle);
if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0)
{
cmm_print(DEBUG_ERROR, "%s: fcntl(%d) failed %s\n", __func__, fd, strerror(errno));
goto err14;
}
#endif
if (nfnl_set_nonblocking_mode((struct nfnl_handle *)nfct_nfnlh(ctx->catch_handle)) < 0)
{
cmm_print(DEBUG_ERROR, "%s: nfnl_set_nonblocking_mode() failed %s\n", __func__, strerror(errno));
goto err14;
}
if (cmm_rtnl_set_nonblocking_mode(&ctx->rth_neigh) < 0)
{
cmm_print(DEBUG_ERROR, "%s: cmm_rtnl_set_nonblocking_mode(NEIGHBOR) failed\n", __func__);
goto err14;
}
if (cmm_rtnl_set_nonblocking_mode(&ctx->rth_link) < 0)
{
cmm_print(DEBUG_ERROR, "%s: cmm_rtnl_set_nonblocking_mode(LINK) failed\n", __func__);
goto err14;
}
if (cmm_rtnl_set_nonblocking_mode(&ctx->rth_ifaddr) < 0)
{
cmm_print(DEBUG_ERROR, "%s: cmm_rtnl_set_nonblocking_mode(IFADDR) failed\n", __func__);
goto err13;
}
if (cmm_rtnl_set_nonblocking_mode(&ctx->rth_route) < 0)
{
cmm_print(DEBUG_ERROR, "%s: cmm_rtnl_set_nonblocking_mode(ROUTE) failed\n", __func__);
goto err14;
}
if (cmm_rtnl_set_nonblocking_mode(&ctx->rth_rule) < 0)
{
cmm_print(DEBUG_ERROR, "%s: cmm_rtnl_set_nonblocking_mode(RULE) failed\n", __func__);
goto err14;
}
if(globalConf.auto_bridge)
if (cmm_rtnl_set_nonblocking_mode(&ctx->rth_abm) < 0)
{
cmm_print(DEBUG_ERROR, "%s: cmm_rtnl_set_nonblocking_mode(abm) failed\n", __func__);
goto err14;
}
// Change socket size to avoid losing messages
nfnl_rcvbufsiz((struct nfnl_handle *)nfct_nfnlh(ctx->catch_handle), NFNL_SOCK_SIZE);
#ifdef ROUTER
nfnl_rcvbufsiz((struct nfnl_handle *)nfct_nfnlh(ctx->handle), 128 * 1024);
#endif
nfnl_set_rcv_buffer_size((struct nfnl_handle *)nfct_nfnlh(ctx->catch_handle), 64 * 1024);
nfnl_unset_sequence_tracking((struct nfnl_handle *)nfct_nfnlh(ctx->catch_handle));
cmm_rtnl_rcvbufsiz(&ctx->rth_neigh, 1024 * 1024);
cmm_rtnl_rcvbufsiz(&ctx->rth_link, 512 * 1024);
fd = fci_fd(ctx->fci_catch_handle);
size = NFNL_SOCK_SIZE;
if(setsockopt(fd, SOL_SOCKET, SO_RCVBUFFORCE, &size, socklen) < 0)