blob: fcc9fb2983e2ccf0d3ebd16105dd910a25a688d2 [file] [log] [blame]
/******************************************************************************
*(C) Copyright 2015 Marvell International Ltd.
* All Rights Reserved
******************************************************************************/
#include <uci.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <include/log.h>
#include "chl_db.h"
#include "chl_uci.h"
#include "chl_util.h"
struct chl_route {
char * type;
char * name;
char * target;
char * iface;
char * metric;
};
static inline void uci_set_ptr(const char *p, const char *s, const char *o,
const char *v, struct uci_ptr *ptr)
{
memset(ptr, 0, sizeof(*ptr));
ptr->package = p;
ptr->section = s;
ptr->option = o;
ptr->value = v;
}
static int uci_get_ptr(struct uci_context *c, const char *p, const char *s,
const char *o, const char *v, struct uci_ptr *ptr)
{
int rc;
uci_set_ptr(p, s, o, v, ptr);
rc = uci_lookup_ptr(c, ptr, NULL, false);
if (rc || !(ptr->flags & UCI_LOOKUP_COMPLETE))
return 1;
if (ptr->last->type != UCI_TYPE_OPTION)
return 1;
return 0;
}
static int uci_del(struct uci_context *c, const char *p, const char *s,
const char *o, const char *v)
{
struct uci_ptr ptr;
if (uci_get_ptr(c, p, s, o, NULL, &ptr))
return 1;
ptr.value = v;
return uci_delete(c, &ptr);
}
static int uci_set_opt(struct uci_context *c, const char *p, const char *s,
const char *o, const char *v)
{
struct uci_ptr ptr;
uci_set_ptr(p, s, o, v, &ptr);
return uci_set(c, &ptr);
}
static int uci_add_to_list(struct uci_context *c, const char *p, const char *s,
const char *o, const char *v)
{
struct uci_ptr ptr;
uci_set_ptr(p, s, o, v, &ptr);
return uci_add_list(c, &ptr);
}
static int uci_del_from_list(struct uci_context *c, const char *p, const char *s,
const char *o, const char *v)
{
struct uci_ptr ptr;
uci_set_ptr(p, s, o, v, &ptr);
return uci_del_list(c, &ptr);
}
static int uci_del_opt(struct uci_context *c, const char *p, const char *s,
const char *o, const char *v)
{
struct uci_ptr ptr;
uci_set_ptr(p, s, o, v, &ptr);
return uci_delete(c, &ptr);
}
static int uci_add_nw_section(struct uci_context *c, char *pname, char *type,
char *name, struct uci_section **res)
{
struct uci_package *p;
struct uci_section *s;
struct uci_ptr ptr;
memset(&ptr, 0, sizeof(struct uci_ptr));
p = uci_lookup_package(c, pname);
if (p)
goto found;
uci_load(c, pname, &p);
if (!p)
goto err;
found:
if (uci_add_section(c, p, type, &s))
goto err;
if (name) {
ptr.p = p;
ptr.s = s;
ptr.value = name;
if (uci_rename(c, &ptr))
goto err;
}
*res = s;
return 0;
err:
CHL_ERR("failed add section [%s]\n", name);
return 1;
}
static int uci_delete_section(struct uci_context *c, struct uci_package *p,
struct uci_section *s)
{
struct uci_ptr ptr;
memset(&ptr, 0, sizeof(struct uci_ptr));
ptr.p = p;
ptr.s = s;
return uci_delete(c, &ptr);
}
static int __chl_uci_del_spec_route(struct uci_context *c, char *name, char *ip)
{
struct uci_element *e, *itr;
struct uci_section *s;
struct uci_package *p = NULL;
struct uci_option *o = NULL;
p = uci_lookup_package(c, UCI_PKG_NW);
if (!p)
uci_load(c, UCI_PKG_NW, &p);
if (!p)
goto err;
uci_foreach_element_safe(&p->sections, itr, e) {
s = uci_to_section(e);
if (strcmp(s->type, UCI_TYPE_ROUTE) &&
strcmp(s->type, UCI_TYPE_ROUTE6))
continue;
if (name && !str_starts_with(s->e.name, name))
continue;
if (ip) {
o = uci_lookup_option(c, s, UCI_OPT_TARGET);
if (!o)
continue;
if (strcmp(o->v.string, ip))
continue;
}
CHL_INFO("Deleting section [%s]\n", s->e.name);
if (uci_delete_section(c, p, s))
goto err;
}
return 0;
err:
CHL_ERR("failed to remove route [%s]\n", name);
return 1;
}
int chl_uci_del_spec_route(struct uci_context *c, struct chl_client *cl,
char *ip)
{
char name[UCI_MAX_RT_NAME];
sprintf(name, "chl_%s", cl->name);
return __chl_uci_del_spec_route(c, name, ip);
}
static int chl_uci_add_route(struct uci_context *c, struct chl_route *rt)
{
struct uci_section *s;
if (uci_add_nw_section(c, UCI_PKG_NW, rt->type, rt->name, &s))
goto err;
if (uci_set_opt(c, UCI_PKG_NW, s->e.name, UCI_OPT_IFACE, rt->iface))
goto err;
if (uci_set_opt(c, UCI_PKG_NW, s->e.name, UCI_OPT_TARGET, rt->target))
goto err;
if (!rt->metric)
goto done;
if (uci_set_opt(c, UCI_PKG_NW, s->e.name, UCI_OPT_METRIC, rt->metric))
goto err;
done:
return 0;
err:
CHL_ERR("failed to add route for [%s]\n", rt->iface);
return 1;
}
static int __chl_uci_add_spec_route(struct uci_context *c, struct chl_pdp *pdp,
char *name, char *ip)
{
struct chl_route rt;
char sec[UCI_MAX_SEC_STR];
memset(&rt, 0 , sizeof(struct chl_route));
if (!validate_ip(ip, AF_INET))
rt.type = UCI_TYPE_ROUTE;
else if (!validate_ip(ip, AF_INET6))
rt.type = UCI_TYPE_ROUTE6;
else
goto err;
cid_to_section(pdp->cid, sec);
rt.name = name;
rt.target = ip;
rt.iface = sec;
if (chl_uci_add_route(c, &rt))
goto err;
return 0;
err:
CHL_ERR("failed to add route for [%s]\n", sec);
return 1;
}
int chl_uci_add_spec_route(struct uci_context *c, struct chl_client *cl,
char *ip)
{
char name[UCI_MAX_RT_NAME];
sprintf(name, "chl_%s_%d", cl->name, cl->rt_idx++);
return __chl_uci_add_spec_route(c, cl->pdp, name, ip);
}
int chl_uci_del_lan_route(struct uci_context *c, struct chl_pdp *pdp)
{
struct uci_section *s;
struct uci_package *p = NULL;
char sec[UCI_MAX_SEC_STR];
cid_to_section(pdp->cid, sec);
p = uci_lookup_package(c, UCI_PKG_NW);
if (!p)
uci_load(c, UCI_PKG_NW, &p);
if (!p)
goto err;
s = uci_lookup_section(c, p, UCI_RT_SEC_NAME);
if (!s)
goto err;
if (uci_delete_section(c, p, s))
goto err;
return 0;
err:
CHL_ERR("chl_uci_del_lan_route failed\n");
return 1;
}
int chl_uci_add_lan_route(struct uci_context *c, struct chl_pdp *pdp)
{
struct chl_route rt;
char target[MAX_IPV6_STR+20];
memset(&rt, 0 , sizeof(struct chl_route));
if (pdp->ipv6_state != CHL_IPV6_FOUND)
goto err;
sprintf(target, "%s/%d", pdp->gb6addr, pdp->prefix_len);
rt.target = target;
rt.iface = "lan";
rt.name = UCI_RT_SEC_NAME;
rt.metric = "1";
rt.type = UCI_TYPE_ROUTE6;
if (chl_uci_add_route(c, &rt))
goto err;
return 0;
err:
CHL_ERR("failed to add route for [%s]\n", rt.iface);
return 1;
}
static int chl_uci_add_del_mask(struct uci_context *c, struct chl_pdp *pdp,
bool add)
{
int ret = 0;
char sec[UCI_MAX_SEC_STR];
if (pdp->type == IPV6)
return 0;
cid_to_section(pdp->cid, sec);
if (add)
ret = uci_set_opt(c, UCI_PKG_NW, sec, UCI_OPT_MASK, pdp->netmask);
else
ret = uci_del_opt(c, UCI_PKG_NW, sec, UCI_OPT_MASK, pdp->netmask);
if (ret)
goto err;
return 0;
err:
CHL_ERR("failed to [%s] netmask for [%s]\n", add ? "add" : "del", sec);
return ret;
}
static int chl_uci_add_del_gw(struct uci_context *c, struct chl_pdp *pdp,
bool add)
{
int ret = 0;
char sec[UCI_MAX_SEC_STR];
cid_to_section(pdp->cid, sec);
if (add)
ret = uci_set_opt(c, UCI_PKG_NW, sec, UCI_OPT_GW, pdp->gw);
else
ret = uci_del_opt(c, UCI_PKG_NW, sec, UCI_OPT_GW, pdp->gw);
if (ret)
goto err;
return 0;
err:
CHL_ERR("failed to [%s] gw for [%s]\n", add ? "add" : "del", sec);
return ret;
}
static int chl_uci_add_del_addr(struct uci_context *c, struct chl_pdp *pdp,
bool add)
{
int ret = 0;
char sec[UCI_MAX_SEC_STR];
char ip6addr[MAX_IPV6_STR+20];
cid_to_section(pdp->cid, sec);
if (add && pdp->type & IPV4)
ret = uci_set_opt(c, UCI_PKG_NW, sec, UCI_OPT_IPV4, pdp->ipv4);
else if (pdp->type & IPV4)
ret = uci_del_opt(c, UCI_PKG_NW, sec, UCI_OPT_IPV4, pdp->ipv4);
if (ret)
goto err;
sprintf(ip6addr,"%s/%d", pdp->ipv6, CHL_IPV6_DFLT_PREF);
if (add && pdp->type & IPV6) {
ret = uci_set_opt(c, UCI_PKG_NW, sec, UCI_OPT_IPV6, ip6addr);
} else if (pdp->type & IPV6) {
ret = uci_del_opt(c, UCI_PKG_NW, sec, UCI_OPT_IPV6, ip6addr);
}
if(ret)
goto err;
if (add)
ret = uci_set_opt(c, UCI_PKG_NW, sec, UCI_OPT_PROT, UCI_STATIC);
else
ret = uci_set_opt(c, UCI_PKG_NW, sec, UCI_OPT_PROT, UCI_NONE);
if(ret)
goto err;
return 0;
err:
CHL_ERR("failed to [%s] ip address for [%s]\n", add ? "add" : "del", sec);
return ret;
}
static int chl_uci_add_del_prefix(struct uci_context *c, struct chl_pdp *pdp,
bool add)
{
int ret = 0;
char sec[UCI_MAX_SEC_STR];
char addr[MAX_IPV6_STR+20];
cid_to_section(pdp->cid, sec);
sprintf(addr, "%s/%d", pdp->gb6addr, pdp->prefix_len);
if (add)
ret = uci_set_opt(c, UCI_PKG_NW, sec, UCI_OPT_IP6PREF, addr);
else
ret = uci_del_opt(c, UCI_PKG_NW, sec, UCI_OPT_IP6PREF, addr);
if (ret)
goto err;
return 0;
err:
CHL_ERR("failed to [%s] netmask for [%s]\n", add ? "add" : "del", sec);
return ret;
}
static int chl_uci_add_del_dns(struct uci_context *c, struct chl_pdp *pdp,
bool add)
{
int i, ret = 0;
char sec[UCI_MAX_SEC_STR];
int flags[] = {pdp->dns & CHL_PDP_DNS4P, pdp->dns & CHL_PDP_DNS4S,
pdp->dns & CHL_PDP_DNS6P, pdp->dns & CHL_PDP_DNS6S};
char *addr[] = {pdp->dns4p, pdp->dns4s, pdp->dns6p, pdp->dns6s};
cid_to_section(pdp->cid, sec);
for (i = 0; i < sizeof(flags)/sizeof(int); i++) {
if (flags[i] && add)
ret = uci_add_to_list(c, UCI_PKG_NW, sec, UCI_OPT_DNS,
addr[i]);
else if (flags[i])
ret = uci_del_from_list(c, UCI_PKG_NW, sec, UCI_OPT_DNS,
addr[i]);
if (ret)
goto err;
}
return 0;
err:
CHL_ERR("failed to [%s] dns for [%s]\n", add ? "add" : "del", sec);
return ret;
}
int chl_uci_add_dns_route(struct uci_context *c, struct chl_pdp *pdp)
{
int i, ret = 0;
char sec[UCI_MAX_SEC_STR];
char name[UCI_MAX_RT_NAME];
int flags[] = {pdp->dns & CHL_PDP_DNS4P, pdp->dns & CHL_PDP_DNS4S,
pdp->dns & CHL_PDP_DNS6P, pdp->dns & CHL_PDP_DNS6S};
char *addr[] = {pdp->dns4p, pdp->dns4s, pdp->dns6p, pdp->dns6s};
cid_to_section(pdp->cid, sec);
for (i = 0; i < sizeof(flags)/sizeof(int); i++) {
if (flags[i]){
sprintf(name, "chl_rt%d_%d", pdp->id, pdp->rt_idx++);
ret = __chl_uci_add_spec_route(c, pdp, name, addr[i]);
}
if (ret)
goto err;
}
return 0;
err:
CHL_ERR("failed to add dns route for [%s]\n", sec);
return ret;
}
int chl_uci_del_dns_route(struct uci_context *c, struct chl_pdp *pdp)
{
char name[UCI_MAX_RT_NAME];
sprintf(name, "chl_rt%d_", pdp->id);
return __chl_uci_del_spec_route(c, name, NULL);
}
int chl_uci_add_addr(struct uci_context *c, struct chl_pdp *pdp)
{
return chl_uci_add_del_addr(c, pdp, true);
}
int chl_uci_del_addr(struct uci_context *c, struct chl_pdp *pdp)
{
return chl_uci_add_del_addr(c, pdp, false);
}
int chl_uci_add_gw(struct uci_context *c, struct chl_pdp *pdp)
{
return chl_uci_add_del_gw(c, pdp, true);
}
int chl_uci_del_gw(struct uci_context *c, struct chl_pdp *pdp)
{
return chl_uci_add_del_gw(c, pdp, false);
}
int chl_uci_add_mask(struct uci_context *c, struct chl_pdp *pdp)
{
return chl_uci_add_del_mask(c, pdp, true);
}
int chl_uci_del_mask(struct uci_context *c, struct chl_pdp *pdp)
{
return chl_uci_add_del_mask(c, pdp, false);
}
int chl_uci_add_dns(struct uci_context *c, struct chl_pdp *pdp)
{
return chl_uci_add_del_dns(c, pdp, true);
}
int chl_uci_del_dns(struct uci_context *c, struct chl_pdp *pdp)
{
return chl_uci_add_del_dns(c, pdp, false);
}
int chl_uci_add_prefix(struct uci_context *c, struct chl_pdp *pdp)
{
return chl_uci_add_del_prefix(c, pdp, true);
}
int chl_uci_del_prefix(struct uci_context *c, struct chl_pdp *pdp)
{
return chl_uci_add_del_prefix(c, pdp, false);
}
int chl_uci_init(struct uci_context **c)
{
struct uci_package *p = NULL;
*c = uci_alloc_context();
if (!*c) {
CHL_ERR("failed to allocate context\n");
return 1;
}
uci_load(*c, UCI_PKG_NW, &p);
if (!p) {
uci_free_context(*c);
CHL_ERR("failed to load network package\n");
return 1;
}
return 0;
}
void chl_uci_done(struct uci_context *c, bool commit)
{
struct uci_package *p;
if (!c)
return;
if (commit) {
if((p = uci_lookup_package(c, UCI_PKG_NW)))
uci_commit(c, &p, false);
}
uci_free_context(c);
}
int chl_uci_clean(void)
{
int i;
char section[8];
struct uci_context *c;
struct uci_package *p;
struct uci_section *s;
struct uci_element *e, *itr;
CHL_INFO("cleaning network config\n");
c = uci_alloc_context();
if (!c)
return 1;
uci_load(c, UCI_PKG_NW, &p);
if (!p) {
uci_free_context(c);
return 1;
}
for (i = 0; i < 8; i++) {
sprintf(section, "wan%d", i);
uci_del(c, UCI_PKG_NW, section, UCI_OPT_IPV4, NULL);
uci_del(c, UCI_PKG_NW, section, UCI_OPT_IPV4, NULL);
uci_del(c, UCI_PKG_NW, section, UCI_OPT_IPV6, NULL);
uci_del(c, UCI_PKG_NW, section, UCI_OPT_IP6PREF, NULL);
uci_del(c, UCI_PKG_NW, section, UCI_OPT_MASK, NULL);
uci_del(c, UCI_PKG_NW, section, UCI_OPT_GW, NULL);
uci_del(c, UCI_PKG_NW, section, UCI_OPT_METRIC, NULL);
uci_del(c, UCI_PKG_NW, section, UCI_OPT_DNS, NULL);
uci_set_opt(c, UCI_PKG_NW, section, UCI_OPT_PROT, UCI_NONE);
}
uci_foreach_element_safe(&p->sections, itr, e) {
s = uci_to_section(e);
if (strcmp(s->type, UCI_TYPE_ROUTE) &&
strcmp(s->type, UCI_TYPE_ROUTE6))
continue;
if (!str_starts_with(s->e.name, "chl"))
continue;
uci_delete_section(c, p, s);
}
uci_commit(c, &p, false);
uci_free_context(c);
return 0;
}