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