blob: a81bcc4c539612fcdfab1e9a83aa70a0177105eb [file] [log] [blame]
#include <libubox/blobmsg_json.h>
#include <libubox/list.h>
#include <include/log.h>
#include "diag_API.h"
#include "piped.h"
#include "piped_uci.h"
#include "piped_uevent.h"
#include "piped_util.h"
#include "piped_nw.h"
#include "piped_dhcp.h"
#include "piped_dns.h"
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <string.h>
#include <mqueue.h>
#include <pthread.h>
#include <semaphore.h>
struct piped pd;
static int started_flag = 0;
#define PIPED_CLEAN_ON_START
enum {
IF_UPDOWN_ARG_IFNAME,
__IF_UPDOWN_ARG_MAX,
};
/* UBUS FUNCTIONS*/
static int pd_stop_ubus(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg);
static int pd_reload_ubus(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg);
static int pd_up_ubus(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg);
static int pd_down_ubus(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg);
static int pd_status_ubus(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg);
static int pd_up(struct pd_context *pdc, struct pd_iface *pdi);
static int pd_up6(struct pd_context *pdc, struct pd_iface *pdi);
static int pd_down(struct pd_context *pdc, struct pd_iface *pdi);
static int pd_down6(struct pd_context *pdc, struct pd_iface *pdi);
static const struct blobmsg_policy pd_updown_pol[] = {
{ .name = "interface", .type = BLOBMSG_TYPE_STRING },
};
static const struct ubus_method pd_methods[] = {
UBUS_METHOD_NOARG("stop", pd_stop_ubus),
UBUS_METHOD_NOARG("reload", pd_reload_ubus),
UBUS_METHOD("up", pd_up_ubus, pd_updown_pol),
UBUS_METHOD("down", pd_down_ubus, pd_updown_pol),
UBUS_METHOD_NOARG("status", pd_status_ubus),
};
static struct ubus_object_type pd_object_type =
UBUS_OBJECT_TYPE("piped", pd_methods);
static struct ubus_object pd_obj = {
.name = "piped",
.type = &pd_object_type,
.methods = pd_methods,
.n_methods = ARRAY_SIZE(pd_methods),
};
static int g_pipe_sock_ip;
static pthread_t pd_detect_th;
//static mqd_t s_mqid, r_mqid;
static int pd_pipe_fd[2];
pthread_mutex_t gPipedLockMutex = PTHREAD_MUTEX_INITIALIZER;
static inline bool pd_has_prim4(void)
{
struct pd_iface *pdi;
bool ret;
ret = 0;
list_for_each_entry(pdi, &pd.uplist, uplist) {
PD_ERR(pd_has_prim471, "pd_has_prim4(%s), flags=%x\n", pdi->ifname, pdi->flags);
if (pdi_test(pdi, PDI_UP) && !pdi_test(pdi, PDI_DEDICATEAPN))
ret = 1;
}
PD_ERR(pd_has_prim478, "pd_has_prim4 ret=%d\n", ret);
return ret;
}
static inline bool pd_has_prim6(void)
{
struct pd_iface *pdi;
list_for_each_entry(pdi, &pd.up6list, up6list) {
if (!pdi_test(pdi, PDI_DEDICATEAPN))
return 1;
}
return 0;
}
static inline bool pd_is_prim6(struct pd_iface *pdi)
{
if (pdi_test(pdi, PDI_DEDICATEAPN))
return 0;
else
return 1;
}
static inline bool pd_is_prim4(struct pd_iface *pdi)
{
if (pdi_test(pdi, PDI_DEDICATEAPN))
return 0;
else
return 1;
}
static struct pd_iface *pdi_get_by_sec(char *sec)
{
struct pd_iface *pdi;
list_for_each_entry(pdi, &pd.plist, list) {
if (!strcmp(pdi->sec, sec))
return pdi;
}
return NULL;
}
static struct pd_iface *pdi_get_by_ifname(char *ifname)
{
struct pd_iface *pdi;
list_for_each_entry(pdi, &pd.plist, list) {
if (!strcmp(pdi->ifname, ifname))
return pdi;
}
return NULL;
}
int pd_system(const char *cmd)
{
FILE *fp;
int res;
char buf[1024];
if (cmd == NULL) {
PD_ERR(pd_system58, "cmd is NULL!\n");
return -1;
}
if ((fp = popen(cmd, "r")) == NULL) {
PD_ERR(pd_system62, "popen error: %s\n", strerror(errno));
return -1;
} else {
while (fgets(buf, sizeof(buf), fp)) {
PD_INFO(pd_system66, "buf = %s", buf);
}
if ((res = pclose(fp)) == -1) {
PD_ERR(pd_system69, "close popen file pointer fp error!\n");
return res;
} else if (res == 0) {
return res;
} else {
PD_ERR(pd_system74, "popen res is :%d\n", res);
return res;
}
}
}
static int pd_check_dns_conf_file(char *file_name)
{
int ret = 0;
FILE *conf_fp = NULL;
char tmp[512] = { 0 };
char *line = NULL;
int line_num;
conf_fp = fopen(file_name, "r");
if (conf_fp == NULL)
return 1;
line_num = 0;
while ((line = fgets(tmp, sizeof(tmp), conf_fp)) != NULL) {
char *obj_pos = NULL;
obj_pos = strstr(line, "dhcp-range");
if (obj_pos != NULL) {
line_num++;
PD_ERR(pd_check_dns_conf_file162, "%s: %s, line_num=%d, %p", __func__, line, line_num, obj_pos);
}
obj_pos = strstr(line, "dhcp-option");
if (obj_pos != NULL) {
line_num++;
PD_ERR(pd_check_dns_conf_file166, "%s: %s, line_num=%d, %p", __func__, line, line_num, obj_pos);
}
}
if (line_num > 0)
ret = 1;
else
PD_ERR(pd_check_dns_conf_file172, "%s: line_num=%d, No DHCP Configure line.", __func__, line_num);
fclose(conf_fp);
return ret;
}
static void pd_kill_dnsmasq()
{
char det_cmd[64] = "ps | grep dnsm | grep sbin";
FILE *fp = NULL;
char *line = NULL;
int line_num = 0;
char tmp[512] = { 0 };
char kill_cmd[64] = {0};
int pid_arr[100] = {0};
if ((fp = popen(det_cmd, "r")) == NULL) {
PD_ERR(pd_kill_dnsmasq1521, "pd_kill_dnsmasq: popen error: %s/n", strerror(errno));
} else {
line_num = 0;
while ((line = fgets(tmp, 512, fp)) != NULL) {
int data = 0;
char *pos = NULL;
pos = strstr(line, "dnsmasq");
if (pos != NULL)
{
sscanf(line, "%d", &data);
if (line_num < 100)
pid_arr[line_num] = data;
PD_ERR(pd_kill_dnsmasq1531, "pd_kill_dnsmasq: line(%d)=%s, line_num=%d, dnsmasq's id is %d", strlen(line), line, line_num, data);
line_num ++;
}
}
if ((pclose(fp)) == -1) {
PD_ERR(pd_kill_dnsmasq1536, "pd_kill_dnsmasq: close popen file pointer fp error!\n");
}
if (line_num) {
int i;
for(i=0; i < line_num && i < 100 && pid_arr[i] != 0; i++) {
sprintf(kill_cmd, "kill %d\n", pid_arr[i]);
PD_ERR(pd_kill_dnsmasq1548, "pd_kill_dnsmasq: now %s, number is %d\n", kill_cmd, i);
pd_system(kill_cmd);
}
}
}
return;
}
static int pd_detect_recvQ()
{
char det_cmd[64] = "netstat -nlp | grep dnsm | grep 0.0.0.0:67";
FILE *fp = NULL;
char *line = NULL;
int line_num = 0;
char tmp[512] = { 0 };
int ret = 0;
if ((fp = popen(det_cmd, "r")) == NULL) {
PD_ERR(pd_detect_recvQ1517, "pd_detect_recvQ: popen error: %s/n", strerror(errno));
} else {
line_num = 0;
while ((line = fgets(tmp, 512, fp)) != NULL) {
char first_str[512] = {0};
int data0 = 500;
int data1 = 700;
if ((line[0] == 'U' || line[0] == 'u') && (line[1] == 'D' || line[1] == 'd') )
{
sscanf(line, "%s %d %d", first_str, &data0, &data1);
PD_ERR(pd_detect_recvQ1526, "pd_detect_recvQ: line(%d)=%s, line_num=%d, %s, %d, %d", strlen(line), line, line_num, first_str, data0, data1);
line_num ++;
if (data0 != 0) {
ret = 1;
break;
}
}
}
if ((pclose(fp)) == -1) {
PD_ERR(pd_detect_recvQ1531, "pd_detect_recvQ: close popen file pointer fp error!\n");
}
}
return ret;
}
static void pd_detect_thread(void *data UNUSED)
{
int ret;
char buf[256] = {0};
PD_INFO(pd_detect_thread1508, "starting pd_detect_thread \n");
while(1)
{
ret = read(pd_pipe_fd[0], buf, 256);
PD_INFO(pd_detect_thread1533, "pd_detect_thread: get a message:%s\n", buf);
if (buf[0]== 'r' && buf[1] == 'e' && buf[2] == 'l') {
sleep(20);
PD_INFO(pd_detect_thread1565, "pd_detect_thread: detect dnsmasq's recvQ\n");
ret = pd_detect_recvQ();
if (ret == 0) {
sleep(1);
ret = pd_detect_recvQ();
PD_INFO(pd_detect_thread1598, "pd_detect_thread: detect recvQ first time is 0, second is %d\n", ret);
}
if (ret != 0) {
int i = 0;
for (i = 0; i < 5; i++) {
PD_INFO(pd_detect_thread1603, "pd_detect_thread: detect recvQ is not 0, redetct again:%d\n", i);
sleep(1);
ret = pd_detect_recvQ();
if (ret == 0) {
PD_INFO(pd_detect_thread1607, "pd_detect_thread: redetect recvQ is 0, break:%d\n", i);
break;
}
}
if (i >= 5) {
PD_INFO(pd_detect_thread1586, "pd_detect_thread: recvQ is not 0, for %d time. Need to Kill dnsmasq and send command to recheck\n", i);
pd_kill_dnsmasq();
ret = write(pd_pipe_fd[1], "reload ", strlen("reload "));
}
}
}
memset(buf, 0, 256);
}
}
static int pd_check_os_type(char *file_name)
{
int ret = 0;
FILE *conf_fp = NULL;
char tmp[512] = { 0 };
char *line = NULL;
int line_num;
conf_fp = fopen(file_name, "r");
if (conf_fp == NULL)
return 1;
line_num = 0;
while ((line = fgets(tmp, sizeof(tmp), conf_fp)) != NULL) {
char *obj_pos = NULL;
obj_pos = strstr(line, "linux");
if (obj_pos != NULL) {
line_num++;
}
}
if (line_num > 0)
ret = 1;
fclose(conf_fp);
return ret;
}
static int pd_check_usb_type(char *file_name)
{
int ret = 0;
FILE *conf_fp = NULL;
char tmp[512] = { 0 };
char *line = NULL;
int line_num;
conf_fp = fopen(file_name, "r");
if (conf_fp == NULL)
return 1;
line_num = 0;
while ((line = fgets(tmp, sizeof(tmp), conf_fp)) != NULL) {
char *obj_pos = NULL;
obj_pos = strstr(line, "rndis");
if (obj_pos != NULL) {
line_num++;
}
}
if (line_num > 0)
ret = 1;
fclose(conf_fp);
return ret;
}
static int pd_apply_changes(unsigned int status)
{
// struct ubus_request req;
// uint32_t id;
int rc = 0;
int list_num = 0;
int ret;
#if 0
char buf[MAX_FNAME];
bool usb_en;
#endif
PD_INFO(pd_apply_changes, "applying changes!status=%x\n", status);
/* apply changes made to /etc/config/netwok and /etc/config/dhcp
* if changes were made to dhcp settigs - we reenumerate usb to
* force host to renew dhcp lease.
*/
if (pd_test(&status, PDC_NETWORK_CHANGED)) {
char *network_lan_ip = NULL;
char *network_mask = NULL;
struct uci_list *l_ip = NULL;
struct uci_element *e_ip;
struct uci_context *c = NULL;
struct uci_package *p;
pd_system("/etc/init.d/dnsmasq reload");
list_num = 0;
c = uci_alloc_context();
if (!c) {
PD_ERR(pd_apply_changes, "pd_apply_changes context allocation failed\n");
return 1;
}
pd_uci_set_opt(c, UCI_PKG_NW, UCI_SEC_LAN, UCI_OPT_AUTO, "1");
if((p = uci_lookup_package(c, UCI_PKG_NW)))
uci_commit(c, &p, false);
/* reload netifd*/
PD_INFO(pd_apply_changes1, "reloading netifd\n");
pd_system("/etc/init.d/network reload");
ret = write(pd_pipe_fd[1], "reload\n", strlen("reload\n"));
(void) ret;
network_mask = pd_uci_get_opt(c, UCI_PKG_NW, UCI_SEC_LAN, UCI_OPT_NETMASK);
l_ip = pd_uci_get_list(c, UCI_PKG_NW, UCI_SEC_LAN, UCI_OPT_IPADDR);
if (l_ip != NULL) {
uci_foreach_element(l_ip, e_ip) {
PD_ERR(pd_apply_changes175, "pd_apply_changes: e_name=%s\n", e_ip->name);
network_lan_ip = e_ip->name;
list_num++;
}
if (list_num != 0) {
struct ifreq ifr;
struct sockaddr_in sin;
int ipaddr;
int lan_ip;
int loop_cnt = 0;
lan_ip = inet_addr(network_lan_ip);
PD_ERR(pd_apply_changes190, "pd_apply_changes: network_lan_ip=%s,%x; mask=%s\n", network_lan_ip, lan_ip, network_mask);
memset(&ifr, 0, sizeof(ifr));
strcpy(ifr.ifr_name, "br-lan");
for(loop_cnt = 0; loop_cnt < 150 && g_pipe_sock_ip >= 0; loop_cnt++){
if(ioctl(g_pipe_sock_ip, SIOCGIFADDR, &ifr) >= 0) {
memcpy(&sin, &ifr.ifr_addr, sizeof(sin));
ipaddr = sin.sin_addr.s_addr;
PD_ERR(pd_apply_changes212, "pd_apply_changes:%d local eth0:\t%s, %x\n", loop_cnt, inet_ntoa(sin.sin_addr), ipaddr);
if (lan_ip == ipaddr)
break;
}
}
} else {
PD_ERR(pd_apply_changes191, "pd_apply_changes: no ipaddr list, mask=%s\n", network_mask);
}
} else {
PD_ERR(pd_apply_changes191, "pd_apply_changes: no ipaddr list");
}
uci_free_context(c);
}
if (pd_test(&status, PDC_ODHCPD_CHANGED)) {
PD_INFO(pd_apply_changes4, "reloading odhcpd\n");
/* reload odhcpd*/
pd_system("/etc/init.d/odhcpd reload");
}
if (pd_test(&status, PDC_DNSMASQ_CHANGED)) {
FILE *fp = NULL;
char conf_file[64] = "/tmp/etc/dnsmasq.conf";
char os_type[64] = "/sys/class/android_usb/android0/os";
char usb_type[64] = "/sys/class/android_usb/android0/functions";
char cmd[64] = "ps | grep dnsm";
char tmp[512] = { 0 };
int check_cnt = 0;
char *line = NULL;
int line_num = 0;
char check_time = 160;
int dnsmasq_exist = 0;
int reloading_time = 0;
if (list_num > 0) {
PD_INFO(pd_apply_changes5, "check dnsmasq, reload_time=%d\n", reloading_time);
dnsmasq_exist = 0;
for (check_cnt = 0; check_cnt < check_time; check_cnt++) {
if ((fp = popen(cmd, "r")) == NULL) {
PD_ERR(pd_apply_changes254, "pd_apply_changes: popen error: %s/n", strerror(errno));
} else {
line_num = 0;
while ((line = fgets(tmp, sizeof(tmp), fp)) != NULL) {
char *obj_pos = NULL;
obj_pos = strstr(line, "dnsmasq.conf");
PD_ERR(pd_apply_changes257, "%s: %s, line_num=%d, %p", __func__, line, line_num, obj_pos);
if (obj_pos != NULL)
line_num ++;
}
if ((pclose(fp)) == -1) {
PD_ERR(pd_apply_changes261, "pd_apply_changes: close popen file pointer fp error!\n");
}
if (line_num >= 1) {
PD_ERR(pd_apply_changes270, "pd_apply_changes: %d-->dnsmasq has been load successfully\n", check_cnt);
dnsmasq_exist = 1;
break;
} else {
PD_ERR(pd_apply_changes273, "pd_apply_changes: %d-->fail to load dnsmasq, line_num=%d\n", check_cnt, line_num);
}
}
}
if (dnsmasq_exist > 0) {
while (!pd_check_dns_conf_file(conf_file)) {
PD_ERR(pd_apply_changes279, "pd_apply_changes:list_num = %d, fail to load dnsmasq(%d) after try %d times, retry\n", list_num, dnsmasq_exist, reloading_time);
reloading_time++;
usleep(500*1000);
if (reloading_time > 20) {
reloading_time = 0;
pd_system("/etc/init.d/dnsmasq reload");
}
}
PD_INFO(pd_apply_changes82, "finish reloading dnsmasq, list_num = %d\n", list_num);
pd_system("touch /tmp/dnsmasq_success");
if (!pd_detect_th)
pthread_create( &pd_detect_th, NULL, (void *)pd_detect_thread, NULL);
else {
if(pd_check_os_type(os_type) && pd_check_usb_type(usb_type)) {
pd_system("echo 0 > /sys/class/android_usb/android0/enable");
sleep(1);
pd_system("echo 1 > /sys/class/android_usb/android0/enable");
}
}
}
}
}
return rc;
}
static struct pd_context *pd_context_get(void)
{
return &pd.pdc;
}
static int pd_context_init(struct pd_context *pdc)
{
pdc->c = uci_alloc_context();
if (!pdc->c) {
PD_ERR(pd_context_init, "context allocation failed\n");
return 1;
}
pdc->status = 0;
return 0;
}
static void pd_context_done(struct pd_context *pdc, bool commit)
{
pd_uci_done(pdc->c, commit);
if (commit && pd_apply_changes(pdc->status))
PD_ERR(pd_context_done, "Applying changes failed\n");
}
static void pd_sec_6to4(char *sec6, char *sec4)
{
unsigned int n;
sscanf(sec6, "wan%d", &n);
sprintf(sec4, "wan%d", n - 60);
}
static int pd_check_sec(char *sec, bool *ipv6)
{
unsigned int n;
if (!strcmp(sec, UCI_SEC_LAN)) {
*ipv6 = false;
return 0;
}
if (!str_starts_with(sec, UCI_SEC_WAN))
return 1;
if (sscanf(sec, "wan%d", &n) != 1)
return 1;
if (n <= 7) {
*ipv6 = false;
return 0;
} else if (n >= 60 && n <= 67) {
*ipv6 = true;
return 0;
}
return 1;
}
static void pd_update_if(struct pd_iface *oldi, struct pd_iface *newi,
struct pdi_status *status)
{
unsigned int i;
int flag[] = {PDI_IPADDR, PDI_DNS4P, PDI_DNS4S, PDI_IP6ADDR,
PDI_DNS6P, PDI_DNS6S, PDI_COMBINE_IF};
int len[] = {MAX_IP_STR_LEN, MAX_IP_STR_LEN, MAX_IP_STR_LEN,
MAX_IP6_STR_LEN, MAX_IP6_STR_LEN, MAX_IP6_STR_LEN, MAX_IFNAME};
char *oldp[] = {oldi->ipaddr, oldi->dns4p, oldi->dns4s, oldi->ip6addr,
oldi->dns6p, oldi->dns6s, oldi->combine_if};
char *newp[] = {newi->ipaddr, newi->dns4p, newi->dns4s, newi->ip6addr,
newi->dns6p, newi->dns6s, newi->combine_if};
for (i = 0; i < sizeof(flag)/sizeof(int); i++) {
if (!pd_test(&status->same, flag[i])) {
memcpy(oldp[i], newp[i], len[i]);
if (pdi_test(newi, flag[i]))
pdi_set(oldi, flag[i]);
else
pdi_clear(oldi, flag[i]);
}
}
if (pdi_test(newi, PDI_AUTO))
pdi_set(oldi, PDI_AUTO);
else
pdi_clear(oldi, PDI_AUTO);
if (pdi_test(newi, PDI_DEDICATEAPN))
pdi_set(oldi, PDI_DEDICATEAPN);
else
pdi_clear(oldi, PDI_DEDICATEAPN);
if (pdi_test(newi, PDI_ZVLAN_ID))
pdi_set(oldi, PDI_ZVLAN_ID);
else
pdi_clear(oldi, PDI_ZVLAN_ID);
}
static int pd_validate_if(struct pd_context *pdc, struct pd_iface *pdi)
{
UNUSEDPARAM(pdc);
if (pdi_test(pdi, PDI_IPADDR) && validate_ip(pdi->ipaddr, AF_INET)) {
PD_ERR(pd_validate_if, "ipaddr invalid %s for %s\n", pdi->ipaddr, pdi->ifname);
return 1;
}
if (pdi_test(pdi, PDI_IP6ADDR) && validate_ip(pdi->ip6addr, AF_INET6)) {
PD_ERR(pd_validate_if1, "ipa6ddr invalid %s - %s\n", pdi->ip6addr, pdi->ifname);
return 1;
}
return 0;
}
static int pd_updown_parse(struct blob_attr *msg, struct blob_attr **tb,
char **sec)
{
int rc;
struct blob_attr *cur;
/*parsing blob to be accessed easily with tb array*/
rc = blobmsg_parse(pd_updown_pol, ARRAY_SIZE(pd_updown_pol), tb,
blob_data(msg), blob_len(msg));
if (rc < 0) {
PD_ERR(pd_updown_parse, "parsing fail\n");
return -UBUS_STATUS_INVALID_ARGUMENT;
}
cur = tb[IF_UPDOWN_ARG_IFNAME];
if (!cur) {
PD_ERR(pd_updown_parse1, "1st param input err\n");
return -UBUS_STATUS_INVALID_ARGUMENT;
}
*sec = (char *)blobmsg_data(cur);
return UBUS_STATUS_OK;
}
static int pd_reload6(struct pd_context *pdc, struct pd_iface *oldi,
struct pd_iface *newi, struct pdi_status *status)
{
if (!pdi_test(oldi, PDI_UP6)) {
PD_ERR(pd_reload6318, "pd_reload6 failed for interface(%s) is not up:flags = %x\n", oldi->ifname, oldi->flags);
return 0;
}
if (pd_test(&status->removed, PDI_IP6ADDR)) {
if (pd_down6(pdc, oldi)) {
PD_ERR(pd_reload6, "pd_down6 failed for %s\n", oldi->ifname);
return 1;
}
return 0;
} else if (pd_test(&status->added, PDI_IP6ADDR)) {
PD_ERR(pd_reload61, "no ip6 for interface! %s\n", oldi->ifname);
return 1;
}
if (pdi_test(oldi, PDI_LAN))
return 0;
if (!pd_is_prim6(oldi))
return 0;
if (pd_test(&status->removed, PDI_DNS6P) ||
pd_test(&status->removed, PDI_DNS6S)) {
if (pd_dns6_del(pdc, oldi)) {
PD_ERR(pd_reload62, "pd_dns6_del failed for %s\n", oldi->ifname);
return 1;
}
} else if (pd_test(&status->changed, PDI_DNS6P) ||
pd_test(&status->changed, PDI_DNS6S)) {
if (pd_dns6_change(pdc, oldi, newi)) {
PD_ERR(pd_reload63, "pd_dns6_change failed for %s\n", oldi->ifname);
return 1;
}
} else if (pd_test(&status->changed, PDI_DNS6P) ||
pd_test(&status->changed, PDI_DNS6S)) {
if (pd_dns6_add(pdc, newi)) {
PD_ERR(pd_reload64, "pd_dns6_add failed for %s\n", oldi->ifname);
return 1;
}
}
return 0;
}
static int pd_reload4(struct pd_context *pdc, struct pd_iface *oldi,
struct pd_iface *newi, struct pdi_status *status)
{
if (!pdi_test(oldi, PDI_UP)) {
PD_ERR(pd_reload4_m0, "pd_reload4: interface(%s) is not up:flags = %x\n", oldi->ifname, oldi->flags);
return 0;
}
PD_ERR(pd_reload4_m1, "pd_reload4:status->removed=%x, status->added=%x, status->changed=%x, oldi->flags=%x, newi->flags=%x\n",
status->removed, status->added, status->changed, oldi->flags, newi->flags);
if (pd_test(&status->removed, PDI_IPADDR)) {
if (pd_down(pdc, oldi)) {
PD_ERR(pd_reload4, "pd_down for %s\n", oldi->ifname);
return 1;
}
return 0;
} else if (pd_test(&status->added, PDI_IPADDR)) {
PD_ERR(pd_reload41, "no ip for interface! %s\n", oldi->ifname);
return 1;
}
if (pdi_test(oldi, PDI_LAN))
return 0;
if (!pd_is_prim4(newi))
return 0;
if (pd_test(&status->changed, PDI_IPADDR)) {
if (pd_nw_change(pdc, oldi, newi)) {
PD_ERR(pd_reload42, "pd_nw_change failed for %s\n", oldi->ifname);
return 1;
}
if (pd_dhcp_change(pdc, oldi, newi)) {
PD_ERR(pd_reload43, "pd_dhcp_change failed for %s\n", oldi->ifname);
return 1;
}
}
if (pd_test(&status->removed, PDI_DNS4P) ||
pd_test(&status->removed, PDI_DNS4S)) {
if (pd_dns4_del(pdc, oldi)) {
PD_ERR(pd_reload44, "pd_dns4_del failed for %s\n", oldi->ifname);
return 1;
}
} else if (pd_test(&status->changed, PDI_DNS4P) ||
pd_test(&status->changed, PDI_DNS4S)) {
if (pd_dns4_change(pdc, oldi, newi)) {
PD_ERR(pd_reload45, "pd_dns4_change failed for %s\n", oldi->ifname);
return 1;
}
} else if (pd_test(&status->added, PDI_DNS4P) ||
pd_test(&status->added, PDI_DNS4S)) {
if (pd_dns4_add(pdc, newi)) {
PD_ERR(pd_reload46, "pd_dns4_add failed for %s\n", oldi->ifname);
return 1;
}
}
PD_ERR(pd_reload4_m1, "pdc->status=%x\n", pdc->status);
return 0;
}
static int pd_write_if(struct pd_context *pdc, struct pd_iface *pdi,
struct pdi_status *status, bool force)
{
int rc = 0;
struct pd_path *path = &pdc->path;
char lan_alias[MAX_IP_STR_LEN];
ipv4_get_alias(pdi->ipaddr, lan_alias);
PD_ERR(pd_write_if677, "Enter pd_write_if(%s), force=%d\n", pdi->ifname, force);
pd_path_set_iface(path, pdi->ifname);
pd_path_set_file(path, MPIPE_FNAME_IP);
if (force && pdi_test(pdi, PDI_IPADDR)) {
rc = pd_write_str(path, pdi->ipaddr);
} else if (pd_test(&status->added, PDI_IPADDR) ||
pd_test(&status->changed, PDI_IPADDR)) {
rc = pd_write_str(path, pdi->ipaddr);
} else if (pd_test(&status->removed, PDI_IPADDR)) {
rc = pd_write_str(path, EMPTY_IPV4);
}
pd_path_set_file(path, MPIPE_FNAME_GW);
if (force && pdi_test(pdi, PDI_IPADDR)) {
rc = pd_write_str(path, lan_alias);
} else if (pd_test(&status->added, PDI_IPADDR) ||
pd_test(&status->changed, PDI_IPADDR)) {
rc = pd_write_str(path, lan_alias);
} else if (pd_test(&status->removed, PDI_IPADDR)) {
rc = pd_write_str(path, EMPTY_IPV4);
}
if (rc) {
PD_ERR(pd_write_if, "ipv4 write failed for %s\n", pdi->ifname);
return 1;
}
pd_path_set_file(path, MPIPE_FNAME_IP6);
if (force && pdi_test(pdi, PDI_IP6ADDR)) {
rc = pd_write_str(path, pdi->ip6addr);
} else if (pd_test(&status->added, PDI_IP6ADDR) ||
pd_test(&status->changed, PDI_IP6ADDR)) {
rc = pd_write_str(path, pdi->ip6addr);
} else if (pd_test(&status->removed, PDI_IP6ADDR)) {
rc = pd_write_str(path, EMPTY_IPV6);
pd_path_set_file(path, MPIPE_FNAME_GB6ADDR);
rc = pd_write_str(path, EMPTY_IPV6);
}
pd_path_set_file(path, MPIPE_FNAME_DNS6P);
PD_ERR(pd_write_if645, "write dns6p=%s; 6s=%s\n", pdi->dns6p, pdi->dns6s);
if (force && pdi_test(pdi, PDI_DNS6P)) {
rc = pd_write_str(path, pdi->dns6p);
} else if (pd_test(&status->added, PDI_DNS6P) ||
pd_test(&status->changed, PDI_DNS6P)) {
rc = pd_write_str(path, pdi->dns6p);
} else if (pd_test(&status->removed, PDI_DNS6P)) {
rc = pd_write_str(path, EMPTY_IPV6);
}
pd_path_set_file(path, MPIPE_FNAME_DNS6S);
if (force && pdi_test(pdi, PDI_DNS6S)) {
rc = pd_write_str(path, pdi->dns6s);
} else if (pd_test(&status->added, PDI_DNS6S) ||
pd_test(&status->changed, PDI_DNS6S)) {
rc = pd_write_str(path, pdi->dns6s);
} else if (pd_test(&status->removed, PDI_DNS6S)) {
rc = pd_write_str(path, EMPTY_IPV6);
}
PD_ERR(pd_write_if729, "pd_write_if update combine if\n");
pd_path_set_file(path, MPIPE_FNAME_COMBINEIF);
if (force && pdi_test(pdi, PDI_COMBINE_IF)) {
rc = pd_write_str(path, pdi->combine_if);
} else if (pd_test(&status->added, PDI_COMBINE_IF) ||
pd_test(&status->changed, PDI_COMBINE_IF)) {
rc = pd_write_str(path, pdi->combine_if);
} else if (pd_test(&status->removed, PDI_COMBINE_IF)) {
rc = pd_write_str(path, "NULL_IF");
}
if (rc) {
PD_ERR(pd_write_if1, "ipv6 write failed for %s\n", pdi->ifname);
return 1;
}
PD_ERR(pd_write_if747, "Exit pd_write_if\n");
return 0;
}
static void pd_compare_if(struct pd_iface *oldi, struct pd_iface *newi,
struct pdi_status *status)
{
unsigned int i;
int flag[] = {PDI_IPADDR, PDI_DNS4P, PDI_DNS4S, PDI_IP6ADDR,
PDI_DNS6P, PDI_DNS6S, PDI_COMBINE_IF};
char *oldp[] = {oldi->ipaddr, oldi->dns4p, oldi->dns4s, oldi->ip6addr,
oldi->dns6p, oldi->dns6s, oldi->combine_if};
char *newp[] = {newi->ipaddr, newi->dns4p, newi->dns4s, newi->ip6addr,
newi->dns6p, newi->dns6s, newi->combine_if};
PD_ERR(pd_compare_if, "pd_compare_if oldi(%s):V4:%s V6:%s; newi:V4:%s V6:%s, oldi->flags=%x, newi->flags=%x\n",
oldi->ifname, oldi->ipaddr, oldi->ip6addr, newi->ipaddr, newi->ip6addr, oldi->flags, newi->flags);
for (i = 0; i < sizeof(flag)/sizeof(int); i++) {
if (pdi_test(oldi, flag[i]) && !pdi_test(newi, flag[i]))
pd_set(&status->removed, flag[i]);
else if (!pdi_test(oldi, flag[i]) && pdi_test(newi, flag[i]))
pd_set(&status->added, flag[i]);
else if (pdi_test(oldi, flag[i]) && strcmp(oldp[i], newp[i]))
pd_set(&status->changed, flag[i]);
else
pd_set(&status->same, flag[i]);
PD_ERR(pd_compare_if486, "pd_compare_if status:%x, %x, %x, %x\n", status->removed, status->added, status->changed, status->same);
}
}
static void pd_load_dns(struct pd_iface *pdi, struct uci_list *l)
{
struct uci_element *e;
uci_foreach_element(l, e) {
if (!validate_ip(e->name, AF_INET)) {
if (!pdi_test(pdi, PDI_DNS4P)) {
strncpy(pdi->dns4p, e->name, sizeof(pdi->dns4p) - 1);
pdi_set(pdi, PDI_DNS4P);
} else if (!pdi_test(pdi, PDI_DNS4S)) {
strncpy(pdi->dns4s, e->name, sizeof(pdi->dns4s) - 1);
pdi_set(pdi, PDI_DNS4S);
}
} else if (!validate_ip(e->name, AF_INET6)) {
if (!pdi_test(pdi, PDI_DNS6P)) {
strcpy(pdi->dns6p, e->name);
PD_ERR(pd_load_dns719,"dns6p=%s-->%s\n", e->name, pdi->dns6p);
pdi_set(pdi, PDI_DNS6P);
} else if (!pdi_test(pdi, PDI_DNS6S)) {
strcpy(pdi->dns6s, e->name);
PD_ERR(pd_load_dns724,"dns6s=%s-->%s\n", e->name, pdi->dns6s);
pdi_set(pdi, PDI_DNS6S);
}
}
}
}
static int pd_load_if(struct pd_context *pdc, struct pd_iface *pdi)
{
char pref[MAX_PREFIX_STR_LEN];
struct uci_package *p;
struct uci_section *s;
struct uci_element *e;
struct uci_option *o;
struct uci_context *c = pdc->c;
p = uci_lookup_package(c, UCI_PKG_PIPE);
if (!p)
uci_load(c, UCI_PKG_PIPE, &p);
if (!p) {
PD_ERR(pd_load_if, "uci_load failed\n");
return 1;
}
s = uci_lookup_section(c, p, pdi->sec);
if (!s) {
PD_ERR(pd_load_if1, "uci_lookup_section failed\n");
return 1;
}
pdi_set(pdi, PDI_AUTO);
pdi_clear(pdi, PDI_COMBINE_IF);
pdi_clear(pdi, PDI_ZVLAN_ID);
pdi->combine_if[0] = 0;
uci_foreach_element(&s->options, e) {
o = uci_to_option(e);
if (!strcmp(e->name, UCI_OPT_IPADDR)) {
strncpy(pdi->ipaddr, o->v.string, sizeof(pdi->ipaddr) - 1);
PD_ERR(pd_load_if_m0, "pd_load_if: ipaddr=%s\n", pdi->ipaddr);
pdi_set(pdi, PDI_IPADDR);
} else if (!strcmp(e->name, UCI_OPT_IP6ADDR)) {
ipv6_split_pref(o->v.string, pdi->ip6addr, pref);
PD_ERR(pd_load_if_m549, "pd_load_if: ip6addr=%s\n", pdi->ip6addr);
pdi_set(pdi, PDI_IP6ADDR);
} else if (!strcmp(e->name, UCI_OPT_AUTO)) {
if (pd_uci_parse_bool(o->v.string) == 0)
pdi_clear(pdi, PDI_AUTO);
} else if (!strcmp(e->name, UCI_OPT_DNS) &&
o->type == UCI_TYPE_LIST) {
pd_load_dns(pdi, &o->v.list);
} else if (!strcmp(e->name, UCI_OPT_DEDICATE_OPT) ){
if (pd_uci_parse_bool(o->v.string) == 0)
pdi_clear(pdi, PDI_DEDICATEAPN);
else
pdi_set(pdi, PDI_DEDICATEAPN);
} else if (!strcmp(e->name, UCI_OPT_COMBINE_IF_OPT) ){
if (strlen(o->v.string) > 1) {
strncpy(pdi->combine_if, o->v.string, sizeof(pdi->combine_if) - 1);
PD_ERR(pd_load_if_881, "pd_load_if: combineif=org:%s, copy:%s\n", o->v.string, pdi->combine_if);
pdi_set(pdi, PDI_COMBINE_IF);
}
} else if (!strcmp(e->name, UCI_OPT_VID_OPT)) {
if (atoi(o->v.string) > 0 && atoi(o->v.string) < 4096)
pdi_set(pdi, PDI_ZVLAN_ID);
else
pdi_clear(pdi, PDI_ZVLAN_ID);
}
}
PD_ERR(pd_load_if_m1, "pd_load_if: pdi->flags=%x\n", pdi->flags);
return 0;
}
static int pd_reload_if(struct pd_context *pdc, struct pd_iface *oldi,
bool force)
{
struct pd_iface newi = {0};
struct pdi_status status = {0};
int ret = 0;
PD_ERR(pd_reload_if_m0, "pd_reload_if, ifname=%s, sec=%s, flags=%x, force=%d\n", oldi->ifname, oldi->sec, oldi->flags, force);
newi.idx = oldi->idx;
strcpy(newi.ifname, oldi->ifname);
strcpy(newi.sec, oldi->sec);
if (pd_load_if(pdc, &newi)) {
PD_ERR(pd_reload_if, "pd_load_if failed for %s\n", oldi->sec);
return 1;
}
if (pd_validate_if(pdc, &newi)) {
PD_ERR(pd_reload_if1, "pd_validate_if failed for %s\n", oldi->sec);
return 1;
}
pd_compare_if(oldi, &newi, &status);
if (pd_reload4(pdc, oldi, &newi, &status)) {
PD_ERR(pd_reload_if2, "pd_reload4 failed for %s\n", oldi->sec);
return 1;
}
if (pd_reload6(pdc, oldi, &newi, &status)) {
PD_ERR(pd_reload_if3, "pd_reload6 failed for %s\n", oldi->sec);
return 1;
}
if (pd_write_if(pdc, &newi, &status, force)) {
PD_ERR(pd_reload_if4, "pd_write_if failed for %s\n", oldi->sec);
return 1;
}
pd_update_if(oldi, &newi, &status);
PD_ERR(pd_reload_if_m606, "pd_reload_if(%s):After update, oldi flags=%x, newi flags=%x\n", oldi->ifname, oldi->flags, newi.flags);
if (!pdi_test(oldi, PDI_AUTO))
return 0;
if (pd_up(pdc, oldi)) {
PD_ERR(pd_reload_if5, "pd_up failed for %s\n", oldi->sec);
// return 1;
}
if (pd_up6(pdc, oldi)) {
PD_ERR(pd_reload_if6, "pd_up6 failed failed for %s\n", oldi->sec);
#ifndef CONFIG_NETWORK_WAN_IN_PIPE
return 1;
#endif
}
#ifdef CONFIG_NETWORK_WAN_IN_PIPE
if (!strncasecmp(oldi->sec, "wan", 3)) {
ret = pd_nw_wan_config(oldi, false);
pd_set(&pdc->status, PDC_NETWORK_CHANGED);
}
if (!strncasecmp(newi.sec, "wan", 3)) {
ret |= pd_nw_wan_config(&newi, true);
pd_set(&pdc->status, PDC_NETWORK_CHANGED);
}
#endif
return ret;
}
static int pd_reload_all(struct pd_context *pdc)
{
struct pd_iface *pdi;
list_for_each_entry(pdi, &pd.plist, list) {
if (pd_reload_if(pdc, pdi, false))
PD_ERR(pd_reload_all, "reload if %s failed\n", pdi->sec);
}
return 0;
}
static int pd_reload(void)
{
int rc;
struct pd_context *pdc = pd_context_get();
PD_INFO(pd_reload, "reloading pipe configuration file\n");
if (pd_context_init(pdc)) {
PD_ERR(pd_reload1, "pd_context_init failed\n");
return 1;
}
if ((rc = pd_reload_all(pdc)))
PD_ERR(pd_reload2, "pd_reload_all failed\n");
pd_context_done(pdc, !rc);
return 0;
}
static int pd_reload_ubus(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
UNUSEDPARAM(ctx);
UNUSEDPARAM(obj);
UNUSEDPARAM(req);
UNUSEDPARAM(method);
UNUSEDPARAM(msg);
pthread_mutex_lock(&gPipedLockMutex);
if (pd_reload())
PD_ERR(pd_reload_ubus, "pd_reload failed\n");
pthread_mutex_unlock(&gPipedLockMutex);
return UBUS_STATUS_OK;
}
static int pd_up(struct pd_context *pdc, struct pd_iface *pdi)
{
struct pd_path *path = &pdc->path;
PD_INFO(pd_up, "ifup for %s:%s, flags=%x\n", pdi->ifname, pdi->sec, pdi->flags);
if (pdi_test(pdi, PDI_UP))
return 0;
if (!pdi_test(pdi, PDI_IPADDR)) {
PD_ERR(pd_up1, "ip address not configured %s\n", pdi->ifname);
return 1;
}
pd_path_set_iface(path, pdi->ifname);
pd_path_set_file(path, MPIPE_FNAME_UP);
PD_INFO(pd_up991, "pd_up: path=%s\n", path->buf);
if (pd_write_bool(path, true)) {
PD_ERR(pd_up2, "file write failed\n");
return 1;
}
if (pdi_test(pdi, PDI_LAN))
goto done;
if (pdi_test(pdi, PDI_DEDICATEAPN)) {
PD_ERR(pd_up1021, "interface(%s) is a dedicate PDP\n", pdi->ifname);
goto done;
}
if (pd_has_prim4()) {
PD_ERR(pd_up3, "prim if already set %s\n", pdi->ifname);
goto done;
}
if (!pdi_test(pdi, PDI_ZVLAN_ID)) {
if (pd_nw_add(pdc, pdi)) {
PD_ERR(pd_up4, "pd_nw_add failed %s\n", pdi->ifname);
goto err;
}
if (pd_dhcp_add(pdc, pdi)) {
PD_ERR(pd_up5, "pd_dhcp_add failed %s\n", pdi->ifname);
goto err;
}
if (pd_dns4_add(pdc, pdi)) {
PD_ERR(pd_up1021, "pd_dns4_add failed %s\n", pdi->ifname);
goto err;
}
}
done:
pdi_set(pdi, PDI_UP);
if (!pdi_test(pdi, PDI_LAN)) {
PD_ERR(pd_up760, "pd_up add %s into uplist\n", pdi->ifname);
list_add_tail(&pdi->uplist, &pd.uplist);
}
return 0;
err:
pd_path_set_iface(path, pdi->ifname);
pd_path_set_file(path, MPIPE_FNAME_UP);
pd_write_bool(path, false);
return 1;
}
static int pd_up6(struct pd_context *pdc, struct pd_iface *pdi)
{
struct pd_path *path = &pdc->path;
PD_INFO(pd_up6, "ifup ipv6 for %s:%s, flags=%x\n", pdi->ifname, pdi->sec, pdi->flags);
if (pdi_test(pdi, PDI_UP6))
return 0;
if (!pdi_test(pdi, PDI_IP6ADDR) && !pdi_test(pdi, PDI_LAN)) {
PD_ERR(pd_up61, "ipv6 address not confiured %s\n", pdi->ifname);
return 1;
}
pd_path_set_iface(path, pdi->ifname);
// PD_INFO(pd_up6751, "pd_up6: path for %s-->%s, flags=%x\n", path->iptr, path->fptr, pdi->flags);
pd_path_set_file(path, MPIPE_FNAME_UP6);
PD_INFO(pd_up6751, "pd_up6: path for %s-->%s, buf=%s, flags=%x\n", path->iptr, path->fptr, path->buf, pdi->flags);
if (pd_write_bool(path, true)) {
PD_ERR(pd_up62, "file write failed\n");
return 1;
}
if (pdi_test(pdi, PDI_LAN))
goto done;
if (pd_has_prim6()) {
PD_ERR(pd_up63, "prim if already set %s\n", pdi->ifname);
goto done;
}
if (pd_dns6_add(pdc, pdi)) {
PD_ERR(pd_up64, "pd_dns4_add failed %s\n", pdi->ifname);
goto err;
}
done:
pdi_set(pdi, PDI_UP6);
if (!pdi_test(pdi, PDI_LAN))
list_add_tail(&pdi->up6list, &pd.up6list);
if (!started_flag) {
pd_system("ifup lan");
started_flag = 1;
}
return 0;
err:
pd_path_set_iface(path, pdi->ifname);
pd_path_set_file(path, MPIPE_FNAME_UP6);
pd_write_bool(path, false);
return 1;
}
static int pd_down(struct pd_context *pdc, struct pd_iface *pdi)
{
struct pd_path *path = &pdc->path;
struct pd_iface *npdi = NULL;
PD_INFO(pd_down, "pd_down: ifdown for %s\n", pdi->sec);
if (!pdi_test(pdi, PDI_UP))
return 0;
pd_path_set_iface(path, pdi->ifname);
pd_path_set_file(path, MPIPE_FNAME_UP);
if (pd_write_bool(path, false)) {
PD_ERR(pd_down1, "pd_write_bool for %s\n", path->buf);
return 1;
}
if (pdi_test(pdi, PDI_LAN))
goto done;
list_del_init(&pdi->uplist);
PD_ERR(pd_down852, "device %s is removed from up list\n", pdi->ifname);
if (!pd_is_prim4(pdi)) {
PD_ERR(pd_down833, "device not primary %s\n", pdi->ifname);
pdi_clear(pdi, PDI_DEDICATEAPN);
goto done;
}
if (!list_empty(&pd.uplist)) {
struct pd_iface *tnpdi;
list_for_each_entry(tnpdi, &pd.uplist, uplist) {
if (pdi_test(tnpdi, PDI_UP) && !pdi_test(tnpdi, PDI_DEDICATEAPN)) {
PD_ERR(pd_down858, "found another device %s can be a primary interface, flags=%x\n", tnpdi->ifname, tnpdi->flags);
npdi = tnpdi;
break;
}
}
}
if (npdi) {
PD_ERR(pd_down866, "change primary device from %s to %s\n", pdi->ifname, npdi->ifname);
if (pd_nw_change(pdc, pdi, npdi)) {
PD_ERR(pd_down2, "pd_nw_change failed for %s\n", pdi->ifname);
goto err;
}
if (pd_dhcp_change(pdc, pdi, npdi)) {
PD_ERR(pd_down3, "pd_dhcp_change failed for %s\n", pdi->ifname);
goto err;
}
if (pd_dns4_change(pdc, pdi, npdi)) {
PD_ERR(pd_down4, "pd_dns4_change failed for %s\n", pdi->ifname);
goto err;
}
} else {
PD_ERR(pd_down880, "delete primary device from %s\n", pdi->ifname);
if (pd_nw_del(pdc, pdi)) {
PD_ERR(pd_down5, "pd_nw_del failed for %s\n", pdi->ifname);
goto err;
}
if (pd_dhcp_del(pdc, pdi)) {
PD_ERR(pd_down6, "pd_dhcp_del failed for %s\n", pdi->ifname);
goto err;
}
if (pd_dns4_del(pdc, pdi)) {
PD_ERR(pd_down7, "pd_dns4_del failed for %s\n", pdi->ifname);
goto err;
}
}
done:
pdi_clear(pdi, PDI_UP);
return 0;
err:
pd_path_set_iface(path, pdi->ifname);
pd_path_set_file(path, MPIPE_FNAME_UP);
pd_write_bool(path, true);
if (!pdi_test(pdi, PDI_LAN))
list_add(&pdi->uplist, &pd.uplist);
return 1;
}
static int pd_down6(struct pd_context *pdc, struct pd_iface *pdi)
{
struct pd_path *path = &pdc->path;
struct pd_iface *npdi = NULL;
PD_INFO(pd_down6, "ifdown ipv6 for %s\n", pdi->sec);
if (!pdi_test(pdi, PDI_UP6))
return 0;
pd_path_set_iface(path, pdi->ifname);
pd_path_set_file(path, MPIPE_FNAME_UP6);
if (pd_write_bool(path, false)) {
PD_ERR(pd_down61, "file write failed\n");
return 1;
}
if (pdi_test(pdi, PDI_LAN))
goto done;
list_del_init(&pdi->up6list);
if (!pd_is_prim6(pdi)) {
PD_ERR(pd_down62, "device not primary %s\n", pdi->ifname);
pdi_clear(pdi, PDI_DEDICATEAPN);
goto done;
}
if (!list_empty(&pd.up6list))
npdi = list_first_entry(&pd.up6list, struct pd_iface, up6list);
if (npdi) {
if (pd_dns6_change(pdc, pdi, npdi)) {
PD_ERR(pd_down63, "pd_nw_change failed %s\n", pdi->ifname);
goto err;
}
} else {
if (pd_dns6_del(pdc, pdi)) {
PD_ERR(pd_down64, "pd_dns4_del failed %s\n", pdi->ifname);
goto err;
}
}
done:
pdi_clear(pdi, PDI_UP6);
return 0;
err:
pd_path_set_iface(path, pdi->ifname);
pd_path_set_file(path, MPIPE_FNAME_UP);
pd_write_bool(path, true);
if (!pdi_test(pdi, PDI_LAN))
list_add(&pdi->up6list, &pd.up6list);
return 1;
}
static int pd_updown(struct pd_iface *pdi, bool up, bool ipv6)
{
int rc = 0;
struct pd_context *pdc = pd_context_get();
if (pd_context_init(pdc)) {
PD_ERR(pd_updown, "pd_context_init failed\n");
return 1;
}
// if (pd_reload_all(pdc)) {
if (pd_reload_if(pdc, pdi, false)) {
PD_ERR(pd_updown1, "pd_reload_if failed\n");
//return 1;
}
PD_ERR(pd_updown928, "pd_updown:before %s for %s, ipv6: %d, flags=0x%x\n", up ? "UP" : "DOWN",
pdi->ifname, ipv6, pdi->flags);
if (up && ipv6)
rc = pd_up6(pdc, pdi);
else if (up && !ipv6)
rc = pd_up(pdc, pdi);
else if (!up && ipv6)
rc = pd_down6(pdc, pdi);
else
rc = pd_down(pdc, pdi);
PD_ERR(pd_updown938, "pd_updown:after %s for %s, ipv6: %d, flags=0x%x\n", up ? "UP" : "DOWN",
pdi->ifname, ipv6, pdi->flags);
if (rc)
PD_ERR(pd_updown2, "%s failed for %s, ipv6: %d\n", up ? "UP" : "DOWN",
pdi->ifname, ipv6);
pd_context_done(pdc, !rc);
return rc;
}
static int pd_updown_ubus(struct blob_attr *msg, bool up)
{
struct blob_attr *tb[__IF_UPDOWN_ARG_MAX];
char *sec;
char buf[MAX_SEC_NAME];
int rc;
bool ipv6;
struct pd_iface *pdi;
if ((rc = pd_updown_parse(msg, tb, &sec))) {
PD_ERR(pd_updown_ubus, "ubus parse failed\n");
return rc;
}
PD_INFO(pd_updown_ubus1, "UBUS %s %s\n", up ? "UP" : "DOWN", sec);
if ((rc = pd_check_sec(sec, &ipv6))) {
PD_ERR(pd_updown_ubus2, "section validation failed\n");
return UBUS_STATUS_OK;
}
if (ipv6) {
pd_sec_6to4(sec, buf);
sec = buf;
}
pdi = pdi_get_by_sec(sec);
if (!pdi) {
PD_ERR(pd_updown_ubus3, "could not get interface\n");
return UBUS_STATUS_OK;
}
if (pd_updown(pdi, up, ipv6))
PD_ERR(pd_updown_ubus4, "updown failed\n");
return UBUS_STATUS_OK;
}
static int pd_up_ubus(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
UNUSEDPARAM(ctx);
UNUSEDPARAM(obj);
UNUSEDPARAM(req);
UNUSEDPARAM(method);
UNUSEDPARAM(msg);
int ret;
pthread_mutex_lock(&gPipedLockMutex);
ret = pd_updown_ubus(msg, true);
pthread_mutex_unlock(&gPipedLockMutex);
return ret;
}
static int pd_down_ubus(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
UNUSEDPARAM(ctx);
UNUSEDPARAM(obj);
UNUSEDPARAM(req);
UNUSEDPARAM(method);
UNUSEDPARAM(msg);
int ret;
pthread_mutex_lock(&gPipedLockMutex);
ret = pd_updown_ubus(msg, false);
pthread_mutex_unlock(&gPipedLockMutex);
return ret;
}
static int pd_stop_ubus(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
UNUSEDPARAM(ctx);
UNUSEDPARAM(obj);
UNUSEDPARAM(req);
UNUSEDPARAM(method);
UNUSEDPARAM(msg);
pthread_mutex_lock(&gPipedLockMutex);
PD_INFO(pd_stop_ubus, "UBUS stop\n");
pthread_mutex_unlock(&gPipedLockMutex);
uloop_cancelled = true;
return UBUS_STATUS_OK;
}
void pd_uevent_cb(char *ifname, bool added)
{
int rc;
struct pd_iface *pdi;
struct pd_context *pdc;
PD_INFO(pd_uevent_cb, "got event for %s, added: %d\n", ifname, added);
pdi = pdi_get_by_ifname(ifname);
if (!pdi) {
PD_ERR(pd_uevent_cb1, "pdi_get_by_ifname failed for (%s)\n", ifname);
return;
}
if (!added) {
pdi_clear(pdi, PDI_UP);
pdi_clear(pdi, PDI_UP6);
return;
}
pdc = pd_context_get();
if (pd_context_init(pdc)) {
PD_ERR(pd_uevent_cb2, "pd_context_init failed\n");
return;
}
if ((rc = pd_reload_if(pdc, pdi, true)))
PD_ERR(pd_uevent_cb3, "pd_reload_if for (%s)\n", ifname);
pd_context_done(pdc, !rc);
}
static int pd_clean(void)
{
struct pd_context *pdc = pd_context_get();
if (pd_context_init(pdc)) {
PD_ERR(pd_clean, "context allocation failed\n");
return 1;
}
pd_nw_clean(pdc);
pd_dhcp_clean(pdc);
pd_dns_clean(pdc);
pd_context_done(pdc, pdc->status);
return 0;
}
static void pd_dump_pdi(struct pd_iface *pdi)
{
UNUSEDPARAM(pdi);
PD_INFO(pd_dump_pdi, "%d] %s - %s: up (%d) up6 (%d) lan (%d) auto %d\n", pdi->idx,
pdi->sec, pdi->ifname, pdi_test(pdi, PDI_UP),
pdi_test(pdi, PDI_UP6), pdi_test(pdi, PDI_LAN),
pdi_test(pdi, PDI_AUTO));
PD_INFO(pd_dump_pdi1, "ipv4: %s dns4p %s, dns4s %s\n", pdi->ipaddr, pdi->dns4p,
pdi->dns4s);
PD_INFO(pd_dump_pdi2, "ipv6: %s dns6p %s, dns6s %s\n", pdi->ip6addr, pdi->dns6p,
pdi->dns6s);
PD_INFO(pd_dump_pdi3, "flags: ip (%d) dns4p (%d) dns4s (%d) ip6 (%d) dns6p (%d) dns6s (%d)\n",
pdi_test(pdi, PDI_IPADDR), pdi_test(pdi, PDI_DNS4P),
pdi_test(pdi, PDI_DNS4S), pdi_test(pdi, PDI_IP6ADDR),
pdi_test(pdi, PDI_DNS6P), pdi_test(pdi, PDI_DNS6S));
}
static int pd_status_ubus(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
char buf[64];
char *s = buf;
struct pd_iface *pdi;
UNUSEDPARAM(ctx);
UNUSEDPARAM(obj);
UNUSEDPARAM(req);
UNUSEDPARAM(method);
UNUSEDPARAM(msg);
PD_INFO(pd_status_ubus, "PIPED DEVICE LIST\n\n");
list_for_each_entry(pdi, &pd.plist, list) {
pd_dump_pdi(pdi);
}
PD_INFO(pd_status_ubus0, "PIPED UP LIST\n\n");
list_for_each_entry(pdi, &pd.uplist, uplist) {
s += sprintf(s, " (%d) ", pdi->idx);
}
*(s + 1) = '\0';
PD_INFO(pd_status_ubus1, "%s\n", buf);
s = buf;
PD_INFO(pd_status_ubus2, "PIPED UP6 LIST\n\n");
list_for_each_entry(pdi, &pd.up6list, up6list) {
s += sprintf(s, " (%d) ", pdi->idx);
}
*(s + 1) = '\0';
PD_INFO(pd_status_ubus3, "%s\n", buf);
PD_INFO(pd_status_ubus4, "PIPED INFO\n");
PD_INFO(pd_status_ubus5, "lan (%d) rand_gw (%d)\n", pd.lan->idx, pd.rand_gw);
return UBUS_STATUS_OK;
}
static int pd_init(bool rand_gw)
{
int i = 0;
bool ipv6, wan;
struct uci_context *c;
struct uci_package *p;
struct uci_section *s;
struct uci_element *e1, *e2;
struct uci_option *o;
struct pd_iface *pdi;
char *ifname = NULL;
memset(&pd.pdc, 0, sizeof(struct pd_context));
pd.rand_gw = rand_gw;
pd.lan = NULL;
INIT_LIST_HEAD(&pd.plist);
INIT_LIST_HEAD(&pd.uplist);
INIT_LIST_HEAD(&pd.up6list);
c = uci_alloc_context();
if (!c) {
PD_ERR(pd_init, "uci_alloc_context failed\n");
return 1;
}
uci_load(c, UCI_PKG_PIPE, &p);
if (!p) {
PD_ERR(pd_init1, "uci_load failed");
return 1;
}
uci_foreach_element(&p->sections, e1) {
s = uci_to_section(e1);
if (strcmp(s->type, UCI_SEC_INTERFACE))
continue;
if ((wan = strcmp(s->e.name, UCI_SEC_LAN)) &&
!str_starts_with(s->e.name, UCI_SEC_WAN))
continue;
if (pd_check_sec(s->e.name, &ipv6) || ipv6)
continue;
uci_foreach_element(&s->options, e2) {
o = uci_to_option(e2);
if (!strcmp(e2->name, UCI_OPT_DEVICE)) {
ifname = o->v.string;
break;
}
}
if (!ifname)
continue;
pdi = malloc(sizeof(struct pd_iface));
memset(pdi, 0, sizeof(*pdi));
pdi->idx = i++;
if (!wan) {
pd.lan = pdi;
pdi_set(pdi, PDI_LAN);
}
strncpy(pdi->sec, s->e.name, sizeof(pdi->sec) - 1);
strncpy(pdi->ifname, ifname, sizeof(pdi->ifname) - 1);
INIT_LIST_HEAD(&pdi->list);
INIT_LIST_HEAD(&pdi->uplist);
INIT_LIST_HEAD(&pdi->up6list);
list_add_tail(&pdi->list, &pd.plist);
PD_INFO(pd_init2, "added interface %s\n", pdi->sec);
}
uci_unload(c, p);
uci_free_context(c);
if (!pd.lan) {
PD_ERR(pd_init3, "no lan secion found\n");
return 1;
}
pd_path_init(&pd.pdc.path);
pd_uevent_init();
return 0;
}
static int pd_main(bool rand_gw, bool autoload)
{
int rc;
if (pd_init(rand_gw)) {
PD_ERR(pd_main, "pd_init failed\n");
return 1;
}
rc = ubus_add_object(pd.u_ctx, &pd_obj);
if (rc) {
PD_ERR(pd_main1, "ubus_add_object failed: %s\n", ubus_strerror(rc));
return rc;
}
#ifdef PIPED_CLEAN_ON_START
if (pd_clean()) {
PD_ERR(pd_main2, "pd_clean failed\n");
return 1;
}
#endif
if (autoload && pd_reload()) {
PD_ERR(pd_main3, "pd_reload failed\n");
return 1;
}
uloop_run();
rc = ubus_remove_object(pd.u_ctx, &pd_obj);
return rc;
}
int main(int argc, char **argv)
{
struct pd_iface *pdi = NULL, *tmp = NULL;
bool rand_gw = false, autoload = false;
int i, rc = 0;
for (i = 1; i < argc; i++) {
if (!strcmp(argv[i], "-r"))
rand_gw = true;
if (!strcmp(argv[i], "-a"))
autoload = true;
}
set_service_log_tag("PIPED");
g_pipe_sock_ip = socket(AF_INET, SOCK_DGRAM, 0);
PD_INFO(main, "starting piped with autostart (%d), random gw (%d), pipe_socket=%d\n",
autoload, rand_gw, g_pipe_sock_ip);
uloop_init();
pd.u_ctx = ubus_connect(NULL);
if (!pd.u_ctx) {
PD_ERR(main1, "Failed to connect to ubus\n");
goto done;
}
ubus_add_uloop(pd.u_ctx);
if (pipe(pd_pipe_fd) < 0)
{
PD_INFO(main1555,"piped open pipe failed: %s!\n", strerror(errno));
}
rc = pd_main(rand_gw, autoload);
if (rc)
PD_ERR(main2, "pd_main failed rc=%s\n", ubus_strerror(rc));
ubus_free(pd.u_ctx);
list_for_each_entry_safe(pdi, tmp, &pd.plist, list) {
list_del(&pdi->list);
free(pdi);
}
done:
uloop_done();
return 0;
}