blob: d3fe96856260d0d0f1c928d68995d83052d4496f [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 "route_cache.h"
#include "itf.h"
#include "ffbridge.h"
#include <net/if.h>
struct list_head rt_table[2 * ROUTE_HASH_TABLE_SIZE];
struct list_head rt_table_by_gw_ip[2 * NEIGHBOR_HASH_TABLE_SIZE];
struct list_head fpp_rt_table[ROUTE_HASH_TABLE_SIZE];
static u_int32_t route_ids[ROUTE_MAX_ID / (8 * sizeof(u_int32_t))] = {0, };
static u_int32_t route_id = 0;
pthread_mutex_t rtMutex = PTHREAD_MUTEX_INITIALIZER; /*mutex to prevent race condition on the route table*/
extern unsigned short TunMtu;
static int cmmRouteNetlinkLookupFilter(const struct sockaddr_nl *nladdr, struct nlmsghdr *nlh, void *arg)
{
struct RtEntry *route = arg;
struct rtmsg *rtm;
struct rtattr *attr[RTA_MAX + 1];
if (nlh->nlmsg_type != RTM_NEWROUTE) {
cmm_print(DEBUG_ERROR, "%s::%d: unexpected netlink message(%d)\n",
__func__, __LINE__, nlh->nlmsg_type);
goto err;
}
rtm = NLMSG_DATA(nlh);
cmm_parse_rtattr(attr, RTA_MAX, RTM_RTA(rtm), RTM_PAYLOAD(nlh));
if (!attr[RTA_DST] || !attr[RTA_OIF]) {
cmm_print(DEBUG_ERROR, "%s::%d: rtnetlink message missing mandatory attribute\n", __func__, __LINE__);
goto err;
}
route->family = rtm->rtm_family;
route->table = rtm->rtm_table;
route->scope = rtm->rtm_scope;
route->type = rtm->rtm_type;
route->mtu = 0;
if (attr[RTA_METRICS]) {
struct rtattr *mxrta;
mxrta = cmm_get_rtattr(RTA_DATA(attr[RTA_METRICS]), RTA_PAYLOAD(attr[RTA_METRICS]), RTAX_MTU);
if (mxrta)
route->mtu = *(unsigned int *)RTA_DATA(mxrta);
}
route->dAddrLen = RTA_PAYLOAD(attr[RTA_DST]);
memcpy(route->dAddr, RTA_DATA(attr[RTA_DST]), route->dAddrLen);
if (attr[RTA_GATEWAY]) {
route->gwAddrLen = RTA_PAYLOAD(attr[RTA_GATEWAY]);
memcpy(route->gwAddr, RTA_DATA(attr[RTA_GATEWAY]), route->gwAddrLen);
}
else
{
route->gwAddrLen = route->dAddrLen;
memcpy(route->gwAddr, route->dAddr, route->dAddrLen);
}
route->oifindex = *(int *)RTA_DATA(attr[RTA_OIF]);
/* Always stop parsing on first match */
return RTNL_CB_STOP;
err:
return RTNL_CB_ERROR;
}
static int cmmRouteNetlinkLookup(struct flow *flow, struct RtEntry *route)
{
struct rtnl_handle rth;
int ipAddrLen = IPADDRLEN(flow->family);
char iifname[IFNAMSIZ], oifname[IFNAMSIZ];
char buf1[INET6_ADDRSTRLEN], buf2[INET6_ADDRSTRLEN], buf3[INET6_ADDRSTRLEN];
char buf[256] __attribute__ ((aligned (4)));
struct nlmsghdr *nlh = (struct nlmsghdr *)buf;
struct rtmsg *rtm;
cmm_print(DEBUG_INFO, "%s\n", __func__);
if (cmm_rtnl_open(&rth, 0) < 0) {
cmm_print(DEBUG_ERROR, "%s::%d: cmm_rtnl_open() failed, %s\n", __func__, __LINE__, strerror(errno));
goto err0;
}
cmm_nlh_init(nlh, sizeof(struct rtmsg), RTM_GETROUTE, NLM_F_REQUEST);
rtm = NLMSG_DATA(nlh);
memset(rtm, 0, sizeof(struct rtmsg));
rtm->rtm_family = flow->family;
rtm->rtm_dst_len = ipAddrLen * 8;
rtm->rtm_table = RT_TABLE_UNSPEC;
rtm->rtm_protocol = RTPROT_UNSPEC;
rtm->rtm_scope = RT_SCOPE_UNIVERSE;
rtm->rtm_type = RTN_UNSPEC;
cmm_addattr_l(nlh, sizeof(buf), RTA_DST, flow->dAddr, ipAddrLen);
if (flow->sAddr)
{
cmm_addattr_l(nlh, sizeof(buf), RTA_SRC, flow->sAddr, ipAddrLen);
rtm->rtm_src_len = ipAddrLen * 8;
}
else
rtm->rtm_src_len = 0;
if (flow->iifindex)
cmm_addattr_l(nlh, sizeof(buf), RTA_IIF, &flow->iifindex, sizeof(int));
if (flow->fwmark)
cmm_addattr_l(nlh, sizeof(buf), RTA_FWMARK, &flow->fwmark, sizeof(unsigned int));
if (cmm_rtnl_send(&rth, nlh) < 0)
goto err1;
if (cmm_rtnl_listen(&rth, cmmRouteNetlinkLookupFilter, route) < 0)
goto err1;
route->fwmark = flow->fwmark;
route->iifindex = flow->iifindex;
route->sAddrLen = ipAddrLen;
memcpy(route->sAddr, flow->sAddr, route->sAddrLen);
cmm_print(DEBUG_INFO, "route entry: idev:%s saddr:%s fwmark:%08x daddr:%s gw:%s odev:%s mtu:%d\n",
if_indextoname(route->iifindex, iifname),
inet_ntop(route->family, route->sAddr, buf1, INET6_ADDRSTRLEN),
route->fwmark,
inet_ntop(route->family, route->dAddr, buf2, INET6_ADDRSTRLEN),
inet_ntop(route->family, route->gwAddr, buf3, INET6_ADDRSTRLEN),
if_indextoname(route->oifindex, oifname), route->mtu);
cmm_rtnl_close(&rth);
return 0;
err1:
cmm_rtnl_close(&rth);
err0:
return -1;
}
/*****************************************************************
* __cmmRouteFind
*
*
******************************************************************/
struct RtEntry *__cmmRouteFind(struct flow *flow)
{
struct RtEntry *route;
struct list_head *entry;
char sbuf[INET6_ADDRSTRLEN], dbuf[INET6_ADDRSTRLEN];
int key;
int ipAddrLen = IPADDRLEN(flow->family);
cmm_print(DEBUG_INFO, "%s: Route(%s, %s)\n", __func__, inet_ntop(flow->family, flow->sAddr, sbuf, sizeof(sbuf)),
inet_ntop(flow->family, flow->dAddr, dbuf, sizeof(dbuf)));
key = HASH_RT(flow->family, flow->sAddr, flow->dAddr);
entry = list_first(&rt_table[key]);
while (entry != &rt_table[key])
{
route = container_of(entry, struct RtEntry, list);
if (cmmRouteEqual(route, flow, ipAddrLen))
{
if (route->flags & INVALID)
route = NULL;
goto found;
}
entry = list_next(entry);
}
route = NULL;
found:
return route;
}
static u_int32_t new_route_id(void)
{
int offset, mask;
for (;;)
{
if (++route_id >= ROUTE_MAX_ID)
route_id = 1;
offset = route_id / (8 * sizeof(u_int32_t));
mask = 1 << (route_id & 0x1f);
if (!(route_ids[offset] & mask))
break;
}
route_ids[offset] |= mask;
return route_id;
}
static void del_route_id(u_int32_t route_id)
{
int offset = route_id / (8 * sizeof(u_int32_t));
int mask = 1 << (route_id & 0x1f);
route_ids[offset] &= ~mask;
}
/*****************************************************************
* __cmmRouteRemove
*
*
******************************************************************/
/* NOTE: The rtMutex must be locked by the caller of this routine. */
void __cmmRouteRemove(struct RtEntry *route)
{
char sbuf[INET6_ADDRSTRLEN], dbuf[INET6_ADDRSTRLEN];
cmm_print(DEBUG_INFO, "%s: Route(%s, %s) removed\n", __func__, inet_ntop(route->family, route->sAddr, sbuf, sizeof(sbuf)),
inet_ntop(route->family, route->dAddr, dbuf, sizeof(dbuf)));
if (!(route->flags & RT_POLICY))
list_del(&route->list);
list_del(&route->list_by_gw_ip);
free(route);
}
/*****************************************************************
* __cmmRoutePut
*
*
******************************************************************/
void __cmmRoutePut(struct RtEntry *route)
{
char sbuf[INET6_ADDRSTRLEN], dbuf[INET6_ADDRSTRLEN];
cmm_print(DEBUG_INFO, "%s: Route(%s, %s) put\n", __func__, inet_ntop(route->family, route->sAddr, sbuf, sizeof(sbuf)),
inet_ntop(route->family, route->dAddr, dbuf, sizeof(dbuf)));
route->count--;
if (route->count <= 0)
{
__cmmRouteRemove(route);
}
}
/*****************************************************************
* __cmmRouteAdd
*
*
******************************************************************/
struct RtEntry *__cmmRouteAdd(struct flow *flow)
{
struct RtEntry *route;
char sbuf[INET6_ADDRSTRLEN], dbuf[INET6_ADDRSTRLEN];
int key;
#ifdef SAM_LEGACY
struct interface *itf = NULL;
#endif
route = malloc(sizeof(struct RtEntry));
if (!route)
{
cmm_print(DEBUG_ERROR, "%s::%d: malloc() failed\n", __func__, __LINE__);
goto err0;
}
memset(route, 0, sizeof(struct RtEntry));
route->count = 0;
/* Get Route information from kernel */
if (cmmRouteNetlinkLookup(flow, route) < 0)
{
cmm_print(DEBUG_WARNING, "%s::%d: cmmRouteNetlinkLookup() failed\n", __func__, __LINE__);
goto err1;
}
#ifdef SAM_LEGACY
if(((itf = __itf_get(route->oifindex)) != NULL) && (____itf_is_4o6_tunnel(itf)))
route->mtu = TunMtu;
#endif
key = HASH_RT(route->family, route->sAddr, route->dAddr);
list_add(&rt_table[key], &route->list);
key = HASH_NEIGHBOR(route->family, route->gwAddr);
list_add(&rt_table_by_gw_ip[key], &route->list_by_gw_ip);
cmm_print(DEBUG_INFO, "%s: Route(%s, %s) added\n", __func__, inet_ntop(route->family, route->sAddr, sbuf, sizeof(sbuf)),
inet_ntop(route->family, route->dAddr, dbuf, sizeof(dbuf)));
return route;
err1:
free(route);
err0:
return NULL;
}
/*****************************************************************
* __cmmRouteGet
*
*
******************************************************************/
struct RtEntry *__cmmRouteGet(struct flow *flow)
{
struct RtEntry *route;
route = __cmmRouteFind(flow);
if (!route)
{
route = __cmmRouteAdd(flow);
if (!route)
goto err;
}
route->count++;
return route;
err:
return NULL;
}
/*****************************************************************
* __cmmFPPRouteFind
*
*
******************************************************************/
struct fpp_rt *__cmmFPPRouteFind(int oifindex, const unsigned char *dst_mac, int mtu, const unsigned int *dst_addr, int dst_addr_len)
{
struct fpp_rt *route;
char mac[MAC_ADDRSTRLEN];
struct list_head *entry;
int key;
cmm_print(DEBUG_INFO, "%s: Route(%d, %s)\n", __func__, oifindex, mac_ntop(dst_mac, mac, MAC_ADDRSTRLEN));
key = HASH_FPP_RT(oifindex, dst_mac);
entry = list_first(&fpp_rt_table[key]);
while (entry != &fpp_rt_table[key])
{
route = container_of(entry, struct fpp_rt, list);
if (!memcmp(route->dst_mac, dst_mac, ETH_ALEN) && (route->oifindex == oifindex) &&
((dst_addr_len == route->dst_addr_len) && (!dst_addr_len || !memcmp(route->dst_addr, dst_addr, dst_addr_len))))
{
goto found;
}
entry = list_next(entry);
}
route = NULL;
found:
return route;
}
/*****************************************************************
* __cmmFPPRouteRemove
*
*
******************************************************************/
/* NOTE: The rtMutex must be locked by the caller of this routine. */
void __cmmFPPRouteRemove(struct fpp_rt *route)
{
char mac[MAC_ADDRSTRLEN];
cmm_print(DEBUG_INFO, "%s: Route(%d, %s) removed\n", __func__, route->oifindex, mac_ntop(route->dst_mac, mac, MAC_ADDRSTRLEN));
list_del(&route->list);
del_route_id(route->id);
free(route);
}
/*****************************************************************
* __cmmFPPRoutePut
*
*
******************************************************************/
void __cmmFPPRoutePut(struct fpp_rt *route)
{
char mac[MAC_ADDRSTRLEN];
cmm_print(DEBUG_INFO, "%s: Route(%d, %s) put\n", __func__, route->oifindex, mac_ntop(route->dst_mac, mac, MAC_ADDRSTRLEN));
route->count--;
if (route->count <= 0)
{
__cmmFPPRouteRemove(route);
}
}
/*****************************************************************
* __cmmFPPRouteAdd
*
*
******************************************************************/
struct fpp_rt *__cmmFPPRouteAdd(int oifindex, const unsigned char *dst_mac, int mtu, const unsigned int *dst_addr, int dst_addr_len)
{
struct fpp_rt *route;
char mac[MAC_ADDRSTRLEN];
int key;
route = malloc(sizeof(struct fpp_rt));
if (!route)
{
cmm_print(DEBUG_ERROR, "%s::%d: malloc() failed\n", __func__, __LINE__);
goto err0;
}
memset(route, 0, sizeof(struct fpp_rt));
route->count = 0;
memcpy(route->dst_mac, dst_mac, ETH_ALEN);
route->oifindex = oifindex;
route->mtu = mtu;
if (dst_addr_len)
{
route->dst_addr_len = dst_addr_len;
memcpy(route->dst_addr, dst_addr, dst_addr_len);
}
/* For now just use the pointer as the unique id, should be improved later */
route->id = new_route_id();
key = HASH_FPP_RT(route->oifindex, route->dst_mac);
list_add(&fpp_rt_table[key], &route->list);
cmm_print(DEBUG_INFO, "%s: Route(%d, %s) added\n", __func__, route->oifindex, mac_ntop(route->dst_mac, mac, MAC_ADDRSTRLEN));
return route;
free(route);
err0:
return NULL;
}
/*****************************************************************
* __cmmFPPRouteGet
*
*
******************************************************************/
struct fpp_rt *__cmmFPPRouteGet(int oifindex, const unsigned char *dst_mac, int mtu, const unsigned int *dst_addr, int dst_addr_len)
{
struct fpp_rt *route;
route = __cmmFPPRouteFind(oifindex, dst_mac, mtu, dst_addr, dst_addr_len);
if (!route)
{
route = __cmmFPPRouteAdd(oifindex, dst_mac, mtu, dst_addr, dst_addr_len);
if (!route)
goto err;
}
route->count++;
return route;
err:
return NULL;
}
/*****************************************************************
* cmmFPPRtShow
*
*
******************************************************************/
int cmmFPPRtShow(struct cli_def * cli, char *command, char *argv[], int argc)
{
struct fpp_rt *route;
struct list_head *entry;
int i, n;
char mac_buf[MAC_ADDRSTRLEN], oifname[IFNAMSIZ];
cli_print(cli, "FPP Route:");
n = 0;
for (i = 0; i < ROUTE_HASH_TABLE_SIZE; i++)
{
__pthread_mutex_lock(&rtMutex);
for (entry = list_first(&fpp_rt_table[i]); entry != &fpp_rt_table[i]; entry = list_next(entry))
{
// char daddr_buf[INET_ADDRSTRLEN];
route = container_of(entry, struct fpp_rt, list);
// inet_ntop(AF_INET, &route->dst_addr, daddr_buf, sizeof(daddr_buf));
cli_print(cli, "OIf: %s, Mtu: %d, Mac: %s, Id: %d, Count: %d, Flags: %x",
if_indextoname(route->oifindex, oifname),
route->mtu,
mac_ntop(route->dst_mac, mac_buf, sizeof(mac_buf)),
route->id,
route->count, route->flags);
n++;
}
__pthread_mutex_unlock(&rtMutex);
}
if (n > 0)
cli_print(cli, "%d FPP Routes printed\n", n);
return CLI_OK;
}
/*****************************************************************
* cmmRtShow
*
*
******************************************************************/
int cmmRtShow(struct cli_def * cli, char *command, char *argv[], int argc)
{
struct RtEntry *route;
struct list_head *entry;
int i, n;
char iifname[IFNAMSIZ], oifname[IFNAMSIZ], phys_oifname[IFNAMSIZ];
cli_print(cli, "IPv4 Route:");
n = 0;
for (i = 0; i < ROUTE_HASH_TABLE_SIZE; i++)
{
__pthread_mutex_lock(&rtMutex);
for (entry = list_first(&rt_table[i]); entry != &rt_table[i]; entry = list_next(entry))
{
char saddr_buf[INET_ADDRSTRLEN], daddr_buf[INET_ADDRSTRLEN], gw_buf[INET_ADDRSTRLEN];
route = container_of(entry, struct RtEntry, list);
inet_ntop(AF_INET, &route->sAddr, saddr_buf, sizeof(saddr_buf));
inet_ntop(AF_INET, &route->dAddr, daddr_buf, sizeof(daddr_buf));
inet_ntop(AF_INET, &route->gwAddr, gw_buf, sizeof(gw_buf));
cli_print(cli, "IIf: %s, Mark: %08x, Src: %s, Dst: %s --> Gateway: %s, OIf: %s, PhysOif: %s, Count: %d",
if_indextoname(route->iifindex, iifname), route->fwmark,
saddr_buf, daddr_buf, gw_buf, if_indextoname(route->oifindex, oifname),
if_indextoname(route->phys_oifindex, phys_oifname), route->count);
n++;
}
__pthread_mutex_unlock(&rtMutex);
}
if (n > 0)
cli_print(cli, "%d IPv4 Routes printed\n", n);
cli_print(cli, "IPv6 Route:");
n = 0;
for (i = ROUTE_HASH_TABLE_SIZE; i < 2 * ROUTE_HASH_TABLE_SIZE; i++)
{
__pthread_mutex_lock(&rtMutex);
for (entry = list_first(&rt_table[i]); entry != &rt_table[i]; entry = list_next(entry))
{
char saddr_buf[INET6_ADDRSTRLEN], daddr_buf[INET6_ADDRSTRLEN], gw_buf[INET6_ADDRSTRLEN];
route = container_of(entry, struct RtEntry, list);
inet_ntop(AF_INET6, route->sAddr, saddr_buf, sizeof(saddr_buf));
inet_ntop(AF_INET6, route->dAddr, daddr_buf, sizeof(daddr_buf));
inet_ntop(AF_INET6, route->gwAddr, gw_buf, sizeof(gw_buf));
cli_print(cli, "IIf: %s, Mark: %08x, Src: %s, Dst: %s --> Gateway: %s, Oif: %s, PhysOif: %s, Count: %d",
if_indextoname(route->iifindex, iifname), route->fwmark,
saddr_buf, daddr_buf, gw_buf, if_indextoname(route->oifindex, oifname),
if_indextoname(route->phys_oifindex, phys_oifname), route->count);
n++;
}
__pthread_mutex_unlock(&rtMutex);
}
if (n > 0)
cli_print(cli, "%d IPv6 Routes printed\n", n);
return CLI_OK;
}
/*****************************************************************
* __cmmCtTunnelRouteUpdate
*
*
******************************************************************/
static void __cmmCtTunnelRouteUpdate(FCI_CLIENT *fci_handle, struct ctTable *ctEntry, struct RtEntry *route, int dir)
{
struct ct_route rt;
cmm_print(DEBUG_INFO, "%s\n", __func__);
if (dir == ORIGINATOR)
rt = ctEntry->orig_tunnel;
else
rt = ctEntry->rep_tunnel;
if (route->flags & INVALID)
{
if (dir == ORIGINATOR)
{
ctEntry->orig_tunnel.route = NULL;
ctEntry->orig_tunnel.fpp_route = NULL;
list_del(&ctEntry->list_by_orig_tunnel_route);
}
else
{
ctEntry->rep_tunnel.route = NULL;
ctEntry->rep_tunnel.fpp_route = NULL;
list_del(&ctEntry->list_by_rep_tunnel_route);
}
}
else
{
rt.route = NULL;
if (dir == ORIGINATOR)
ctEntry->orig_tunnel.fpp_route = NULL;
else
ctEntry->rep_tunnel.fpp_route = NULL;
}
____cmmCtRegister(fci_handle, ctEntry);
if (dir == ORIGINATOR)
__cmmRouteDeregister(fci_handle, &rt, "originator tunnel");
else
__cmmRouteDeregister(fci_handle, &rt, "replier tunnel");
}
/*****************************************************************
* __cmmCtRouteUpdate
*
*
******************************************************************/
static void __cmmCtRouteUpdate(FCI_CLIENT *fci_handle, struct ctTable *ctEntry, struct RtEntry *route, int dir)
{
struct ct_route rt;
struct ct_route tunnel_rt;
cmm_print(DEBUG_INFO, "%s\n", __func__);
if (dir == ORIGINATOR)
{
rt = ctEntry->orig;
tunnel_rt = ctEntry->orig_tunnel;
if (ctEntry->orig_tunnel.route)
{
ctEntry->orig_tunnel.route = NULL;
ctEntry->orig_tunnel.fpp_route = NULL;
list_del(&ctEntry->list_by_orig_tunnel_route);
}
}
else
{
rt = ctEntry->rep;
tunnel_rt = ctEntry->rep_tunnel;
if (ctEntry->rep_tunnel.route)
{
ctEntry->rep_tunnel.route = NULL;
ctEntry->rep_tunnel.fpp_route = NULL;
list_del(&ctEntry->list_by_rep_tunnel_route);
}
}
if (route->flags & INVALID)
{
if (dir == ORIGINATOR)
{
ctEntry->orig.route = NULL;
ctEntry->orig.fpp_route = NULL;
}
else
{
ctEntry->rep.route = NULL;
ctEntry->rep.fpp_route = NULL;
}
}
else
{
rt.route = NULL;
if (dir == ORIGINATOR)
ctEntry->orig.fpp_route = NULL;
else
ctEntry->rep.fpp_route = NULL;
}
____cmmCtRegister(fci_handle, ctEntry);
if (dir == ORIGINATOR)
{
__cmmRouteDeregister(fci_handle, &rt, "originator");
__cmmRouteDeregister(fci_handle, &tunnel_rt, "originator tunnel");
}
else
{
__cmmRouteDeregister(fci_handle, &rt, "replier");
__cmmRouteDeregister(fci_handle, &tunnel_rt, "replier tunnel");
}
}
/*****************************************************************
* __cmmTunnelRouteUpdate
*
*
******************************************************************/
static void __cmmTunnelRouteUpdate(FCI_CLIENT *fci_handle, struct interface *itf, struct RtEntry *route)
{
struct ct_route rt = itf->rt;
cmm_print(DEBUG_INFO, "%s\n", __func__);
if (route->flags & INVALID)
{
itf->rt.route = NULL;
itf->rt.fpp_route = NULL;
}
else
{
rt.route = NULL;
itf->rt.fpp_route = NULL;
}
__tunnel_add(fci_handle, itf);
__cmmRouteDeregister(fci_handle, &rt, "tunnel");
}
/*****************************************************************
* __cmmSocketRouteUpdate
*
*
******************************************************************/
static void __cmmSocketRouteUpdate(FCI_CLIENT *fci_handle, struct socket *s, struct RtEntry *route)
{
struct ct_route rt = s->rt;
cmm_print(DEBUG_INFO, "%s\n", __func__);
if (route->flags & INVALID)
{
s->rt.route = NULL;
s->rt.fpp_route = NULL;
}
else
{
rt.route = NULL;
s->rt.fpp_route = NULL;
}
__pthread_mutex_lock(&socket_lock);
__socket_open(fci_handle, s);
__pthread_mutex_unlock(&socket_lock);
__cmmRouteDeregister(fci_handle, &rt, "socket");
}
/*****************************************************************
* __cmmRouteUpdate
*
*
******************************************************************/
static void __cmmRouteUpdate(FCI_CLIENT *fci_handle, struct RtEntry *route)
{
struct RtEntry route_prev;
struct ctTable *ctEntry;
struct interface *itf;
struct socket *s;
struct list_head *entry;
struct NeighborEntry *neigh = NULL;
struct flow flow = {
.family = route->family,
.sAddr = route->sAddr,
.dAddr = route->dAddr,
.fwmark = route->fwmark,
.iifindex = route->iifindex,
};
int key;
int i;
cmm_print(DEBUG_INFO, "%s: Update route entry\n", __func__);
memcpy(&route_prev, route, sizeof(struct RtEntry));
if (cmmRouteNetlinkLookup(&flow, route) < 0)
{
cmm_print(DEBUG_INFO, "%s::%d: route was removed\n", __func__, __LINE__);
route->flags |= INVALID;
goto update;
}
if (memcmp(route->gwAddr, route_prev.gwAddr, route->gwAddrLen))
{
cmm_print(DEBUG_INFO, "%s::%d: route changed gateway address\n", __func__, __LINE__);
route->phys_oifindex = 0;
list_del(&route->list_by_gw_ip);
key = HASH_NEIGHBOR(route->family, route->gwAddr);
list_add(&rt_table_by_gw_ip[key], &route->list_by_gw_ip);
neigh = route->neighEntry;
route->neighEntry = NULL;
goto update;
}
if (route->oifindex != route_prev.oifindex)
{
cmm_print(DEBUG_INFO, "%s::%d: route changed output interface\n", __func__, __LINE__);
route->phys_oifindex = 0;
neigh = route->neighEntry;
route->neighEntry = NULL;
goto update;
}
if (route->mtu != route_prev.mtu)
{
cmm_print(DEBUG_INFO, "%s::%d: route changed mtu\n", __func__, __LINE__);
goto update;
}
return;
update:
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)
__cmmCtRouteUpdate(fci_handle, ctEntry, 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)
__cmmCtRouteUpdate(fci_handle, ctEntry, route, REPLIER);
}
/* Conntracks pointing to tunnel interface */
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)
__cmmCtTunnelRouteUpdate(fci_handle, ctEntry, route, ORIGINATOR);
}
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)
__cmmCtTunnelRouteUpdate(fci_handle, ctEntry, route, REPLIER);
}
/* Tunnel interfaces */
for (i = 0; i < ITF_HASH_TABLE_SIZE; i++)
{
for (entry = list_first(&itf_table.hash[i]); entry != &itf_table.hash[i]; entry = list_next(entry))
{
itf = container_of(entry, struct interface, list);
if (!__itf_is_tunnel(itf))
continue;
if (itf->rt.route == route)
__cmmTunnelRouteUpdate(fci_handle, itf, route);
}
}
/* Sockets */
for (i = 0; i < HASH_SOCKET_SIZE; i++)
{
for (entry = list_first(&socket_table[i]); entry != &socket_table[i]; entry = list_next(entry))
{
s = container_of(entry, struct socket, list);
if (s->rt.route == route)
__cmmSocketRouteUpdate(fci_handle, s, route);
}
}
if (neigh)
__cmmNeighPut(neigh);
}
/*****************************************************************
* cmmRouteFlushCache
*
*
******************************************************************/
static void cmmRouteFlushCache(int family)
{
int fd;
const char buf[] = "0";
if (family == AF_INET)
fd = open("/proc/sys/net/ipv4/route/flush", O_WRONLY);
else
fd = open("/proc/sys/net/ipv6/route/flush", O_WRONLY);
if (fd < 0)
{
cmm_print(DEBUG_ERROR, "%s: open() failed %s\n", __func__, strerror(errno));
goto out;
}
write(fd, buf, sizeof(buf));
close(fd);
out:
return;
}
/*****************************************************************
* __cmmRouteNew
*
*
******************************************************************/
static void __cmmRouteNew(FCI_CLIENT *fci_handle, struct rtmsg *rtm, unsigned int *dAddr, int flushed)
{
struct list_head *entry;
struct ctTable *ctEntry;
struct interface *itf;
const unsigned int *ct_daddr;
int i;
/* Look for connections waiting for a route */
for (i = 0; i < CONNTRACK_HASH_TABLE_SIZE; i++)
{
for (entry = list_first(&ct_table[i]); entry != &ct_table[i]; entry = list_next(entry))
{
unsigned int tunnel_daddr;
ctEntry = container_of(entry, struct ctTable, list);
if (ctEntry->family != rtm->rtm_family)
goto tunnel_originator;
if ((ctEntry->dir & ORIGINATOR) && (!ctEntry->orig.route))
{
if (ctEntry->family == AF_INET)
ct_daddr = nfct_get_attr(ctEntry->ct, ATTR_REPL_IPV4_SRC);
else
ct_daddr = nfct_get_attr(ctEntry->ct, ATTR_REPL_IPV6_SRC);
if (cmmPrefixEqual(ct_daddr, dAddr, rtm->rtm_dst_len))
{
if (!flushed)
{
cmmRouteFlushCache(rtm->rtm_family);
flushed = 1;
}
____cmmCtRegister(fci_handle, ctEntry);
}
}
if ((ctEntry->dir & REPLIER) && (!ctEntry->rep.route))
{
if (ctEntry->family == AF_INET)
ct_daddr = nfct_get_attr(ctEntry->ct, ATTR_ORIG_IPV4_SRC);
else
ct_daddr = nfct_get_attr(ctEntry->ct, ATTR_ORIG_IPV6_SRC);
if (cmmPrefixEqual(ct_daddr, dAddr, rtm->rtm_dst_len))
{
if (!flushed)
{
cmmRouteFlushCache(rtm->rtm_family);
flushed = 1;
}
____cmmCtRegister(fci_handle, ctEntry);
}
}
tunnel_originator:
/* Conntracks pointing to a tunnel interface */
if ((ctEntry->dir & ORIGINATOR) && ctEntry->orig.route &&
!ctEntry->orig_tunnel.route)
{
itf = __itf_get(ctEntry->orig.route->oifindex);
if (!itf)
goto tunnel_replier;
if (!____itf_is_floating_sit_tunnel(itf))
goto tunnel_replier1;
if (itf->tunnel_family != rtm->rtm_family)
goto tunnel_replier1;
tunnel_daddr = tunnel_get_ipv4_dst(ctEntry->orig.route, itf);
if (!tunnel_daddr)
goto tunnel_replier1;
if (cmmPrefixEqual(&tunnel_daddr, dAddr, rtm->rtm_dst_len))
{
if (!flushed)
{
cmmRouteFlushCache(rtm->rtm_family);
flushed = 1;
}
____cmmCtRegister(fci_handle, ctEntry);
}
tunnel_replier1:
__itf_put(itf);
}
tunnel_replier:
if ((ctEntry->dir & REPLIER) && ctEntry->rep.route &&
!ctEntry->rep_tunnel.route)
{
itf = __itf_get(ctEntry->rep.route->oifindex);
if (!itf)
continue;
if (!____itf_is_floating_sit_tunnel(itf))
goto next;
if (itf->tunnel_family != rtm->rtm_family)
goto next;
tunnel_daddr = tunnel_get_ipv4_dst(ctEntry->rep.route, itf);
if (!tunnel_daddr)
goto next;
if (cmmPrefixEqual(&tunnel_daddr, dAddr, rtm->rtm_dst_len))
{
if (!flushed)
{
cmmRouteFlushCache(rtm->rtm_family);
flushed = 1;
}
____cmmCtRegister(fci_handle, ctEntry);
}
next:
__itf_put(itf);
}
}
}
/* Look for tunnels waiting for a route */
for (i = 0; i < ITF_HASH_TABLE_SIZE; i++)
{
for (entry = list_first(&itf_table.hash[i]); entry != &itf_table.hash[i]; entry = list_next(entry))
{
const unsigned int *tunnel_daddr;
itf = container_of(entry, struct interface, list);
if (!__itf_is_tunnel(itf))
continue;
if (itf->rt.route)
continue;
if (itf->tunnel_family != rtm->rtm_family)
continue;
if (itf->tunnel_family == AF_INET)
tunnel_daddr = &itf->tunnel_parm4.iph.saddr;
else
tunnel_daddr = itf->tunnel_parm6.raddr.s6_addr32;
if (cmmPrefixEqual(tunnel_daddr, dAddr, rtm->rtm_dst_len))
{
if (!flushed)
{
cmmRouteFlushCache(rtm->rtm_family);
flushed = 1;
}
__tunnel_add(fci_handle, itf);
}
}
}
}
/*****************************************************************
* cmmRtnlRule
*
*
******************************************************************/
int cmmRtnlRule(const struct sockaddr_nl *who, struct nlmsghdr *nlh, void *arg)
{
struct fib_rule_hdr *frh;
struct rtattr *tb[FRA_MAX + 1];
switch (nlh->nlmsg_type) {
case RTM_NEWRULE:
case RTM_DELRULE:
break;
default:
cmm_print(DEBUG_ERROR, "%s: unsupported RULE netlink message %x\n", __func__, nlh->nlmsg_type);
goto out;
break;
}
frh = NLMSG_DATA(nlh);
cmm_parse_rtattr(tb, FRA_MAX, FRA_RTA(frh), FRA_PAYLOAD(nlh));
if (nlh->nlmsg_type == RTM_NEWRULE)
{
cmm_print(DEBUG_INFO, "%s: RTM_NEWRULE\n", __func__);
}
else
{
cmm_print(DEBUG_INFO, "%s: RTM_DELRULE\n", __func__);
}
out:
return RTNL_CB_CONTINUE;
}
static unsigned int default_route[4] = { 0, };
/*****************************************************************
* cmmRtnlRoute
*
*
******************************************************************/
int cmmRtnlRoute(const struct sockaddr_nl *who, struct nlmsghdr *nlh, void *arg)
{
struct cmm_ct *ctx = arg;
struct rtmsg *rtm;
struct rtattr *tb[RTA_MAX + 1];
unsigned int *dAddr, *gwAddr;
char dst[INET6_ADDRSTRLEN];
char gateway[INET6_ADDRSTRLEN];
struct list_head *entry, *next_entry;
struct RtEntry *route;
int flushed = 1;
int i;
switch (nlh->nlmsg_type)
{
case RTM_NEWROUTE:
case RTM_DELROUTE:
break;
default:
cmm_print(DEBUG_ERROR, "%s: unsupported ROUTE netlink message %x\n", __func__, nlh->nlmsg_type);
goto out;
break;
}
rtm = NLMSG_DATA(nlh);
cmm_print(DEBUG_INFO, "%s: rtmsg family: %x, dst_len: %d, src_len: %d, tos: %x, table: %d, protocol: %d, scope: %d, type: %d, flags: %x\n", __func__,
rtm->rtm_family, rtm->rtm_dst_len,
rtm->rtm_src_len, rtm->rtm_tos, rtm->rtm_table,
rtm->rtm_protocol, rtm->rtm_scope,
rtm->rtm_type, rtm->rtm_flags);
/* Don't process local routes */
if (rtm->rtm_table == RT_TABLE_LOCAL)
goto out;
if (rtm->rtm_scope >= RT_SCOPE_HOST)
goto out;
/* Don't process multicast, broadcast routes */
if (rtm->rtm_type != RTN_UNICAST)
goto out;
cmm_parse_rtattr(tb, RTA_MAX, RTM_RTA(rtm), RTM_PAYLOAD(nlh));
if (!tb[RTA_DST])
{
if (rtm->rtm_dst_len)
{
cmm_print(DEBUG_ERROR, "%s: route missing destination address\n", __func__);
goto out;
}
dAddr = default_route;
}
else
dAddr = RTA_DATA(tb[RTA_DST]);
if (tb[RTA_GATEWAY])
gwAddr = RTA_DATA(tb[RTA_GATEWAY]);
else
gwAddr = dAddr;
if (nlh->nlmsg_type == RTM_NEWROUTE)
{
cmm_print(DEBUG_INFO, "%s: RTM_NEWROUTE %s/%d %s\n", __func__, inet_ntop(rtm->rtm_family, dAddr, dst, sizeof(dst)), rtm->rtm_dst_len, inet_ntop(rtm->rtm_family, gwAddr, gateway, sizeof(gateway)));
}
else
{
cmm_print(DEBUG_INFO, "%s: RTM_DELROUTE %s/%d %s\n", __func__, inet_ntop(rtm->rtm_family, dAddr, dst, sizeof(dst)), rtm->rtm_dst_len, inet_ntop(rtm->rtm_family, gwAddr, gateway, sizeof(gateway)));
}
/* Ignore all route cache events */
if(rtm->rtm_flags & RTM_F_CLONED)
goto out;
__pthread_mutex_lock(&itf_table.lock);
__pthread_mutex_lock(&ctMutex);
__pthread_mutex_lock(&rtMutex);
__pthread_mutex_lock(&neighMutex);
__pthread_mutex_lock(&flowMutex);
/* Don't flush the route cache if the event is generated by the cache itself */
/* Route cache events are now disabled */
flushed = 0;
/* Look for connections/tunnels using this route */
for (i = 0; i < ROUTE_HASH_TABLE_SIZE * 2; i++)
{
for (entry = list_first(&rt_table[i]); next_entry = list_next(entry), entry != &rt_table[i]; entry = next_entry)
{
route = container_of(entry, struct RtEntry, list);
if (route->family != rtm->rtm_family)
continue;
if (cmmPrefixEqual(route->dAddr, dAddr, rtm->rtm_dst_len))
{
if (!flushed)
{
cmmRouteFlushCache(rtm->rtm_family);
flushed = 1;
}
__cmmRouteUpdate(ctx->fci_handle, route);
}
}
}
if (nlh->nlmsg_type == RTM_DELROUTE)
goto unlock;
__cmmRouteNew(ctx->fci_handle, rtm, dAddr, flushed);
unlock:
__pthread_mutex_unlock(&flowMutex);
__pthread_mutex_unlock(&neighMutex);
__pthread_mutex_unlock(&rtMutex);
__pthread_mutex_unlock(&ctMutex);
__pthread_mutex_unlock(&itf_table.lock);
out:
return RTNL_CB_CONTINUE;
}