blob: a69b7442fb30dc272cd0f4bd8b8194a9c4e1cec8 [file] [log] [blame]
/******************************************************************************
*(C) Copyright 2014 Marvell International Ltd.
* All Rights Reserved
******************************************************************************/
/* -------------------------------------------------------------------------------------------------------------------
*
* Filename: chl_agent.c
*
* Authors: Hagai zalach
*
* Description: An agent to the CHL application
*
* HISTORY:
*
* AUG 13, 2015 - Initial Version
*
* Notes:
*
******************************************************************************/
/******************************************************************************
* Include files
******************************************************************************/
#include <errno.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <include/log.h>
#include <uci.h>
#include <chl.h>
#include "ml_utils.h"
#include "chl_agent.h"
/******************************************************************************
* Defines
******************************************************************************/
/******************************************************************************
* Structs
******************************************************************************/
enum AGT_LOG_LEVEL log_level = AGT_LOG_LEVEL_INFO;
static struct agent_conf curr_conf;
FILE *fd_console;
/******************************************************************************
* Methods
******************************************************************************/
int is_wifi_ld()
{
int ret;
FILE *fd = popen("lsmod | grep sd", "r");
char buf[16] = {0};
if ( (ret = fread (buf, 1, sizeof (buf), fd) > 0) )
{
/* if there is some result the wifi module must be loaded*/
AGT_DBG("%s\n",buf);
pclose(fd);
return 1;
}
else
{
AGT_DBG("%d\n",ret);
pclose(fd);
return 0;
}
}
int is_data_enabled()
{
int ret;
FILE *fd = popen("serial_atcmd at*donof? | grep 1", "r");
char buf[16] = {0};
if ( (ret = fread (buf, 1, sizeof (buf), fd) > 0) )
{
/* if there is some result data is enabled*/
AGT_DBG("%s\n",buf);
pclose(fd);
return 1;
}
else
{
AGT_DBG("%d\n",ret);
pclose(fd);
return 0;
}
}
void print_console(enum AGT_LOG_LEVEL level, const char *fmt, ...)
{
if (level > log_level)
return;
va_list args;
va_start(args, fmt);
vfprintf(fd_console, fmt, args);
va_end(args);
}
__attribute__((unused)) void print_configuration( struct agent_conf *conf)
{
int i;
AGT_DBG("\n\n\n\n\nglobal configuration\n");
AGT_DBG("agent_active=%d\n",conf->global_conf.agent_active);
AGT_DBG("data_enable=%d\n",conf->global_conf.data_enable);
AGT_DBG("downlink_only=%d\n",conf->global_conf.downlink_only);
AGT_DBG("host_ip=%s\n",conf->global_conf.host_ip);
for (i=0;i<MAX_ROUTES;i++) {
AGT_DBG("\n\n\npdp_%d\n",i);
AGT_DBG("pdp_enable=%d\n",conf->agent_pdps[i].pdp_enable);
AGT_DBG("apn=%s\n",conf->agent_pdps[i].apn);
AGT_DBG("ip_type=%d\n",conf->agent_pdps[i].ip_type);
AGT_DBG("is_lte_default=%d\n",conf->agent_pdps[i].is_lte_default);
AGT_DBG("is_default_gw=%d\n",conf->agent_pdps[i].is_default_gw);
}
}
void add_del_portforwarding( bool add , bool old_state, char *host_ip )
{
char cmd[CMD_LEN] = {0};
if ( (add != old_state) && (host_ip != NULL) ) {
AGT_DBG("%s port forwarding\n", add ? "enable" : "disable");
sprintf(cmd, "port_forwarding.sh %s %s %d %d",add ? "A" : "D", host_ip, 5000, 6000);
AGT_DBG("%s\n", cmd);
system(cmd);
}
}
void checkroute_pdps(struct agent_conf *new_conf)
{
int i;
for (i=0;i<MAX_PDPS;i++) {
if (curr_conf.agent_pdps[i].action == CHECK_ROUTE) {
new_conf->agent_pdps[i].pdp_state = curr_conf.agent_pdps[i].pdp_state;
new_conf->agent_pdps[i].id = curr_conf.agent_pdps[i].id;
update_routes( &new_conf->agent_pdps[i],
&curr_conf.agent_pdps[i] );
}
}
}
void copy_pdps(struct agent_conf *new_conf)
{
int i;
for (i=0;i<MAX_PDPS;i++) {
if (curr_conf.agent_pdps[i].action == DO_NOTHING) {
new_conf->agent_pdps[i].pdp_state = curr_conf.agent_pdps[i].pdp_state;
new_conf->agent_pdps[i].id = curr_conf.agent_pdps[i].id;
}
}
}
void open_pdps(struct agent_conf *new_conf)
{
int i,ret;
for (i=0;i<MAX_PDPS;i++) {
if (curr_conf.agent_pdps[i].action == OPEN_PDP ||
curr_conf.agent_pdps[i].action == CLOSE_OPEN_PDP) {
/* ubus invoke lte default+register+open+add routes */
if (new_conf->agent_pdps[i].is_lte_default){
ret = agt_set_lte_dflt(&new_conf->agent_pdps[i]);
if (ret)
continue;
}
ret = agt_register_pdp(&new_conf->agent_pdps[i],i);
if (ret)
continue;
ret = agt_open_pdp(&new_conf->agent_pdps[i]);
if (ret) {
if (new_conf->agent_pdps[i].pdp_state == DATA_UNAVAILABLE) {
agt_close_pdp(&new_conf->agent_pdps[i]);
agt_unregister_pdp(&new_conf->agent_pdps[i]);
AGT_INFO("pdp %d:%s is closed\n", i, curr_conf.agent_pdps[i].apn);
}
continue;
}
if (new_conf->agent_pdps[i].pdp_state == DATA_AVAILABLE){
AGT_INFO("pdp %d:%s is open\n", i, new_conf->agent_pdps[i].apn);
add_del_pdp_routes(&new_conf->agent_pdps[i],1);
}
}
}
}
void close_pdps()
{
int i;
for (i=0;i<MAX_PDPS;i++) {
if (curr_conf.agent_pdps[i].action == CLOSE_PDP ||
curr_conf.agent_pdps[i].action == CLOSE_OPEN_PDP) {
/* ubus invoke delete routes+close+unregister */
add_del_pdp_routes(&curr_conf.agent_pdps[i],0);
agt_close_pdp(&curr_conf.agent_pdps[i]);
agt_unregister_pdp(&curr_conf.agent_pdps[i]);
AGT_INFO("pdp %d:%s is closed\n", i, curr_conf.agent_pdps[i].apn);
}
}
}
void stop_agent()
{
int i;
for (i=0;i<MAX_PDPS;i++) {
//AGT_DBG("old pdp_%d_enable=%d\n",i,curr_conf.agent_pdps[i].pdp_enable ); //for debug
if (curr_conf.agent_pdps[i].pdp_enable == 1) {
add_del_pdp_routes(&curr_conf.agent_pdps[i],0);
agt_close_pdp(&curr_conf.agent_pdps[i]);
agt_unregister_pdp(&curr_conf.agent_pdps[i]);
}
}
}
void ind_handler(int pdp_id, enum chl_nw_status nw_status)
{
enum chl_nw_status old_state;
int i;
for (i=0;i<MAX_PDPS;i++) {
old_state = curr_conf.agent_pdps[i].pdp_state;
if ( pdp_id == curr_conf.agent_pdps[i].id ){
curr_conf.agent_pdps[i].pdp_state = nw_status;
switch (nw_status) {
case DATA_AVAILABLE:
if (old_state == DATA_AVAILABLE)
return;
else
AGT_INFO("data is now avalible\n");
AGT_INFO("pdp %d: %s is now open\n",i,curr_conf.agent_pdps[i].apn);
add_del_pdp_routes( &curr_conf.agent_pdps[i], 1);
break;
case DATA_PENDING:
AGT_INFO("data is pending\n");
if (old_state == DATA_UNAVAILABLE || old_state == DATA_PENDING ){
AGT_INFO("pdp %d: %s is now closed\n",i,curr_conf.agent_pdps[i].apn);
return;
} else if (old_state == DATA_AVAILABLE){
AGT_INFO("pdp %d: %s is now closed\n",i,curr_conf.agent_pdps[i].apn);
add_del_pdp_routes( &curr_conf.agent_pdps[i], 0);
}
case DATA_UNAVAILABLE:
AGT_INFO("data is unavalible\n");
if (old_state == DATA_UNAVAILABLE || old_state == DATA_PENDING ){
AGT_INFO("pdp %d: %s is closed\n",i,curr_conf.agent_pdps[i].apn);
return;
} else if (old_state == DATA_AVAILABLE){
AGT_INFO("pdp %d: %s is now closed\n",i,curr_conf.agent_pdps[i].apn);
add_del_pdp_routes( &curr_conf.agent_pdps[i], 0);
}
break;
default:
break;
}
}
else
continue;
}
return;
}
void add_del_pdp_routes( struct agent_pdp *pdp, bool add)
{
int i;
for (i=0;i<MAX_ROUTES;i++) {
if ( strcmp(pdp->route_dst[i],"") ) {
AGT_DBG("%s route: %s\n", add ? "add":"del", pdp->route_dst[i]);
if (add)
agt_add_route( pdp->route_dst[i], pdp->id );
else
agt_del_route( pdp->route_dst[i], pdp->id );
}
}
}
int update_configuration()
{
int i,wifi_state, data_state;
struct agent_conf new_conf;
memset(&new_conf,0,sizeof(struct agent_conf));
if (parse_ucifile(&new_conf)){
AGT_ERR("%s: parse configuration file - failed\n", __func__);
return 1;
}
/*check if chl_agent is on/off*/
if (new_conf.global_conf.agent_active == 0){
AGT_INFO("chl_agent is disabled\n");
if (curr_conf.global_conf.agent_active != 0){
AGT_INFO("close all pdp's\n");
/* close and unregister all open PDP's */
stop_agent();
}
memset(&curr_conf,0,sizeof(struct agent_conf));
return 0;
}
/*check if data is on/off*/
data_state = is_data_enabled();
if (new_conf.global_conf.data_enable !=
data_state ){
AGT_INFO("data state change - send at*donof\n");
system("serial_atcmd at*donof");
}
/*check if wifi is on/off*/
wifi_state = is_wifi_ld();
if ( wifi_state )
AGT_DBG("wifi module is loaded\n");
else
AGT_DBG("wifi module is not loaded\n");
if ( new_conf.global_conf.wifi_enable == 1 &&
wifi_state == 0)
system("./etc/init.d/mwlan start");
else if ( new_conf.global_conf.wifi_enable == 0 &&
wifi_state == 1)
system("./etc/init.d/mwlan stop");
/*check if need port forwarding*/
add_del_portforwarding(new_conf.global_conf.downlink_only,
curr_conf.global_conf.downlink_only,
(char *) &new_conf.global_conf.host_ip);
AGT_DBG("end port forwarding\n");
// print_configuration(&new_conf); for debug
for (i=0;i<MAX_PDPS;i++) {
new_conf.agent_pdps[i].action = compare_pdp( &new_conf,i);
curr_conf.agent_pdps[i].action = new_conf.agent_pdps[i].action;
AGT_DBG("pdp_%d\n",i);
AGT_DBG("action - %s\n",pdp_action_to_str(new_conf.agent_pdps[i].action));
}
close_pdps();
open_pdps(&new_conf);
checkroute_pdps(&new_conf);
copy_pdps(&new_conf);
AGT_DBG("%s: store the new configuration\n", __func__);
memcpy(&curr_conf,&new_conf,sizeof(struct agent_conf));
return 0;
}
void update_routes(struct agent_pdp *new_pdp,
struct agent_pdp *cur_pdp)
{
char *new_p, *cur_p;
int i,j;
int ret = 0;
/*add the new routes*/
for (i=0;i<MAX_ROUTES;i++) {
for (j=0;j<MAX_ROUTES;j++) {
new_p = (char *) new_pdp->route_dst[i];
cur_p = (char *) cur_pdp->route_dst[j];
ret = strcmp(new_p,cur_p);
if (ret == 0)
break;
}
// AGT_INFO("new_p=%s,cur_p=%s,ret=%d\n",new_p,cur_p,ret); for debug
if (ret != 0){
AGT_INFO("add route %s\n",new_p);
agt_add_route(new_p,new_pdp->id);
}
}
/*delete the old routes*/
for (j=0;j<MAX_ROUTES;j++) {
for (i=0;i<MAX_ROUTES;i++) {
new_p = (char *) new_pdp->route_dst[i];
cur_p = (char *) cur_pdp->route_dst[j];
ret = strcmp(new_p,cur_p);
if (ret == 0)
break;
}
// AGT_DBG("new_p=%s,cur_p=%s,ret=%d\n",new_p,cur_p,ret); for debug
if ( (ret != 0) && strlen(cur_p) ){
AGT_INFO("delete route %s\n",cur_p);
agt_del_route(cur_p,new_pdp->id);
break;
}
}
}
int check_delta(struct agent_pdp *new_p,
struct agent_pdp *cur_p)
{
if (strcmp(new_p->apn,cur_p->apn) != 0)
return 1;
if (new_p->ip_type != cur_p->ip_type)
return 1;
if (new_p->is_default_gw != cur_p->is_default_gw)
return 1;
if (new_p->is_lte_default != cur_p->is_lte_default)
return 1;
return 0;
}
enum pdp_action compare_pdp(struct agent_conf *new_conf,int i)
{
struct agent_pdp *cur_p = &(curr_conf.agent_pdps[i]);
struct agent_pdp *new_p = &(new_conf->agent_pdps[i]);
// AGT_DBG("old state=%d, new_state=%d\n", cur_p->pdp_enable,new_p->pdp_enable); for debug
if (new_p->pdp_enable == 0)
{
if (cur_p->pdp_enable == 0)
return DO_NOTHING;
else if (cur_p->pdp_enable == 1)
return CLOSE_PDP;
}
else if (new_p->pdp_enable == 1){
if (cur_p->pdp_enable == 0)
return OPEN_PDP;
else if (cur_p->pdp_enable == 1){
if (check_delta(new_p,cur_p))
return CLOSE_OPEN_PDP;
if (cur_p->pdp_state == DATA_UNAVAILABLE)
return OPEN_PDP;
else if (cur_p->pdp_state == DATA_AVAILABLE)
return CHECK_ROUTE;
else if (cur_p->pdp_state == DATA_PENDING){
AGT_DBG("%s:DATA PENDING\n", __func__);
return DO_NOTHING;
}
}
}
AGT_ERR("%s: error unknown value of pdp_enable field: %d\n"
, __func__,new_p->pdp_enable);
return ERR_VAL;
}
static void usage(const char *s)
{
fprintf(stderr, "Usage: %s [-d]\n", s);
fprintf(stderr, "-h - print this help message\n");
fprintf(stderr, "-d - enable debug logs\n");
exit(1);
}
int main(int argc, char *argv[])
{
int c = 0;
int ret = 0;
fd_console = fopen("/dev/console","w");
if (fd_console == NULL)
return 0;
if (argc > 2)
usage(argv[0]);
set_service_log_level(8);
do {
c = getopt(argc, argv, "hd");
if (c == EOF)
break;
switch (c) {
case 'h':
usage(argv[0]);
case 'd':
log_level = AGT_LOG_LEVEL_DBG;
break;
}
} while (1);
set_service_log_tag("chl_agent");
AGT_INFO("starting CHL agent\n");
if ((ret = ubus_init())) {
AGT_ERR("Failed to init ubus. ret=%d\n", ret);
goto out1;
}
if (chl_agt_lookup_chl())
goto out1;
if ( (ret = add_chl_agt_obj()) ) {
AGT_ERR("Failed to add chl agent object. ret=%d\n", ret);
goto out2;
}
if ((ret = enable_chl_indications())) {
AGT_ERR("Failed to enable CHL indications. ret=%s\n",
ubus_strerror(ret));
goto out2;
}
memset(&curr_conf,0,sizeof(struct agent_conf));
curr_conf.global_conf.data_enable = 1;
if ( update_configuration() ){
AGT_ERR("%s: parse initial configuration file - failed\n", __func__);
goto out2;
}
AGT_DBG("%s:\tuloop run\n", __func__);
uloop_run();
disable_chl_indications();
goto out;
out2:
AGT_INFO("%s: exiting CHL agent due to error\n", __func__);
chl_agt_ubus_exit(2);
uloop_done();
return 1;
out1:
AGT_INFO("%s: exiting CHL agent due to error\n", __func__);
chl_agt_ubus_exit(1);
uloop_done();
return 1;
out:
chl_agt_ubus_exit(2);
uloop_done();
AGT_INFO("%s: exiting CHL agent\n", __func__);
return 0;
}