blob: 1d5ef6c1d677a3f259225cc93f531dd8ad5cefaa [file] [log] [blame]
#include <string.h>
#include <errno.h>
#include <net/if.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <time.h>
#include <linux/netlink.h>
#include <linux/sockios.h>
#include <linux/rtnetlink.h>
#include <linux/if_bridge.h>
#include <linux/if_vlan.h>
#include <net/if_arp.h>
#include "cmm.h"
#include "itf.h"
#include "pppoe.h"
#include "ffbridge.h"
struct gemac_port port_table[GEM_PORTS] = {
{"eth0", "wan", GEMAC_PORT_TYPE_WAN, 0, GEMAC0_PORT, 1},
{"eth2", "lan", GEMAC_PORT_TYPE_LAN, 0, GEMAC1_PORT, 1},
{"eth3", "wan1", GEMAC_PORT_TYPE_WAN, 0, GEMAC2_PORT, 1}
};
struct interface_table itf_table;
static struct interface_addr *__addr_find(struct interface *itf, unsigned int *ipaddr, unsigned int len);
#ifdef WIFI_ENABLE
static int ____itf_is_bridge(struct interface *itf);
extern struct wifi_ff_entry glbl_wifi_ff_ifs[MAX_WIFI_FF_IFS];
#endif
extern int tunnel_send_cmd(FCI_CLIENT *fci_handle, int request, struct interface *itf);
int LO_IFINDEX;
static void __addr_remove(struct interface_addr *addr)
{
cmm_print(DEBUG_INFO, "%s: address removed\n", __func__);
list_del(&addr->list);
free(addr);
}
static struct interface_addr *__addr_add(struct interface *itf)
{
struct interface_addr *addr;
addr = malloc(sizeof(struct interface_addr));
if (!addr)
{
cmm_print(DEBUG_ERROR, "%s::%d: malloc() failed\n", __func__, __LINE__);
goto err;
}
memset(addr, 0, sizeof(struct interface_addr));
list_add(&itf->addr_list, &addr->list);
cmm_print(DEBUG_INFO, "%s: address added\n", __func__);
return addr;
err:
return NULL;
}
static void __addr_update(struct interface_addr *addr, struct ifaddrmsg *ifa, struct rtattr *tb[])
{
struct rtattr *attr;
attr = tb[IFA_ADDRESS];
addr->len = RTA_PAYLOAD(attr);
memcpy(addr->address, RTA_DATA(attr), addr->len);
addr->prefixlen = ifa->ifa_prefixlen;
addr->scope = ifa->ifa_scope;
addr->family = ifa->ifa_family;
}
static void __newaddr(struct ifaddrmsg *ifa, struct rtattr *tb[])
{
struct interface *itf;
struct interface_addr *addr;
itf = __itf_find(ifa->ifa_index);
if (!itf)
{
cmm_print(DEBUG_ERROR, "%s::%d: __itf_find(%d) failed\n", __func__, __LINE__, ifa->ifa_index);
goto out;
}
addr = __addr_add(itf);
if (!addr)
{
cmm_print(DEBUG_ERROR, "%s::%d: __addr_add(%d) failed\n", __func__, __LINE__, ifa->ifa_index);
goto out;
}
__addr_update(addr, ifa, tb);
out:
return;
}
static void newaddr(struct interface_table *ctx, struct ifaddrmsg *ifa, struct rtattr *tb[])
{
__pthread_mutex_lock(&ctx->lock);
__newaddr(ifa, tb);
__pthread_mutex_unlock(&ctx->lock);
}
static void __deladdr(struct ifaddrmsg *ifa, struct rtattr *tb[])
{
struct interface *itf;
struct interface_addr *addr;
struct rtattr *attr;
attr = tb[IFA_ADDRESS];
itf = __itf_find(ifa->ifa_index);
if (!itf)
{
cmm_print(DEBUG_ERROR, "%s::%d: __itf_find(%d) failed\n", __func__, __LINE__, ifa->ifa_index);
goto out;
}
addr = __addr_find(itf, RTA_DATA(attr), RTA_PAYLOAD(attr));
if (!addr)
{
cmm_print(DEBUG_ERROR, "%s::%d: __addr_find failed\n", __func__, __LINE__);
goto out;
}
__addr_remove(addr);
out:
return;
}
static void deladdr(struct interface_table *ctx, struct ifaddrmsg *ifa, struct rtattr *tb[])
{
__pthread_mutex_lock(&ctx->lock);
__deladdr(ifa, tb);
__pthread_mutex_unlock(&ctx->lock);
}
static struct interface_addr *__addr_find(struct interface *itf, unsigned int *ipaddr, unsigned int len)
{
struct interface_addr *addr;
struct list_head *entry;
for (entry = list_first(&itf->addr_list); entry != &itf->addr_list; entry = list_next(entry))
{
addr = container_of(entry, struct interface_addr, list);
if (cmmPrefixEqual(addr->address, ipaddr, 8 * len))
goto found;
}
return NULL;
found:
return addr;
}
#ifndef SAM_LEGACY
static struct map_rule * mr_add(struct interface *itf)
{
struct map_rule *mr;
mr = malloc(sizeof(struct map_rule));
if (!mr)
{
cmm_print(DEBUG_ERROR, "%s::%d: malloc() failed\n", __func__, __LINE__);
goto err;
}
memset(mr, 0, sizeof(struct map_rule));
list_add(&itf->mr_list, &mr->list);
cmm_print(DEBUG_INFO, "%s: map rule added\n", __func__);
return mr;
err:
return NULL;
}
static void mr_debug( struct interface * itf)
{
struct list_head *entry, *next_entry;
struct map_rule *mr;
for (entry = list_first(&itf->mr_list); next_entry = list_next(entry), entry != &itf->mr_list; entry = next_entry)
{
mr = container_of(entry, struct map_rule, list);
cmm_print(DEBUG_ERROR, "%03d : %03d.%03d.%03d.%03d/%02d %02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x/%03d %02x%02x :%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x/%03d eabit:%03d offset:%03d \n",
mr->rule.entry_num,
(ntohl(mr->rule.prefix) >> 24) & 0xff,
(ntohl(mr->rule.prefix) >> 16) & 0xff,
(ntohl(mr->rule.prefix) >> 8) & 0xff,
ntohl(mr->rule.prefix) & 0xff,
mr->rule.prefixlen,
mr->rule.relay_prefix.s6_addr[0],
mr->rule.relay_prefix.s6_addr[1],
mr->rule.relay_prefix.s6_addr[2],
mr->rule.relay_prefix.s6_addr[3],
mr->rule.relay_prefix.s6_addr[4],
mr->rule.relay_prefix.s6_addr[5],
mr->rule.relay_prefix.s6_addr[6],
mr->rule.relay_prefix.s6_addr[7],
mr->rule.relay_prefix.s6_addr[8],
mr->rule.relay_prefix.s6_addr[9],
mr->rule.relay_prefix.s6_addr[10],
mr->rule.relay_prefix.s6_addr[11],
mr->rule.relay_prefix.s6_addr[12],
mr->rule.relay_prefix.s6_addr[13],
mr->rule.relay_prefix.s6_addr[14],
mr->rule.relay_prefix.s6_addr[15],
mr->rule.relay_prefixlen,
mr->rule.relay_suffix.s6_addr[0],
mr->rule.relay_suffix.s6_addr[1],
mr->rule.relay_suffix.s6_addr[2],
mr->rule.relay_suffix.s6_addr[3],
mr->rule.relay_suffix.s6_addr[4],
mr->rule.relay_suffix.s6_addr[5],
mr->rule.relay_suffix.s6_addr[6],
mr->rule.relay_suffix.s6_addr[7],
mr->rule.relay_suffix.s6_addr[8],
mr->rule.relay_suffix.s6_addr[9],
mr->rule.relay_suffix.s6_addr[10],
mr->rule.relay_suffix.s6_addr[11],
mr->rule.relay_suffix.s6_addr[12],
mr->rule.relay_suffix.s6_addr[13],
mr->rule.relay_suffix.s6_addr[14],
mr->rule.relay_suffix.s6_addr[15],
mr->rule.relay_suffixlen,
mr->rule.eabit_len,
mr->rule.psid_offsetlen );
}
return ;
}
static struct map_rule * mr_find( int entry_num,struct interface * itf)
{
struct list_head *entry, *next_entry;
struct map_rule *mr;
cmm_print(DEBUG_INFO, "%s: mapping rule entry find %d \n", __func__, entry_num);
for (entry = list_first(&itf->mr_list); next_entry = list_next(entry), entry != &itf->mr_list; entry = next_entry)
{
mr = container_of(entry, struct map_rule, list);
if(mr->rule.entry_num == entry_num)
return mr;
}
return NULL;
}
static void __mr_delete(struct map_rule *mr)
{
cmm_print(DEBUG_INFO, "%s: mapping rule removed\n", __func__);
list_del(&mr->list);
free(mr);
}
static void mr_delete(int entry_num, int reset, struct interface* itf)
{
struct list_head *entry, *next_entry;
struct map_rule *mr;
cmm_print(DEBUG_INFO, "%s: mapping rule entry delete %d reset is %d \n", __func__, entry_num,reset);
for (entry = list_first(&itf->mr_list); next_entry = list_next(entry), entry != &itf->mr_list; entry = next_entry)
{
mr = container_of(entry, struct map_rule, list);
if(reset)
__mr_delete(mr);
else if(mr->rule.entry_num == entry_num)
{
__mr_delete(mr);
return;
}
}
return ;
}
static void __mr_update(struct map_rule * mr, struct ip6_4rd_map_msg *mr_msg)
{
mr->rule.prefix = mr_msg->prefix ;
memcpy(&mr->rule.relay_prefix,&mr_msg->relay_prefix, sizeof(mr_msg->relay_prefix));
memcpy(&mr->rule.relay_suffix,&mr_msg->relay_suffix, sizeof(mr_msg->relay_suffix));
mr->rule.prefixlen = mr_msg->prefixlen ;
mr->rule.relay_prefixlen = mr_msg->relay_prefixlen ;
mr->rule.relay_suffixlen = mr_msg->relay_suffixlen ;
mr->rule.psid_offsetlen = mr_msg->psid_offsetlen ;
mr->rule.eabit_len = mr_msg->eabit_len ;
mr->rule.entry_num = mr_msg->entry_num ;
return;
}
static void mr_update(FCI_CLIENT *fci_handle,struct ip6_4rd_map_msg *mr_msg)
{
struct interface *itf;
struct map_rule *mr;
itf = __itf_find(mr_msg->ifindex);
if (!itf)
{
cmm_print(DEBUG_ERROR, "%s::%d: __itf_find(%d) failed\n", __func__, __LINE__, mr_msg->ifindex);
goto out;
}
mr = mr_find(mr_msg->entry_num, itf);
if (!mr)
{
// Add new mapping rule
mr = mr_add(itf);
if(!mr)
{
cmm_print(DEBUG_ERROR, "%s::%d: __mr_add(%d) failed\n", __func__, __LINE__, mr_msg->entry_num);
goto out;
}
if(!(itf->tunnel_flags & TNL_4RD))
{
/* Set tunnel mode to 4RD and update tunnel in FPP */
itf->tunnel_flags |= TNL_4RD;
itf->flags |= FPP_NEEDS_UPDATE;
tunnel_send_cmd(fci_handle, UPDATE,itf);
}
}
__mr_update(mr, mr_msg);
out:
return;
}
static void mr_remove(FCI_CLIENT *fci_handle,struct ip6_4rd_map_msg *mr_msg)
{
struct interface *itf;
struct list_head *entry;
itf = __itf_find(mr_msg->ifindex);
if (!itf)
{
cmm_print(DEBUG_ERROR, "%s::%d: __itf_find(%d) failed\n", __func__, __LINE__, mr_msg->ifindex);
goto out;
}
mr_delete(mr_msg->entry_num, mr_msg->reset, itf);
entry = &itf->mr_list;
if(list_empty(entry))
{
if(itf->tunnel_flags & TNL_4RD)
{
itf->tunnel_flags &= ~TNL_4RD;
/* The tunnel needs to be updated as type 4o6 */
itf->flags |= FPP_NEEDS_UPDATE;
tunnel_send_cmd(fci_handle, UPDATE,itf);
}
}
out:
return;
}
#endif
static void __itf_remove(struct interface *itf)
{
struct interface_addr *addr;
struct list_head *entry, *next_entry;
#ifndef SAM_LEGACY
struct map_rule *mr;
#endif
cmm_print(DEBUG_INFO, "%s: interface(%d) removed\n", __func__, itf->ifindex);
for (entry = list_first(&itf->addr_list); next_entry = list_next(entry), entry != &itf->addr_list; entry = next_entry)
{
addr = container_of(entry, struct interface_addr, list);
__addr_remove(addr);
}
#ifndef SAM_LEGACY
for (entry = list_first(&itf->mr_list); next_entry = list_next(entry), entry != &itf->mr_list; entry = next_entry)
{
mr = container_of(entry, struct map_rule, list);
__mr_delete(mr);
}
#endif
if (__itf_is_l2tp(itf))
l2tp_itf_del(itf_table.fci_handle, itf);
list_del(&itf->list);
free(itf);
}
static void __itf_update(struct interface_table *ctx, struct interface *itf, struct ifinfomsg *ifi, struct rtattr *tb[])
{
struct rtattr *attr;
itf->ifindex = ifi->ifi_index;
itf->type = ifi->ifi_type;
itf->ifi_flags = ifi->ifi_flags;
/* If the interface is down don't try to update the other fields,
the information may already be missing in the kernel and we will get wrong results */
if (!__itf_is_up(itf))
goto out;
#ifdef WIFI_ENABLE
/* FIXME : Skip wireless events */
attr = tb[IFLA_WIRELESS];
if(attr)
goto out;
#endif
attr = tb[IFLA_ADDRESS];
if (attr)
{
/* mark mac address valid */
itf->macaddr_len = RTA_PAYLOAD(attr);
/* Ethip kernel mod WA compatibility */
if(itf->macaddr_len > 6)
itf->macaddr_len = 6;
memcpy(itf->macaddr, RTA_DATA(attr), itf->macaddr_len);
}
else
{
memset(itf->macaddr, 0, 6);
itf->macaddr_len = 0;
}
attr = tb[IFLA_MTU];
if (attr)
itf->mtu = *(unsigned int *)RTA_DATA(attr);
else
itf->mtu = 0;
attr = tb[IFLA_LINK];
if (attr)
itf->phys_ifindex = *(int *)RTA_DATA(attr);
else
/* will be updated later if pppoe */
itf->phys_ifindex = itf->ifindex;
if (__itf_is_pppoe(itf))
{
itf->itf_flags &= ~ITF_PPPOE_SESSION_UP;
if (!ctx->fp)
{
ctx->fp = fopen(PPPOE_PATH, "r");
if (!ctx->fp)
{
cmm_print(DEBUG_ERROR, "%s::%d: fopen(%s) error %s\n", __func__, __LINE__, PPPOE_PATH, strerror(errno));
goto out;
}
}
__cmmGetPPPoESession(ctx->fp, itf);
}
else
{
__cmmGetVlan(ctx->fd, itf);
#ifdef WIFI_ENABLE
__cmmGetWiFi(ctx->fd, itf);
#endif
itf->itf_flags &= ~ITF_BRIDGE;
__cmmGetBridges(ctx->fd);
__cmmGetTunnel(ctx->fd, itf);
#ifdef IFLA_LINKINFO
unsigned char *pInfoKind = NULL ;
unsigned int uLen = 0 ;
attr = tb[IFLA_LINKINFO];
if ( attr )
{
struct rtattr *attr2;
attr2 = cmm_get_rtattr(RTA_DATA(attr), RTA_PAYLOAD(attr), IFLA_INFO_KIND);
if ( attr2 )
{
uLen = RTA_PAYLOAD(attr2);
pInfoKind = (unsigned char *)RTA_DATA(attr2) ;
}
}
itf->itf_flags &= ~ITF_MACVLAN;
if ( uLen >= INFO_KIND_MACVLAN_STR_LEN )
{
__cmmGetMacVlan(ctx->fd, itf, pInfoKind );
}
#endif
}
out:
cmm_print(DEBUG_INFO, "%s: itf: %lx, ifindex: %d, phys_ifindex: %d, flags: %x\n", __func__, (unsigned long)itf, itf->ifindex, itf->phys_ifindex, itf->itf_flags);
/* FIXME resolve physical interface for vlan + bridge, bridge + vlan */
}
static struct interface *__itf_add(struct interface_table *ctx, int ifindex)
{
struct interface *itf;
int key;
itf = malloc(sizeof(struct interface));
if (!itf)
{
cmm_print(DEBUG_ERROR, "%s::%d: malloc() failed\n", __func__, __LINE__);
goto err;
}
memset(itf, 0, sizeof(struct interface));
itf->count = 0;
list_head_init(&itf->addr_list);
#ifndef SAM_LEGACY
list_head_init(&itf->mr_list);
#endif
key = HASH_ITF(ifindex);
list_add(&ctx->hash[key], &itf->list);
cmm_print(DEBUG_INFO, "%s: interface(%d) added\n", __func__, ifindex);
return itf;
err:
return NULL;
}
#ifndef SAM_LEGACY
static int __cmmGetMappingRuleFilter(const struct sockaddr_nl *nladdr, struct nlmsghdr *nlh, void *arg)
{
struct interface_table *ctx = arg;
struct ip6_4rd_map_msg *mr;
// struct rtattr *tb[IF_MR_MAX + 1];
if (nlh->nlmsg_type != RTM_NEW4RD)
{
cmm_print(DEBUG_ERROR, "%s::%d: unexpected netlink message(%d)\n",
__func__, __LINE__, nlh->nlmsg_type);
goto out;
}
mr = NLMSG_DATA(nlh);
mr_update(ctx->fci_handle,mr);
struct interface * itf = __itf_find(mr->ifindex);
if (itf)
{
mr_debug(itf);
}
out:
return RTNL_CB_CONTINUE;
}
static int __cmmGetMappingRule(struct interface_table *ctx)
{
struct ip6_4rd_map_msg mr;
int rc;
memset(&mr,0,sizeof(mr));
if ((rc = cmm_rtnl_dump_request(&ctx->rth, RTM_GET4RD, &mr, sizeof(struct ip6_4rd_map_msg))) < 0)
goto out;
rc = cmm_rtnl_listen(&ctx->rth, __cmmGetMappingRuleFilter, ctx);
out:
return rc;
}
#endif
void __itf_update_connection(FCI_CLIENT *fci_handle, int ifindex)
{
struct list_head *entry, *next_entry;
struct RtEntry *route;
int i;
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->oifindex == ifindex) || (route->phys_oifindex == ifindex)))
continue;
/* Force lookup of bridge port */
if (route->neighEntry)
route->neighEntry->port = -1;
route->flags |= CHECK_BRIDGE_PORT;
__cmmCtUpdateWithRoute(fci_handle, route);
__cmmTunnelUpdateWithRoute(fci_handle, route);
__cmmSocketUpdateWithRoute(fci_handle, route);
}
}
}
static void __updatelink(struct interface_table *ctx, struct ifinfomsg *ifi, struct rtattr *tb[], int dellink)
{
struct interface *itf;
itf = __itf_find(ifi->ifi_index);
if (!itf)
{
if (dellink && !(ifi->ifi_flags & IFF_UP) && (ifi->ifi_change == 0xffffffff))
goto out;
itf = __itf_add(ctx, ifi->ifi_index);
if (!itf)
{
cmm_print(DEBUG_ERROR, "%s::%d: __itf_add(%d) failed\n", __func__, __LINE__, ifi->ifi_index);
goto out;
}
}
__itf_update(ctx, itf, ifi, tb);
/* For IP tunnels we don't care about physical output interface here,
everything is handled later through the tunnel route output interface */
if (__itf_is_tunnel(itf))
{
if (__itf_is_up(itf))
__tunnel_add(ctx->fci_handle, itf);
else
__tunnel_del(ctx->fci_handle, ctx->fci_key_handle, itf);
}
else if (__itf_is_l2tp(itf))
{
if (__itf_is_up(itf))
/* L2TP interface being a virtual interface, the physical and logical ifindices are the same */
l2tp_itf_add(ctx->fci_handle, ADD, itf);
else
__l2tp_itf_del(ctx->fci_handle, itf);
}
else
{
#ifdef WIFI_ENABLE
if(__itf_is_wifi(itf))
{
// cmm_print( DEBUG_INFO, "%s : vap : %s\n", __func__, itf->ifname);
if (__itf_is_up(itf))
cmmFeWiFiUpdate(ctx->fci_handle, ctx->fd, ADD, itf);
else
cmmFeWiFiUpdate(ctx->fci_handle, ctx->fd, REMOVE, itf);
}
else
#endif
if (__itf_is_programmed(itf->phys_ifindex) > 0)
{
if (__itf_is_pppoe(itf) && ctx->fp)
{
if (__itf_is_up(itf) && (itf->itf_flags & ITF_PPPOE_SESSION_UP))
cmmFePPPoEUpdate(ctx->fci_handle, ADD, itf);
else
cmmFePPPoEUpdate(ctx->fci_handle, REMOVE, itf);
}
else if (__itf_is_vlan(itf))
{
if (cmmVlanCheckPolicy(itf))
{
if (__itf_is_up(itf))
cmmFeVLANUpdate(ctx->fci_handle, ADD, itf);
else
cmmFeVLANUpdate(ctx->fci_handle, REMOVE, itf);
}
}
else if (__itf_is_macvlan(itf))
{
if (__itf_is_up(itf)) {
cmmFeMacVlanUpdate(ctx->fci_handle,ctx->fd,ADD,itf);
}
else {
cmmFeMacVlanUpdate(ctx->fci_handle,ctx->fd,REMOVE,itf);
}
}
}
#ifdef WIFI_ENABLE
else if (____itf_is_bridge( itf ))
{
if (__itf_is_up(itf))
cmmFeWiFiBridgeUpdate(ctx->fci_handle, ctx->fd, ADD, itf);
}
#endif
}
if (dellink && !__itf_is_up(itf) && (ifi->ifi_change == 0xffffffff))
__itf_remove(itf);
out:
return;
}
static void updatelink(struct interface_table *ctx, struct ifinfomsg *ifi, struct rtattr *tb[], int dellink)
{
__pthread_mutex_lock(&ctx->lock);
__pthread_mutex_lock(&ctMutex);
__pthread_mutex_lock(&rtMutex);
__pthread_mutex_lock(&neighMutex);
__pthread_mutex_lock(&flowMutex);
__updatelink(ctx, ifi, tb, dellink);
__itf_update_connection(ctx->fci_handle, ifi->ifi_index);
mc_update_table(ctx->fci_handle, tb, ifi);
__pthread_mutex_unlock(&flowMutex);
__pthread_mutex_unlock(&neighMutex);
__pthread_mutex_unlock(&rtMutex);
__pthread_mutex_unlock(&ctMutex);
__pthread_mutex_unlock(&ctx->lock);
}
struct interface *__itf_find(int ifindex)
{
struct list_head *entry;
struct interface *itf;
int key;
cmm_print(DEBUG_INFO, "%s: find interface(%d)\n", __func__, ifindex);
key = HASH_ITF(ifindex);
entry = list_first(&itf_table.hash[key]);
while (entry != &itf_table.hash[key])
{
itf = container_of(entry, struct interface, list);
if (itf->ifindex == ifindex)
goto found;
entry = list_next(entry);
}
itf = NULL;
found:
return itf;
}
static int __cmmGetAddrFilter(const struct sockaddr_nl *nladdr, struct nlmsghdr *nlh, void *arg)
{
struct ifaddrmsg *ifa;
struct rtattr *tb[IFA_MAX + 1];
if (nlh->nlmsg_type != RTM_NEWADDR)
{
cmm_print(DEBUG_ERROR, "%s::%d: unexpected netlink message(%d)\n",
__func__, __LINE__, nlh->nlmsg_type);
goto out;
}
ifa = NLMSG_DATA(nlh);
cmm_parse_rtattr(tb, IFA_MAX, IFA_RTA(ifa), IFA_PAYLOAD(nlh));
if (!tb[IFA_ADDRESS])
{
cmm_print(DEBUG_ERROR, "%s::%d: rtnetlink message missing interface addr\n", __func__, __LINE__);
goto out;
}
__newaddr(ifa, tb);
out:
return RTNL_CB_CONTINUE;
}
static int __cmmGetAddr(struct rtnl_handle *rth, int family)
{
struct ifaddrmsg ifa = {
.ifa_family = family,
.ifa_prefixlen = 0,
.ifa_flags = 0,
.ifa_scope = 0,
.ifa_index = 0,
};
int rc;
if ((rc = cmm_rtnl_dump_request(rth, RTM_GETADDR, &ifa, sizeof(struct ifaddrmsg))) < 0)
goto out;
rc = cmm_rtnl_listen(rth, __cmmGetAddrFilter, NULL);
out:
return rc;
}
static int __cmmGetLinkFilter(const struct sockaddr_nl *nladdr, struct nlmsghdr *nlh, void *arg)
{
struct interface_table *ctx = arg;
struct ifinfomsg *ifi;
struct rtattr *tb[IFLA_MAX + 1];
if (nlh->nlmsg_type != RTM_NEWLINK)
{
cmm_print(DEBUG_ERROR, "%s::%d: unexpected netlink message(%d)\n",
__func__, __LINE__, nlh->nlmsg_type);
goto out;
}
ifi = NLMSG_DATA(nlh);
cmm_parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), IFLA_PAYLOAD(nlh));
__updatelink(ctx, ifi, tb, 0);
out:
return RTNL_CB_CONTINUE;
}
static int __cmmGetLink(struct interface_table *ctx)
{
struct ifinfomsg ifi = {
.ifi_family = AF_UNSPEC,
.ifi_type = 0,
.ifi_index = 0,
.ifi_flags = 0,
.ifi_change = 0,
};
int rc;
if ((rc = cmm_rtnl_dump_request(&ctx->rth, RTM_GETLINK, &ifi, sizeof(struct ifinfomsg))) < 0)
goto out;
rc = cmm_rtnl_listen(&ctx->rth, __cmmGetLinkFilter, ctx);
out:
return rc;
}
static int __itf_table_update(struct interface_table *ctx)
{
__cmmGetLink(ctx);
__cmmGetAddr(&ctx->rth, AF_INET);
__cmmGetAddr(&ctx->rth, AF_INET6);
#ifndef SAM_LEGACY
__cmmGetMappingRule(ctx);
#endif
return 0;
}
static int itf_table_update(struct interface_table *ctx)
{
int rc;
__pthread_mutex_lock(&ctx->lock);
__pthread_mutex_lock(&ctMutex);
__pthread_mutex_lock(&rtMutex);
__pthread_mutex_lock(&neighMutex);
__pthread_mutex_lock(&flowMutex);
rc = __itf_table_update(ctx);
__pthread_mutex_unlock(&flowMutex);
__pthread_mutex_unlock(&neighMutex);
__pthread_mutex_unlock(&rtMutex);
__pthread_mutex_unlock(&ctMutex);
__pthread_mutex_unlock(&ctx->lock);
return rc;
}
struct interface *__itf_get(int ifindex)
{
struct interface *itf;
itf = __itf_find(ifindex);
if (!itf)
{
__itf_table_update(&itf_table);
itf = __itf_find(ifindex);
if (!itf)
goto err;
}
// itf->count++;
return itf;
err:
return NULL;
}
void __itf_put(struct interface *itf)
{
#if 0
itf->count--;
if (itf->count <= 0)
__itf_remove(itf);
#endif
}
int itf_get_ipaddr(int ifindex, int family, unsigned char scope, unsigned int *ipaddr, unsigned int *target)
{
struct interface *itf;
struct interface_addr *addr;
struct list_head *entry;
int rc = -1;
__pthread_mutex_lock(&itf_table.lock);
__pthread_mutex_lock(&ctMutex);
__pthread_mutex_lock(&rtMutex);
__pthread_mutex_lock(&neighMutex);
__pthread_mutex_lock(&flowMutex);
itf = __itf_get(ifindex);
if (!itf)
goto unlock;
for (entry = list_first(&itf->addr_list); entry != &itf->addr_list; entry = list_next(entry))
{
addr = container_of(entry, struct interface_addr, list);
if ((addr->family == family) && (addr->scope == scope) && (((family == AF_INET) && cmmPrefixEqual(target, addr->address, addr->prefixlen)) || (family == AF_INET6)))
{
memcpy(ipaddr, addr->address, addr->len);
rc = 0;
break;
}
}
__itf_put(itf);
unlock:
__pthread_mutex_unlock(&flowMutex);
__pthread_mutex_unlock(&neighMutex);
__pthread_mutex_unlock(&rtMutex);
__pthread_mutex_unlock(&ctMutex);
__pthread_mutex_unlock(&itf_table.lock);
return rc;
}
int __itf_get_macaddr(struct interface *itf, unsigned char *macaddr)
{
if (!itf->macaddr_len)
goto err;
memcpy(macaddr, itf->macaddr, itf->macaddr_len);
return 0;
err:
return -1;
}
int itf_get_macaddr(int ifindex, unsigned char *macaddr)
{
struct interface *itf;
int rc = -1;
__pthread_mutex_lock(&itf_table.lock);
__pthread_mutex_lock(&ctMutex);
__pthread_mutex_lock(&rtMutex);
__pthread_mutex_lock(&neighMutex);
__pthread_mutex_lock(&flowMutex);
itf = __itf_get(ifindex);
if (!itf)
goto unlock;
rc = __itf_get_macaddr(itf, macaddr);
__itf_put(itf);
unlock:
__pthread_mutex_unlock(&flowMutex);
__pthread_mutex_unlock(&neighMutex);
__pthread_mutex_unlock(&rtMutex);
__pthread_mutex_unlock(&ctMutex);
__pthread_mutex_unlock(&itf_table.lock);
return rc;
}
int __itf_get_mtu(int ifindex)
{
struct interface *itf;
int rc = -1;
itf = __itf_find(ifindex);
if (!itf)
goto out;
rc = itf->mtu;
out:
return rc;
}
int ____itf_get_name(struct interface *itf, char *ifname, int len)
{
if (!if_indextoname(itf->ifindex, itf->ifname))
{
cmm_print(DEBUG_WARNING, "%s: if_indextoname() failed\n", __func__);
if (itf->ifname[0] == '\0')
goto err;
}
len = (len < IFNAMSIZ ? len : IFNAMSIZ) - 1;
ifname[len] = '\0';
memcpy(ifname, itf->ifname, len);
return 0;
err:
return -1;
}
int __itf_get_name(int ifindex, char *ifname, int len)
{
struct interface *itf;
int rc = -1;
itf = __itf_find(ifindex);
if (!itf)
goto out;
rc = ____itf_get_name(itf, ifname, len);
out:
return rc;
}
static int ____itf_is_bridge(struct interface *itf)
{
if (itf->itf_flags & ITF_BRIDGE)
return 1;
return 0;
}
int __itf_is_bridge(int ifindex)
{
struct interface *itf;
int rc = -1;
itf = __itf_find(ifindex);
if (!itf)
goto out;
if (itf->itf_flags & ITF_BRIDGE)
rc = 1;
else
rc = 0;
out:
return rc;
}
#ifdef WIFI_ENABLE
int __itf_is_wifi(struct interface *itf)
{
if (itf->itf_flags & ITF_WIFI)
return 1;
return 0;
}
#endif
int __itf_is_vlan(struct interface *itf)
{
if (itf->itf_flags & ITF_VLAN)
return 1;
return 0;
}
int __itf_is_pointopoint(struct interface *itf)
{
if (itf->ifi_flags & IFF_POINTOPOINT)
return 1;
return 0;
}
int __itf_is_pppoe(struct interface *itf)
{
if ((itf->type == ARPHRD_PPP) && !(itf->itf_flags & ITF_L2TP))
return 1;
return 0;
}
int __itf_is_noarp(int ifindex)
{
struct interface *itf;
int rc = -1;
itf = __itf_find(ifindex);
if (!itf)
goto out;
if (itf->ifi_flags & IFF_NOARP)
rc = 1;
else
rc = 0;
out:
return rc;
}
int __itf_is_up(struct interface *itf)
{
if (itf->ifi_flags & IFF_UP)
return 1;
return 0;
}
int __itf_is_tunnel(struct interface *itf)
{
if (itf->itf_flags & ITF_TUNNEL)
return 1;
return 0;
}
int __itf_is_l2tp(struct interface *itf)
{
if (itf->itf_flags & ITF_L2TP)
return 1;
return 0;
}
int __itf_get_from_bridge_port(int ifindex, int port)
{
struct interface *itf;
int rc = -1;
itf = __itf_find(ifindex);
if (!itf)
goto out;
if (!____itf_is_bridge(itf))
goto out;
if (port >= MAX_PORTS)
goto out;
rc = itf->ifindices[port];
out:
return rc;
}
static int ____itf_is_programmed(struct interface *itf)
{
if (itf->flags & FPP_PROGRAMMED)
return 1;
else
return 0;
}
int __itf_is_programmed(int ifindex)
{
struct interface *itf;
int rc = -1, ii;
/* LAN/WAN interfaces are programmed in FPP by default */
for (ii = 0; ii < GEM_PORTS; ii++)
{
if (port_table[ii].enable && port_table[ii].ifindex == ifindex)
{
rc = 1;
goto out;
}
}
itf = __itf_find(ifindex);
if (!itf)
goto out;
rc = ____itf_is_programmed(itf);
out:
return rc;
}
int itf_is_programmed(int ifindex)
{
struct interface *itf;
int rc = -1, ii;
__pthread_mutex_lock(&itf_table.lock);
__pthread_mutex_lock(&ctMutex);
__pthread_mutex_lock(&rtMutex);
__pthread_mutex_lock(&neighMutex);
__pthread_mutex_lock(&flowMutex);
/* LAN/WAN interfaces are programmed in FPP by default */
for (ii = 0; ii < GEM_PORTS; ii++)
{
if (port_table[ii].enable && port_table[ii].ifindex == ifindex)
{
rc = 1;
goto out;
}
}
itf = __itf_get(ifindex);
if (!itf)
goto out;
rc = ____itf_is_programmed(itf);
out:
__pthread_mutex_unlock(&flowMutex);
__pthread_mutex_unlock(&neighMutex);
__pthread_mutex_unlock(&rtMutex);
__pthread_mutex_unlock(&ctMutex);
__pthread_mutex_unlock(&itf_table.lock);
return rc;
}
int __itf_is_macvlan(struct interface *itf)
{
if (itf->itf_flags & ITF_MACVLAN)
return 1;
return 0;
}
int ____itf_is_4o6_tunnel(struct interface *itf)
{
if (!__itf_is_tunnel(itf) || (itf->tunnel_parm6.proto != IPPROTO_IPIP ))
return 0;
else
return 1;
}
int ____itf_is_floating_sit_tunnel(struct interface *itf)
{
if (!__itf_is_tunnel(itf) || (itf->type != ARPHRD_SIT) || itf->tunnel_parm4.iph.daddr)
return 0;
else
return 1;
}
int __itf_is_floating_sit_tunnel(int ifindex)
{
struct interface *itf;
int rc = -1;
itf = __itf_find(ifindex);
if (!itf)
goto out;
rc = ____itf_is_floating_sit_tunnel(itf);
out:
return rc;
}
int itf_name_update(FCI_CLIENT *fci_handle, struct gemac_port *port)
{
/* Send a message to FPP to set the interface name associated with each GEM Port */
fpp_port_update_cmd_t cmd;
int ret;
cmd.port_id = port->port_id;
strncpy(cmd.ifname, port->ifname, sizeof(cmd.ifname));
cmd.ifname[sizeof(cmd.ifname) - 1] = '\0';
cmm_print(DEBUG_INFO, "%s: port mapping %d <=> %s\n", __func__, cmd.port_id, cmd.ifname);
if (FPP_ERR_OK != (ret = fci_write(fci_handle, FPP_CMD_PORT_UPDATE , sizeof(cmd), (unsigned short *) &cmd)))
{
cmm_print(DEBUG_CRIT, "%s: Port update failed in FPP %d \n", __func__, ret);
return -1;
}
return 0;
}
int itf_table_init(struct interface_table *ctx)
{
int i;
pthread_mutex_init(&ctx->lock, NULL);
for (i = 0; i < ITF_HASH_TABLE_SIZE; i++)
list_head_init(&ctx->hash[i]);
for ( i = 0; i < GEM_PORTS; i++)
{
port_table[i].ifindex = if_nametoindex(port_table[i].ifname);
if (!port_table[i].ifindex)
cmm_print(DEBUG_ERROR, "%s::%d: if_nametoindex(%s) failed\n", __func__, __LINE__, port_table[i].ifname);
}
LO_IFINDEX = if_nametoindex(LO_INTERFACE_NAME);
if (!LO_IFINDEX)
cmm_print(DEBUG_ERROR, "%s::%d: if_nametoindex(%s) failed\n", __func__, __LINE__, LO_INTERFACE_NAME);
ctx->fp = fopen(PPPOE_PATH, "r");
/* we will retry later if it fails here, this happens when ppp modules are not loaded yet */
ctx->fd = socket(AF_INET, SOCK_DGRAM, 0);
if (ctx->fd < 0)
{
cmm_print(DEBUG_ERROR, "%s::%d: inet socket() %s\n", __func__, __LINE__, strerror(errno));
goto err1;
}
/* get netlink link and address information */
if (cmm_rtnl_open(&ctx->rth, 0) < 0)
{
cmm_print(DEBUG_ERROR, "%s::%d: netlink socket() %s\n", __func__, __LINE__, strerror(errno));
goto err2;
}
ctx->fci_handle = fci_open(FCILIB_FF_TYPE, 0);
if (!ctx->fci_handle)
{
cmm_print(DEBUG_ERROR, "%s::%d: fci_open() %s\n", __func__, __LINE__, strerror(errno));
goto err3;
}
#if !defined(IPSEC_SUPPORT_DISABLED)
ctx->fci_key_handle = fci_open(FCILIB_KEY_TYPE, 0);
if (!ctx->fci_key_handle)
{
cmm_print(DEBUG_ERROR, "%s::%d: fci_open() %s\n", __func__, __LINE__, strerror(errno));
goto err4;
}
#endif
#ifdef WIFI_ENABLE
cmmWiFiReset(ctx->fci_handle);
#endif
cmmVlanReset(ctx->fci_handle);
itf_table_update(ctx);
return 0;
#if !defined(IPSEC_SUPPORT_DISABLED)
err4:
fci_close(ctx->fci_handle);
#endif
err3:
cmm_rtnl_close(&ctx->rth);
err2:
close(ctx->fd);
err1:
if (ctx->fp)
fclose(ctx->fp);
return -1;
}
/*****************************************************************
* cmmRtnlLink
*
*
******************************************************************/
int cmmRtnlLink(const struct sockaddr_nl *who, struct nlmsghdr *nlh, void *arg)
{
struct interface_table *ctx = arg;
struct ifinfomsg *ifi;
struct rtattr *tb[IFLA_MAX + 1];
char ifname[IFNAMSIZ];
switch (nlh->nlmsg_type)
{
case RTM_NEWLINK:
case RTM_DELLINK:
break;
default:
cmm_print(DEBUG_ERROR, "%s: unsupported LINK netlink message %x\n", __func__, nlh->nlmsg_type);
goto out;
break;
}
ifi = NLMSG_DATA(nlh);
if (nlh->nlmsg_type == RTM_NEWLINK)
{
cmm_print(DEBUG_INFO, "%s: RTM_NEWLINK %s\n", __func__, if_indextoname(ifi->ifi_index, ifname));
}
else
{
cmm_print(DEBUG_INFO, "%s: RTM_DELLINK %s\n", __func__, if_indextoname(ifi->ifi_index, ifname));
}
cmm_print(DEBUG_INFO, "%s: ifinfomsg family: %x, type: %x, index: %d, flags: %x, change: %x\n", __func__,
ifi->ifi_family, ifi->ifi_type,
ifi->ifi_index, ifi->ifi_flags, ifi->ifi_change);
cmm_parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), IFLA_PAYLOAD(nlh));
updatelink(ctx, ifi, tb, nlh->nlmsg_type == RTM_DELLINK);
out:
return RTNL_CB_CONTINUE;
}
/*****************************************************************
* cmmRtnlIfAddr
*
*
******************************************************************/
int cmmRtnlIfAddr(const struct sockaddr_nl *who, struct nlmsghdr *nlh, void *arg)
{
struct interface_table *ctx = arg;
struct ifaddrmsg *ifa;
struct rtattr *tb[IFA_MAX + 1];
char address[INET6_ADDRSTRLEN];
unsigned int *ipaddr;
#ifndef SAM_LEGACY
struct ip6_4rd_map_msg *mr;
#endif
switch (nlh->nlmsg_type)
{
#ifndef SAM_LEGACY
case RTM_NEW4RD:
case RTM_DEL4RD:
{
mr = NLMSG_DATA(nlh);
if(nlh->nlmsg_type == RTM_NEW4RD)
mr_update(ctx->fci_handle,mr);
else if(nlh->nlmsg_type == RTM_DEL4RD)
mr_remove(ctx->fci_handle,mr);
struct interface * itf = __itf_find(mr->ifindex);
if (itf)
{
mr_debug(itf);
}
return 0;
}
#endif
case RTM_NEWADDR:
case RTM_DELADDR:
break;
default:
cmm_print(DEBUG_ERROR, "%s: unsupported IFADDR netlink message %x\n", __func__, nlh->nlmsg_type);
goto out;
break;
}
ifa = NLMSG_DATA(nlh);
cmm_print(DEBUG_INFO, "%s: ifaddr family: %x, prefixlen: %d, flags: %x, scope: %d, index: %d\n", __func__,
ifa->ifa_family, ifa->ifa_prefixlen,
ifa->ifa_flags, ifa->ifa_scope, ifa->ifa_index);
cmm_parse_rtattr(tb, IFA_MAX, IFA_RTA(ifa), IFA_PAYLOAD(nlh));
if (!tb[IFA_ADDRESS])
goto out;
ipaddr = RTA_DATA(tb[IFA_ADDRESS]);
if (nlh->nlmsg_type == RTM_NEWADDR)
{
cmm_print(DEBUG_INFO, "%s: RTM_NEWADDR %s\n", __func__, inet_ntop(ifa->ifa_family, ipaddr, address, sizeof(address)));
newaddr(ctx, ifa, tb);
}
else
{
cmm_print(DEBUG_INFO, "%s: RTM_DELADDR %s\n", __func__, inet_ntop(ifa->ifa_family, ipaddr, address, sizeof(address)));
deladdr(ctx, ifa, tb);
}
out:
return RTNL_CB_CONTINUE;
}