aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenis Vlasenko <vda.linux@googlemail.com>2009-04-26 01:08:51 +0000
committerDenis Vlasenko <vda.linux@googlemail.com>2009-04-26 01:08:51 +0000
commit71c165780ac83b0562a7cbc3b57e87807f02055b (patch)
treeac33a1f5952103df64846103301c672e32b38606
parent5bf59c104af94e918ce879352f27b40099bf51be (diff)
downloadbusybox-w32-71c165780ac83b0562a7cbc3b57e87807f02055b.tar.gz
busybox-w32-71c165780ac83b0562a7cbc3b57e87807f02055b.tar.bz2
busybox-w32-71c165780ac83b0562a7cbc3b57e87807f02055b.zip
ifplugd: new applet by Maksym Kryzhanovskyy (xmaks AT email.cz)
+3k code and 0.5k in messages. Most of the bloat due to compat :(
-rw-r--r--include/applets.h1
-rw-r--r--include/usage.h57
-rw-r--r--networking/Config.in6
-rw-r--r--networking/Kbuild1
-rw-r--r--networking/ifplugd.c810
5 files changed, 859 insertions, 16 deletions
diff --git a/include/applets.h b/include/applets.h
index 820aaffe2..a41f75e31 100644
--- a/include/applets.h
+++ b/include/applets.h
@@ -189,6 +189,7 @@ IF_ID(APPLET(id, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
189IF_IFCONFIG(APPLET(ifconfig, _BB_DIR_SBIN, _BB_SUID_NEVER)) 189IF_IFCONFIG(APPLET(ifconfig, _BB_DIR_SBIN, _BB_SUID_NEVER))
190IF_IFUPDOWN(APPLET_ODDNAME(ifdown, ifupdown, _BB_DIR_SBIN, _BB_SUID_NEVER, ifdown)) 190IF_IFUPDOWN(APPLET_ODDNAME(ifdown, ifupdown, _BB_DIR_SBIN, _BB_SUID_NEVER, ifdown))
191IF_IFENSLAVE(APPLET(ifenslave, _BB_DIR_SBIN, _BB_SUID_NEVER)) 191IF_IFENSLAVE(APPLET(ifenslave, _BB_DIR_SBIN, _BB_SUID_NEVER))
192IF_IFPLUGD(APPLET(ifplugd, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
192IF_IFUPDOWN(APPLET_ODDNAME(ifup, ifupdown, _BB_DIR_SBIN, _BB_SUID_NEVER, ifup)) 193IF_IFUPDOWN(APPLET_ODDNAME(ifup, ifupdown, _BB_DIR_SBIN, _BB_SUID_NEVER, ifup))
193IF_INETD(APPLET(inetd, _BB_DIR_USR_SBIN, _BB_SUID_NEVER)) 194IF_INETD(APPLET(inetd, _BB_DIR_USR_SBIN, _BB_SUID_NEVER))
194IF_INIT(APPLET(init, _BB_DIR_SBIN, _BB_SUID_NEVER)) 195IF_INIT(APPLET(init, _BB_DIR_SBIN, _BB_SUID_NEVER))
diff --git a/include/usage.h b/include/usage.h
index 7b88a2acc..3fb996126 100644
--- a/include/usage.h
+++ b/include/usage.h
@@ -1727,21 +1727,46 @@
1727/* "\n -r, --receive-slave Create a receive-only slave" */ 1727/* "\n -r, --receive-slave Create a receive-only slave" */
1728 1728
1729#define ifenslave_example_usage \ 1729#define ifenslave_example_usage \
1730 "To create a bond device, simply follow these three steps :\n" \ 1730 "To create a bond device, simply follow these three steps:\n" \
1731 "- ensure that the required drivers are properly loaded :\n" \ 1731 "- ensure that the required drivers are properly loaded:\n" \
1732 " # modprobe bonding ; modprobe <3c59x|eepro100|pcnet32|tulip|...>\n" \ 1732 " # modprobe bonding ; modprobe <3c59x|eepro100|pcnet32|tulip|...>\n" \
1733 "- assign an IP address to the bond device :\n" \ 1733 "- assign an IP address to the bond device:\n" \
1734 " # ifconfig bond0 <addr> netmask <mask> broadcast <bcast>\n" \ 1734 " # ifconfig bond0 <addr> netmask <mask> broadcast <bcast>\n" \
1735 "- attach all the interfaces you need to the bond device :\n" \ 1735 "- attach all the interfaces you need to the bond device:\n" \
1736 " # ifenslave bond0 eth0 eth1 eth2\n" \ 1736 " # ifenslave bond0 eth0 eth1 eth2\n" \
1737 " If bond0 didn't have a MAC address, it will take eth0's. Then, all\n" \ 1737 " If bond0 didn't have a MAC address, it will take eth0's. Then, all\n" \
1738 " interfaces attached AFTER this assignment will get the same MAC addr.\n\n" \ 1738 " interfaces attached AFTER this assignment will get the same MAC addr.\n\n" \
1739 " To detach a dead interface without setting the bond device down :\n" \ 1739 " To detach a dead interface without setting the bond device down:\n" \
1740 " # ifenslave -d bond0 eth1\n\n" \ 1740 " # ifenslave -d bond0 eth1\n\n" \
1741 " To set the bond device down and automatically release all the slaves :\n" \ 1741 " To set the bond device down and automatically release all the slaves:\n" \
1742 " # ifconfig bond0 down\n\n" \ 1742 " # ifconfig bond0 down\n\n" \
1743 " To change active slave :\n" \ 1743 " To change active slave:\n" \
1744 " # ifenslave -c bond0 eth0\n" \ 1744 " # ifenslave -c bond0 eth0\n" \
1745
1746#define ifplugd_trivial_usage \
1747 "[options]"
1748#define ifplugd_full_usage "\n\n" \
1749 "Network interface plug detection daemon.\n\n" \
1750 "Options:\n" \
1751 "\n -n Do not daemonize" \
1752 "\n -s Do not log to syslog" \
1753 "\n -i IFACE Interface" \
1754 "\n -f/-F Treat link detection error as link down/link up" \
1755 "\n (otherwise exit on error)" \
1756 "\n -a Do not up interface automatically" \
1757 "\n -M Monitor creation/destruction of interface" \
1758 "\n (otherwise it must exist)" \
1759 "\n -r PROG Script to run" \
1760 "\n -x ARG Extra argument for script" \
1761 "\n -I Don't exit on nonzero exit code from script" \
1762 "\n -p Don't run script on daemon startup" \
1763 "\n -q Don't run script on daemon quit" \
1764 "\n -l Run script on startup even if no cable is detected" \
1765 "\n -t SECS Poll time in seconds" \
1766 "\n -u SECS Delay before running script after link up" \
1767 "\n -d SECS Delay after link down" \
1768 "\n -m MODE API mode (mii, priv, ethtool, wlan, auto)" \
1769 "\n -k Kill running daemon" \
1745 1770
1746#define ifup_trivial_usage \ 1771#define ifup_trivial_usage \
1747 "[-ain"IF_FEATURE_IFUPDOWN_MAPPING("m")"vf] ifaces..." 1772 "[-ain"IF_FEATURE_IFUPDOWN_MAPPING("m")"vf] ifaces..."
@@ -4346,17 +4371,17 @@
4346 "CMD: {add|del|change|replace|show}\n" \ 4371 "CMD: {add|del|change|replace|show}\n" \
4347 "\n" \ 4372 "\n" \
4348 "qdisc [ handle QHANDLE ] [ root |"IF_FEATURE_TC_INGRESS(" ingress |")" parent CLASSID ]\n" \ 4373 "qdisc [ handle QHANDLE ] [ root |"IF_FEATURE_TC_INGRESS(" ingress |")" parent CLASSID ]\n" \
4349 /* "\t[ estimator INTERVAL TIME_CONSTANT ]\n" */ \ 4374 /* "[ estimator INTERVAL TIME_CONSTANT ]\n" */ \
4350 "\t[ [ QDISC_KIND ] [ help | OPTIONS ] ]\n" \ 4375 " [ [ QDISC_KIND ] [ help | OPTIONS ] ]\n" \
4351 "\tQDISC_KIND := { [p|b]fifo | tbf | prio | cbq | red | etc. }\n" \ 4376 " QDISC_KIND := { [p|b]fifo | tbf | prio | cbq | red | etc. }\n" \
4352 "qdisc show [ dev STRING ]"IF_FEATURE_TC_INGRESS(" [ingress]")"\n" \ 4377 "qdisc show [ dev STRING ]"IF_FEATURE_TC_INGRESS(" [ingress]")"\n" \
4353 "class [ classid CLASSID ] [ root | parent CLASSID ]\n" \ 4378 "class [ classid CLASSID ] [ root | parent CLASSID ]\n" \
4354 "\t[ [ QDISC_KIND ] [ help | OPTIONS ] ]\n" \ 4379 " [ [ QDISC_KIND ] [ help | OPTIONS ] ]\n" \
4355 "class show [ dev STRING ] [ root | parent CLASSID ]\n" \ 4380 "class show [ dev STRING ] [ root | parent CLASSID ]\n" \
4356 "filter [ pref PRIO ] [ protocol PROTO ]\n" \ 4381 "filter [ pref PRIO ] [ protocol PROTO ]\n" \
4357 /* "\t[ estimator INTERVAL TIME_CONSTANT ]\n" */ \ 4382 /* "\t[ estimator INTERVAL TIME_CONSTANT ]\n" */ \
4358 "\t[ root | classid CLASSID ] [ handle FILTERID ]\n" \ 4383 " [ root | classid CLASSID ] [ handle FILTERID ]\n" \
4359 "\t[ [ FILTER_TYPE ] [ help | OPTIONS ] ]\n" \ 4384 " [ [ FILTER_TYPE ] [ help | OPTIONS ] ]\n" \
4360 "filter show [ dev STRING ] [ root | parent CLASSID ]" 4385 "filter show [ dev STRING ] [ root | parent CLASSID ]"
4361 4386
4362#define tcpsvd_trivial_usage \ 4387#define tcpsvd_trivial_usage \
diff --git a/networking/Config.in b/networking/Config.in
index 62a21f738..9907ac907 100644
--- a/networking/Config.in
+++ b/networking/Config.in
@@ -303,6 +303,12 @@ config IFENSLAVE
303 Userspace application to bind several interfaces 303 Userspace application to bind several interfaces
304 to a logical interface (use with kernel bonding driver). 304 to a logical interface (use with kernel bonding driver).
305 305
306config IFPLUGD
307 bool "ifplugd"
308 default n
309 help
310 Network interface plug detection daemon.
311
306config IFUPDOWN 312config IFUPDOWN
307 bool "ifupdown" 313 bool "ifupdown"
308 default n 314 default n
diff --git a/networking/Kbuild b/networking/Kbuild
index d632774ff..866d42f6b 100644
--- a/networking/Kbuild
+++ b/networking/Kbuild
@@ -18,6 +18,7 @@ lib-$(CONFIG_HOSTNAME) += hostname.o
18lib-$(CONFIG_HTTPD) += httpd.o 18lib-$(CONFIG_HTTPD) += httpd.o
19lib-$(CONFIG_IFCONFIG) += ifconfig.o interface.o 19lib-$(CONFIG_IFCONFIG) += ifconfig.o interface.o
20lib-$(CONFIG_IFENSLAVE) += ifenslave.o interface.o 20lib-$(CONFIG_IFENSLAVE) += ifenslave.o interface.o
21lib-$(CONFIG_IFPLUGD) += ifplugd.o
21lib-$(CONFIG_IFUPDOWN) += ifupdown.o 22lib-$(CONFIG_IFUPDOWN) += ifupdown.o
22lib-$(CONFIG_INETD) += inetd.o 23lib-$(CONFIG_INETD) += inetd.o
23lib-$(CONFIG_IP) += ip.o 24lib-$(CONFIG_IP) += ip.o
diff --git a/networking/ifplugd.c b/networking/ifplugd.c
new file mode 100644
index 000000000..5a389fdf9
--- /dev/null
+++ b/networking/ifplugd.c
@@ -0,0 +1,810 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * ifplugd for busybox
4 *
5 * Copyright (C) 2009
6 *
7 * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
8 */
9#include "libbb.h"
10
11#include <linux/if.h>
12#include <linux/ethtool.h>
13#include <net/ethernet.h>
14#include <linux/netlink.h>
15#include <linux/rtnetlink.h>
16#include <linux/sockios.h>
17#include <syslog.h>
18
19#define __user
20#include <linux/wireless.h>
21
22#define IFPLUGD_ENV_PREVIOUS "IFPLUGD_PREVIOUS"
23#define IFPLUGD_ENV_CURRENT "IFPLUGD_CURRENT"
24
25enum {
26 FLAG_NO_AUTO = 1 << 0, // -a, Do not enable interface automatically
27 FLAG_NO_DAEMON = 1 << 1, // -n, Do not daemonize
28 FLAG_NO_SYSLOG = 1 << 2, // -s, Do not use syslog, use stderr instead
29 FLAG_IGNORE_FAIL = 1 << 3, // -f, Ignore detection failure, retry instead (failure is treated as DOWN)
30 FLAG_IGNORE_FAIL_POSITIVE = 1 << 4, // -F, Ignore detection failure, retry instead (failure is treated as UP)
31 FLAG_IFACE = 1 << 5, // -i, Specify ethernet interface
32 FLAG_RUN = 1 << 6, // -r, Specify program to execute
33 FLAG_IGNORE_RETVAL = 1 << 7, // -I, Don't exit on nonzero return value of program executed
34 FLAG_POLL_TIME = 1 << 8, // -t, Specify poll time in seconds
35 FLAG_DELAY_UP = 1 << 9, // -u, Specify delay for configuring interface
36 FLAG_DELAY_DOWN = 1 << 10, // -d, Specify delay for deconfiguring interface
37 FLAG_API_MODE = 1 << 11, // -m, Force API mode (mii, priv, ethtool, wlan, auto)
38 FLAG_NO_STARTUP = 1 << 12, // -p, Don't run script on daemon startup
39 FLAG_NO_SHUTDOWN = 1 << 13, // -q, Don't run script on daemon quit
40 FLAG_INITIAL_DOWN = 1 << 14, // -l, Run "down" script on startup if no cable is detected
41 FLAG_EXTRA_ARG = 1 << 15, // -x, Specify an extra argument for action script
42 FLAG_MONITOR = 1 << 16, // -M, Use interface monitoring
43#if ENABLE_FEATURE_PIDFILE
44 FLAG_KILL = 1 << 17, // -k, Kill a running daemon
45#endif
46};
47#if ENABLE_FEATURE_PIDFILE
48# define OPTION_STR "+ansfFi:r:It:u:d:m:pqlx:Mk"
49#else
50# define OPTION_STR "+ansfFi:r:It:u:d:m:pqlx:M"
51#endif
52
53enum { // api mode
54 API_AUTO = 'a',
55 API_ETHTOOL = 'e',
56 API_MII = 'm',
57 API_PRIVATE = 'p',
58 API_WLAN = 'w',
59 API_IFF = 'i',
60};
61
62enum { // interface status
63 IFSTATUS_ERR = -1,
64 IFSTATUS_DOWN = 0,
65 IFSTATUS_UP = 1,
66};
67
68enum { // constant fds
69 ioctl_fd = 3,
70 netlink_fd = 4,
71};
72
73struct globals {
74 smallint iface_last_status;
75 smallint iface_exists;
76
77 /* Used in getopt32, must have sizeof == sizeof(int) */
78 unsigned poll_time;
79 unsigned delay_up;
80 unsigned delay_down;
81
82 const char *iface;
83 const char *api_mode;
84 const char *script_name;
85 const char *extra_arg;
86
87 smallint (*detect_link_func)(void);
88 smallint (*cached_detect_link_func)(void);
89};
90#define G (*ptr_to_globals)
91#define INIT_G() do { \
92 SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
93 G.iface_last_status = -1; \
94 G.iface_exists = 1; \
95 G.poll_time = 1; \
96 G.delay_down = 5; \
97 G.iface = "eth0"; \
98 G.api_mode = "a"; \
99 G.script_name = "/etc/ifplugd/ifplugd.action"; \
100} while (0)
101
102
103static int run_script(const char *action)
104{
105 pid_t pid;
106 int r;
107
108 bb_error_msg("executing '%s %s %s'", G.script_name, G.iface, action);
109
110#if 1
111 pid = vfork();
112 if (pid < 0) {
113 bb_perror_msg("fork");
114 return -1;
115 }
116
117 if (pid == 0) {
118 /* child */
119 execlp(G.script_name, G.script_name, G.iface, action, G.extra_arg, NULL);
120 bb_perror_msg_and_die("can't execute '%s'", G.script_name);
121 }
122
123 /* parent */
124 wait(&r);
125 r = WEXITSTATUS(r);
126
127 bb_error_msg("exit code: %u", r);
128 return (option_mask32 & FLAG_IGNORE_RETVAL) ? 0 : r;
129
130#else /* insanity */
131
132 struct fd_pair pipe_pair;
133 char buf[256];
134 int i = 0;
135
136 xpiped_pair(pipe_pair);
137
138 pid = vfork();
139 if (pid < 0) {
140 bb_perror_msg("fork");
141 return -1;
142 }
143
144 /* child */
145 if (pid == 0) {
146 xmove_fd(pipe_pair.wr, 1);
147 xdup2(1, 2);
148 if (pipe_pair.rd > 2)
149 close(pipe_pair.rd);
150
151 // umask(0022); // Set up a sane umask
152
153 execlp(G.script_name, G.script_name, G.iface, action, G.extra_arg, NULL);
154 _exit(EXIT_FAILURE);
155 }
156
157 /* parent */
158 close(pipe_pair.wr);
159
160 while (1) {
161 if (bb_got_signal && bb_got_signal != SIGCHLD) {
162 bb_error_msg("killing child");
163 kill(pid, SIGTERM);
164 bb_got_signal = 0;
165 break;
166 }
167
168 r = read(pipe_pair.rd, &buf[i], 1);
169
170 if (buf[i] == '\n' || i == sizeof(buf)-2 || r != 1) {
171 if (r == 1 && buf[i] != '\n')
172 i++;
173
174 buf[i] = '\0';
175
176 if (i > 0)
177 bb_error_msg("client: %s", buf);
178
179 i = 0;
180 } else {
181 i++;
182 }
183
184 if (r != 1)
185 break;
186 }
187
188 close(pipe_pair.rd);
189
190 wait(&r);
191
192 if (!WIFEXITED(r) || WEXITSTATUS(r) != 0) {
193 bb_error_msg("program execution failed, return value is %i",
194 WEXITSTATUS(r));
195 return option_mask32 & FLAG_IGNORE_RETVAL ? 0 : WEXITSTATUS(r);
196 }
197 bb_error_msg("program executed successfully");
198 return 0;
199#endif
200}
201
202static int network_ioctl(int request, void* data)
203{
204 return ioctl(ioctl_fd, request, data);
205}
206
207static void set_ifreq_to_ifname(struct ifreq *ifreq)
208{
209 memset(ifreq, 0, sizeof(struct ifreq));
210 strncpy(ifreq->ifr_name, G.iface, IFNAMSIZ);
211}
212
213static const char *strstatus(int status)
214{
215 if (status == IFSTATUS_ERR)
216 return "error";
217 return "down\0up" + (status * 5);
218}
219
220static void up_iface(void)
221{
222 struct ifreq ifrequest;
223
224 if (!G.iface_exists)
225 return;
226
227 set_ifreq_to_ifname(&ifrequest);
228 if (network_ioctl(SIOCGIFFLAGS, &ifrequest) < 0) {
229 bb_perror_msg("can't %cet interface flags", 'g');
230 G.iface_exists = 0;
231 return;
232 }
233
234 if (!(ifrequest.ifr_flags & IFF_UP)) {
235 ifrequest.ifr_flags |= IFF_UP;
236 /* Let user know we mess up with interface */
237 bb_error_msg("upping interface");
238 if (network_ioctl(SIOCSIFFLAGS, &ifrequest) < 0)
239 bb_perror_msg_and_die("can't %cet interface flags", 's');
240 }
241
242#if 0 /* why do we mess with IP addr? It's not our business */
243 if (network_ioctl(SIOCGIFADDR, &ifrequest) < 0) {
244 bb_error_msg("can't get interface address");
245 } else if (ifrequest.ifr_addr.sa_family != AF_INET) {
246 bb_perror_msg("The interface is not IP-based");
247 } else {
248 ((struct sockaddr_in*)(&ifrequest.ifr_addr))->sin_addr.s_addr = INADDR_ANY;
249 if (network_ioctl(SIOCSIFADDR, &ifrequest) < 0)
250 bb_perror_msg("can't set interface address");
251 }
252 if (network_ioctl(SIOCGIFFLAGS, &ifrequest) < 0) {
253 bb_perror_msg("can't get interface flags");
254 return;
255 }
256#endif
257}
258
259static void maybe_up_new_iface(void)
260{
261 if (!(option_mask32 & FLAG_NO_AUTO))
262 up_iface();
263
264#if 0 /* bloat */
265 struct ifreq ifrequest;
266 struct ethtool_drvinfo driver_info;
267
268 set_ifreq_to_ifname(&ifrequest);
269 driver_info.cmd = ETHTOOL_GDRVINFO;
270 ifrequest.ifr_data = &driver_info;
271 if (network_ioctl(SIOCETHTOOL, &ifrequest) == 0) {
272 char buf[sizeof("/xx:xx:xx:xx:xx:xx")];
273
274 /* Get MAC */
275 buf[0] = '\0';
276 set_ifreq_to_ifname(&ifrequest);
277 if (network_ioctl(SIOCGIFHWADDR, &ifrequest) == 0) {
278 sprintf(buf, "/%02X:%02X:%02X:%02X:%02X:%02X",
279 (uint8_t)(ifrequest.ifr_hwaddr.sa_data[0]),
280 (uint8_t)(ifrequest.ifr_hwaddr.sa_data[1]),
281 (uint8_t)(ifrequest.ifr_hwaddr.sa_data[2]),
282 (uint8_t)(ifrequest.ifr_hwaddr.sa_data[3]),
283 (uint8_t)(ifrequest.ifr_hwaddr.sa_data[4]),
284 (uint8_t)(ifrequest.ifr_hwaddr.sa_data[5]));
285 }
286
287 bb_error_msg("Using interface %s%s with driver<%s> (version: %s)",
288 G.iface, buf, driver_info.driver, driver_info.version);
289 }
290#endif
291
292 G.cached_detect_link_func = NULL;
293}
294
295static smallint detect_link_mii(void)
296{
297 struct ifreq ifreq;
298
299 set_ifreq_to_ifname(&ifreq);
300
301 if (network_ioctl(SIOCGMIIPHY, &ifreq) < 0) {
302 bb_perror_msg("SIOCGMIIPHY failed");
303 return IFSTATUS_ERR;
304 }
305
306 ((unsigned short*)&ifreq.ifr_data)[1] = 1;
307
308 if (network_ioctl(SIOCGMIIREG, &ifreq) < 0) {
309 bb_perror_msg("SIOCGMIIREG failed");
310 return IFSTATUS_ERR;
311 }
312
313 return (((unsigned short*)&ifreq.ifr_data)[3] & 0x0004) ?
314 IFSTATUS_UP : IFSTATUS_DOWN;
315}
316
317static smallint detect_link_priv(void)
318{
319 struct ifreq ifreq;
320
321 set_ifreq_to_ifname(&ifreq);
322
323 if (network_ioctl(SIOCDEVPRIVATE, &ifreq) < 0) {
324 bb_perror_msg("SIOCDEVPRIVATE failed");
325 return IFSTATUS_ERR;
326 }
327
328 ((unsigned short*) &ifreq.ifr_data)[1] = 1;
329
330 if (network_ioctl(SIOCDEVPRIVATE+1, &ifreq) < 0) {
331 bb_perror_msg("SIOCDEVPRIVATE+1 failed");
332 return IFSTATUS_ERR;
333 }
334
335 return (((unsigned short*)&ifreq.ifr_data)[3] & 0x0004) ?
336 IFSTATUS_UP : IFSTATUS_DOWN;
337}
338
339static smallint detect_link_ethtool(void)
340{
341 struct ifreq ifreq;
342 struct ethtool_value edata;
343
344 set_ifreq_to_ifname(&ifreq);
345
346 edata.cmd = ETHTOOL_GLINK;
347 ifreq.ifr_data = &edata;
348
349 if (network_ioctl(SIOCETHTOOL, &ifreq) < 0) {
350 bb_perror_msg("ETHTOOL_GLINK failed");
351 return IFSTATUS_ERR;
352 }
353
354 return edata.data ? IFSTATUS_UP : IFSTATUS_DOWN;
355}
356
357static smallint detect_link_iff(void)
358{
359 struct ifreq ifreq;
360
361 set_ifreq_to_ifname(&ifreq);
362
363 if (network_ioctl(SIOCGIFFLAGS, &ifreq) < 0) {
364 bb_perror_msg("SIOCGIFFLAGS failed");
365 return IFSTATUS_ERR;
366 }
367
368 return (ifreq.ifr_flags & IFF_RUNNING) ? IFSTATUS_UP : IFSTATUS_DOWN;
369}
370
371static smallint detect_link_wlan(void)
372{
373 struct iwreq iwrequest;
374 uint8_t mac[ETH_ALEN];
375
376 memset(&iwrequest, 0, sizeof(struct iwreq));
377 strncpy(iwrequest.ifr_ifrn.ifrn_name, G.iface, IFNAMSIZ);
378
379 if (network_ioctl(SIOCGIWAP, &iwrequest) < 0) {
380 bb_perror_msg("SIOCGIWAP failed");
381 return IFSTATUS_ERR;
382 }
383
384 memcpy(mac, &(iwrequest.u.ap_addr.sa_data), ETH_ALEN);
385
386 if (mac[0] == 0xFF || mac[0] == 0x44 || mac[0] == 0x00) {
387 for (int i = 1; i < ETH_ALEN; ++i) {
388 if (mac[i] != mac[0])
389 return IFSTATUS_UP;
390 }
391 return IFSTATUS_DOWN;
392 }
393
394 return IFSTATUS_UP;
395}
396
397static smallint detect_link_auto(void)
398{
399 const char *method;
400 smallint iface_status;
401 smallint sv_logmode;
402
403 if (G.cached_detect_link_func) {
404 iface_status = G.cached_detect_link_func();
405 if (iface_status != IFSTATUS_ERR)
406 return iface_status;
407 }
408
409 sv_logmode = logmode;
410 logmode = LOGMODE_NONE;
411
412 iface_status = detect_link_ethtool();
413 if (iface_status != IFSTATUS_ERR) {
414 G.cached_detect_link_func = detect_link_ethtool;
415 method = "SIOCETHTOOL";
416 found_method:
417 logmode = sv_logmode;
418 bb_error_msg("using %s detection mode", method);
419 return iface_status;
420 }
421
422 iface_status = detect_link_mii();
423 if (iface_status != IFSTATUS_ERR) {
424 G.cached_detect_link_func = detect_link_mii;
425 method = "SIOCGMIIPHY";
426 goto found_method;
427 }
428
429 iface_status = detect_link_priv();
430 if (iface_status != IFSTATUS_ERR) {
431 G.cached_detect_link_func = detect_link_priv;
432 method = "SIOCDEVPRIVATE";
433 goto found_method;
434 }
435
436 iface_status = detect_link_wlan();
437 if (iface_status != IFSTATUS_ERR) {
438 G.cached_detect_link_func = detect_link_wlan;
439 method = "wireless extension";
440 goto found_method;
441 }
442
443 iface_status = detect_link_iff();
444 if (iface_status != IFSTATUS_ERR) {
445 G.cached_detect_link_func = detect_link_iff;
446 method = "IFF_RUNNING";
447 goto found_method;
448 }
449
450 logmode = sv_logmode;
451 return iface_status; /* IFSTATUS_ERR */
452}
453
454static smallint detect_link(void)
455{
456 smallint status;
457
458 if (!G.iface_exists)
459 return (option_mask32 & FLAG_MONITOR) ? IFSTATUS_DOWN : IFSTATUS_ERR;
460
461#if 0
462/* Why? This behavior makes it hard to temporary down the iface.
463 * It makes a bit more sense to do only in maybe_up_new_iface.
464 * OTOH, maybe detect_link_wlan needs this. Then it should be done
465 * _only_ there.
466 */
467 if (!(option_mask32 & FLAG_NO_AUTO))
468 up_iface();
469#endif
470
471 status = G.detect_link_func();
472 if (status == IFSTATUS_ERR) {
473 if (option_mask32 & FLAG_IGNORE_FAIL)
474 status = IFSTATUS_DOWN;
475 if (option_mask32 & FLAG_IGNORE_FAIL_POSITIVE)
476 status = IFSTATUS_UP;
477 }
478
479 if (status == IFSTATUS_ERR
480 && G.detect_link_func == detect_link_auto
481 ) {
482 bb_error_msg("failed to detect link status");
483 }
484
485 if (status != G.iface_last_status) {
486//TODO: is it safe to repeatedly do this?
487 setenv(IFPLUGD_ENV_PREVIOUS, strstatus(G.iface_last_status), 1);
488 setenv(IFPLUGD_ENV_CURRENT, strstatus(status), 1);
489 G.iface_last_status = status;
490 }
491
492 return status;
493}
494
495static NOINLINE int check_existence_through_netlink(void)
496{
497 char replybuf[1024];
498
499 while (1) {
500 struct nlmsghdr *mhdr;
501 ssize_t bytes;
502
503 bytes = recv(netlink_fd, &replybuf, sizeof(replybuf), MSG_DONTWAIT);
504 if (bytes < 0) {
505 if (errno == EAGAIN)
506 return G.iface_exists;
507 if (errno == EINTR)
508 continue;
509
510 bb_perror_msg("netlink: recv");
511 return -1;
512 }
513
514 mhdr = (struct nlmsghdr*)replybuf;
515 while (bytes > 0) {
516 if (!NLMSG_OK(mhdr, bytes)
517 || bytes < sizeof(struct nlmsghdr)
518 || bytes < mhdr->nlmsg_len
519 ) {
520 bb_error_msg("netlink packet too small or truncated");
521 return -1;
522 }
523
524 if (mhdr->nlmsg_type == RTM_NEWLINK || mhdr->nlmsg_type == RTM_DELLINK) {
525 struct rtattr *attr;
526 struct ifinfomsg *imsg;
527 int attr_len;
528
529 imsg = NLMSG_DATA(mhdr);
530
531 if (mhdr->nlmsg_len < NLMSG_LENGTH(sizeof(struct ifinfomsg))) {
532 bb_error_msg("netlink packet too small or truncated");
533 return -1;
534 }
535
536 attr = (struct rtattr*)((char*)imsg + NLMSG_ALIGN(sizeof(struct ifinfomsg)));
537 attr_len = NLMSG_PAYLOAD(mhdr, sizeof(struct ifinfomsg));
538
539 while (RTA_OK(attr, attr_len)) {
540 if (attr->rta_type == IFLA_IFNAME) {
541 char ifname[IFNAMSIZ + 1];
542 int len = RTA_PAYLOAD(attr);
543
544 if (len > IFNAMSIZ)
545 len = IFNAMSIZ;
546 memcpy(ifname, RTA_DATA(attr), len);
547 if (strcmp(G.iface, ifname) == 0) {
548 G.iface_exists = (mhdr->nlmsg_type == RTM_NEWLINK);
549 }
550 }
551 attr = RTA_NEXT(attr, attr_len);
552 }
553 }
554
555 mhdr = NLMSG_NEXT(mhdr, bytes);
556 }
557 }
558
559 return G.iface_exists;
560}
561
562static NOINLINE int netlink_open(void)
563{
564 int fd;
565 struct sockaddr_nl addr;
566
567 fd = xsocket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
568
569 memset(&addr, 0, sizeof(addr));
570 addr.nl_family = AF_NETLINK;
571 addr.nl_groups = RTMGRP_LINK;
572 addr.nl_pid = getpid();
573
574 xbind(fd, (struct sockaddr*)&addr, sizeof(addr));
575
576 return fd;
577}
578
579static NOINLINE pid_t read_pid(const char *filename)
580{
581 int len;
582 char buf[128];
583
584 len = open_read_close(filename, buf, 127);
585 if (len > 0) {
586 buf[len] = '\0';
587 /* returns ULONG_MAX on error => -1 */
588 return bb_strtoul(buf, NULL, 10);
589 }
590 return 0;
591}
592
593int ifplugd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
594int ifplugd_main(int argc UNUSED_PARAM, char **argv)
595{
596 int iface_status;
597 int delay_time;
598 const char *iface_status_str;
599 struct pollfd netlink_pollfd[1];
600 unsigned opts;
601#if ENABLE_FEATURE_PIDFILE
602 char *pidfile_name;
603 pid_t pid_from_pidfile;
604#endif
605
606 INIT_G();
607
608 opt_complementary = "t+:u+:d+";
609 opts = getopt32(argv, OPTION_STR,
610 &G.iface, &G.script_name, &G.poll_time, &G.delay_up,
611 &G.delay_down, &G.api_mode, &G.extra_arg);
612
613 applet_name = xasprintf("ifplugd(%s)", G.iface);
614
615#if ENABLE_FEATURE_PIDFILE
616 pidfile_name = xasprintf(_PATH_VARRUN"ifplugd.%s.pid", G.iface);
617 pid_from_pidfile = read_pid(pidfile_name);
618
619 if (opts & FLAG_KILL) {
620 if (pid_from_pidfile > 0)
621 kill(pid_from_pidfile, SIGQUIT);
622 return EXIT_SUCCESS;
623 }
624
625 if (pid_from_pidfile > 0 && kill(pid_from_pidfile, 0) == 0)
626 bb_error_msg_and_die("daemon already running");
627#endif
628
629 switch (G.api_mode[0]) {
630 case API_AUTO:
631 G.detect_link_func = detect_link_auto;
632 break;
633 case API_ETHTOOL:
634 G.detect_link_func = detect_link_ethtool;
635 break;
636 case API_MII:
637 G.detect_link_func = detect_link_mii;
638 break;
639 case API_PRIVATE:
640 G.detect_link_func = detect_link_priv;
641 break;
642 case API_WLAN:
643 G.detect_link_func = detect_link_wlan;
644 break;
645 case API_IFF:
646 G.detect_link_func = detect_link_iff;
647 break;
648 default:
649 bb_error_msg_and_die("unknown API mode '%s'", G.api_mode);
650 }
651
652 if (!(opts & FLAG_NO_DAEMON))
653 bb_daemonize_or_rexec(DAEMON_CHDIR_ROOT, argv);
654
655 xmove_fd(xsocket(AF_INET, SOCK_DGRAM, 0), ioctl_fd);
656 if (opts & FLAG_MONITOR) {
657 xmove_fd(netlink_open(), netlink_fd);
658 }
659
660 write_pidfile(pidfile_name);
661
662 /* this can't be moved before socket creation */
663 if (!(opts & FLAG_NO_SYSLOG)) {
664 openlog(applet_name, 0, LOG_DAEMON);
665 logmode |= LOGMODE_SYSLOG;
666 }
667
668 bb_signals(0
669 | (1 << SIGINT )
670 | (1 << SIGTERM)
671 | (1 << SIGQUIT)
672 | (1 << SIGHUP ) /* why we ignore it? */
673 /* | (1 << SIGCHLD) - run_script does not use it anymore */
674 , record_signo);
675
676 bb_error_msg("started: %s", bb_banner);
677
678 if (opts & FLAG_MONITOR) {
679 struct ifreq ifrequest;
680 set_ifreq_to_ifname(&ifrequest);
681 G.iface_exists = (network_ioctl(SIOCGIFINDEX, &ifrequest) == 0);
682 }
683
684 if (G.iface_exists)
685 maybe_up_new_iface();
686
687 iface_status = detect_link();
688 if (iface_status == IFSTATUS_ERR)
689 goto exiting;
690 iface_status_str = strstatus(iface_status);
691
692 if (opts & FLAG_MONITOR) {
693 bb_error_msg("interface %s",
694 G.iface_exists ? "exists"
695 : "doesn't exist, waiting");
696 }
697 /* else we assume it always exists, but don't mislead user
698 * by potentially lying that it really exists */
699
700 if (G.iface_exists) {
701 bb_error_msg("link is %s", iface_status_str);
702 }
703
704 if ((!(opts & FLAG_NO_STARTUP)
705 && iface_status == IFSTATUS_UP
706 )
707 || (opts & FLAG_INITIAL_DOWN)
708 ) {
709 if (run_script(iface_status_str) != 0)
710 goto exiting;
711 }
712
713 /* Main loop */
714 netlink_pollfd[0].fd = netlink_fd;
715 netlink_pollfd[0].events = POLLIN;
716 delay_time = 0;
717 while (1) {
718 int iface_status_old;
719 int iface_exists_old;
720
721 switch (bb_got_signal) {
722 case SIGINT:
723 case SIGTERM:
724 bb_got_signal = 0;
725 goto cleanup;
726 case SIGQUIT:
727 bb_got_signal = 0;
728 goto exiting;
729 default:
730 bb_got_signal = 0;
731 break;
732 }
733
734 if (poll(netlink_pollfd,
735 (opts & FLAG_MONITOR) ? 1 : 0,
736 G.poll_time * 1000
737 ) < 0
738 ) {
739 if (errno == EINTR)
740 continue;
741 bb_perror_msg("poll");
742 goto exiting;
743 }
744
745 iface_status_old = iface_status;
746 iface_exists_old = G.iface_exists;
747
748 if ((opts & FLAG_MONITOR)
749 && (netlink_pollfd[0].revents & POLLIN)
750 ) {
751 G.iface_exists = check_existence_through_netlink();
752 if (G.iface_exists < 0) /* error */
753 goto exiting;
754 if (iface_exists_old != G.iface_exists) {
755 bb_error_msg("interface %sappeared",
756 G.iface_exists ? "" : "dis");
757 if (G.iface_exists)
758 maybe_up_new_iface();
759 }
760 }
761
762 /* note: if !G.iface_exists, returns DOWN */
763 iface_status = detect_link();
764 if (iface_status == IFSTATUS_ERR) {
765 if (!(opts & FLAG_MONITOR))
766 goto exiting;
767 iface_status = IFSTATUS_DOWN;
768 }
769 iface_status_str = strstatus(iface_status);
770
771 if (iface_status_old != iface_status) {
772 bb_error_msg("link is %s", iface_status_str);
773
774 if (delay_time) {
775 /* link restored its old status before
776 * we run script. don't run the script: */
777 delay_time = 0;
778 } else {
779 delay_time = monotonic_sec();
780 if (iface_status == IFSTATUS_UP)
781 delay_time += G.delay_up;
782 if (iface_status == IFSTATUS_DOWN)
783 delay_time += G.delay_down;
784 if (delay_time == 0)
785 delay_time++;
786 }
787 }
788
789 if (delay_time && (int)(monotonic_sec() - delay_time) >= 0) {
790 delay_time = 0;
791 if (run_script(iface_status_str) != 0)
792 goto exiting;
793 }
794 } /* while (1) */
795
796 cleanup:
797 if (!(opts & FLAG_NO_SHUTDOWN)
798 && (iface_status == IFSTATUS_UP
799 || (iface_status == IFSTATUS_DOWN && delay_time)
800 )
801 ) {
802 setenv(IFPLUGD_ENV_PREVIOUS, strstatus(iface_status), 1);
803 setenv(IFPLUGD_ENV_CURRENT, strstatus(-1), 1);
804 run_script("down\0up"); /* reusing string */
805 }
806
807 exiting:
808 remove_pidfile(pidfile_name);
809 bb_error_msg_and_die("exiting");
810}