| |
| /****************************************************************************** |
| *(C) Copyright 2015 Marvell International Ltd. |
| * All Rights Reserved |
| ******************************************************************************/ |
| |
| #include <libubox/list.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <include/log.h> |
| #include <cm.h> |
| #include "chl.h" |
| #include "chl_main.h" |
| #include "chl_db.h" |
| #include "chl_ril.h" |
| #include "chl_util.h" |
| #include "ril.h" |
| |
| #define MAX_CID 16 |
| |
| struct chl_db { |
| struct chl_pdp *cid_map[MAX_CID]; |
| struct list_head pdp_list; |
| int clid; |
| int pdpid; |
| }; |
| |
| static struct chl_db _chl_db = { |
| .clid = 1, |
| .pdpid = 1, |
| .pdp_list = LIST_HEAD_INIT(_chl_db.pdp_list), |
| .cid_map = {0}, |
| }; |
| |
| static struct chl_db *chl_db_get() |
| { |
| return &_chl_db; |
| } |
| |
| char *ipv6_state_to_str(int state) |
| { |
| switch(state) { |
| case CHL_IPV6_IDLE: |
| return "CHL_IPV6_IDLE"; |
| case CHL_IPV6_SEARCHING: |
| return "CHL_IPV6_SEARCHING"; |
| case CHL_IPV6_FOUND: |
| return "CHL_IPV6_FOUND"; |
| default: |
| return "Unknown"; |
| } |
| } |
| |
| struct list_head *chl_db_get_list(void) |
| { |
| struct chl_db *db = chl_db_get(); |
| return &db->pdp_list; |
| } |
| |
| struct chl_client *chl_db_add_client(struct reg_param *rp, struct chl_pdp *pdp) |
| { |
| struct chl_db *db = chl_db_get(); |
| |
| struct chl_client *cl = malloc(sizeof(*cl)); |
| if (!cl) { |
| CHL_ERR("Failed to allocated client\n"); |
| return NULL; |
| } |
| |
| memset(cl, 0, sizeof(*cl)); |
| |
| INIT_LIST_HEAD(&cl->list); |
| cl->pdp = pdp; |
| cl->req_open = false; |
| cl->id = db->clid++; |
| cl->rt_idx = 0; |
| cl->type = rp->ip_type; |
| |
| if (rp->internet) |
| cl->req_gw = true; |
| |
| pdp->client_count++; |
| |
| strncpy(cl->name, rp->name, MAX_CLIENT_STR); |
| cl->name[MAX_CLIENT_STR - 1] = '\0'; |
| |
| list_add_tail(&cl->list, &pdp->clients); |
| |
| CHL_INFO("Created client [%s] pdp [%s] idx [%d] ip type [%s]\n", |
| cl->name, cl->pdp->apn, cl->id, ip_type_to_str(cl->type)); |
| |
| return cl; |
| } |
| |
| struct chl_pdp *chl_db_add_pdp(struct reg_param *rp) |
| { |
| struct chl_db *db = chl_db_get(); |
| |
| struct chl_pdp *pdp = malloc(sizeof(*pdp)); |
| if (!pdp) { |
| CHL_ERR("Failed to allocated pdp\n"); |
| return NULL; |
| } |
| |
| memset(pdp, 0 ,sizeof(*pdp)); |
| |
| INIT_LIST_HEAD(&pdp->list); |
| INIT_LIST_HEAD(&pdp->clients); |
| |
| strcpy(pdp->apn, rp->apn); |
| pdp->id = db->pdpid++; |
| pdp->rt_idx = 0; |
| pdp->type = rp->ip_type ? rp->ip_type : IPV4; |
| pdp->open_count = 0; |
| pdp->nw_status = DATA_UNAVAILABLE; |
| pdp->ipv6_state = CHL_IPV6_IDLE; |
| pdp->untracked = false; |
| |
| if (rp->ext) |
| memcpy(&pdp->extra, rp->ext, sizeof(struct extra_param)); |
| |
| list_add_tail(&pdp->list, &db->pdp_list); |
| |
| CHL_INFO("Created pdp, apn [%s] ip type [%s]\n", rp->apn, |
| ip_type_to_str(pdp->type)); |
| |
| return pdp; |
| } |
| |
| unsigned int pdp_match_ext_param(struct reg_param *rp, struct chl_pdp *pdp) |
| { |
| if (((rp->ext->flags & EXTP_RADIO) && (pdp->extra.flags & EXTP_RADIO)) && |
| (rp->ext->radio_tech != pdp->extra.radio_tech)) |
| return CONFLICT_RADIO_TECH; |
| |
| if (((rp->ext->flags & EXTP_AUTH) && (pdp->extra.flags & EXTP_AUTH)) && |
| (rp->ext->auth_type != pdp->extra.auth_type)) |
| return CONFLICT_AUTH; |
| |
| if (((rp->ext->flags & EXTP_PROF) && (pdp->extra.flags & EXTP_PROF)) && |
| strcmp(rp->ext->data_profile, pdp->extra.data_profile)) |
| return CONFLICT_DATA_PROFILE; |
| |
| if (((rp->ext->flags & EXTP_UNAME) && (pdp->extra.flags & EXTP_UNAME)) && |
| strcmp(rp->ext->username, pdp->extra.username)) |
| return CONFLICT_AUTH; |
| |
| if (((rp->ext->flags & EXTP_PWD) && (pdp->extra.flags & EXTP_PWD)) && |
| strcmp(rp->ext->password, pdp->extra.password)) |
| return CONFLICT_AUTH; |
| |
| return CHL_REG_OK; |
| } |
| |
| static struct chl_client *__get_client_by_id(struct chl_pdp *pdp, int id) |
| { |
| |
| struct chl_client *cl; |
| |
| list_for_each_entry(cl, &pdp->clients, list) { |
| if (cl->id == id) |
| return cl; |
| } |
| |
| return NULL; |
| } |
| |
| struct chl_pdp *chl_db_get_autoconf_pdp(struct chl_pdp *except) |
| { |
| struct chl_pdp *pdp = NULL; |
| struct chl_db *db = chl_db_get(); |
| |
| list_for_each_entry(pdp, &db->pdp_list, list) { |
| if (pdp->autoconf_pdp && pdp != except) |
| return pdp; |
| } |
| |
| return NULL; |
| } |
| |
| struct chl_client *chl_db_get_client_by_id(int id) |
| { |
| struct chl_client *cl; |
| struct chl_pdp *pdp = NULL; |
| struct chl_db *db = chl_db_get(); |
| |
| list_for_each_entry(pdp, &db->pdp_list, list) { |
| cl = __get_client_by_id(pdp, id); |
| if (cl) |
| return cl; |
| } |
| |
| return NULL; |
| } |
| |
| struct chl_pdp *chl_db_get_pdp_by_id(int id) |
| { |
| struct chl_client *cl = chl_db_get_client_by_id(id); |
| if (cl) |
| return cl->pdp; |
| return NULL; |
| } |
| |
| struct chl_pdp *chl_db_get_pdp_by_apn(char *apn) |
| { |
| struct chl_db *db = chl_db_get(); |
| struct chl_pdp *pdp = NULL; |
| |
| list_for_each_entry(pdp, &db->pdp_list, list) { |
| if(str_starts_with(pdp->apn, apn)) |
| return pdp; |
| } |
| |
| return NULL; |
| } |
| |
| struct chl_pdp *chl_db_get_pdp_by_cid(int cid) |
| { |
| struct chl_db *db = chl_db_get(); |
| return db->cid_map[cid]; |
| } |
| |
| bool chl_db_pdp_has_clients(struct chl_pdp *pdp) |
| { |
| return !list_empty(&pdp->clients); |
| } |
| |
| void chl_db_del_pdp(struct chl_pdp *pdp) |
| { |
| struct chl_db *db = chl_db_get(); |
| |
| if (pdp->client_count) { |
| CHL_ERR("Deleting a pdp with clients! [%s]\n", pdp->apn); |
| return; |
| } |
| |
| CHL_INFO("Deleted pdp, apn [%s] type [%s]\n", pdp->apn, |
| ip_type_to_str(pdp->type)); |
| |
| if (pdp->cid) |
| db->cid_map[pdp->cid] = NULL; |
| |
| list_del(&pdp->list); |
| free(pdp); |
| } |
| |
| void chl_db_del_client(struct chl_client *cl) |
| { |
| struct chl_pdp *pdp = cl->pdp; |
| CHL_INFO("Deleting client [%s], idx [%d]\n", cl->name, cl->id); |
| |
| if (!pdp) |
| goto done; |
| |
| pdp->client_count--; |
| |
| if (cl->req_open) |
| pdp->open_count--; |
| done: |
| list_del(&cl->list); |
| free(cl); |
| } |
| |
| void chl_db_clear_pdp(struct chl_pdp *pdp, int nw_status) |
| { |
| struct chl_db *db = chl_db_get(); |
| |
| CHL_INFO("Clearing pdp [%s], nw status [%s]\n", pdp->apn, |
| nw_status_to_str(nw_status)); |
| |
| memset(pdp->ipv4, 0, MAX_IPV4_STR); |
| memset(pdp->dns4p, 0, MAX_IPV4_STR); |
| memset(pdp->dns4s, 0, MAX_IPV4_STR); |
| memset(pdp->netmask, 0, MAX_IPV4_STR); |
| |
| |
| memset(pdp->ipv6, 0, MAX_IPV6_STR); |
| memset(pdp->dns6p, 0, MAX_IPV6_STR); |
| memset(pdp->dns6s, 0, MAX_IPV6_STR); |
| memset(pdp->gw, 0, MAX_IPV6_STR); |
| memset(pdp->gb6addr, 0, MAX_IPV6_STR); |
| pdp->prefix_len = 0; |
| pdp->dns = 0; |
| |
| if(pdp->cid) |
| db->cid_map[pdp->cid] = NULL; |
| pdp->cid = 0; |
| |
| pdp->nw_status = nw_status; |
| } |
| |
| void chl_db_take_over_pdp(struct chl_pdp *pdp) |
| { |
| CHL_INFO("Taking over pdp [%s]]\n", pdp->apn); |
| pdp->untracked = false; |
| pdp->nw_status = DATA_UNAVAILABLE; |
| } |
| |
| void chl_db_update_pdp(Ubus_Data_Call_Response *rsp, struct chl_pdp *pdp, |
| enum chl_nw_status nw_status) |
| { |
| char *token; |
| bool gw_set = false; |
| struct chl_db *db = chl_db_get(); |
| |
| CHL_INFO("Updating pdp, apn [%s], nw stats [%s]\n", pdp->apn, |
| nw_status_to_str(nw_status)); |
| |
| pdp->nw_status = nw_status; |
| pdp->last_ril_status = rsp->status; |
| pdp->cid = rsp->cid; |
| |
| strcpy(pdp->apn, rsp->apn); |
| |
| pdp->type = chl_ril_type_to_int(rsp->type); |
| |
| token = strtok(rsp->addresses, " "); |
| |
| while (token) { |
| if (!validate_ip(token, AF_INET)) |
| strcpy(pdp->ipv4, token); |
| if (!validate_ip(token, AF_INET6)) |
| strcpy(pdp->ipv6, token); |
| |
| token = strtok(NULL, " "); |
| } |
| |
| token = strtok(rsp->dnses, " "); |
| |
| while (token) { |
| if (!validate_ip(token, AF_INET)) { |
| if(!(pdp->dns & CHL_PDP_DNS4P)) { |
| strcpy(pdp->dns4p, token); |
| pdp->dns |= CHL_PDP_DNS4P; |
| } else if(!(pdp->dns & CHL_PDP_DNS4S)) { |
| strcpy(pdp->dns4s, token); |
| pdp->dns |= CHL_PDP_DNS4S; |
| } |
| } else if (!validate_ip(token, AF_INET6)) { |
| if(!(pdp->dns & CHL_PDP_DNS6P)) { |
| strcpy(pdp->dns6p, token); |
| pdp->dns |= CHL_PDP_DNS6P; |
| } else if(!(pdp->dns & CHL_PDP_DNS6S)) { |
| strcpy(pdp->dns6s, token); |
| pdp->dns |= CHL_PDP_DNS6S; |
| } |
| } |
| |
| token = strtok(NULL, " "); |
| } |
| |
| token = strtok(rsp->gateways, " "); |
| |
| while (token) { |
| if (!validate_ip(token, AF_INET)) { |
| strcpy(pdp->gw, token); |
| gw_set = true; |
| } |
| |
| token = strtok(NULL, " "); |
| } |
| |
| if (!gw_set) |
| strcpy(pdp->gw, pdp->ipv4); |
| |
| strcpy(pdp->netmask, CHL_DFLT_MASK); |
| |
| if (db->cid_map[pdp->cid] && db->cid_map[pdp->cid] != pdp) |
| CHL_ERR("Overriding cid! [%d], [%s] -> [%s]\n", pdp->cid, |
| db->cid_map[pdp->cid]->apn, pdp->apn); |
| db->cid_map[pdp->cid] = pdp; |
| } |
| |
| static void chl_db_dump_client(struct chl_client *cl) |
| { |
| CHL_INFO("\tname:[%s] id:[%d] req_open:[%d] req_gw:[%d] ip_type:[%s]\n", |
| cl->name, cl->id, cl->req_open, cl->req_gw, |
| ip_type_to_str(cl->type)); |
| |
| } |
| |
| static void chl_db_dump_pdp(struct chl_pdp *pdp) |
| { |
| struct chl_client *cl; |
| |
| CHL_INFO("cid:[%d] nw_status:[%s] APN:[%s] IP TYPE:[%s] untracked:[%d] autoconf:[%d]\n", |
| pdp->cid, |
| nw_status_to_str(pdp->nw_status), |
| pdp->apn, |
| ip_type_to_str(pdp->type), |
| pdp->untracked, |
| pdp->autoconf_pdp); |
| CHL_INFO("client_count:[%d] open_count:[%d] req_gw:[%d] pdp_id [%d]\n", |
| pdp->client_count, |
| pdp->open_count, |
| pdp->req_gw, |
| pdp->id); |
| CHL_INFO("ipv4: [%s] dns4p [%s], dns4s [%s] gw [%s] netmask [%s]\n", |
| pdp->ipv4, pdp->dns4p, pdp->dns4s, pdp->gw, pdp->netmask); |
| CHL_INFO("ipv6: [%s] dns6p [%s], dns6s [%s] gb_addr[%s] gb_pref[%d] ipv6_state[%s]\n", |
| pdp->ipv6, pdp->dns6p, pdp->dns6s, pdp->gb6addr, pdp->prefix_len, |
| ipv6_state_to_str(pdp->ipv6_state)); |
| |
| CHL_INFO("CLIENTS:\n"); |
| list_for_each_entry(cl, &pdp->clients, list) |
| chl_db_dump_client(cl); |
| |
| CHL_INFO("\n"); |
| } |
| |
| void chl_db_status() |
| { |
| struct chl_pdp *pdp; |
| struct chl_conf *conf = chl_get_conf(); |
| struct chl_db *db = chl_db_get(); |
| |
| CHL_INFO("==========CHL INFO==========\n"); |
| CHL_INFO("autoconf: [%d], internet pdp: [%s]\n", |
| conf->autoconf, conf->internet ? conf->internet->apn : "None"); |
| |
| CHL_INFO("========CHL PDP LIST========\n"); |
| list_for_each_entry(pdp, &db->pdp_list, list) { |
| if (!pdp->untracked) |
| chl_db_dump_pdp(pdp); |
| } |
| |
| CHL_INFO("=======UNTRACKED LIST=======\n"); |
| list_for_each_entry(pdp, &db->pdp_list, list) { |
| if (pdp->untracked) |
| chl_db_dump_pdp(pdp); |
| } |
| } |