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