blob: 96701e99b5ab0baa3933457f2ed246a6705ccef4 [file] [log] [blame]
/*************************************************************
Description:
C file for network control.
Author:
LiuBin
Date:
2019/7/24 17:13:06
*************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/un.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <linux/route.h>
#include <cutils/properties.h>
#include <telephony/ril.h>
#include "mbtk_type.h"
#include "mbtk_net_control.h"
#include "mbtk_task.h"
#include "mbtk_utils.h"
#include "mbtk_str.h"
#ifdef LOG_TAG
#undef LOG_TAG
#endif
#define LOG_TAG "mbtk_net_control"
#include "mbtk_log.h"
/*************************************************************
Constants and Macros
*************************************************************/
#define NET_CONTROL_BUF_SIZE 1024
#ifndef INFTIM
#define INFTIM (-1) /* infinite poll timeout */
#endif
#define MBTK_NET_PING_HOST "223.5.5.5"
#define MBTK_NET_PING_IP "180.97.33.107" // IP for www.baidu.com
// #define MBTK_NET_MONITOR_SUPPORT
/*************************************************************
Variables:local
*************************************************************/
static char net_interface[20];
static char net_ip[20];
static bool net_if_inited = FALSE;
static mbtk_net_state_t net_state = MBTK_NET_STATE_OFF;
static bool net_control_thread_running = FALSE;
#ifdef MBTK_NET_MONITOR_SUPPORT
static pthread_t net_control_thread_id;
static int net_control_fd = -1;
static char net_if_name[100] = {0};
#endif
/*************************************************************
Variables:public
*************************************************************/
/*************************************************************
Local Function Declaration
*************************************************************/
static mbtk_net_state_callback_func net_state_cb = NULL;
/*************************************************************
Local Function Definitions
*************************************************************/
// Test network connected?
// ping www.baidu.com
static bool net_connected(const char *inf)
{
char cmd[100];
char cmd_rsp[100];
// IP get now, ping www.baidu.com
memset(cmd,0,100);
snprintf(cmd,100,
"ping -I %s -c1 -s0 -w1000 %s | grep \"8 bytes from \"",
inf,
MBTK_NET_PING_HOST);
if(!mbtk_cmd_line(cmd,cmd_rsp,100))
{
LOGE("ping www.baidu.com cmd error.");
return FALSE;
}
LOGI("cmd_rsp:%s",cmd_rsp);
// ping www.baidu.com success.
if(str_startwith(cmd_rsp, "8 bytes from "))
{
return TRUE;
}
#if 0
else if(str_contains(cmd_rsp, "unknown host"))
{
// DNS error,ping IP angin.
memset(cmd,0,100);
snprintf(cmd,100,
"ping -I %s -c1 -s0 -w1000 %s | grep \"8 bytes from \"",
inf,
MBTK_NET_PING_IP);
if(!mbtk_cmd_line(cmd,cmd_rsp,100))
{
LOGW("ping www.baidu.com IP cmd error.");
return FALSE;
}
if(str_startwith(cmd_rsp, "8 bytes from "))
{
return TRUE;
}
else
{
LOGW("Network unconnected.(ping baidu IP fail)");
return FALSE;
}
}
#endif
else
{
LOGW("Network unconnected.(ping baidu host fail)");
return FALSE;
}
LOGW("ifconfig cmd fail.");
return FALSE;
}
#ifdef MBTK_NET_MONITOR_SUPPORT
static int net_control_netlink_init()
{
struct sockaddr_nl sa;
int len = 2048;
net_control_fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
if(net_control_fd < 0)
{
LOGE("socket() fail.[%d]",errno);
return -1;
}
if(setsockopt(net_control_fd,
SOL_SOCKET, SO_RCVBUF, &len, sizeof(len)) < 0)
{
LOGE("setsockopt() fail.[%d]",errno);
return -1;
}
bzero(&sa, sizeof(sa));
sa.nl_family = AF_NETLINK;
sa.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR | RTMGRP_IPV4_ROUTE /*| RTMGRP_IPV6_IFADDR | RTMGRP_IPV6_ROUTE*/;
if(bind(net_control_fd,
(struct sockaddr *) &sa, sizeof(sa)) < 0)
{
LOGE("bind() fail.[%d]",errno);
return -1;
}
return 0;
}
static void parse_rtattr(struct rtattr **tb, int max, struct rtattr *attr, int len)
{
for ( ; RTA_OK(attr, len); attr = RTA_NEXT(attr, len)) {
if (attr->rta_type <= max) {
tb[attr->rta_type] = attr;
}
}
}
static void net_control_if_change(struct nlmsghdr *nh)
{
int msg_len;
struct rtattr *tb[IFLA_MAX + 1];
struct ifinfomsg *ifinfo;
bzero(tb, sizeof(tb));
if(nh == NULL)
{
LOGE("mbtk_net_if_change() nh == NULL");
return;
}
ifinfo = NLMSG_DATA(nh);
if(ifinfo == NULL)
{
LOGE("mbtk_net_if_change() ifinfo == NULL");
return;
}
msg_len = nh->nlmsg_len - NLMSG_SPACE(sizeof(*ifinfo));
parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifinfo), msg_len); /* 8 */
LOGD("Interface changed:if_index=%d,if_name=%s,type=%s,state=%s",
ifinfo->ifi_index, (tb[IFLA_IFNAME] ? RTA_DATA(tb[IFLA_IFNAME]) : " "),
(nh->nlmsg_type == RTM_NEWLINK) ? "NEWLINK" : "DELLINK",
(ifinfo->ifi_flags & IFF_UP) ? "up" : "down");
if(net_state_cb) {
mbtk_net_if_change_info_t if_info;
memset(&if_info, 0x0, sizeof(mbtk_net_if_change_info_t));
if_info.if_index = ifinfo->ifi_index;
if(tb[IFLA_IFNAME]) {
memcpy(if_info.if_name, RTA_DATA(tb[IFLA_IFNAME]), strlen(RTA_DATA(tb[IFLA_IFNAME])));
}
if_info.type = (nh->nlmsg_type == RTM_NEWLINK) ? MBTK_NET_IF_CHANGE_TYPE_ADD : MBTK_NET_IF_CHANGE_TYPE_DEL;
if_info.state = (ifinfo->ifi_flags & IFF_UP) ? MBTK_NET_IF_CHANGE_STATE_UP : MBTK_NET_IF_CHANGE_STATE_DOWN;
if(str_empty(net_if_name)) { // No set if name, process all interface change.
net_state_cb(MBTK_NET_CHANGE_IF, &if_info);
} else {
// Only monitor specific interface.
if(strcmp(net_if_name, if_info.if_name) == 0) {
net_state_cb(MBTK_NET_CHANGE_IF, &if_info);
}
}
}
}
static void net_control_addr_change(struct nlmsghdr *nlh)
{
int len;
struct rtattr *tb[IFA_MAX + 1];
struct ifaddrmsg *ifaddr;
bzero(tb, sizeof(tb));
ifaddr = NLMSG_DATA(nlh);
len =nlh->nlmsg_len - NLMSG_SPACE(sizeof(*ifaddr));
parse_rtattr(tb, IFA_MAX, IFA_RTA (ifaddr), len);
if (tb[IFA_ADDRESS] != NULL) {
char tmp[256] = {0};
inet_ntop(ifaddr->ifa_family, RTA_DATA(tb[IFA_ADDRESS]), tmp, sizeof(tmp));
LOGD("Address changed:index=%d,type=%s,if_name=%s,addr=%s", ifaddr->ifa_index,
(nlh->nlmsg_type == RTM_NEWADDR) ? "NEWADDR":"DELADDR",
tb[IFA_LABEL] ? RTA_DATA(tb[IFA_LABEL]) : "Unknown", tmp);
if(net_state_cb) {
mbtk_net_addr_change_info_t addr_info;
memset(&addr_info, 0x0, sizeof(mbtk_net_addr_change_info_t));
addr_info.if_index = ifaddr->ifa_index;
addr_info.type = (nlh->nlmsg_type == RTM_NEWADDR) ? MBTK_NET_ADDR_CHANGE_TYPE_ADD : MBTK_NET_ADDR_CHANGE_TYPE_DEL;
if(tb[IFA_LABEL] != NULL) {
memcpy(addr_info.if_name, RTA_DATA(tb[IFA_LABEL]), strlen(RTA_DATA(tb[IFA_LABEL])));
}
if (strlen(tmp) > 0) {
memcpy(addr_info.addr, tmp, strlen(tmp));
}
if(str_empty(net_if_name)) { // No set if name, process all address change.
net_state_cb(MBTK_NET_CHANGE_ADDR, &addr_info);
} else {
// Only monitor specific address.
if(strcmp(net_if_name, addr_info.if_name) == 0) {
net_state_cb(MBTK_NET_CHANGE_ADDR, &addr_info);
}
}
}
} else {
LOGD("Address changed:type=%s,if_name=%s,addr=%s",
(nlh->nlmsg_type == RTM_NEWADDR) ? "NEWADDR":"DELADDR",
tb[IFA_LABEL] ? RTA_DATA(tb[IFA_LABEL]) : "Unknown", "Unknown");
if(net_state_cb) {
mbtk_net_addr_change_info_t addr_info;
memset(&addr_info, 0x0, sizeof(mbtk_net_addr_change_info_t));
addr_info.if_index = ifaddr->ifa_index;
addr_info.type = (nlh->nlmsg_type == RTM_NEWADDR) ? MBTK_NET_ADDR_CHANGE_TYPE_ADD : MBTK_NET_ADDR_CHANGE_TYPE_DEL;
if(tb[IFA_LABEL] != NULL) {
memcpy(addr_info.if_name, RTA_DATA(tb[IFA_LABEL]), strlen(RTA_DATA(tb[IFA_LABEL])));
}
if(str_empty(net_if_name)) { // No set if name, process all address change.
net_state_cb(MBTK_NET_CHANGE_ADDR, &addr_info);
} else {
// Only monitor specific address.
if(strcmp(net_if_name, addr_info.if_name) == 0) {
net_state_cb(MBTK_NET_CHANGE_ADDR, &addr_info);
}
}
}
}
}
static void* net_control_monitor_run(void *arg)
{
LOGI("net_control_monitor_run start.");
if(net_control_netlink_init() < 0)
{
LOGE("mbtk_net_monitor_run() fail.");
return ((void*)0);
}
fd_set rd_set;
struct timeval timeout;
int select_r;
int read_r;
// struct sockaddr_nl sa;
struct nlmsghdr *nh;
char buff[NET_CONTROL_BUF_SIZE];
while (net_control_thread_running)
{
FD_ZERO(&rd_set);
FD_SET(net_control_fd, &rd_set);
timeout.tv_sec = 5;
timeout.tv_usec = 0;
select_r = select(net_control_fd + 1, &rd_set, NULL, NULL, &timeout);
if (select_r < 0)
{
perror("select");
}
else if (select_r > 0)
{
if (FD_ISSET(net_control_fd, &rd_set))
{
read_r = read(net_control_fd, buff, NET_CONTROL_BUF_SIZE);
LOGI("Net change:read len:%d",read_r);
#if 0
int i;
for(i = 0; i < 32 && i < read_r; i++)
LOGI("data:%x",buff[i]);
#endif
for (nh = (struct nlmsghdr *) buff; NLMSG_OK(nh, read_r); nh = NLMSG_NEXT(nh, read_r))
{
LOGI("msg_type:%d",nh->nlmsg_type);
switch (nh->nlmsg_type)
{
default:
LOGI("nh->nlmsg_type = %d\n", nh->nlmsg_type);
break;
case NLMSG_DONE:
case NLMSG_ERROR:
break;
case RTM_NEWLINK:
case RTM_DELLINK:
net_control_if_change(nh);
break;
case RTM_NEWADDR:
case RTM_DELADDR:
net_control_addr_change(nh);
break;
case RTM_NEWROUTE:
case RTM_DELROUTE:
//print_rtmsg(nh);
break;
}
}
}
}
}
LOGD("mbtk_net_monitor_run exist ...");
return ((void*)0);
}
static mbtk_task_info net_control_thread =
{
.task_id = &net_control_thread_id,
.thread_run = net_control_monitor_run,
.args = NULL
};
#endif
static int net_control_interface_init()
{
// seth_ltex
int i = 0;
int size = 0;
char result[NET_CONTROL_BUF_SIZE];
char cmd[100];
int index = 0;
while(i <= 7)
{
size = snprintf(cmd, 100,"ifconfig ccinet%d", i);
cmd[size] = '\0';
memset(result,0x0,NET_CONTROL_BUF_SIZE);
if(mbtk_cmd_line(cmd, result, NET_CONTROL_BUF_SIZE))
{
index = str_indexof(result,"inet addr:");
if(index > 0)
{
size = snprintf(net_interface, 20,"ccinet%d", i);
net_interface[size] = '\0';
memcpy(net_ip,result + index + 10,20);
char *ptr = net_ip;
while(*ptr && *ptr != ' ')
{
ptr++;
}
*ptr = '\0';
break;
}
}
i++;
}
LOGI("Interface : %s, IP : %s",net_interface,net_ip);
if(index )
{
return 0;
}
else{
return -1;
}
}
static int net_control_init()
{
if(net_if_inited)
{
LOGD("Network control has inited.");
return 0;
}
memset(net_ip,0x0,20);
memset(net_interface,0x0,20);
if(net_control_interface_init())
return -1;
net_if_inited = TRUE;
LOGI("net_control_init() success.");
return 0;
}
static int net_control_state_change(bool enable)
{
int size;
char result[NET_CONTROL_BUF_SIZE];
char cmd[100];
if(enable)
{
// ifconfig seth_lte1 up
// ip route add default via 10.94.251.205 dev seth_lte1
size = snprintf(cmd,100,"ifconfig %s up",net_interface);
cmd[size] = '\0';
if(mbtk_cmd_line(cmd, result, NET_CONTROL_BUF_SIZE))
{
size = snprintf(cmd,100,"ip route add default via %s dev %s",net_ip,net_interface);
cmd[size] = '\0';
if(mbtk_cmd_line(cmd, result, NET_CONTROL_BUF_SIZE))
{
net_state = MBTK_NET_STATE_CONN;
return 0;
}
}
}
else
{
// ifconfig seth_lte1 down
size = snprintf(cmd,100,"ifconfig %s down",net_interface);
cmd[size] = '\0';
if(mbtk_cmd_line(cmd, result, NET_CONTROL_BUF_SIZE))
{
net_state = MBTK_NET_STATE_OFF;
return 0;
}
}
return -1;
}
/*************************************************************
Public Function Definitions
*************************************************************/
/*=============================================
FUNCTION
mbtk_net_state_get()
DESCRIPTION
Get network state.
DEPENDENCIES
None
PARAMETERS
None
RETURN VALUE
Current network state.
SIDE EFFECTS
None
=============================================*/
mbtk_net_state_t mbtk_net_state_get()
{
net_control_init();
net_state = MBTK_NET_STATE_OFF;
if(strlen(net_ip) > 0)
{
if(net_connected(net_interface))
net_state = MBTK_NET_STATE_CONN;
}
LOGI("[GET]Net state:%d",net_state);
if(net_state == MBTK_NET_STATE_CONN)
{
char value[PROPERTY_VALUE_MAX] = {0};
if (property_get("persist.mbtk.netstate", value, "0,0") > 0 && strcmp(value,"0,0")) {
int regStatus = 0, gprsState = 0;
char *ptr = value;
regStatus = atoi(ptr);
if((ptr = strstr(ptr, ",")))
{
gprsState = atoi(ptr + 1);
}
LOGI("regStatus : %d, gprsState : %d", regStatus, gprsState);
if(regStatus != 1 && regStatus != 5) // Not Home/Roaming Network.
{
net_state = MBTK_NET_STATE_CONN_UNKNOWN;
}
else
{
if (gprsState == RADIO_TECH_LTE || gprsState == RADIO_TECH_LTEP)
{
net_state = MBTK_NET_STATE_CONN_4G;
}
else if ((gprsState == RADIO_TECH_GPRS) || (gprsState == RADIO_TECH_EDGE) || (gprsState == RADIO_TECH_GSM))
{
net_state = MBTK_NET_STATE_CONN_2G;
} else if((gprsState == RADIO_TECH_UMTS) || (gprsState == RADIO_TECH_HSDPA)
|| (gprsState == RADIO_TECH_HSUPA) || (gprsState == RADIO_TECH_HSPA)) {
net_state = MBTK_NET_STATE_CONN_3G;
} else {
net_state = MBTK_NET_STATE_CONN_UNKNOWN;
}
}
}
else
{
LOGE("property_get persist.mbtk.netstate fail.");
net_state = MBTK_NET_STATE_CONN_UNKNOWN;
goto end;
}
}
end:
return net_state;
}
/*=============================================
FUNCTION
mbtk_net_enable()
DESCRIPTION
Set network state.
DEPENDENCIES
None
PARAMETERS
enable
TRUE : Enable network.
FALSE: Diable network.
RETURN VALUE
0 : Success
-1: Fail
SIDE EFFECTS
None
=============================================*/
int mbtk_net_enable(bool enable)
{
if( net_control_init())
return -1;
int result = net_control_state_change(enable);
LOGI("[SET]Net state:%d",net_state);
return result;
}
int mbtk_net_monitor_reg(const char* if_name, mbtk_net_state_callback_func state_cb)
{
if( net_control_init())
return -1;
#ifdef MBTK_NET_MONITOR_SUPPORT
net_control_thread_running = TRUE;
if(mbtk_task_start(&net_control_thread))
{
LOGE("Create thread fail.");
net_control_thread_running = FALSE;
return -1;
}
#endif
net_state_cb = state_cb;
if(str_empty(if_name)) {
memset(net_if_name, 0x0, sizeof(net_if_name));
} else {
memset(net_if_name, 0x0, sizeof(net_if_name));
memcpy(net_if_name, if_name, strlen(if_name));
}
return 0;
}