static void up_iface(void) { struct ifreq ifrequest; if (!G.iface_exists) return; set_ifreq_to_ifname(&ifrequest); if (network_ioctl(SIOCGIFFLAGS, &ifrequest, "getting interface flags") < 0) { G.iface_exists = 0; return; } if (!(ifrequest.ifr_flags & IFF_UP)) { ifrequest.ifr_flags |= IFF_UP; /* Let user know we mess up with interface */ bb_error_msg("upping interface"); if (network_ioctl(SIOCSIFFLAGS, &ifrequest, "setting interface flags") < 0) xfunc_die(); } #if 0 /* why do we mess with IP addr? It's not our business */ if (network_ioctl(SIOCGIFADDR, &ifrequest, "can't get interface address") < 0) { } else if (ifrequest.ifr_addr.sa_family != AF_INET) { bb_perror_msg("the interface is not IP-based"); } else { ((struct sockaddr_in*)(&ifrequest.ifr_addr))->sin_addr.s_addr = INADDR_ANY; network_ioctl(SIOCSIFADDR, &ifrequest, "can't set interface address"); } network_ioctl(SIOCGIFFLAGS, &ifrequest, "can't get interface flags"); #endif }
static void maybe_up_new_iface(void) { if (!(option_mask32 & FLAG_NO_AUTO)) up_iface(); #if 0 /* bloat */ struct ifreq ifrequest; struct ethtool_drvinfo driver_info; set_ifreq_to_ifname(&ifrequest); driver_info.cmd = ETHTOOL_GDRVINFO; ifrequest.ifr_data = &driver_info; if (network_ioctl(SIOCETHTOOL, &ifrequest, NULL) == 0) { char buf[sizeof("/xx:xx:xx:xx:xx:xx")]; /* Get MAC */ buf[0] = '\0'; set_ifreq_to_ifname(&ifrequest); if (network_ioctl(SIOCGIFHWADDR, &ifrequest, NULL) == 0) { sprintf(buf, "/%02X:%02X:%02X:%02X:%02X:%02X", (uint8_t)(ifrequest.ifr_hwaddr.sa_data[0]), (uint8_t)(ifrequest.ifr_hwaddr.sa_data[1]), (uint8_t)(ifrequest.ifr_hwaddr.sa_data[2]), (uint8_t)(ifrequest.ifr_hwaddr.sa_data[3]), (uint8_t)(ifrequest.ifr_hwaddr.sa_data[4]), (uint8_t)(ifrequest.ifr_hwaddr.sa_data[5])); } bb_error_msg("using interface %s%s with driver<%s> (version: %s)", G.iface, buf, driver_info.driver, driver_info.version); } #endif if (G.api_mode[0] == 'a') G.api_method_num = API_AUTO; }
static smallint detect_link_wlan(void) { int i; struct iwreq iwrequest; uint8_t mac[ETH_ALEN]; memset(&iwrequest, 0, sizeof(iwrequest)); strncpy_IFNAMSIZ(iwrequest.ifr_ifrn.ifrn_name, G.iface); if (network_ioctl(SIOCGIWAP, &iwrequest, "SIOCGIWAP") < 0) { return IFSTATUS_ERR; } memcpy(mac, &iwrequest.u.ap_addr.sa_data, ETH_ALEN); if (mac[0] == 0xFF || mac[0] == 0x44 || mac[0] == 0x00) { for (i = 1; i < ETH_ALEN; ++i) { if (mac[i] != mac[0]) return IFSTATUS_UP; } return IFSTATUS_DOWN; } return IFSTATUS_UP; }
static smallint detect_link_priv(void) { struct ifreq ifreq; struct mii_ioctl_data *mii = (void *)&ifreq.ifr_data; set_ifreq_to_ifname(&ifreq); if (network_ioctl(SIOCDEVPRIVATE, &ifreq, "SIOCDEVPRIVATE failed") < 0) { return IFSTATUS_ERR; } mii->reg_num = 1; if (network_ioctl(SIOCDEVPRIVATE+1, &ifreq, "SIOCDEVPRIVATE+1 failed") < 0) { return IFSTATUS_ERR; } return (mii->val_out & 0x0004) ? IFSTATUS_UP : IFSTATUS_DOWN; }
static smallint detect_link_priv(void) { /* char buffer instead of bona-fide struct avoids aliasing warning */ char buf[sizeof(struct ifreq)]; struct ifreq *const ifreq = (void *)buf; struct mii_ioctl_data *mii = (void *)&ifreq->ifr_data; set_ifreq_to_ifname(ifreq); if (network_ioctl(SIOCDEVPRIVATE, ifreq, "SIOCDEVPRIVATE") < 0) { return IFSTATUS_ERR; } mii->reg_num = 1; if (network_ioctl(SIOCDEVPRIVATE+1, ifreq, "SIOCDEVPRIVATE+1") < 0) { return IFSTATUS_ERR; } return (mii->val_out & 0x0004) ? IFSTATUS_UP : IFSTATUS_DOWN; }
static smallint detect_link_ethtool(void) { struct ifreq ifreq; struct ethtool_value edata; set_ifreq_to_ifname(&ifreq); edata.cmd = ETHTOOL_GLINK; ifreq.ifr_data = (void*) &edata; if (network_ioctl(SIOCETHTOOL, &ifreq, "ETHTOOL_GLINK") < 0) { return IFSTATUS_ERR; } return edata.data ? IFSTATUS_UP : IFSTATUS_DOWN; }
static smallint detect_link_iff(void) { struct ifreq ifreq; set_ifreq_to_ifname(&ifreq); if (network_ioctl(SIOCGIFFLAGS, &ifreq, "SIOCGIFFLAGS") < 0) { return IFSTATUS_ERR; } /* If IFF_UP is not set (interface is down), IFF_RUNNING is never set * regardless of link status. Simply continue to report last status - * no point in reporting spurious link downs if interface is disabled * by admin. When/if it will be brought up, * we'll report real link status. */ if (!(ifreq.ifr_flags & IFF_UP) && G.iface_last_status != IFSTATUS_ERR) return G.iface_last_status; return (ifreq.ifr_flags & IFF_RUNNING) ? IFSTATUS_UP : IFSTATUS_DOWN; }
int ifplugd_main(int argc UNUSED_PARAM, char **argv) { int iface_status; int delay_time; const char *iface_status_str; struct pollfd netlink_pollfd[1]; unsigned opts; const char *api_mode_found; #if ENABLE_FEATURE_PIDFILE char *pidfile_name; pid_t pid_from_pidfile; #endif INIT_G(); opt_complementary = "t+:u+:d+"; opts = getopt32(argv, OPTION_STR, &G.iface, &G.script_name, &G.poll_time, &G.delay_up, &G.delay_down, &G.api_mode, &G.extra_arg); G.poll_time *= 1000; applet_name = xasprintf("ifplugd(%s)", G.iface); #if ENABLE_FEATURE_PIDFILE pidfile_name = xasprintf(CONFIG_PID_FILE_PATH "/ifplugd.%s.pid", G.iface); pid_from_pidfile = read_pid(pidfile_name); if (opts & FLAG_KILL) { if (pid_from_pidfile > 0) /* Upstream tool use SIGINT for -k */ kill(pid_from_pidfile, SIGINT); return EXIT_SUCCESS; } if (pid_from_pidfile > 0 && kill(pid_from_pidfile, 0) == 0) bb_error_msg_and_die("daemon already running"); #endif api_mode_found = strchr(api_modes, G.api_mode[0]); if (!api_mode_found) bb_error_msg_and_die("unknown API mode '%s'", G.api_mode); G.api_method_num = api_mode_found - api_modes; if (!(opts & FLAG_NO_DAEMON)) bb_daemonize_or_rexec(DAEMON_CHDIR_ROOT, argv); xmove_fd(xsocket(AF_INET, SOCK_DGRAM, 0), ioctl_fd); if (opts & FLAG_MONITOR) { struct sockaddr_nl addr; int fd = xsocket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); memset(&addr, 0, sizeof(addr)); addr.nl_family = AF_NETLINK; addr.nl_groups = RTMGRP_LINK; addr.nl_pid = getpid(); xbind(fd, (struct sockaddr*)&addr, sizeof(addr)); xmove_fd(fd, netlink_fd); } write_pidfile(pidfile_name); /* this can't be moved before socket creation */ if (!(opts & FLAG_NO_SYSLOG)) { openlog(applet_name, 0, LOG_DAEMON); logmode |= LOGMODE_SYSLOG; } bb_signals(0 | (1 << SIGINT ) | (1 << SIGTERM) | (1 << SIGQUIT) | (1 << SIGHUP ) /* why we ignore it? */ /* | (1 << SIGCHLD) - run_script does not use it anymore */ , record_signo); bb_error_msg("started: %s", bb_banner); if (opts & FLAG_MONITOR) { struct ifreq ifrequest; set_ifreq_to_ifname(&ifrequest); G.iface_exists = (network_ioctl(SIOCGIFINDEX, &ifrequest, NULL) == 0); } if (G.iface_exists) maybe_up_new_iface(); iface_status = detect_link(); if (iface_status == IFSTATUS_ERR) goto exiting; iface_status_str = strstatus(iface_status); if (opts & FLAG_MONITOR) { bb_error_msg("interface %s", G.iface_exists ? "exists" : "doesn't exist, waiting"); } /* else we assume it always exists, but don't mislead user * by potentially lying that it really exists */ if (G.iface_exists) { bb_error_msg("link is %s", iface_status_str); } if ((!(opts & FLAG_NO_STARTUP) && iface_status == IFSTATUS_UP ) || (opts & FLAG_INITIAL_DOWN) ) { if (run_script(iface_status_str) != 0) goto exiting; } /* Main loop */ netlink_pollfd[0].fd = netlink_fd; netlink_pollfd[0].events = POLLIN; delay_time = 0; while (1) { int iface_status_old; int iface_exists_old; switch (bb_got_signal) { case SIGINT: case SIGTERM: bb_got_signal = 0; goto cleanup; case SIGQUIT: bb_got_signal = 0; goto exiting; default: bb_got_signal = 0; break; } if (poll(netlink_pollfd, (opts & FLAG_MONITOR) ? 1 : 0, G.poll_time ) < 0 ) { if (errno == EINTR) continue; bb_perror_msg("poll"); goto exiting; } iface_status_old = iface_status; iface_exists_old = G.iface_exists; if ((opts & FLAG_MONITOR) && (netlink_pollfd[0].revents & POLLIN) ) { G.iface_exists = check_existence_through_netlink(); if (G.iface_exists < 0) /* error */ goto exiting; if (iface_exists_old != G.iface_exists) { bb_error_msg("interface %sappeared", G.iface_exists ? "" : "dis"); if (G.iface_exists) maybe_up_new_iface(); } } /* note: if !G.iface_exists, returns DOWN */ iface_status = detect_link(); if (iface_status == IFSTATUS_ERR) { if (!(opts & FLAG_MONITOR)) goto exiting; iface_status = IFSTATUS_DOWN; } iface_status_str = strstatus(iface_status); if (iface_status_old != iface_status) { bb_error_msg("link is %s", iface_status_str); if (delay_time) { /* link restored its old status before * we run script. don't run the script: */ delay_time = 0; } else { delay_time = monotonic_sec(); if (iface_status == IFSTATUS_UP) delay_time += G.delay_up; if (iface_status == IFSTATUS_DOWN) delay_time += G.delay_down; if (delay_time == 0) delay_time++; } } if (delay_time && (int)(monotonic_sec() - delay_time) >= 0) { delay_time = 0; if (run_script(iface_status_str) != 0) goto exiting; } } /* while (1) */ cleanup: if (!(opts & FLAG_NO_SHUTDOWN) && (iface_status == IFSTATUS_UP || (iface_status == IFSTATUS_DOWN && delay_time) ) ) { setenv(IFPLUGD_ENV_PREVIOUS, strstatus(iface_status), 1); setenv(IFPLUGD_ENV_CURRENT, strstatus(-1), 1); run_script("down\0up"); /* reusing string */ } exiting: remove_pidfile(pidfile_name); bb_error_msg_and_die("exiting"); }