| /****************************************************************************** |
| * *(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; |
| } |
| |