| |
| #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; |
| } |