blob: 4b63755609bc676131aeea367dbbcc317df134f8 [file] [log] [blame]
/******************************************************************************
* *(C) Copyright 2008 Marvell International Ltd.
* * All Rights Reserved
* ******************************************************************************/
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>
#include <cutils/properties.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <netdb.h>
#ifdef BIONIC
#include <netinet/in6.h>
#endif
#include <sys/ioctl.h>
#ifdef BIONIC
#include <netutils/ifc.h>
#endif
#include <atchannel/atchannel.h>
#include "dataapi.h"
#include "marvell-ril.h"
#include "modem-device.h"
#include "ifc.h"
#define PATH_PROC_IFINET6 "/proc/net/if_inet6"
#define IPV6_ADDR_LINKLOCAL 0x0020U
#define IPV6_ADDR_GLOBAL 0
#define IPV6_ADDR_SCOPE_MASK 0x00f0U
#define PERSIST_DEVICE_NUM 8
#define CCINET_IOCTL_AQUIRE SIOCDEVPRIVATE
#define CCINET_IOCTL_RELEASE (SIOCDEVPRIVATE+1)
#define CCINET_IOCTL_SET_V6_CID (SIOCDEVPRIVATE+2)
#define CCINET_IOCTL_IF_ENABLE (SIOCDEVPRIVATE+3)
#define CCINET_IOCTL_IF_DISABLE (SIOCDEVPRIVATE+4)
static int getInterfaceAddr6(const char* ifname, char* ipaddress, size_t ipsize);
static int set_ifru_ivalue(const char* ifname, int cmd, int value, const char* cmd_str)
{
int ret;
struct ifreq ifr;
UNUSED(cmd_str);
int ifc_ctl_sock = socket(AF_INET, SOCK_DGRAM, 0);
if (ifc_ctl_sock < 0) {
RLOGE(set_ifru_ivalue, "%s:socket failed: %s", __FUNCTION__, strerror(errno));
return -1;
}
memset(&ifr, 0, sizeof(struct ifreq));
strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
ifr.ifr_ifru.ifru_ivalue = value;
ret = ioctl(ifc_ctl_sock, cmd, &ifr);
if (ret < 0) {
RLOGE(set_ifru_ivalue1, "%s(ifname:%s,cmd:%s,value:%d) failed: %s",
__FUNCTION__, ifname, cmd_str, value, strerror(errno));
}
close(ifc_ctl_sock);
return ret;
}
static inline int set_ifru_ivalue_for_if_index(
RIL_SOCKET_ID socketId, int index, int cmd, int value, const char* cmd_str)
{
char ifname[32];
ModemDevice::get_current_modem(socketId)->get_netcard_name(ifname, sizeof(ifname), index);
return set_ifru_ivalue(ifname, cmd, value, cmd_str);
}
#define SET_IFRU_IVALUE(ifname, cmd, value) set_ifru_ivalue(ifname, cmd, value, #cmd)
#define SET_IFRU_IVALUE_FOR_IF_INDEX(socketId, index, cmd, value) \
set_ifru_ivalue_for_if_index(socketId, index, cmd, value, #cmd)
static inline int createNetif(RIL_SOCKET_ID socketId, int index)
{
return SET_IFRU_IVALUE_FOR_IF_INDEX(socketId, 0, CCINET_IOCTL_IF_ENABLE, index);
}
static inline int destroyNetif(RIL_SOCKET_ID socketId, int index)
{
return SET_IFRU_IVALUE_FOR_IF_INDEX(socketId, 0, CCINET_IOCTL_IF_DISABLE, index);
}
// cid starts from 0
int configInterface(RIL_SOCKET_ID socketId, int index, char* v4_address, char* v6_address, int v6_index)
{
if (index >= PERSIST_DEVICE_NUM && createNetif(socketId, index) < 0) {
return -1;
}
int ret = 0;
char ifname[32];
ModemDevice::get_current_modem(socketId)->get_netcard_name(ifname, sizeof(ifname), index);
if (SET_IFRU_IVALUE(ifname, CCINET_IOCTL_AQUIRE, socketId)) {
return -1;
}
ifc_init();
if (ifc_up(ifname))
{
RLOGE(configInterface, "failed to turn on interface");
ret = -1;
goto exit;
}
if (v4_address) {
if (ifc_add_address(ifname, v4_address, 32))
{
RLOGE(configInterface1, "failed to ifc_add_address %s/32", v4_address);
ret = -1;
goto exit;
}
}
if (v6_address) {
ret = v4_address ? 0 : -1;
if (v6_index >= 0) {
if (SET_IFRU_IVALUE(ifname, CCINET_IOCTL_SET_V6_CID, v6_index) < 0) {
RLOGW(configInterface2, "failed to config v6_cid");
goto exit;
}
}
char ip6_addr[INET6_ADDRSTRLEN] = { '\0' };
struct in6_addr addr;
inet_pton(AF_INET6, v6_address, &addr);
addr.s6_addr32[0] = htonl(0xfe800000);
addr.s6_addr32[1] = 0;
inet_ntop(AF_INET6, &addr, ip6_addr, sizeof(ip6_addr));
if (ifc_add_address(ifname, ip6_addr, 64)) {
RLOGW(configInterface3, "failed to ifc_add_address: %s/64", ip6_addr);
goto exit;
}
if (!v4_address) {
//Wait to get the gloabal address
int count;
ret = -2; //give a special return value for can't get RA from network.
for (count = 12 ;count > 0; count--) {
sleep(1);
char ipaddress[INET6_ADDRSTRLEN];
if ( getInterfaceAddr6(ifname, ipaddress, sizeof(ipaddress)) ==0 ) {
ret = 0;
goto exit;
}
}
}
}
exit:
if (ret < 0) {
ifc_down(ifname);
SET_IFRU_IVALUE(ifname, CCINET_IOCTL_RELEASE, socketId);
if (index >= PERSIST_DEVICE_NUM)
destroyNetif(socketId, index);
}
ifc_close();
return ret;
}
void disableInterface(RIL_SOCKET_ID socketId, int index)
{
char ifname[32];
ModemDevice::get_current_modem(socketId)->get_netcard_name(ifname, sizeof(ifname), index);
if (SET_IFRU_IVALUE(ifname, CCINET_IOCTL_RELEASE, socketId) < 0) {
RLOGE(disableInterface, "%s: NOT disable %s, which is acquired by the other SIM", __FUNCTION__,ifname);
return;
}
RLOGT(disableInterface1, "disable %s", ifname);
ifc_disable(ifname);
if (index >= PERSIST_DEVICE_NUM) {
destroyNetif(socketId, index);
}
}
static int getInterfaceAddr4(const char* ifname, char* ipaddress, size_t ipsize)
{
int ret = -1;
in_addr_t myaddr = 0;
ifc_init();
ifc_get_addr(ifname, &myaddr);
if ( myaddr )
{
ret = 0;
inet_ntop(AF_INET, &myaddr, ipaddress, ipsize);
RLOGT(getInterfaceAddr4, "%s IP address is: %s!\n", ifname, ipaddress);
}
ifc_close();
return ret;
}
// get global ipv6 address
static int getInterfaceAddr6(const char* ifname, char* ipaddress, size_t ipsize)
{
int ret = -1;
char devname[20];
int plen, scope, dad_status, if_idx;
char addr6p[8][5];
FILE *f = fopen(PATH_PROC_IFINET6, "r");
if (f == NULL) {
RLOGE(getInterfaceAddr6, "Cannot open file: %s", PATH_PROC_IFINET6);
goto exit;
}
while (fscanf(f, "%4s%4s%4s%4s%4s%4s%4s%4s %08x %02x %02x %02x %20s\n",
addr6p[0], addr6p[1], addr6p[2], addr6p[3], addr6p[4],
addr6p[5], addr6p[6], addr6p[7], &if_idx, &plen, &scope,
&dad_status, devname) != EOF) {
if (!strcmp(devname, ifname)) {
if ((scope & IPV6_ADDR_SCOPE_MASK) == 0) {
snprintf(ipaddress, ipsize, "%s:%s:%s:%s:%s:%s:%s:%s",
addr6p[0], addr6p[1], addr6p[2], addr6p[3],
addr6p[4], addr6p[5], addr6p[6], addr6p[7]);
RLOGD(getInterfaceAddr61, "%s Global inet6 addr: %s/%d", ifname, ipaddress, plen);
ret = 0;
break;
}
}
}
fclose(f);
exit:
return ret;
}
int getInterfaceAddr(int af, const char* ifname, char* ipaddress, size_t ipsize)
{
switch (af) {
case AF_INET:
return getInterfaceAddr4(ifname, ipaddress, ipsize);
case AF_INET6:
return getInterfaceAddr6(ifname, ipaddress, ipsize);
default:
errno = EAFNOSUPPORT;
return (-1);
}
}
int isInterfaceUp(const char* ifname)
{
int sock = socket(AF_INET, SOCK_DGRAM, 0);
if (sock < 0) {
RLOGE(isInterfaceUp, "%s: Couldn't create IP socket: %s", __FUNCTION__, strerror(errno));
return 0;
}
struct ifreq ifr;
memset (&ifr, 0, sizeof (ifr));
strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
ioctl(sock, SIOCGIFFLAGS, &ifr);
close(sock);
return ifr.ifr_flags & IFF_UP;
}