blob: b8777cf395a9a4de753d6c061fb4a5e9d0e003d6 [file] [log] [blame]
/******************************************************************************
*(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);
}
}