diff options
author | Denis Vlasenko <vda.linux@googlemail.com> | 2006-12-27 04:35:04 +0000 |
---|---|---|
committer | Denis Vlasenko <vda.linux@googlemail.com> | 2006-12-27 04:35:04 +0000 |
commit | 8d42f86b146871ae4c4cafd3801a85f381249a14 (patch) | |
tree | b963999fc54eddb65f1929b894f868e24851fc9c /networking | |
download | busybox-w32-8d42f86b146871ae4c4cafd3801a85f381249a14.tar.gz busybox-w32-8d42f86b146871ae4c4cafd3801a85f381249a14.tar.bz2 busybox-w32-8d42f86b146871ae4c4cafd3801a85f381249a14.zip |
Correcting branch name to be like previous ones
Diffstat (limited to 'networking')
81 files changed, 26725 insertions, 0 deletions
diff --git a/networking/Config.in b/networking/Config.in new file mode 100644 index 000000000..e6711078c --- /dev/null +++ b/networking/Config.in | |||
@@ -0,0 +1,720 @@ | |||
1 | # | ||
2 | # For a description of the syntax of this configuration file, | ||
3 | # see scripts/kbuild/config-language.txt. | ||
4 | # | ||
5 | |||
6 | menu "Networking Utilities" | ||
7 | |||
8 | config FEATURE_IPV6 | ||
9 | bool "Enable IPv6 support" | ||
10 | default n | ||
11 | help | ||
12 | Enable IPv6 support in busybox. | ||
13 | This adds IPv6 support in the networking applets. | ||
14 | |||
15 | config ARPING | ||
16 | bool "arping" | ||
17 | default n | ||
18 | help | ||
19 | Ping hosts by ARP packets | ||
20 | |||
21 | config DNSD | ||
22 | bool "dnsd" | ||
23 | default n | ||
24 | help | ||
25 | Small and static DNS server daemon. | ||
26 | |||
27 | config ETHER_WAKE | ||
28 | bool "ether-wake" | ||
29 | default n | ||
30 | help | ||
31 | Send a magic packet to wake up sleeping machines. | ||
32 | |||
33 | config FAKEIDENTD | ||
34 | bool "fakeidentd" | ||
35 | default n | ||
36 | select FEATURE_SYSLOG | ||
37 | help | ||
38 | fakeidentd listens on the ident port and returns a predefined | ||
39 | fake value on any query. | ||
40 | |||
41 | config FTPGET | ||
42 | bool "ftpget" | ||
43 | default n | ||
44 | help | ||
45 | Retrieve a remote file via FTP. | ||
46 | |||
47 | config FTPPUT | ||
48 | bool "ftpput" | ||
49 | default n | ||
50 | help | ||
51 | Store a remote file via FTP. | ||
52 | |||
53 | config FEATURE_FTPGETPUT_LONG_OPTIONS | ||
54 | bool "Enable long options in ftpget/ftpput" | ||
55 | default n | ||
56 | depends on GETOPT_LONG && (FTPGET || FTPPUT) | ||
57 | help | ||
58 | Support long options for the ftpget/ftpput applet. | ||
59 | |||
60 | config HOSTNAME | ||
61 | bool "hostname" | ||
62 | default n | ||
63 | help | ||
64 | Show or set the system's host name | ||
65 | |||
66 | config HTTPD | ||
67 | bool "httpd" | ||
68 | default n | ||
69 | help | ||
70 | Serve web pages via an HTTP server. | ||
71 | |||
72 | config FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP | ||
73 | bool "Support reloading the global config file using hup signal" | ||
74 | default n | ||
75 | depends on HTTPD && FEATURE_HTTPD_WITHOUT_INETD | ||
76 | help | ||
77 | This option enables processing of SIGHUP to reload cached | ||
78 | configuration settings. | ||
79 | |||
80 | config FEATURE_HTTPD_SETUID | ||
81 | bool "Enable support -u <user> option" | ||
82 | default n | ||
83 | depends on HTTPD && FEATURE_HTTPD_WITHOUT_INETD | ||
84 | help | ||
85 | This option allows the server to run as a specific user | ||
86 | rather than defaulting to the user that starts the server. | ||
87 | Use of this option requires special privileges to change to a | ||
88 | different user. | ||
89 | |||
90 | config FEATURE_HTTPD_BASIC_AUTH | ||
91 | bool "Enable Basic http Authentication" | ||
92 | default y | ||
93 | depends on HTTPD | ||
94 | help | ||
95 | Utilizes password settings from /etc/httpd.conf for basic | ||
96 | authentication on a per url basis. | ||
97 | |||
98 | config FEATURE_HTTPD_AUTH_MD5 | ||
99 | bool "Support MD5 crypted passwords for http Authentication" | ||
100 | default n | ||
101 | depends on FEATURE_HTTPD_BASIC_AUTH | ||
102 | help | ||
103 | Enables basic per URL authentication from /etc/httpd.conf | ||
104 | using md5 passwords. | ||
105 | |||
106 | config FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES | ||
107 | bool "Support loading additional MIME types at run-time" | ||
108 | default n | ||
109 | depends on HTTPD | ||
110 | help | ||
111 | This option enables support for additional MIME types at | ||
112 | run-time to be specified in the configuration file. | ||
113 | |||
114 | config FEATURE_HTTPD_CGI | ||
115 | bool "Support Common Gateway Interface (CGI)" | ||
116 | default y | ||
117 | depends on HTTPD | ||
118 | help | ||
119 | This option allows scripts and executables to be invoked | ||
120 | when specific URLs are requested. | ||
121 | |||
122 | config FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR | ||
123 | bool "Enable support for running scripts through an interpreter" | ||
124 | default n | ||
125 | depends on FEATURE_HTTPD_CGI | ||
126 | help | ||
127 | This option enables support for running scripts through an | ||
128 | interpreter. Turn this on if you want PHP scripts to work | ||
129 | properly. You need to supply an addition line in your httpd | ||
130 | config file: | ||
131 | *.php:/path/to/your/php | ||
132 | |||
133 | config FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV | ||
134 | bool "Support the REMOTE_PORT environment variable for CGI" | ||
135 | default n | ||
136 | depends on FEATURE_HTTPD_CGI | ||
137 | help | ||
138 | Use of this option can assist scripts in generating | ||
139 | references that contain a unique port number. | ||
140 | |||
141 | config FEATURE_HTTPD_ENCODE_URL_STR | ||
142 | bool "Enable the -e option for shell script CGI simplification." | ||
143 | default y | ||
144 | depends on HTTPD | ||
145 | help | ||
146 | This option allows html encoding arbitrary | ||
147 | strings for display of the browser. Output goes to stdout. | ||
148 | For example, httpd -e "<Hello World>" as | ||
149 | "<Hello World>". | ||
150 | |||
151 | config IFCONFIG | ||
152 | bool "ifconfig" | ||
153 | default n | ||
154 | help | ||
155 | Ifconfig is used to configure the kernel-resident network interfaces. | ||
156 | |||
157 | config FEATURE_IFCONFIG_STATUS | ||
158 | bool "Enable status reporting output (+7k)" | ||
159 | default y | ||
160 | depends on IFCONFIG | ||
161 | help | ||
162 | If ifconfig is called with no arguments it will display the status | ||
163 | of the currently active interfaces. | ||
164 | |||
165 | config FEATURE_IFCONFIG_SLIP | ||
166 | bool "Enable slip-specific options \"keepalive\" and \"outfill\"" | ||
167 | default n | ||
168 | depends on IFCONFIG | ||
169 | help | ||
170 | Allow "keepalive" and "outfill" support for SLIP. If you're not | ||
171 | planning on using serial lines, leave this unchecked. | ||
172 | |||
173 | config FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ | ||
174 | bool "Enable options \"mem_start\", \"io_addr\", and \"irq\"" | ||
175 | default n | ||
176 | depends on IFCONFIG | ||
177 | help | ||
178 | Allow the start address for shared memory, start address for I/O, | ||
179 | and/or the interrupt line used by the specified device. | ||
180 | |||
181 | config FEATURE_IFCONFIG_HW | ||
182 | bool "Enable option \"hw\" (ether only)" | ||
183 | default y | ||
184 | depends on IFCONFIG | ||
185 | help | ||
186 | Set the hardware address of this interface, if the device driver | ||
187 | supports this operation. Currently, we only support the 'ether' | ||
188 | class. | ||
189 | |||
190 | config FEATURE_IFCONFIG_BROADCAST_PLUS | ||
191 | bool "Set the broadcast automatically" | ||
192 | default n | ||
193 | depends on IFCONFIG | ||
194 | help | ||
195 | Setting this will make ifconfig attempt to find the broadcast | ||
196 | automatically if the value '+' is used. | ||
197 | |||
198 | config IFUPDOWN | ||
199 | bool "ifupdown" | ||
200 | default n | ||
201 | select RUN_PARTS | ||
202 | help | ||
203 | Activate or deactivate the specified interfaces. This applet makes | ||
204 | use of either "ifconfig" and "route" or the "ip" command to actually | ||
205 | configure network interfaces. Therefore, you will probably also want | ||
206 | to enable either IFCONFIG and ROUTE, or enable | ||
207 | FEATURE_IFUPDOWN_IP and the various IP options. Of | ||
208 | course you could use non-busybox versions of these programs, so | ||
209 | against my better judgement (since this will surely result in plenty | ||
210 | of support questions on the mailing list), I do not force you to | ||
211 | enable these additional options. It is up to you to supply either | ||
212 | "ifconfig" and "route" or the "ip" command, either via busybox or via | ||
213 | standalone utilities. | ||
214 | |||
215 | config FEATURE_IFUPDOWN_IP | ||
216 | bool "Use ip applet" | ||
217 | default n | ||
218 | depends on IFUPDOWN | ||
219 | help | ||
220 | Use the iproute "ip" command to implement "ifup" and "ifdown", rather | ||
221 | than the default of using the older 'ifconfig' and 'route' utilities. | ||
222 | |||
223 | config FEATURE_IFUPDOWN_IP_BUILTIN | ||
224 | bool "Use busybox ip applet" | ||
225 | default y | ||
226 | depends on FEATURE_IFUPDOWN_IP | ||
227 | select IP | ||
228 | select FEATURE_IP_ADDRESS | ||
229 | select FEATURE_IP_LINK | ||
230 | select FEATURE_IP_ROUTE | ||
231 | help | ||
232 | Use the busybox iproute "ip" applet to implement "ifupdown". | ||
233 | |||
234 | If left disabled, you must install the full-blown iproute2 | ||
235 | utility or the "ifup" and "ifdown" applets will not work. | ||
236 | |||
237 | config FEATURE_IFUPDOWN_IFCONFIG_BUILTIN | ||
238 | bool "Use busybox ifconfig and route applets" | ||
239 | default y | ||
240 | depends on IFUPDOWN && !FEATURE_IFUPDOWN_IP | ||
241 | select IFCONFIG | ||
242 | select ROUTE | ||
243 | help | ||
244 | Use the busybox iproute "ifconfig" and "route" applets to | ||
245 | implement the "ifup" and "ifdown" utilities. | ||
246 | |||
247 | If left disabled, you must install the full-blown ifconfig | ||
248 | and route utilities, or the "ifup" and "ifdown" applets will not | ||
249 | work. | ||
250 | |||
251 | config FEATURE_IFUPDOWN_IPV4 | ||
252 | bool "Enable support for IPv4" | ||
253 | default y | ||
254 | depends on IFUPDOWN | ||
255 | help | ||
256 | If you want busybox to talk IPv4, leave this on. | ||
257 | |||
258 | config FEATURE_IFUPDOWN_IPV6 | ||
259 | bool "Enable support for IPv6" | ||
260 | default n | ||
261 | depends on IFUPDOWN && FEATURE_IPV6 | ||
262 | help | ||
263 | If you need support for IPv6, turn this option on. | ||
264 | |||
265 | config FEATURE_IFUPDOWN_IPX | ||
266 | bool "Enable support for IPX" | ||
267 | default n | ||
268 | depends on IFUPDOWN | ||
269 | help | ||
270 | If this option is selected you can use busybox to work with IPX | ||
271 | networks. | ||
272 | |||
273 | config FEATURE_IFUPDOWN_MAPPING | ||
274 | bool "Enable mapping support" | ||
275 | default n | ||
276 | depends on IFUPDOWN | ||
277 | help | ||
278 | This enables support for the "mapping" stanza, unless you have | ||
279 | a weird network setup you don't need it. | ||
280 | |||
281 | config INETD | ||
282 | bool "inetd" | ||
283 | default n | ||
284 | select FEATURE_SYSLOG | ||
285 | help | ||
286 | Internet superserver daemon | ||
287 | |||
288 | config FEATURE_INETD_SUPPORT_BUILTIN_ECHO | ||
289 | bool "Support echo service" | ||
290 | default y | ||
291 | depends on INETD | ||
292 | help | ||
293 | Echo received data internal inetd service | ||
294 | |||
295 | config FEATURE_INETD_SUPPORT_BUILTIN_DISCARD | ||
296 | bool "Support discard service" | ||
297 | default y | ||
298 | depends on INETD | ||
299 | help | ||
300 | Internet /dev/null internal inetd service | ||
301 | |||
302 | config FEATURE_INETD_SUPPORT_BUILTIN_TIME | ||
303 | bool "Support time service" | ||
304 | default y | ||
305 | depends on INETD | ||
306 | help | ||
307 | Return 32 bit time since 1900 internal inetd service | ||
308 | |||
309 | config FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME | ||
310 | bool "Support daytime service" | ||
311 | default y | ||
312 | depends on INETD | ||
313 | help | ||
314 | Return human-readable time internal inetd service | ||
315 | |||
316 | config FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN | ||
317 | bool "Support chargen service" | ||
318 | default y | ||
319 | depends on INETD | ||
320 | help | ||
321 | Familiar character generator internal inetd service | ||
322 | |||
323 | config FEATURE_INETD_RPC | ||
324 | bool "Support RPC services" | ||
325 | default n | ||
326 | depends on INETD | ||
327 | depends on FEATURE_HAVE_RPC | ||
328 | help | ||
329 | Support Sun-RPC based services | ||
330 | |||
331 | config IP | ||
332 | bool "ip" | ||
333 | default n | ||
334 | help | ||
335 | The "ip" applet is a TCP/IP interface configuration and routing | ||
336 | utility. You generally don't need "ip" to use busybox with | ||
337 | TCP/IP. | ||
338 | |||
339 | config FEATURE_IP_ADDRESS | ||
340 | bool "ip address" | ||
341 | default y | ||
342 | depends on IP | ||
343 | help | ||
344 | Address manipulation support for the "ip" applet. | ||
345 | |||
346 | config FEATURE_IP_LINK | ||
347 | bool "ip link" | ||
348 | default y | ||
349 | depends on IP | ||
350 | help | ||
351 | Configure network devices with "ip". | ||
352 | |||
353 | config FEATURE_IP_ROUTE | ||
354 | bool "ip route" | ||
355 | default y | ||
356 | depends on IP | ||
357 | help | ||
358 | Add support for routing table management to "ip". | ||
359 | |||
360 | config FEATURE_IP_TUNNEL | ||
361 | bool "ip tunnel" | ||
362 | default n | ||
363 | depends on IP | ||
364 | help | ||
365 | Add support for tunneling commands to "ip". | ||
366 | |||
367 | config FEATURE_IP_RULE | ||
368 | bool "ip rule" | ||
369 | default n | ||
370 | depends on IP | ||
371 | help | ||
372 | Add support for rule commands to "ip". | ||
373 | |||
374 | config FEATURE_IP_SHORT_FORMS | ||
375 | bool "Support short forms of ip commands." | ||
376 | default n | ||
377 | depends on IP | ||
378 | help | ||
379 | Also support short-form of ip <OBJECT> commands: | ||
380 | ip addr -> ipaddr | ||
381 | ip link -> iplink | ||
382 | ip route -> iproute | ||
383 | ip tunnel -> iptunnel | ||
384 | |||
385 | Say N unless you desparately need the short form of the ip | ||
386 | object commands. | ||
387 | |||
388 | config IPADDR | ||
389 | bool | ||
390 | default y | ||
391 | depends on FEATURE_IP_SHORT_FORMS && FEATURE_IP_ADDRESS | ||
392 | |||
393 | config IPLINK | ||
394 | bool | ||
395 | default y | ||
396 | depends on FEATURE_IP_SHORT_FORMS && FEATURE_IP_LINK | ||
397 | |||
398 | config IPROUTE | ||
399 | bool | ||
400 | default y | ||
401 | depends on FEATURE_IP_SHORT_FORMS && FEATURE_IP_ROUTE | ||
402 | |||
403 | config IPTUNNEL | ||
404 | bool | ||
405 | default y | ||
406 | depends on FEATURE_IP_SHORT_FORMS && FEATURE_IP_TUNNEL | ||
407 | |||
408 | config IPRULE | ||
409 | bool | ||
410 | default y | ||
411 | depends on FEATURE_IP_SHORT_FORMS && FEATURE_IP_RULE | ||
412 | |||
413 | config IPCALC | ||
414 | bool "ipcalc" | ||
415 | default n | ||
416 | help | ||
417 | ipcalc takes an IP address and netmask and calculates the | ||
418 | resulting broadcast, network, and host range. | ||
419 | |||
420 | config FEATURE_IPCALC_FANCY | ||
421 | bool "Fancy IPCALC, more options, adds 1 kbyte" | ||
422 | default y | ||
423 | depends on IPCALC | ||
424 | help | ||
425 | Adds the options hostname, prefix and silent to the output of "ipcalc". | ||
426 | |||
427 | config FEATURE_IPCALC_LONG_OPTIONS | ||
428 | bool "Enable long options" | ||
429 | default n | ||
430 | depends on IPCALC && GETOPT_LONG | ||
431 | help | ||
432 | Support long options for the ipcalc applet. | ||
433 | |||
434 | config NAMEIF | ||
435 | bool "nameif" | ||
436 | default n | ||
437 | select FEATURE_SYSLOG | ||
438 | help | ||
439 | nameif is used to rename network interface by its MAC address. | ||
440 | Renamed interfaces MUST be in the down state. | ||
441 | It is possible to use a file (default: /etc/mactab) | ||
442 | with list of new interface names and MACs. | ||
443 | Maximum interface name length: IF_NAMESIZE = 16 | ||
444 | File fields are separated by space or tab. | ||
445 | File format: | ||
446 | # Comment | ||
447 | new_interface_name XX:XX:XX:XX:XX:XX | ||
448 | |||
449 | config NC | ||
450 | bool "nc" | ||
451 | default n | ||
452 | help | ||
453 | A simple Unix utility which reads and writes data across network | ||
454 | connections. | ||
455 | |||
456 | config NC_SERVER | ||
457 | bool "Netcat server options (-lp)" | ||
458 | default n | ||
459 | depends on NC | ||
460 | help | ||
461 | Allow netcat to act as a server. | ||
462 | |||
463 | config NC_EXTRA | ||
464 | bool "Netcat extensions (-eiw and filename)" | ||
465 | default n | ||
466 | depends on NC | ||
467 | help | ||
468 | Add -e (support for executing the rest of the command line after | ||
469 | making or receiving a successful connection), -i (delay interval for | ||
470 | lines sent), -w (timeout for initial connection). | ||
471 | |||
472 | config NETSTAT | ||
473 | bool "netstat" | ||
474 | default n | ||
475 | help | ||
476 | netstat prints information about the Linux networking subsystem. | ||
477 | |||
478 | config NSLOOKUP | ||
479 | bool "nslookup" | ||
480 | default n | ||
481 | help | ||
482 | nslookup is a tool to query Internet name servers. | ||
483 | |||
484 | config PING | ||
485 | bool "ping" | ||
486 | default n | ||
487 | help | ||
488 | ping uses the ICMP protocol's mandatory ECHO_REQUEST datagram to | ||
489 | elicit an ICMP ECHO_RESPONSE from a host or gateway. | ||
490 | |||
491 | config FEATURE_FANCY_PING | ||
492 | bool "Enable fancy ping output" | ||
493 | default y | ||
494 | depends on PING | ||
495 | help | ||
496 | Make the output from the ping applet include statistics, and at the | ||
497 | same time provide full support for ICMP packets. | ||
498 | |||
499 | config PING6 | ||
500 | bool "ping6" | ||
501 | default n | ||
502 | depends on FEATURE_IPV6 | ||
503 | help | ||
504 | This will give you a ping that can talk IPv6. | ||
505 | |||
506 | config FEATURE_FANCY_PING6 | ||
507 | bool "Enable fancy ping6 output" | ||
508 | default y | ||
509 | depends on PING6 | ||
510 | help | ||
511 | Make the output from the ping6 applet include statistics, and at the | ||
512 | same time provide full support for ICMP packets. | ||
513 | |||
514 | config ROUTE | ||
515 | bool "route" | ||
516 | default n | ||
517 | help | ||
518 | Route displays or manipulates the kernel's IP routing tables. | ||
519 | |||
520 | config TELNET | ||
521 | bool "telnet" | ||
522 | default n | ||
523 | help | ||
524 | Telnet is an interface to the TELNET protocol, but is also commonly | ||
525 | used to test other simple protocols. | ||
526 | |||
527 | config FEATURE_TELNET_TTYPE | ||
528 | bool "Pass TERM type to remote host" | ||
529 | default y | ||
530 | depends on TELNET | ||
531 | help | ||
532 | Setting this option will forward the TERM environment variable to the | ||
533 | remote host you are connecting to. This is useful to make sure that | ||
534 | things like ANSI colors and other control sequences behave. | ||
535 | |||
536 | config FEATURE_TELNET_AUTOLOGIN | ||
537 | bool "Pass USER type to remote host" | ||
538 | default y | ||
539 | depends on TELNET | ||
540 | help | ||
541 | Setting this option will forward the USER environment variable to the | ||
542 | remote host you are connecting to. This is useful when you need to | ||
543 | log into a machine without telling the username (autologin). This | ||
544 | option enables `-a' and `-l USER' arguments. | ||
545 | |||
546 | config TELNETD | ||
547 | bool "telnetd" | ||
548 | default n | ||
549 | select FEATURE_SYSLOG | ||
550 | help | ||
551 | A daemon for the TELNET protocol, allowing you to log onto the host | ||
552 | running the daemon. Please keep in mind that the TELNET protocol | ||
553 | sends passwords in plain text. If you can't afford the space for an | ||
554 | SSH daemon and you trust your network, you may say 'y' here. As a | ||
555 | more secure alternative, you should seriously consider installing the | ||
556 | very small Dropbear SSH daemon instead: | ||
557 | http://matt.ucc.asn.au/dropbear/dropbear.html | ||
558 | |||
559 | Note that for busybox telnetd to work you need several things: | ||
560 | First of all, your kernel needs: | ||
561 | UNIX98_PTYS=y | ||
562 | DEVPTS_FS=y | ||
563 | |||
564 | Next, you need a /dev/pts directory on your root filesystem: | ||
565 | |||
566 | $ ls -ld /dev/pts | ||
567 | drwxr-xr-x 2 root root 0 Sep 23 13:21 /dev/pts/ | ||
568 | |||
569 | Next you need the pseudo terminal master multiplexer /dev/ptmx: | ||
570 | |||
571 | $ ls -la /dev/ptmx | ||
572 | crw-rw-rw- 1 root tty 5, 2 Sep 23 13:55 /dev/ptmx | ||
573 | |||
574 | Any /dev/ttyp[0-9]* files you may have can be removed. | ||
575 | Next, you need to mount the devpts filesystem on /dev/pts using: | ||
576 | |||
577 | mount -t devpts devpts /dev/pts | ||
578 | |||
579 | You need to be sure that Busybox has LOGIN and | ||
580 | FEATURE_SUID enabled. And finally, you should make | ||
581 | certain that Busybox has been installed setuid root: | ||
582 | |||
583 | chown root.root /bin/busybox | ||
584 | chmod 4755 /bin/busybox | ||
585 | |||
586 | with all that done, telnetd _should_ work.... | ||
587 | |||
588 | |||
589 | config FEATURE_TELNETD_STANDALONE | ||
590 | bool "Support standalone telnetd (not inetd only)" | ||
591 | default n | ||
592 | depends on TELNETD | ||
593 | help | ||
594 | Selecting this will make telnetd able to run standalone. | ||
595 | |||
596 | config TFTP | ||
597 | bool "tftp" | ||
598 | default n | ||
599 | help | ||
600 | This enables the Trivial File Transfer Protocol client program. TFTP | ||
601 | is usually used for simple, small transfers such as a root image | ||
602 | for a network-enabled bootloader. | ||
603 | |||
604 | config FEATURE_TFTP_GET | ||
605 | bool "Enable \"get\" command" | ||
606 | default y | ||
607 | depends on TFTP | ||
608 | help | ||
609 | Add support for the GET command within the TFTP client. This allows | ||
610 | a client to retrieve a file from a TFTP server. | ||
611 | |||
612 | config FEATURE_TFTP_PUT | ||
613 | bool "Enable \"put\" command" | ||
614 | default y | ||
615 | depends on TFTP | ||
616 | help | ||
617 | Add support for the PUT command within the TFTP client. This allows | ||
618 | a client to transfer a file to a TFTP server. | ||
619 | |||
620 | config FEATURE_TFTP_BLOCKSIZE | ||
621 | bool "Enable \"blocksize\" command" | ||
622 | default n | ||
623 | depends on TFTP | ||
624 | help | ||
625 | Allow the client to specify the desired block size for transfers. | ||
626 | |||
627 | config DEBUG_TFTP | ||
628 | bool "Enable debug" | ||
629 | default n | ||
630 | depends on TFTP | ||
631 | help | ||
632 | Enable debug settings for tftp. This is useful if you're running | ||
633 | into problems with tftp as the protocol doesn't help you much when | ||
634 | you run into problems. | ||
635 | |||
636 | config TRACEROUTE | ||
637 | bool "traceroute" | ||
638 | default n | ||
639 | help | ||
640 | Utility to trace the route of IP packets | ||
641 | |||
642 | config FEATURE_TRACEROUTE_VERBOSE | ||
643 | bool "Enable verbose output" | ||
644 | default n | ||
645 | depends on TRACEROUTE | ||
646 | help | ||
647 | Add some verbosity to traceroute. This includes amongst other things | ||
648 | hostnames and ICMP response types. | ||
649 | |||
650 | config FEATURE_TRACEROUTE_SOURCE_ROUTE | ||
651 | bool "Enable loose source route" | ||
652 | default n | ||
653 | depends on TRACEROUTE | ||
654 | help | ||
655 | Add option to specify a loose source route gateway | ||
656 | (8 maximum). | ||
657 | |||
658 | config FEATURE_TRACEROUTE_USE_ICMP | ||
659 | bool "Use ICMP instead of UDP" | ||
660 | default n | ||
661 | depends on TRACEROUTE | ||
662 | help | ||
663 | Add feature to allow for ICMP ECHO instead of UDP datagrams. | ||
664 | |||
665 | source networking/udhcp/Config.in | ||
666 | |||
667 | config VCONFIG | ||
668 | bool "vconfig" | ||
669 | default n | ||
670 | help | ||
671 | Creates, removes, and configures VLAN interfaces | ||
672 | |||
673 | config WGET | ||
674 | bool "wget" | ||
675 | default n | ||
676 | help | ||
677 | wget is a utility for non-interactive download of files from HTTP, | ||
678 | HTTPS, and FTP servers. | ||
679 | |||
680 | config FEATURE_WGET_STATUSBAR | ||
681 | bool "Enable a nifty process meter (+2k)" | ||
682 | default y | ||
683 | depends on WGET | ||
684 | help | ||
685 | Enable the transfer progress bar for wget transfers. | ||
686 | |||
687 | config FEATURE_WGET_AUTHENTICATION | ||
688 | bool "Enable HTTP authentication" | ||
689 | default y | ||
690 | depends on WGET | ||
691 | help | ||
692 | Support authenticated HTTP transfers. | ||
693 | |||
694 | config FEATURE_WGET_IP6_LITERAL | ||
695 | bool "Enable IPv6 literal addresses" | ||
696 | default y | ||
697 | depends on WGET && FEATURE_IPV6 | ||
698 | help | ||
699 | Support IPv6 address literal notation in URLs. | ||
700 | |||
701 | config FEATURE_WGET_LONG_OPTIONS | ||
702 | bool "Enable long options" | ||
703 | default n | ||
704 | depends on WGET && GETOPT_LONG | ||
705 | help | ||
706 | Support long options for the wget applet. | ||
707 | |||
708 | config ZCIP | ||
709 | bool "zcip" | ||
710 | default n | ||
711 | select FEATURE_SYSLOG | ||
712 | help | ||
713 | ZCIP provides ZeroConf IPv4 address selection, according to RFC 3927. | ||
714 | It's a daemon that allocates and defends a dynamically assigned | ||
715 | address on the 169.254/16 network, requiring no system administrator. | ||
716 | |||
717 | See http://www.zeroconf.org for further details, and "zcip.script" | ||
718 | in the busybox examples. | ||
719 | |||
720 | endmenu | ||
diff --git a/networking/Kbuild b/networking/Kbuild new file mode 100644 index 000000000..a9a51fc2e --- /dev/null +++ b/networking/Kbuild | |||
@@ -0,0 +1,39 @@ | |||
1 | # Makefile for busybox | ||
2 | # | ||
3 | # Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org> | ||
4 | # | ||
5 | # Licensed under the GPL v2, see the file LICENSE in this tarball. | ||
6 | |||
7 | lib-y:= | ||
8 | lib-$(CONFIG_ARPING) += arping.o | ||
9 | lib-$(CONFIG_DNSD) += dnsd.o | ||
10 | lib-$(CONFIG_ETHER_WAKE) += ether-wake.o | ||
11 | lib-$(CONFIG_FAKEIDENTD) += fakeidentd.o | ||
12 | lib-$(CONFIG_FTPGET) += ftpgetput.o | ||
13 | lib-$(CONFIG_FTPPUT) += ftpgetput.o | ||
14 | lib-$(CONFIG_HOSTNAME) += hostname.o | ||
15 | lib-$(CONFIG_HTTPD) += httpd.o | ||
16 | lib-$(CONFIG_IFCONFIG) += ifconfig.o interface.o | ||
17 | lib-$(CONFIG_IFUPDOWN) += ifupdown.o | ||
18 | lib-$(CONFIG_INETD) += inetd.o | ||
19 | lib-$(CONFIG_IP) += ip.o | ||
20 | lib-$(CONFIG_IPCALC) += ipcalc.o | ||
21 | lib-$(CONFIG_IPADDR) += ipaddr.o | ||
22 | lib-$(CONFIG_IPLINK) += iplink.o | ||
23 | lib-$(CONFIG_IPROUTE) += iproute.o | ||
24 | lib-$(CONFIG_IPRULE) += iprule.o | ||
25 | lib-$(CONFIG_IPTUNNEL) += iptunnel.o | ||
26 | lib-$(CONFIG_NAMEIF) += nameif.o | ||
27 | lib-$(CONFIG_NC) += nc.o | ||
28 | lib-$(CONFIG_NETSTAT) += netstat.o | ||
29 | lib-$(CONFIG_NSLOOKUP) += nslookup.o | ||
30 | lib-$(CONFIG_PING) += ping.o | ||
31 | lib-$(CONFIG_PING6) += ping6.o | ||
32 | lib-$(CONFIG_ROUTE) += route.o | ||
33 | lib-$(CONFIG_TELNET) += telnet.o | ||
34 | lib-$(CONFIG_TELNETD) += telnetd.o | ||
35 | lib-$(CONFIG_TFTP) += tftp.o | ||
36 | lib-$(CONFIG_TRACEROUTE) += traceroute.o | ||
37 | lib-$(CONFIG_VCONFIG) += vconfig.o | ||
38 | lib-$(CONFIG_WGET) += wget.o | ||
39 | lib-$(CONFIG_ZCIP) += zcip.o | ||
diff --git a/networking/arping.c b/networking/arping.c new file mode 100644 index 000000000..2d92bf4be --- /dev/null +++ b/networking/arping.c | |||
@@ -0,0 +1,446 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * arping.c - Ping hosts by ARP requests/replies | ||
4 | * | ||
5 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | ||
6 | * | ||
7 | * Author: Alexey Kuznetsov <kuznet@ms2.inr.ac.ru> | ||
8 | * Busybox port: Nick Fedchik <nick@fedchik.org.ua> | ||
9 | */ | ||
10 | |||
11 | #include <sys/ioctl.h> | ||
12 | #include <signal.h> | ||
13 | |||
14 | #include <errno.h> | ||
15 | #include <stdlib.h> | ||
16 | #include <string.h> | ||
17 | #include <unistd.h> | ||
18 | |||
19 | #include <arpa/inet.h> | ||
20 | #include <net/if.h> | ||
21 | #include <netinet/ether.h> | ||
22 | #include <netpacket/packet.h> | ||
23 | |||
24 | #include "busybox.h" | ||
25 | |||
26 | static struct in_addr src; | ||
27 | static struct in_addr dst; | ||
28 | static struct sockaddr_ll me; | ||
29 | static struct sockaddr_ll he; | ||
30 | static struct timeval last; | ||
31 | |||
32 | enum cfg_e { | ||
33 | dad = 1, | ||
34 | unsolicited = 2, | ||
35 | advert = 4, | ||
36 | quiet = 8, | ||
37 | quit_on_reply = 16, | ||
38 | broadcast_only = 32, | ||
39 | unicasting = 64 | ||
40 | }; | ||
41 | static int cfg; | ||
42 | |||
43 | static int s; | ||
44 | static unsigned count = UINT_MAX; | ||
45 | static unsigned timeout; | ||
46 | static int sent; | ||
47 | static int brd_sent; | ||
48 | static int received; | ||
49 | static int brd_recv; | ||
50 | static int req_recv; | ||
51 | |||
52 | |||
53 | #define MS_TDIFF(tv1,tv2) ( ((tv1).tv_sec-(tv2).tv_sec)*1000 + \ | ||
54 | ((tv1).tv_usec-(tv2).tv_usec)/1000 ) | ||
55 | static int send_pack(int sock, struct in_addr *src_addr, | ||
56 | struct in_addr *dst_addr, struct sockaddr_ll *ME, | ||
57 | struct sockaddr_ll *HE) | ||
58 | { | ||
59 | int err; | ||
60 | struct timeval now; | ||
61 | RESERVE_CONFIG_UBUFFER(buf, 256); | ||
62 | struct arphdr *ah = (struct arphdr *) buf; | ||
63 | unsigned char *p = (unsigned char *) (ah + 1); | ||
64 | |||
65 | ah->ar_hrd = htons(ME->sll_hatype); | ||
66 | ah->ar_hrd = htons(ARPHRD_ETHER); | ||
67 | ah->ar_pro = htons(ETH_P_IP); | ||
68 | ah->ar_hln = ME->sll_halen; | ||
69 | ah->ar_pln = 4; | ||
70 | ah->ar_op = cfg&advert ? htons(ARPOP_REPLY) : htons(ARPOP_REQUEST); | ||
71 | |||
72 | memcpy(p, &ME->sll_addr, ah->ar_hln); | ||
73 | p += ME->sll_halen; | ||
74 | |||
75 | memcpy(p, src_addr, 4); | ||
76 | p += 4; | ||
77 | |||
78 | if (cfg&advert) | ||
79 | memcpy(p, &ME->sll_addr, ah->ar_hln); | ||
80 | else | ||
81 | memcpy(p, &HE->sll_addr, ah->ar_hln); | ||
82 | p += ah->ar_hln; | ||
83 | |||
84 | memcpy(p, dst_addr, 4); | ||
85 | p += 4; | ||
86 | |||
87 | gettimeofday(&now, NULL); | ||
88 | err = sendto(sock, buf, p - buf, 0, (struct sockaddr *) HE, sizeof(*HE)); | ||
89 | if (err == p - buf) { | ||
90 | last = now; | ||
91 | sent++; | ||
92 | if (!(cfg&unicasting)) | ||
93 | brd_sent++; | ||
94 | } | ||
95 | RELEASE_CONFIG_BUFFER(buf); | ||
96 | return err; | ||
97 | } | ||
98 | |||
99 | static void finish(void) | ||
100 | { | ||
101 | if (!(cfg&quiet)) { | ||
102 | printf("Sent %d probes (%d broadcast(s))\n" | ||
103 | "Received %d repl%s", | ||
104 | sent, brd_sent, | ||
105 | received, (received > 1) ? "ies" : "y"); | ||
106 | if (brd_recv || req_recv) { | ||
107 | printf(" ("); | ||
108 | if (req_recv) | ||
109 | printf("%d request(s)", req_recv); | ||
110 | if (brd_recv) | ||
111 | printf("%s%d broadcast(s)", req_recv ? ", " : "", brd_recv); | ||
112 | putchar(')'); | ||
113 | } | ||
114 | putchar('\n'); | ||
115 | fflush(stdout); | ||
116 | } | ||
117 | if (cfg&dad) | ||
118 | exit(!!received); | ||
119 | if (cfg&unsolicited) | ||
120 | exit(0); | ||
121 | exit(!received); | ||
122 | } | ||
123 | |||
124 | static void catcher(void) | ||
125 | { | ||
126 | struct timeval tv; | ||
127 | static struct timeval start; | ||
128 | |||
129 | gettimeofday(&tv, NULL); | ||
130 | |||
131 | if (start.tv_sec == 0) | ||
132 | start = tv; | ||
133 | |||
134 | if (count-- == 0 | ||
135 | || (timeout && MS_TDIFF(tv, start) > timeout * 1000 + 500)) | ||
136 | finish(); | ||
137 | |||
138 | if (last.tv_sec == 0 || MS_TDIFF(tv, last) > 500) { | ||
139 | send_pack(s, &src, &dst, &me, &he); | ||
140 | if (count == 0 && cfg&unsolicited) | ||
141 | finish(); | ||
142 | } | ||
143 | alarm(1); | ||
144 | } | ||
145 | |||
146 | static int recv_pack(unsigned char *buf, int len, struct sockaddr_ll *FROM) | ||
147 | { | ||
148 | struct arphdr *ah = (struct arphdr *) buf; | ||
149 | unsigned char *p = (unsigned char *) (ah + 1); | ||
150 | struct in_addr src_ip, dst_ip; | ||
151 | |||
152 | /* Filter out wild packets */ | ||
153 | if (FROM->sll_pkttype != PACKET_HOST && | ||
154 | FROM->sll_pkttype != PACKET_BROADCAST && | ||
155 | FROM->sll_pkttype != PACKET_MULTICAST) | ||
156 | return 0; | ||
157 | |||
158 | /* Only these types are recognised */ | ||
159 | if (ah->ar_op != htons(ARPOP_REQUEST) && ah->ar_op != htons(ARPOP_REPLY)) | ||
160 | return 0; | ||
161 | |||
162 | /* ARPHRD check and this darned FDDI hack here :-( */ | ||
163 | if (ah->ar_hrd != htons(FROM->sll_hatype) && | ||
164 | (FROM->sll_hatype != ARPHRD_FDDI | ||
165 | || ah->ar_hrd != htons(ARPHRD_ETHER))) | ||
166 | return 0; | ||
167 | |||
168 | /* Protocol must be IP. */ | ||
169 | if (ah->ar_pro != htons(ETH_P_IP)) | ||
170 | return 0; | ||
171 | if (ah->ar_pln != 4) | ||
172 | return 0; | ||
173 | if (ah->ar_hln != me.sll_halen) | ||
174 | return 0; | ||
175 | if (len < sizeof(*ah) + 2 * (4 + ah->ar_hln)) | ||
176 | return 0; | ||
177 | memcpy(&src_ip, p + ah->ar_hln, 4); | ||
178 | memcpy(&dst_ip, p + ah->ar_hln + 4 + ah->ar_hln, 4); | ||
179 | if (!(cfg&dad)) { | ||
180 | if (src_ip.s_addr != dst.s_addr) | ||
181 | return 0; | ||
182 | if (src.s_addr != dst_ip.s_addr) | ||
183 | return 0; | ||
184 | if (memcmp(p + ah->ar_hln + 4, &me.sll_addr, ah->ar_hln)) | ||
185 | return 0; | ||
186 | } else { | ||
187 | /* DAD packet was: | ||
188 | src_ip = 0 (or some src) | ||
189 | src_hw = ME | ||
190 | dst_ip = tested address | ||
191 | dst_hw = <unspec> | ||
192 | |||
193 | We fail, if receive request/reply with: | ||
194 | src_ip = tested_address | ||
195 | src_hw != ME | ||
196 | if src_ip in request was not zero, check | ||
197 | also that it matches to dst_ip, otherwise | ||
198 | dst_ip/dst_hw do not matter. | ||
199 | */ | ||
200 | if (src_ip.s_addr != dst.s_addr) | ||
201 | return 0; | ||
202 | if (memcmp(p, &me.sll_addr, me.sll_halen) == 0) | ||
203 | return 0; | ||
204 | if (src.s_addr && src.s_addr != dst_ip.s_addr) | ||
205 | return 0; | ||
206 | } | ||
207 | if (!(cfg&quiet)) { | ||
208 | int s_printed = 0; | ||
209 | struct timeval tv; | ||
210 | |||
211 | gettimeofday(&tv, NULL); | ||
212 | |||
213 | printf("%s %s from %s [%s]", | ||
214 | FROM->sll_pkttype == PACKET_HOST ? "Unicast" : "Broadcast", | ||
215 | ah->ar_op == htons(ARPOP_REPLY) ? "reply" : "request", | ||
216 | inet_ntoa(src_ip), | ||
217 | ether_ntoa((struct ether_addr *) p)); | ||
218 | if (dst_ip.s_addr != src.s_addr) { | ||
219 | printf("for %s ", inet_ntoa(dst_ip)); | ||
220 | s_printed = 1; | ||
221 | } | ||
222 | if (memcmp(p + ah->ar_hln + 4, me.sll_addr, ah->ar_hln)) { | ||
223 | if (!s_printed) | ||
224 | printf("for "); | ||
225 | printf("[%s]", | ||
226 | ether_ntoa((struct ether_addr *) p + ah->ar_hln + 4)); | ||
227 | } | ||
228 | |||
229 | if (last.tv_sec) { | ||
230 | long usecs = (tv.tv_sec - last.tv_sec) * 1000000 + | ||
231 | tv.tv_usec - last.tv_usec; | ||
232 | long msecs = (usecs + 500) / 1000; | ||
233 | |||
234 | usecs -= msecs * 1000 - 500; | ||
235 | printf(" %ld.%03ldms\n", msecs, usecs); | ||
236 | } else { | ||
237 | printf(" UNSOLICITED?\n"); | ||
238 | } | ||
239 | fflush(stdout); | ||
240 | } | ||
241 | received++; | ||
242 | if (FROM->sll_pkttype != PACKET_HOST) | ||
243 | brd_recv++; | ||
244 | if (ah->ar_op == htons(ARPOP_REQUEST)) | ||
245 | req_recv++; | ||
246 | if (cfg&quit_on_reply) | ||
247 | finish(); | ||
248 | if (!(cfg&broadcast_only)) { | ||
249 | memcpy(he.sll_addr, p, me.sll_halen); | ||
250 | cfg |= unicasting; | ||
251 | } | ||
252 | return 1; | ||
253 | } | ||
254 | |||
255 | int arping_main(int argc, char **argv) | ||
256 | { | ||
257 | char *device = "eth0"; | ||
258 | int ifindex; | ||
259 | char *source = NULL; | ||
260 | char *target; | ||
261 | |||
262 | s = xsocket(PF_PACKET, SOCK_DGRAM, 0); | ||
263 | |||
264 | // Drop suid root privileges | ||
265 | xsetuid(getuid()); | ||
266 | |||
267 | { | ||
268 | unsigned opt; | ||
269 | char *_count, *_timeout; | ||
270 | |||
271 | /* Dad also sets quit_on_reply. | ||
272 | * Advert also sets unsolicited. | ||
273 | */ | ||
274 | opt_complementary = "Df:AU"; | ||
275 | opt = getopt32(argc, argv, "DUAqfbc:w:i:s:", | ||
276 | &_count, &_timeout, &device, &source); | ||
277 | cfg |= opt & 0x3f; /* set respective flags */ | ||
278 | if (opt & 0x40) /* -c: count */ | ||
279 | count = xatou(_count); | ||
280 | if (opt & 0x80) /* -w: timeout */ | ||
281 | timeout = xatoul_range(_timeout, 0, INT_MAX/2000); | ||
282 | if (opt & 0x100) { /* -i: interface */ | ||
283 | if (strlen(device) > IF_NAMESIZE) { | ||
284 | bb_error_msg_and_die("interface name '%s' is too long", | ||
285 | device); | ||
286 | } | ||
287 | } | ||
288 | //if (opt & 0x200) /* -s: source */ | ||
289 | } | ||
290 | argc -= optind; | ||
291 | argv += optind; | ||
292 | |||
293 | if (argc != 1) | ||
294 | bb_show_usage(); | ||
295 | |||
296 | target = *argv; | ||
297 | |||
298 | xfunc_error_retval = 2; | ||
299 | |||
300 | { | ||
301 | struct ifreq ifr; | ||
302 | |||
303 | memset(&ifr, 0, sizeof(ifr)); | ||
304 | strncpy(ifr.ifr_name, device, IFNAMSIZ - 1); | ||
305 | if (ioctl(s, SIOCGIFINDEX, &ifr) < 0) { | ||
306 | bb_error_msg_and_die("interface %s not found", device); | ||
307 | } | ||
308 | ifindex = ifr.ifr_ifindex; | ||
309 | |||
310 | if (ioctl(s, SIOCGIFFLAGS, (char *) &ifr)) { | ||
311 | bb_error_msg_and_die("SIOCGIFFLAGS"); | ||
312 | } | ||
313 | if (!(ifr.ifr_flags & IFF_UP)) { | ||
314 | bb_error_msg_and_die("interface %s is down", device); | ||
315 | } | ||
316 | if (ifr.ifr_flags & (IFF_NOARP | IFF_LOOPBACK)) { | ||
317 | bb_error_msg("interface %s is not ARPable", device); | ||
318 | exit(cfg&dad ? 0 : 2); | ||
319 | } | ||
320 | } | ||
321 | |||
322 | if (!inet_aton(target, &dst)) { | ||
323 | struct hostent *hp; | ||
324 | |||
325 | hp = gethostbyname2(target, AF_INET); | ||
326 | if (!hp) { | ||
327 | bb_error_msg_and_die("invalid or unknown target %s", target); | ||
328 | } | ||
329 | memcpy(&dst, hp->h_addr, 4); | ||
330 | } | ||
331 | |||
332 | if (source && !inet_aton(source, &src)) { | ||
333 | bb_error_msg_and_die("invalid source address %s", source); | ||
334 | } | ||
335 | |||
336 | if (!(cfg&dad) && cfg&unsolicited && src.s_addr == 0) | ||
337 | src = dst; | ||
338 | |||
339 | if (!(cfg&dad) || src.s_addr) { | ||
340 | struct sockaddr_in saddr; | ||
341 | int probe_fd = xsocket(AF_INET, SOCK_DGRAM, 0); | ||
342 | |||
343 | if (device) { | ||
344 | if (setsockopt | ||
345 | (probe_fd, SOL_SOCKET, SO_BINDTODEVICE, device, | ||
346 | strlen(device) + 1) == -1) | ||
347 | bb_error_msg("warning: interface %s is ignored", device); | ||
348 | } | ||
349 | memset(&saddr, 0, sizeof(saddr)); | ||
350 | saddr.sin_family = AF_INET; | ||
351 | if (src.s_addr) { | ||
352 | saddr.sin_addr = src; | ||
353 | if (bind(probe_fd, (struct sockaddr *) &saddr, sizeof(saddr)) == -1) { | ||
354 | bb_error_msg_and_die("bind"); | ||
355 | } | ||
356 | } else if (!(cfg&dad)) { | ||
357 | static const int on = 1; | ||
358 | socklen_t alen = sizeof(saddr); | ||
359 | |||
360 | saddr.sin_port = htons(1025); | ||
361 | saddr.sin_addr = dst; | ||
362 | |||
363 | if (setsockopt | ||
364 | (probe_fd, SOL_SOCKET, SO_DONTROUTE, (char *) &on, | ||
365 | sizeof(on)) == -1) | ||
366 | bb_perror_msg("warning: setsockopt(SO_DONTROUTE)"); | ||
367 | if (connect(probe_fd, (struct sockaddr *) &saddr, sizeof(saddr)) | ||
368 | == -1) { | ||
369 | bb_error_msg_and_die("connect"); | ||
370 | } | ||
371 | if (getsockname(probe_fd, (struct sockaddr *) &saddr, &alen) == | ||
372 | -1) { | ||
373 | bb_error_msg_and_die("getsockname"); | ||
374 | } | ||
375 | src = saddr.sin_addr; | ||
376 | } | ||
377 | close(probe_fd); | ||
378 | }; | ||
379 | |||
380 | me.sll_family = AF_PACKET; | ||
381 | me.sll_ifindex = ifindex; | ||
382 | me.sll_protocol = htons(ETH_P_ARP); | ||
383 | if (bind(s, (struct sockaddr *) &me, sizeof(me)) == -1) { | ||
384 | bb_error_msg_and_die("bind"); | ||
385 | } | ||
386 | |||
387 | { | ||
388 | socklen_t alen = sizeof(me); | ||
389 | |||
390 | if (getsockname(s, (struct sockaddr *) &me, &alen) == -1) { | ||
391 | bb_error_msg_and_die("getsockname"); | ||
392 | } | ||
393 | } | ||
394 | if (me.sll_halen == 0) { | ||
395 | bb_error_msg("interface \"%s\" is not ARPable (no ll address)", device); | ||
396 | exit(cfg&dad ? 0 : 2); | ||
397 | } | ||
398 | he = me; | ||
399 | memset(he.sll_addr, -1, he.sll_halen); | ||
400 | |||
401 | if (!(cfg&quiet)) { | ||
402 | printf("ARPING to %s from %s via %s\n", | ||
403 | inet_ntoa(dst), inet_ntoa(src), | ||
404 | device ? device : "unknown"); | ||
405 | } | ||
406 | |||
407 | if (!src.s_addr && !(cfg&dad)) { | ||
408 | bb_error_msg_and_die("no src address in the non-DAD mode"); | ||
409 | } | ||
410 | |||
411 | { | ||
412 | struct sigaction sa; | ||
413 | |||
414 | memset(&sa, 0, sizeof(sa)); | ||
415 | sa.sa_flags = SA_RESTART; | ||
416 | |||
417 | sa.sa_handler = (void (*)(int)) finish; | ||
418 | sigaction(SIGINT, &sa, NULL); | ||
419 | |||
420 | sa.sa_handler = (void (*)(int)) catcher; | ||
421 | sigaction(SIGALRM, &sa, NULL); | ||
422 | } | ||
423 | |||
424 | catcher(); | ||
425 | |||
426 | while (1) { | ||
427 | sigset_t sset, osset; | ||
428 | RESERVE_CONFIG_UBUFFER(packet, 4096); | ||
429 | struct sockaddr_ll from; | ||
430 | socklen_t alen = sizeof(from); | ||
431 | int cc; | ||
432 | |||
433 | if ((cc = recvfrom(s, packet, 4096, 0, | ||
434 | (struct sockaddr *) &from, &alen)) < 0) { | ||
435 | bb_perror_msg("recvfrom"); | ||
436 | continue; | ||
437 | } | ||
438 | sigemptyset(&sset); | ||
439 | sigaddset(&sset, SIGALRM); | ||
440 | sigaddset(&sset, SIGINT); | ||
441 | sigprocmask(SIG_BLOCK, &sset, &osset); | ||
442 | recv_pack(packet, cc, &from); | ||
443 | sigprocmask(SIG_SETMASK, &osset, NULL); | ||
444 | RELEASE_CONFIG_BUFFER(packet); | ||
445 | } | ||
446 | } | ||
diff --git a/networking/dnsd.c b/networking/dnsd.c new file mode 100644 index 000000000..5e9cf52f1 --- /dev/null +++ b/networking/dnsd.c | |||
@@ -0,0 +1,456 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * Mini DNS server implementation for busybox | ||
4 | * | ||
5 | * Copyright (C) 2005 Roberto A. Foglietta (me@roberto.foglietta.name) | ||
6 | * Copyright (C) 2005 Odd Arild Olsen (oao at fibula dot no) | ||
7 | * Copyright (C) 2003 Paul Sheer | ||
8 | * | ||
9 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | ||
10 | * | ||
11 | * Odd Arild Olsen started out with the sheerdns [1] of Paul Sheer and rewrote | ||
12 | * it into a shape which I believe is both easier to understand and maintain. | ||
13 | * I also reused the input buffer for output and removed services he did not | ||
14 | * need. [1] http://threading.2038bug.com/sheerdns/ | ||
15 | * | ||
16 | * Some bugfix and minor changes was applied by Roberto A. Foglietta who made | ||
17 | * the first porting of oao' scdns to busybox also. | ||
18 | */ | ||
19 | |||
20 | #include "busybox.h" | ||
21 | |||
22 | static char *fileconf = "/etc/dnsd.conf"; | ||
23 | #define LOCK_FILE "/var/run/dnsd.lock" | ||
24 | #define LOG_FILE "/var/log/dnsd.log" | ||
25 | |||
26 | // Must matct getopt32 call | ||
27 | #define OPT_daemon (option_mask32 & 0x10) | ||
28 | #define OPT_verbose (option_mask32 & 0x20) | ||
29 | |||
30 | //#define DEBUG 1 | ||
31 | |||
32 | enum { | ||
33 | MAX_HOST_LEN = 16, // longest host name allowed is 15 | ||
34 | IP_STRING_LEN = 18, // .xxx.xxx.xxx.xxx\0 | ||
35 | |||
36 | //must be strlen('.in-addr.arpa') larger than IP_STRING_LEN | ||
37 | MAX_NAME_LEN = (IP_STRING_LEN + 13), | ||
38 | |||
39 | /* Cannot get bigger packets than 512 per RFC1035 | ||
40 | In practice this can be set considerably smaller: | ||
41 | Length of response packet is header (12B) + 2*type(4B) + 2*class(4B) + | ||
42 | ttl(4B) + rlen(2B) + r (MAX_NAME_LEN =21B) + | ||
43 | 2*querystring (2 MAX_NAME_LEN= 42B), all together 90 Byte | ||
44 | */ | ||
45 | MAX_PACK_LEN = 512 + 1, | ||
46 | |||
47 | DEFAULT_TTL = 30, // increase this when not testing? | ||
48 | |||
49 | REQ_A = 1, | ||
50 | REQ_PTR = 12 | ||
51 | }; | ||
52 | |||
53 | struct dns_repl { // resource record, add 0 or 1 to accepted dns_msg in resp | ||
54 | uint16_t rlen; | ||
55 | uint8_t *r; // resource | ||
56 | uint16_t flags; | ||
57 | }; | ||
58 | |||
59 | struct dns_head { // the message from client and first part of response mag | ||
60 | uint16_t id; | ||
61 | uint16_t flags; | ||
62 | uint16_t nquer; // accepts 0 | ||
63 | uint16_t nansw; // 1 in response | ||
64 | uint16_t nauth; // 0 | ||
65 | uint16_t nadd; // 0 | ||
66 | }; | ||
67 | struct dns_prop { | ||
68 | uint16_t type; | ||
69 | uint16_t class; | ||
70 | }; | ||
71 | struct dns_entry { // element of known name, ip address and reversed ip address | ||
72 | struct dns_entry *next; | ||
73 | char ip[IP_STRING_LEN]; // dotted decimal IP | ||
74 | char rip[IP_STRING_LEN]; // length decimal reversed IP | ||
75 | char name[MAX_HOST_LEN]; | ||
76 | }; | ||
77 | |||
78 | static struct dns_entry *dnsentry = NULL; | ||
79 | // FIXME! unused! :( | ||
80 | static int daemonmode; | ||
81 | static uint32_t ttl = DEFAULT_TTL; | ||
82 | |||
83 | /* | ||
84 | * Convert host name from C-string to dns length/string. | ||
85 | */ | ||
86 | static void convname(char *a, uint8_t *q) | ||
87 | { | ||
88 | int i = (q[0] == '.') ? 0 : 1; | ||
89 | for (; i < MAX_HOST_LEN-1 && *q; i++, q++) | ||
90 | a[i] = tolower(*q); | ||
91 | a[0] = i - 1; | ||
92 | a[i] = 0; | ||
93 | } | ||
94 | |||
95 | /* | ||
96 | * Insert length of substrings instead of dots | ||
97 | */ | ||
98 | static void undot(uint8_t * rip) | ||
99 | { | ||
100 | int i = 0, s = 0; | ||
101 | while (rip[i]) | ||
102 | i++; | ||
103 | for (--i; i >= 0; i--) { | ||
104 | if (rip[i] == '.') { | ||
105 | rip[i] = s; | ||
106 | s = 0; | ||
107 | } else s++; | ||
108 | } | ||
109 | } | ||
110 | |||
111 | /* | ||
112 | * Append message to log file | ||
113 | */ | ||
114 | static void log_message(char *filename, char *message) | ||
115 | { | ||
116 | FILE *logfile; | ||
117 | if (!daemonmode) | ||
118 | return; | ||
119 | logfile = fopen(filename, "a"); | ||
120 | if (!logfile) | ||
121 | return; | ||
122 | fprintf(logfile, "%s\n", message); | ||
123 | fclose(logfile); | ||
124 | } | ||
125 | |||
126 | /* | ||
127 | * Read one line of hostname/IP from file | ||
128 | * Returns 0 for each valid entry read, -1 at EOF | ||
129 | * Assumes all host names are lower case only | ||
130 | * Hostnames with more than one label is not handled correctly. | ||
131 | * Presently the dot is copied into name without | ||
132 | * converting to a length/string substring for that label. | ||
133 | */ | ||
134 | |||
135 | static int getfileentry(FILE * fp, struct dns_entry *s) | ||
136 | { | ||
137 | unsigned int a,b,c,d; | ||
138 | char *r, *name; | ||
139 | |||
140 | restart: | ||
141 | r = xmalloc_fgets(fp); | ||
142 | if (!r) | ||
143 | return -1; | ||
144 | while (*r == ' ' || *r == '\t') { | ||
145 | r++; | ||
146 | if (!*r || *r == '#' || *r == '\n') | ||
147 | goto restart; /* skipping empty/blank and commented lines */ | ||
148 | } | ||
149 | name = r; | ||
150 | while (*r != ' ' && *r != '\t') | ||
151 | r++; | ||
152 | *r++ = 0; | ||
153 | if (sscanf(r, "%u.%u.%u.%u", &a, &b, &c, &d) != 4) | ||
154 | goto restart; /* skipping wrong lines */ | ||
155 | |||
156 | sprintf(s->ip, "%u.%u.%u.%u", a, b, c, d); | ||
157 | sprintf(s->rip, ".%u.%u.%u.%u", d, c, b, a); | ||
158 | undot((uint8_t*)s->rip); | ||
159 | convname(s->name,(uint8_t*)name); | ||
160 | |||
161 | if (OPT_verbose) | ||
162 | fprintf(stderr, "\tname:%s, ip:%s\n", &(s->name[1]),s->ip); | ||
163 | |||
164 | return 0; | ||
165 | } | ||
166 | |||
167 | /* | ||
168 | * Read hostname/IP records from file | ||
169 | */ | ||
170 | static void dnsentryinit(void) | ||
171 | { | ||
172 | FILE *fp; | ||
173 | struct dns_entry *m, *prev; | ||
174 | prev = dnsentry = NULL; | ||
175 | |||
176 | fp = xfopen(fileconf, "r"); | ||
177 | |||
178 | while (1) { | ||
179 | m = xmalloc(sizeof(struct dns_entry)); | ||
180 | |||
181 | m->next = NULL; | ||
182 | if (getfileentry(fp, m)) | ||
183 | break; | ||
184 | |||
185 | if (prev == NULL) | ||
186 | dnsentry = m; | ||
187 | else | ||
188 | prev->next = m; | ||
189 | prev = m; | ||
190 | } | ||
191 | fclose(fp); | ||
192 | } | ||
193 | |||
194 | |||
195 | /* | ||
196 | * Set up UDP socket | ||
197 | */ | ||
198 | static int listen_socket(char *iface_addr, int listen_port) | ||
199 | { | ||
200 | struct sockaddr_in a; | ||
201 | char msg[100]; | ||
202 | int s; | ||
203 | s = xsocket(PF_INET, SOCK_DGRAM, 0); | ||
204 | if (setsockopt_reuseaddr(s) < 0) | ||
205 | bb_perror_msg_and_die("setsockopt() failed"); | ||
206 | memset(&a, 0, sizeof(a)); | ||
207 | a.sin_port = htons(listen_port); | ||
208 | a.sin_family = AF_INET; | ||
209 | if (!inet_aton(iface_addr, &a.sin_addr)) | ||
210 | bb_perror_msg_and_die("bad iface address"); | ||
211 | xbind(s, (struct sockaddr *)&a, sizeof(a)); | ||
212 | xlisten(s, 50); | ||
213 | sprintf(msg, "accepting UDP packets on addr:port %s:%d\n", | ||
214 | iface_addr, (int)listen_port); | ||
215 | log_message(LOG_FILE, msg); | ||
216 | return s; | ||
217 | } | ||
218 | |||
219 | /* | ||
220 | * Look query up in dns records and return answer if found | ||
221 | * qs is the query string, first byte the string length | ||
222 | */ | ||
223 | static int table_lookup(uint16_t type, uint8_t * as, uint8_t * qs) | ||
224 | { | ||
225 | int i; | ||
226 | struct dns_entry *d=dnsentry; | ||
227 | |||
228 | do { | ||
229 | #ifdef DEBUG | ||
230 | char *p,*q; | ||
231 | q = (char *)&(qs[1]); | ||
232 | p = &(d->name[1]); | ||
233 | fprintf(stderr, "\n%s: %d/%d p:%s q:%s %d", | ||
234 | __FUNCTION__, strlen(p), (int)(d->name[0]), p, q, strlen(q)); | ||
235 | #endif | ||
236 | if (type == REQ_A) { /* search by host name */ | ||
237 | for (i = 1; i <= (int)(d->name[0]); i++) | ||
238 | if (tolower(qs[i]) != d->name[i]) | ||
239 | break; | ||
240 | if (i > (int)(d->name[0])) { | ||
241 | #ifdef DEBUG | ||
242 | fprintf(stderr, " OK"); | ||
243 | #endif | ||
244 | strcpy((char *)as, d->ip); | ||
245 | #ifdef DEBUG | ||
246 | fprintf(stderr, " as:%s\n", as); | ||
247 | #endif | ||
248 | return 0; | ||
249 | } | ||
250 | } else | ||
251 | if (type == REQ_PTR) { /* search by IP-address */ | ||
252 | if (!strncmp((char*)&d->rip[1], (char*)&qs[1], strlen(d->rip)-1)) { | ||
253 | strcpy((char *)as, d->name); | ||
254 | return 0; | ||
255 | } | ||
256 | } | ||
257 | d = d->next; | ||
258 | } while (d); | ||
259 | return -1; | ||
260 | } | ||
261 | |||
262 | |||
263 | /* | ||
264 | * Decode message and generate answer | ||
265 | */ | ||
266 | #define eret(s) do { fputs(s, stderr); return -1; } while (0) | ||
267 | static int process_packet(uint8_t * buf) | ||
268 | { | ||
269 | struct dns_head *head; | ||
270 | struct dns_prop *qprop; | ||
271 | struct dns_repl outr; | ||
272 | void *next, *from, *answb; | ||
273 | |||
274 | uint8_t answstr[MAX_NAME_LEN + 1]; | ||
275 | int lookup_result, type, len, packet_len; | ||
276 | uint16_t flags; | ||
277 | |||
278 | answstr[0] = '\0'; | ||
279 | |||
280 | head = (struct dns_head *)buf; | ||
281 | if (head->nquer == 0) | ||
282 | eret("no queries\n"); | ||
283 | |||
284 | if (head->flags & 0x8000) | ||
285 | eret("ignoring response packet\n"); | ||
286 | |||
287 | from = (void *)&head[1]; // start of query string | ||
288 | next = answb = from + strlen((char *)from) + 1 + sizeof(struct dns_prop); // where to append answer block | ||
289 | |||
290 | outr.rlen = 0; // may change later | ||
291 | outr.r = NULL; | ||
292 | outr.flags = 0; | ||
293 | |||
294 | qprop = (struct dns_prop *)(answb - 4); | ||
295 | type = ntohs(qprop->type); | ||
296 | |||
297 | // only let REQ_A and REQ_PTR pass | ||
298 | if (!(type == REQ_A || type == REQ_PTR)) { | ||
299 | goto empty_packet; /* we can't handle the query type */ | ||
300 | } | ||
301 | |||
302 | if (ntohs(qprop->class) != 1 /* class INET */ ) { | ||
303 | outr.flags = 4; /* not supported */ | ||
304 | goto empty_packet; | ||
305 | } | ||
306 | /* we only support standard queries */ | ||
307 | |||
308 | if ((ntohs(head->flags) & 0x7800) != 0) | ||
309 | goto empty_packet; | ||
310 | |||
311 | // We have a standard query | ||
312 | log_message(LOG_FILE, (char *)from); | ||
313 | lookup_result = table_lookup(type, answstr, (uint8_t*)from); | ||
314 | if (lookup_result != 0) { | ||
315 | outr.flags = 3 | 0x0400; //name do not exist and auth | ||
316 | goto empty_packet; | ||
317 | } | ||
318 | if (type == REQ_A) { // return an address | ||
319 | struct in_addr a; | ||
320 | if (!inet_aton((char*)answstr, &a)) {//dotted dec to long conv | ||
321 | outr.flags = 1; /* Frmt err */ | ||
322 | goto empty_packet; | ||
323 | } | ||
324 | memcpy(answstr, &a.s_addr, 4); // save before a disappears | ||
325 | outr.rlen = 4; // uint32_t IP | ||
326 | } | ||
327 | else | ||
328 | outr.rlen = strlen((char *)answstr) + 1; // a host name | ||
329 | outr.r = answstr; // 32 bit ip or a host name | ||
330 | outr.flags |= 0x0400; /* authority-bit */ | ||
331 | // we have an answer | ||
332 | head->nansw = htons(1); | ||
333 | |||
334 | // copy query block to answer block | ||
335 | len = answb - from; | ||
336 | memcpy(answb, from, len); | ||
337 | next += len; | ||
338 | |||
339 | // and append answer rr | ||
340 | *(uint32_t *) next = htonl(ttl); | ||
341 | next += 4; | ||
342 | *(uint16_t *) next = htons(outr.rlen); | ||
343 | next += 2; | ||
344 | memcpy(next, (void *)answstr, outr.rlen); | ||
345 | next += outr.rlen; | ||
346 | |||
347 | empty_packet: | ||
348 | |||
349 | flags = ntohs(head->flags); | ||
350 | // clear rcode and RA, set responsebit and our new flags | ||
351 | flags |= (outr.flags & 0xff80) | 0x8000; | ||
352 | head->flags = htons(flags); | ||
353 | head->nauth = head->nadd = htons(0); | ||
354 | head->nquer = htons(1); | ||
355 | |||
356 | packet_len = next - (void *)buf; | ||
357 | return packet_len; | ||
358 | } | ||
359 | |||
360 | /* | ||
361 | * Exit on signal | ||
362 | */ | ||
363 | static void interrupt(int x) | ||
364 | { | ||
365 | unlink(LOCK_FILE); | ||
366 | write(2, "interrupt exiting\n", 18); | ||
367 | exit(2); | ||
368 | } | ||
369 | |||
370 | int dnsd_main(int argc, char **argv) | ||
371 | { | ||
372 | int udps; | ||
373 | uint16_t port = 53; | ||
374 | uint8_t buf[MAX_PACK_LEN]; | ||
375 | char *listen_interface = "0.0.0.0"; | ||
376 | char *sttl, *sport; | ||
377 | |||
378 | getopt32(argc, argv, "i:c:t:p:dv", &listen_interface, &fileconf, &sttl, &sport); | ||
379 | //if (option_mask32 & 0x1) // -i | ||
380 | //if (option_mask32 & 0x2) // -c | ||
381 | if (option_mask32 & 0x4) // -t | ||
382 | if (!(ttl = atol(sttl))) | ||
383 | bb_show_usage(); | ||
384 | if (option_mask32 & 0x8) // -p | ||
385 | if (!(port = atol(sport))) | ||
386 | bb_show_usage(); | ||
387 | |||
388 | if (OPT_verbose) { | ||
389 | bb_info_msg("listen_interface: %s", listen_interface); | ||
390 | bb_info_msg("ttl: %d, port: %d", ttl, port); | ||
391 | bb_info_msg("fileconf: %s", fileconf); | ||
392 | } | ||
393 | |||
394 | if (OPT_daemon) | ||
395 | #ifdef BB_NOMMU | ||
396 | /* reexec for vfork() do continue parent */ | ||
397 | vfork_daemon_rexec(1, 0, argc, argv, "-d"); | ||
398 | #else | ||
399 | xdaemon(1, 0); | ||
400 | #endif | ||
401 | |||
402 | dnsentryinit(); | ||
403 | |||
404 | signal(SIGINT, interrupt); | ||
405 | signal(SIGPIPE, SIG_IGN); | ||
406 | signal(SIGHUP, SIG_IGN); | ||
407 | #ifdef SIGTSTP | ||
408 | signal(SIGTSTP, SIG_IGN); | ||
409 | #endif | ||
410 | #ifdef SIGURG | ||
411 | signal(SIGURG, SIG_IGN); | ||
412 | #endif | ||
413 | |||
414 | udps = listen_socket(listen_interface, port); | ||
415 | if (udps < 0) | ||
416 | exit(1); | ||
417 | |||
418 | while (1) { | ||
419 | fd_set fdset; | ||
420 | int r; | ||
421 | |||
422 | FD_ZERO(&fdset); | ||
423 | FD_SET(udps, &fdset); | ||
424 | // Block until a message arrives | ||
425 | r = select(udps + 1, &fdset, NULL, NULL, NULL); | ||
426 | if (r < 0) | ||
427 | bb_perror_msg_and_die("select error"); | ||
428 | if (r == 0) | ||
429 | bb_perror_msg_and_die("select spurious return"); | ||
430 | |||
431 | /* Can this test ever be false? - yes */ | ||
432 | if (FD_ISSET(udps, &fdset)) { | ||
433 | struct sockaddr_in from; | ||
434 | int fromlen = sizeof(from); | ||
435 | r = recvfrom(udps, buf, sizeof(buf), 0, | ||
436 | (struct sockaddr *)&from, | ||
437 | (void *)&fromlen); | ||
438 | if (OPT_verbose) | ||
439 | fprintf(stderr, "\n--- Got UDP "); | ||
440 | log_message(LOG_FILE, "\n--- Got UDP "); | ||
441 | |||
442 | if (r < 12 || r > 512) { | ||
443 | bb_error_msg("invalid packet size"); | ||
444 | continue; | ||
445 | } | ||
446 | if (r > 0) { | ||
447 | r = process_packet(buf); | ||
448 | if (r > 0) | ||
449 | sendto(udps, buf, | ||
450 | r, 0, (struct sockaddr *)&from, | ||
451 | fromlen); | ||
452 | } | ||
453 | } // end if | ||
454 | } // end while | ||
455 | return 0; | ||
456 | } | ||
diff --git a/networking/ether-wake.c b/networking/ether-wake.c new file mode 100644 index 000000000..e205ffc00 --- /dev/null +++ b/networking/ether-wake.c | |||
@@ -0,0 +1,284 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * ether-wake.c - Send a magic packet to wake up sleeping machines. | ||
4 | * | ||
5 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | ||
6 | * | ||
7 | * Author: Donald Becker, http://www.scyld.com/"; http://www.scyld.com/wakeonlan.html | ||
8 | * Busybox port: Christian Volkmann <haveaniceday@online.de> | ||
9 | * Used version of ether-wake.c: v1.09 11/12/2003 Donald Becker, http://www.scyld.com/"; | ||
10 | */ | ||
11 | |||
12 | /* full usage according Donald Becker | ||
13 | * usage: ether-wake [-i <ifname>] [-p aa:bb:cc:dd[:ee:ff]] 00:11:22:33:44:55\n" | ||
14 | * | ||
15 | * This program generates and transmits a Wake-On-LAN (WOL)\n" | ||
16 | * \"Magic Packet\", used for restarting machines that have been\n" | ||
17 | * soft-powered-down (ACPI D3-warm state).\n" | ||
18 | * It currently generates the standard AMD Magic Packet format, with\n" | ||
19 | * an optional password appended.\n" | ||
20 | * | ||
21 | * The single required parameter is the Ethernet MAC (station) address\n" | ||
22 | * of the machine to wake or a host ID with known NSS 'ethers' entry.\n" | ||
23 | * The MAC address may be found with the 'arp' program while the target\n" | ||
24 | * machine is awake.\n" | ||
25 | * | ||
26 | * Options:\n" | ||
27 | * -b Send wake-up packet to the broadcast address.\n" | ||
28 | * -D Increase the debug level.\n" | ||
29 | * -i ifname Use interface IFNAME instead of the default 'eth0'.\n" | ||
30 | * -p <pw> Append the four or six byte password PW to the packet.\n" | ||
31 | * A password is only required for a few adapter types.\n" | ||
32 | * The password may be specified in ethernet hex format\n" | ||
33 | * or dotted decimal (Internet address)\n" | ||
34 | * -p 00:22:44:66:88:aa\n" | ||
35 | * -p 192.168.1.1\n"; | ||
36 | * | ||
37 | * | ||
38 | * This program generates and transmits a Wake-On-LAN (WOL) "Magic Packet", | ||
39 | * used for restarting machines that have been soft-powered-down | ||
40 | * (ACPI D3-warm state). It currently generates the standard AMD Magic Packet | ||
41 | * format, with an optional password appended. | ||
42 | * | ||
43 | * This software may be used and distributed according to the terms | ||
44 | * of the GNU Public License, incorporated herein by reference. | ||
45 | * Contact the author for use under other terms. | ||
46 | * | ||
47 | * This source file was originally part of the network tricks package, and | ||
48 | * is now distributed to support the Scyld Beowulf system. | ||
49 | * Copyright 1999-2003 Donald Becker and Scyld Computing Corporation. | ||
50 | * | ||
51 | * The author may be reached as becker@scyld, or C/O | ||
52 | * Scyld Computing Corporation | ||
53 | * 914 Bay Ridge Road, Suite 220 | ||
54 | * Annapolis MD 21403 | ||
55 | * | ||
56 | * Notes: | ||
57 | * On some systems dropping root capability allows the process to be | ||
58 | * dumped, traced or debugged. | ||
59 | * If someone traces this program, they get control of a raw socket. | ||
60 | * Linux handles this safely, but beware when porting this program. | ||
61 | * | ||
62 | * An alternative to needing 'root' is using a UDP broadcast socket, however | ||
63 | * doing so only works with adapters configured for unicast+broadcast Rx | ||
64 | * filter. That configuration consumes more power. | ||
65 | */ | ||
66 | |||
67 | |||
68 | #include <netpacket/packet.h> | ||
69 | #include <net/ethernet.h> | ||
70 | #include <netinet/ether.h> | ||
71 | #include <linux/if.h> | ||
72 | |||
73 | #include "busybox.h" | ||
74 | |||
75 | /* Note: PF_INET, SOCK_DGRAM, IPPROTO_UDP would allow SIOCGIFHWADDR to | ||
76 | * work as non-root, but we need SOCK_PACKET to specify the Ethernet | ||
77 | * destination address. | ||
78 | */ | ||
79 | #ifdef PF_PACKET | ||
80 | # define whereto_t sockaddr_ll | ||
81 | # define make_socket() xsocket(PF_PACKET, SOCK_RAW, 0) | ||
82 | #else | ||
83 | # define whereto_t sockaddr | ||
84 | # define make_socket() xsocket(AF_INET, SOCK_PACKET, SOCK_PACKET) | ||
85 | #endif | ||
86 | |||
87 | #ifdef DEBUG | ||
88 | # define bb_debug_msg(fmt, args...) fprintf(stderr, fmt, ## args) | ||
89 | void bb_debug_dump_packet(unsigned char *outpack, int pktsize) | ||
90 | { | ||
91 | int i; | ||
92 | printf("packet dump:\n"); | ||
93 | for (i = 0; i < pktsize; ++i) { | ||
94 | printf("%2.2x ", outpack[i]); | ||
95 | if (i % 20 == 19) puts(""); | ||
96 | } | ||
97 | printf("\n\n"); | ||
98 | } | ||
99 | #else | ||
100 | # define bb_debug_msg(fmt, args...) | ||
101 | # define bb_debug_dump_packet(outpack, pktsize) | ||
102 | #endif | ||
103 | |||
104 | static inline void get_dest_addr(const char *arg, struct ether_addr *eaddr); | ||
105 | static inline int get_fill(unsigned char *pkt, struct ether_addr *eaddr, int broadcast); | ||
106 | static inline int get_wol_pw(const char *ethoptarg, unsigned char *wol_passwd); | ||
107 | |||
108 | int ether_wake_main(int argc, char *argv[]) | ||
109 | { | ||
110 | char *ifname = "eth0", *pass = NULL; | ||
111 | unsigned long flags; | ||
112 | unsigned char wol_passwd[6]; | ||
113 | int wol_passwd_sz = 0; | ||
114 | |||
115 | int s; /* Raw socket */ | ||
116 | int pktsize; | ||
117 | unsigned char outpack[1000]; | ||
118 | |||
119 | struct ether_addr eaddr; | ||
120 | struct whereto_t whereto; /* who to wake up */ | ||
121 | |||
122 | /* handle misc user options */ | ||
123 | flags = getopt32(argc, argv, "bi:p:", &ifname, &pass); | ||
124 | if (optind == argc) | ||
125 | bb_show_usage(); | ||
126 | if (pass) | ||
127 | wol_passwd_sz = get_wol_pw(pass, wol_passwd); | ||
128 | |||
129 | /* create the raw socket */ | ||
130 | s = make_socket(); | ||
131 | |||
132 | /* now that we have a raw socket we can drop root */ | ||
133 | xsetuid(getuid()); | ||
134 | |||
135 | /* look up the dest mac address */ | ||
136 | get_dest_addr(argv[optind], &eaddr); | ||
137 | |||
138 | /* fill out the header of the packet */ | ||
139 | pktsize = get_fill(outpack, &eaddr, flags /*& 1 [OPT_BROADCAST]*/); | ||
140 | |||
141 | bb_debug_dump_packet(outpack, pktsize); | ||
142 | |||
143 | /* Fill in the source address, if possible. */ | ||
144 | #ifdef __linux__ | ||
145 | { | ||
146 | struct ifreq if_hwaddr; | ||
147 | |||
148 | strncpy(if_hwaddr.ifr_name, ifname, sizeof(if_hwaddr.ifr_name)); | ||
149 | if (ioctl(s, SIOCGIFHWADDR, &if_hwaddr) < 0) | ||
150 | bb_perror_msg_and_die("SIOCGIFHWADDR on %s failed", ifname); | ||
151 | |||
152 | memcpy(outpack+6, if_hwaddr.ifr_hwaddr.sa_data, 6); | ||
153 | |||
154 | # ifdef DEBUG | ||
155 | { | ||
156 | unsigned char *hwaddr = if_hwaddr.ifr_hwaddr.sa_data; | ||
157 | printf("The hardware address (SIOCGIFHWADDR) of %s is type %d " | ||
158 | "%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x\n\n", ifname, | ||
159 | if_hwaddr.ifr_hwaddr.sa_family, hwaddr[0], hwaddr[1], | ||
160 | hwaddr[2], hwaddr[3], hwaddr[4], hwaddr[5]); | ||
161 | } | ||
162 | # endif | ||
163 | } | ||
164 | #endif /* __linux__ */ | ||
165 | |||
166 | bb_debug_dump_packet(outpack, pktsize); | ||
167 | |||
168 | /* append the password if specified */ | ||
169 | if (wol_passwd_sz > 0) { | ||
170 | memcpy(outpack+pktsize, wol_passwd, wol_passwd_sz); | ||
171 | pktsize += wol_passwd_sz; | ||
172 | } | ||
173 | |||
174 | bb_debug_dump_packet(outpack, pktsize); | ||
175 | |||
176 | /* This is necessary for broadcasts to work */ | ||
177 | if (flags /*& 1 [OPT_BROADCAST]*/) { | ||
178 | if (setsockopt_broadcast(s) < 0) | ||
179 | bb_perror_msg("SO_BROADCAST"); | ||
180 | } | ||
181 | |||
182 | #if defined(PF_PACKET) | ||
183 | { | ||
184 | struct ifreq ifr; | ||
185 | strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); | ||
186 | if (ioctl(s, SIOCGIFINDEX, &ifr) == -1) | ||
187 | bb_perror_msg_and_die("SIOCGIFINDEX"); | ||
188 | memset(&whereto, 0, sizeof(whereto)); | ||
189 | whereto.sll_family = AF_PACKET; | ||
190 | whereto.sll_ifindex = ifr.ifr_ifindex; | ||
191 | /* The manual page incorrectly claims the address must be filled. | ||
192 | We do so because the code may change to match the docs. */ | ||
193 | whereto.sll_halen = ETH_ALEN; | ||
194 | memcpy(whereto.sll_addr, outpack, ETH_ALEN); | ||
195 | } | ||
196 | #else | ||
197 | whereto.sa_family = 0; | ||
198 | strcpy(whereto.sa_data, ifname); | ||
199 | #endif | ||
200 | |||
201 | if (sendto(s, outpack, pktsize, 0, (struct sockaddr *)&whereto, sizeof(whereto)) < 0) | ||
202 | bb_perror_msg(bb_msg_write_error); | ||
203 | |||
204 | close(s); | ||
205 | |||
206 | return EXIT_SUCCESS; | ||
207 | } | ||
208 | |||
209 | /* Convert the host ID string to a MAC address. | ||
210 | * The string may be a: | ||
211 | * Host name | ||
212 | * IP address string | ||
213 | * MAC address string | ||
214 | */ | ||
215 | static inline void get_dest_addr(const char *hostid, struct ether_addr *eaddr) | ||
216 | { | ||
217 | struct ether_addr *eap; | ||
218 | |||
219 | eap = ether_aton(hostid); | ||
220 | if (eap) { | ||
221 | *eaddr = *eap; | ||
222 | bb_debug_msg("The target station address is %s\n\n", ether_ntoa(eaddr)); | ||
223 | #if !defined(__UCLIBC__) | ||
224 | } else if (ether_hostton(hostid, eaddr) == 0) { | ||
225 | bb_debug_msg("Station address for hostname %s is %s\n\n", hostid, ether_ntoa(eaddr)); | ||
226 | #else | ||
227 | # warning Need to implement ether_hostton() for uClibc | ||
228 | #endif | ||
229 | } else | ||
230 | bb_show_usage(); | ||
231 | } | ||
232 | |||
233 | static inline int get_fill(unsigned char *pkt, struct ether_addr *eaddr, int broadcast) | ||
234 | { | ||
235 | int offset, i; | ||
236 | unsigned char *station_addr = eaddr->ether_addr_octet; | ||
237 | |||
238 | if (broadcast) | ||
239 | memset(pkt+0, 0xff, 6); | ||
240 | else | ||
241 | memcpy(pkt, station_addr, 6); | ||
242 | memcpy(pkt+6, station_addr, 6); | ||
243 | pkt[12] = 0x08; /* Or 0x0806 for ARP, 0x8035 for RARP */ | ||
244 | pkt[13] = 0x42; | ||
245 | offset = 14; | ||
246 | |||
247 | memset(pkt+offset, 0xff, 6); | ||
248 | offset += 6; | ||
249 | |||
250 | for (i = 0; i < 16; ++i) { | ||
251 | memcpy(pkt+offset, station_addr, 6); | ||
252 | offset += 6; | ||
253 | } | ||
254 | |||
255 | return offset; | ||
256 | } | ||
257 | |||
258 | static inline int get_wol_pw(const char *ethoptarg, unsigned char *wol_passwd) | ||
259 | { | ||
260 | int passwd[6]; | ||
261 | int byte_cnt, i; | ||
262 | |||
263 | /* handle MAC format */ | ||
264 | byte_cnt = sscanf(ethoptarg, "%2x:%2x:%2x:%2x:%2x:%2x", | ||
265 | &passwd[0], &passwd[1], &passwd[2], | ||
266 | &passwd[3], &passwd[4], &passwd[5]); | ||
267 | /* handle IP format */ | ||
268 | if (byte_cnt < 4) | ||
269 | byte_cnt = sscanf(ethoptarg, "%d.%d.%d.%d", | ||
270 | &passwd[0], &passwd[1], &passwd[2], &passwd[3]); | ||
271 | if (byte_cnt < 4) { | ||
272 | bb_error_msg("cannot read Wake-On-LAN pass"); | ||
273 | return 0; | ||
274 | } | ||
275 | |||
276 | for (i = 0; i < byte_cnt; ++i) | ||
277 | wol_passwd[i] = passwd[i]; | ||
278 | |||
279 | bb_debug_msg("password: %2.2x %2.2x %2.2x %2.2x (%d)\n\n", | ||
280 | wol_passwd[0], wol_passwd[1], wol_passwd[2], wol_passwd[3], | ||
281 | byte_cnt); | ||
282 | |||
283 | return byte_cnt; | ||
284 | } | ||
diff --git a/networking/fakeidentd.c b/networking/fakeidentd.c new file mode 100644 index 000000000..04138cca3 --- /dev/null +++ b/networking/fakeidentd.c | |||
@@ -0,0 +1,387 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * A fake identd server | ||
4 | * | ||
5 | * Adapted to busybox by Thomas Lundquist <thomasez@zelow.no> | ||
6 | * Original Author: Tomi Ollila <too@iki.fi> | ||
7 | * http://www.guru-group.fi/~too/sw/ | ||
8 | * | ||
9 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | ||
10 | */ | ||
11 | |||
12 | #include "busybox.h" | ||
13 | #include <sys/syslog.h> | ||
14 | #include <sys/uio.h> | ||
15 | |||
16 | |||
17 | #define IDENT_PORT 113 | ||
18 | #define MAXCONNS 20 | ||
19 | #define MAXIDLETIME 45 | ||
20 | |||
21 | static const char ident_substr[] = " : USERID : UNIX : "; | ||
22 | enum { ident_substr_len = sizeof(ident_substr) - 1 }; | ||
23 | #define PIDFILE "/var/run/identd.pid" | ||
24 | |||
25 | /* | ||
26 | * We have to track the 'first connection socket' so that we | ||
27 | * don't go around closing file descriptors for non-clients. | ||
28 | * | ||
29 | * descriptor setup normally | ||
30 | * 0 = server socket | ||
31 | * 1 = syslog fd (hopefully -- otherwise this won't work) | ||
32 | * 2 = connection socket after detached from tty. standard error before that | ||
33 | * 3 - 2 + MAXCONNS = rest connection sockets | ||
34 | * | ||
35 | * To try to make sure that syslog fd is what is "requested", the that fd | ||
36 | * is closed before openlog() call. It can only severely fail if fd 0 | ||
37 | * is initially closed. | ||
38 | */ | ||
39 | #define FCS 2 | ||
40 | |||
41 | /* | ||
42 | * FD of the connection is always the index of the connection structure | ||
43 | * in `conns' array + FCS | ||
44 | */ | ||
45 | static struct { | ||
46 | char buf[20]; | ||
47 | int len; | ||
48 | time_t lasttime; | ||
49 | } conns[MAXCONNS]; | ||
50 | |||
51 | /* When using global variables, bind those at least to a structure. */ | ||
52 | static struct { | ||
53 | const char *identuser; | ||
54 | fd_set readfds; | ||
55 | int conncnt; | ||
56 | } G; | ||
57 | |||
58 | /* | ||
59 | * Prototypes | ||
60 | */ | ||
61 | static void reply(int s, char *buf); | ||
62 | static void replyError(int s, char *buf); | ||
63 | |||
64 | static const char *nobodystr = "nobody"; /* this needs to be declared like this */ | ||
65 | static char *bind_ip_address = "0.0.0.0"; | ||
66 | |||
67 | static void movefd(int from, int to) | ||
68 | { | ||
69 | if (from != to) { | ||
70 | dup2(from, to); | ||
71 | close(from); | ||
72 | } | ||
73 | } | ||
74 | |||
75 | static void inetbind(void) | ||
76 | { | ||
77 | int s, port; | ||
78 | struct sockaddr_in addr; | ||
79 | int len = sizeof(addr); | ||
80 | struct servent *se; | ||
81 | |||
82 | se = getservbyname("identd", "tcp"); | ||
83 | port = IDENT_PORT; | ||
84 | if (se) | ||
85 | port = se->s_port; | ||
86 | |||
87 | s = xsocket(AF_INET, SOCK_STREAM, 0); | ||
88 | |||
89 | setsockopt_reuseaddr(s); | ||
90 | |||
91 | memset(&addr, 0, sizeof(addr)); | ||
92 | addr.sin_addr.s_addr = inet_addr(bind_ip_address); | ||
93 | addr.sin_family = AF_INET; | ||
94 | addr.sin_port = htons(port); | ||
95 | |||
96 | xbind(s, (struct sockaddr *)&addr, len); | ||
97 | xlisten(s, 5); | ||
98 | |||
99 | movefd(s, 0); | ||
100 | } | ||
101 | |||
102 | static void handlexitsigs(int signum) | ||
103 | { | ||
104 | if (unlink(PIDFILE) < 0) | ||
105 | close(open(PIDFILE, O_WRONLY|O_CREAT|O_TRUNC, 0644)); | ||
106 | exit(0); | ||
107 | } | ||
108 | |||
109 | /* May succeed. If not, won't care. */ | ||
110 | static void writepid(uid_t nobody, uid_t nogrp) | ||
111 | { | ||
112 | char buf[sizeof(int)*3 + 2]; | ||
113 | int fd = open(PIDFILE, O_WRONLY|O_CREAT|O_TRUNC, 0664); | ||
114 | |||
115 | if (fd < 0) | ||
116 | return; | ||
117 | |||
118 | sprintf(buf, "%d\n", getpid()); | ||
119 | write(fd, buf, strlen(buf)); | ||
120 | fchown(fd, nobody, nogrp); | ||
121 | close(fd); | ||
122 | |||
123 | /* should this handle ILL, ... (see signal(7)) */ | ||
124 | signal(SIGTERM, handlexitsigs); | ||
125 | signal(SIGINT, handlexitsigs); | ||
126 | signal(SIGQUIT, handlexitsigs); | ||
127 | } | ||
128 | |||
129 | /* return 0 as parent, 1 as child */ | ||
130 | static int godaemon(void) | ||
131 | { | ||
132 | uid_t nobody, nogrp; | ||
133 | struct passwd *pw; | ||
134 | |||
135 | switch (fork()) { | ||
136 | case -1: | ||
137 | bb_perror_msg_and_die("fork"); | ||
138 | |||
139 | case 0: | ||
140 | pw = getpwnam(nobodystr); | ||
141 | if (pw == NULL) | ||
142 | bb_error_msg_and_die("cannot find uid/gid of user '%s'", nobodystr); | ||
143 | nobody = pw->pw_uid; | ||
144 | nogrp = pw->pw_gid; | ||
145 | writepid(nobody, nogrp); | ||
146 | |||
147 | close(0); | ||
148 | inetbind(); | ||
149 | xsetgid(nogrp); | ||
150 | xsetuid(nobody); | ||
151 | close(1); | ||
152 | close(2); | ||
153 | |||
154 | signal(SIGHUP, SIG_IGN); | ||
155 | signal(SIGPIPE, SIG_IGN); /* connection closed when writing (raises ???) */ | ||
156 | |||
157 | setsid(); | ||
158 | |||
159 | return 1; | ||
160 | } | ||
161 | |||
162 | return 0; | ||
163 | } | ||
164 | |||
165 | static void deleteConn(int s) | ||
166 | { | ||
167 | int i = s - FCS; | ||
168 | |||
169 | close(s); | ||
170 | |||
171 | G.conncnt--; | ||
172 | |||
173 | /* | ||
174 | * Most of the time there is 0 connections. Most often that there | ||
175 | * is connections, there is just one connection. When this one connection | ||
176 | * closes, i == G.conncnt = 0 -> no copying. | ||
177 | * When there is more than one connection, the oldest connections closes | ||
178 | * earlier on average. When this happens, the code below starts copying | ||
179 | * the connection structure w/ highest index to the place which which is | ||
180 | * just deleted. This means that the connection structures are no longer | ||
181 | * in chronological order. I'd quess this means that when there is more | ||
182 | * than 1 connection, on average every other connection structure needs | ||
183 | * to be copied over the time all these connections are deleted. | ||
184 | */ | ||
185 | if (i != G.conncnt) { | ||
186 | memcpy(&conns[i], &conns[G.conncnt], sizeof(conns[0])); | ||
187 | movefd(G.conncnt + FCS, s); | ||
188 | } | ||
189 | |||
190 | FD_CLR(G.conncnt + FCS, &G.readfds); | ||
191 | } | ||
192 | |||
193 | static int closeOldest(void) | ||
194 | { | ||
195 | time_t min = conns[0].lasttime; | ||
196 | int idx = 0; | ||
197 | int i; | ||
198 | |||
199 | for (i = 1; i < MAXCONNS; i++) | ||
200 | if (conns[i].lasttime < min) | ||
201 | idx = i; | ||
202 | |||
203 | replyError(idx + FCS, "X-SERVER-TOO-BUSY"); | ||
204 | close(idx + FCS); | ||
205 | |||
206 | return idx; | ||
207 | } | ||
208 | |||
209 | static int checkInput(char *buf, int len, int l) | ||
210 | { | ||
211 | int i; | ||
212 | for (i = len; i < len + l; ++i) | ||
213 | if (buf[i] == '\n') | ||
214 | return 1; | ||
215 | return 0; | ||
216 | } | ||
217 | |||
218 | int fakeidentd_main(int argc, char **argv) | ||
219 | { | ||
220 | /* This applet is an inetd-style daemon */ | ||
221 | openlog(applet_name, 0, LOG_DAEMON); | ||
222 | logmode = LOGMODE_SYSLOG; | ||
223 | |||
224 | memset(conns, 0, sizeof(conns)); | ||
225 | memset(&G, 0, sizeof(G)); | ||
226 | FD_ZERO(&G.readfds); | ||
227 | FD_SET(0, &G.readfds); | ||
228 | |||
229 | /* handle -b <ip> parameter */ | ||
230 | getopt32(argc, argv, "b:", &bind_ip_address); | ||
231 | /* handle optional REPLY STRING */ | ||
232 | if (optind < argc) | ||
233 | G.identuser = argv[optind]; | ||
234 | else | ||
235 | G.identuser = nobodystr; | ||
236 | |||
237 | /* daemonize and have the parent return */ | ||
238 | if (godaemon() == 0) | ||
239 | return 0; | ||
240 | |||
241 | /* main loop where we process all events and never exit */ | ||
242 | while (1) { | ||
243 | fd_set rfds = G.readfds; | ||
244 | struct timeval tv = { 15, 0 }; | ||
245 | int i; | ||
246 | int tim = time(NULL); | ||
247 | |||
248 | select(G.conncnt + FCS, &rfds, NULL, NULL, G.conncnt? &tv: NULL); | ||
249 | |||
250 | for (i = G.conncnt - 1; i >= 0; i--) { | ||
251 | int s = i + FCS; | ||
252 | |||
253 | if (FD_ISSET(s, &rfds)) { | ||
254 | char *buf = conns[i].buf; | ||
255 | unsigned int len = conns[i].len; | ||
256 | unsigned int l; | ||
257 | |||
258 | if ((l = read(s, buf + len, sizeof(conns[0].buf) - len)) > 0) { | ||
259 | if (checkInput(buf, len, l)) { | ||
260 | reply(s, buf); | ||
261 | goto deleteconn; | ||
262 | } else if (len + l >= sizeof(conns[0].buf)) { | ||
263 | replyError(s, "X-INVALID-REQUEST"); | ||
264 | goto deleteconn; | ||
265 | } else { | ||
266 | conns[i].len += l; | ||
267 | } | ||
268 | } else { | ||
269 | goto deleteconn; | ||
270 | } | ||
271 | |||
272 | conns[i].lasttime = tim; | ||
273 | continue; | ||
274 | |||
275 | deleteconn: | ||
276 | deleteConn(s); | ||
277 | } else { | ||
278 | /* implement as time_after() in linux kernel sources ... */ | ||
279 | if (conns[i].lasttime + MAXIDLETIME <= tim) { | ||
280 | replyError(s, "X-TIMEOUT"); | ||
281 | deleteConn(s); | ||
282 | } | ||
283 | } | ||
284 | } | ||
285 | |||
286 | if (FD_ISSET(0, &rfds)) { | ||
287 | int s = accept(0, NULL, 0); | ||
288 | |||
289 | if (s < 0) { | ||
290 | if (errno != EINTR) /* EINTR */ | ||
291 | bb_perror_msg("accept"); | ||
292 | } else { | ||
293 | if (G.conncnt == MAXCONNS) | ||
294 | i = closeOldest(); | ||
295 | else | ||
296 | i = G.conncnt++; | ||
297 | |||
298 | movefd(s, i + FCS); /* move if not already there */ | ||
299 | FD_SET(i + FCS, &G.readfds); | ||
300 | |||
301 | conns[i].len = 0; | ||
302 | conns[i].lasttime = time(NULL); | ||
303 | } | ||
304 | } | ||
305 | } /* end of while(1) */ | ||
306 | |||
307 | return 0; | ||
308 | } | ||
309 | |||
310 | static int parseAddrs(char *ptr, char **myaddr, char **heraddr); | ||
311 | static void reply(int s, char *buf) | ||
312 | { | ||
313 | char *myaddr, *heraddr; | ||
314 | |||
315 | myaddr = heraddr = NULL; | ||
316 | |||
317 | if (parseAddrs(buf, &myaddr, &heraddr)) | ||
318 | replyError(s, "X-INVALID-REQUEST"); | ||
319 | else { | ||
320 | struct iovec iv[6]; | ||
321 | iv[0].iov_base = myaddr; iv[0].iov_len = strlen(myaddr); | ||
322 | iv[1].iov_base = ", "; iv[1].iov_len = 2; | ||
323 | iv[2].iov_base = heraddr; iv[2].iov_len = strlen(heraddr); | ||
324 | iv[3].iov_base = (void *)ident_substr; iv[3].iov_len = ident_substr_len; | ||
325 | iv[4].iov_base = (void *)G.identuser; iv[4].iov_len = strlen(G.identuser); | ||
326 | iv[5].iov_base = "\r\n"; iv[5].iov_len = 2; | ||
327 | writev(s, iv, 6); | ||
328 | } | ||
329 | } | ||
330 | |||
331 | static void replyError(int s, char *buf) | ||
332 | { | ||
333 | struct iovec iv[3]; | ||
334 | iv[0].iov_base = "0, 0 : ERROR : "; iv[0].iov_len = 15; | ||
335 | iv[1].iov_base = buf; iv[1].iov_len = strlen(buf); | ||
336 | iv[2].iov_base = "\r\n"; iv[2].iov_len = 2; | ||
337 | writev(s, iv, 3); | ||
338 | } | ||
339 | |||
340 | static int chmatch(char c, char *chars) | ||
341 | { | ||
342 | for (; *chars; chars++) | ||
343 | if (c == *chars) | ||
344 | return 1; | ||
345 | return 0; | ||
346 | } | ||
347 | |||
348 | static int skipchars(char **p, char *chars) | ||
349 | { | ||
350 | while (chmatch(**p, chars)) | ||
351 | (*p)++; | ||
352 | if (**p == '\r' || **p == '\n') | ||
353 | return 0; | ||
354 | return 1; | ||
355 | } | ||
356 | |||
357 | static int parseAddrs(char *ptr, char **myaddr, char **heraddr) | ||
358 | { | ||
359 | /* parse <port-on-server> , <port-on-client> */ | ||
360 | |||
361 | if (!skipchars(&ptr, " \t")) | ||
362 | return -1; | ||
363 | |||
364 | *myaddr = ptr; | ||
365 | |||
366 | if (!skipchars(&ptr, "1234567890")) | ||
367 | return -1; | ||
368 | |||
369 | if (!chmatch(*ptr, " \t,")) | ||
370 | return -1; | ||
371 | |||
372 | *ptr++ = '\0'; | ||
373 | |||
374 | if (!skipchars(&ptr, " \t,") ) | ||
375 | return -1; | ||
376 | |||
377 | *heraddr = ptr; | ||
378 | |||
379 | skipchars(&ptr, "1234567890"); | ||
380 | |||
381 | if (!chmatch(*ptr, " \n\r")) | ||
382 | return -1; | ||
383 | |||
384 | *ptr = '\0'; | ||
385 | |||
386 | return 0; | ||
387 | } | ||
diff --git a/networking/ftpgetput.c b/networking/ftpgetput.c new file mode 100644 index 000000000..223d2435c --- /dev/null +++ b/networking/ftpgetput.c | |||
@@ -0,0 +1,340 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * ftpget | ||
4 | * | ||
5 | * Mini implementation of FTP to retrieve a remote file. | ||
6 | * | ||
7 | * Copyright (C) 2002 Jeff Angielski, The PTR Group <jeff@theptrgroup.com> | ||
8 | * Copyright (C) 2002 Glenn McGrath <bug1@iinet.net.au> | ||
9 | * | ||
10 | * Based on wget.c by Chip Rosenthal Covad Communications | ||
11 | * <chip@laserlink.net> | ||
12 | * | ||
13 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | ||
14 | */ | ||
15 | |||
16 | #include "busybox.h" | ||
17 | #include <getopt.h> | ||
18 | |||
19 | typedef struct ftp_host_info_s { | ||
20 | char *user; | ||
21 | char *password; | ||
22 | struct sockaddr_in *s_in; | ||
23 | } ftp_host_info_t; | ||
24 | |||
25 | static char verbose_flag = 0; | ||
26 | static char do_continue = 0; | ||
27 | |||
28 | static int ftpcmd(const char *s1, const char *s2, FILE *stream, char *buf) | ||
29 | { | ||
30 | if (verbose_flag) { | ||
31 | bb_error_msg("cmd %s%s", s1, s2); | ||
32 | } | ||
33 | |||
34 | if (s1) { | ||
35 | if (s2) { | ||
36 | fprintf(stream, "%s%s\r\n", s1, s2); | ||
37 | } else { | ||
38 | fprintf(stream, "%s\r\n", s1); | ||
39 | } | ||
40 | } | ||
41 | do { | ||
42 | char *buf_ptr; | ||
43 | |||
44 | if (fgets(buf, 510, stream) == NULL) { | ||
45 | bb_perror_msg_and_die("fgets"); | ||
46 | } | ||
47 | buf_ptr = strstr(buf, "\r\n"); | ||
48 | if (buf_ptr) { | ||
49 | *buf_ptr = '\0'; | ||
50 | } | ||
51 | } while (!isdigit(buf[0]) || buf[3] != ' '); | ||
52 | |||
53 | return xatou(buf); | ||
54 | } | ||
55 | |||
56 | static int xconnect_ftpdata(ftp_host_info_t *server, const char *buf) | ||
57 | { | ||
58 | char *buf_ptr; | ||
59 | unsigned short port_num; | ||
60 | |||
61 | buf_ptr = strrchr(buf, ','); | ||
62 | *buf_ptr = '\0'; | ||
63 | port_num = xatoul_range(buf_ptr + 1, 0, 255); | ||
64 | |||
65 | buf_ptr = strrchr(buf, ','); | ||
66 | *buf_ptr = '\0'; | ||
67 | port_num += xatoul_range(buf_ptr + 1, 0, 255) * 256; | ||
68 | |||
69 | server->s_in->sin_port = htons(port_num); | ||
70 | return xconnect_tcp_v4(server->s_in); | ||
71 | } | ||
72 | |||
73 | static FILE *ftp_login(ftp_host_info_t *server) | ||
74 | { | ||
75 | FILE *control_stream; | ||
76 | char buf[512]; | ||
77 | |||
78 | /* Connect to the command socket */ | ||
79 | control_stream = fdopen(xconnect_tcp_v4(server->s_in), "r+"); | ||
80 | if (control_stream == NULL) { | ||
81 | bb_perror_msg_and_die("cannot open control stream"); | ||
82 | } | ||
83 | |||
84 | if (ftpcmd(NULL, NULL, control_stream, buf) != 220) { | ||
85 | bb_error_msg_and_die("%s", buf + 4); | ||
86 | } | ||
87 | |||
88 | /* Login to the server */ | ||
89 | switch (ftpcmd("USER ", server->user, control_stream, buf)) { | ||
90 | case 230: | ||
91 | break; | ||
92 | case 331: | ||
93 | if (ftpcmd("PASS ", server->password, control_stream, buf) != 230) { | ||
94 | bb_error_msg_and_die("PASS error: %s", buf + 4); | ||
95 | } | ||
96 | break; | ||
97 | default: | ||
98 | bb_error_msg_and_die("USER error: %s", buf + 4); | ||
99 | } | ||
100 | |||
101 | ftpcmd("TYPE I", NULL, control_stream, buf); | ||
102 | |||
103 | return control_stream; | ||
104 | } | ||
105 | |||
106 | #if !ENABLE_FTPGET | ||
107 | int ftp_receive(ftp_host_info_t *server, FILE *control_stream, | ||
108 | const char *local_path, char *server_path); | ||
109 | #else | ||
110 | static | ||
111 | int ftp_receive(ftp_host_info_t *server, FILE *control_stream, | ||
112 | const char *local_path, char *server_path) | ||
113 | { | ||
114 | char buf[512]; | ||
115 | off_t filesize = 0; | ||
116 | int fd_data; | ||
117 | int fd_local = -1; | ||
118 | off_t beg_range = 0; | ||
119 | |||
120 | /* Connect to the data socket */ | ||
121 | if (ftpcmd("PASV", NULL, control_stream, buf) != 227) { | ||
122 | bb_error_msg_and_die("PASV error: %s", buf + 4); | ||
123 | } | ||
124 | fd_data = xconnect_ftpdata(server, buf); | ||
125 | |||
126 | if (ftpcmd("SIZE ", server_path, control_stream, buf) == 213) { | ||
127 | filesize = BB_STRTOOFF(buf + 4, NULL, 10); | ||
128 | if (errno || filesize < 0) | ||
129 | bb_error_msg_and_die("SIZE error: %s", buf + 4); | ||
130 | } else { | ||
131 | filesize = -1; | ||
132 | do_continue = 0; | ||
133 | } | ||
134 | |||
135 | if ((local_path[0] == '-') && (local_path[1] == '\0')) { | ||
136 | fd_local = STDOUT_FILENO; | ||
137 | do_continue = 0; | ||
138 | } | ||
139 | |||
140 | if (do_continue) { | ||
141 | struct stat sbuf; | ||
142 | if (lstat(local_path, &sbuf) < 0) { | ||
143 | bb_perror_msg_and_die("lstat"); | ||
144 | } | ||
145 | if (sbuf.st_size > 0) { | ||
146 | beg_range = sbuf.st_size; | ||
147 | } else { | ||
148 | do_continue = 0; | ||
149 | } | ||
150 | } | ||
151 | |||
152 | if (do_continue) { | ||
153 | sprintf(buf, "REST %"OFF_FMT"d", beg_range); | ||
154 | if (ftpcmd(buf, NULL, control_stream, buf) != 350) { | ||
155 | do_continue = 0; | ||
156 | } else { | ||
157 | filesize -= beg_range; | ||
158 | } | ||
159 | } | ||
160 | |||
161 | if (ftpcmd("RETR ", server_path, control_stream, buf) > 150) { | ||
162 | bb_error_msg_and_die("RETR error: %s", buf + 4); | ||
163 | } | ||
164 | |||
165 | /* only make a local file if we know that one exists on the remote server */ | ||
166 | if (fd_local == -1) { | ||
167 | if (do_continue) { | ||
168 | fd_local = xopen(local_path, O_APPEND | O_WRONLY); | ||
169 | } else { | ||
170 | fd_local = xopen(local_path, O_CREAT | O_TRUNC | O_WRONLY); | ||
171 | } | ||
172 | } | ||
173 | |||
174 | /* Copy the file */ | ||
175 | if (filesize != -1) { | ||
176 | if (-1 == bb_copyfd_size(fd_data, fd_local, filesize)) | ||
177 | exit(EXIT_FAILURE); | ||
178 | } else { | ||
179 | if (-1 == bb_copyfd_eof(fd_data, fd_local)) | ||
180 | exit(EXIT_FAILURE); | ||
181 | } | ||
182 | |||
183 | /* close it all down */ | ||
184 | close(fd_data); | ||
185 | if (ftpcmd(NULL, NULL, control_stream, buf) != 226) { | ||
186 | bb_error_msg_and_die("ftp error: %s", buf + 4); | ||
187 | } | ||
188 | ftpcmd("QUIT", NULL, control_stream, buf); | ||
189 | |||
190 | return EXIT_SUCCESS; | ||
191 | } | ||
192 | #endif | ||
193 | |||
194 | #if !ENABLE_FTPPUT | ||
195 | int ftp_send(ftp_host_info_t *server, FILE *control_stream, | ||
196 | const char *server_path, char *local_path); | ||
197 | #else | ||
198 | static | ||
199 | int ftp_send(ftp_host_info_t *server, FILE *control_stream, | ||
200 | const char *server_path, char *local_path) | ||
201 | { | ||
202 | struct stat sbuf; | ||
203 | char buf[512]; | ||
204 | int fd_data; | ||
205 | int fd_local; | ||
206 | int response; | ||
207 | |||
208 | /* Connect to the data socket */ | ||
209 | if (ftpcmd("PASV", NULL, control_stream, buf) != 227) { | ||
210 | bb_error_msg_and_die("PASV error: %s", buf + 4); | ||
211 | } | ||
212 | fd_data = xconnect_ftpdata(server, buf); | ||
213 | |||
214 | /* get the local file */ | ||
215 | if ((local_path[0] == '-') && (local_path[1] == '\0')) { | ||
216 | fd_local = STDIN_FILENO; | ||
217 | } else { | ||
218 | fd_local = xopen(local_path, O_RDONLY); | ||
219 | fstat(fd_local, &sbuf); | ||
220 | |||
221 | sprintf(buf, "ALLO %lu", (unsigned long)sbuf.st_size); | ||
222 | response = ftpcmd(buf, NULL, control_stream, buf); | ||
223 | switch (response) { | ||
224 | case 200: | ||
225 | case 202: | ||
226 | break; | ||
227 | default: | ||
228 | close(fd_local); | ||
229 | bb_error_msg_and_die("ALLO error: %s", buf + 4); | ||
230 | break; | ||
231 | } | ||
232 | } | ||
233 | response = ftpcmd("STOR ", server_path, control_stream, buf); | ||
234 | switch (response) { | ||
235 | case 125: | ||
236 | case 150: | ||
237 | break; | ||
238 | default: | ||
239 | close(fd_local); | ||
240 | bb_error_msg_and_die("STOR error: %s", buf + 4); | ||
241 | } | ||
242 | |||
243 | /* transfer the file */ | ||
244 | if (bb_copyfd_eof(fd_local, fd_data) == -1) { | ||
245 | exit(EXIT_FAILURE); | ||
246 | } | ||
247 | |||
248 | /* close it all down */ | ||
249 | close(fd_data); | ||
250 | if (ftpcmd(NULL, NULL, control_stream, buf) != 226) { | ||
251 | bb_error_msg_and_die("error: %s", buf + 4); | ||
252 | } | ||
253 | ftpcmd("QUIT", NULL, control_stream, buf); | ||
254 | |||
255 | return EXIT_SUCCESS; | ||
256 | } | ||
257 | #endif | ||
258 | |||
259 | #define FTPGETPUT_OPT_CONTINUE 1 | ||
260 | #define FTPGETPUT_OPT_VERBOSE 2 | ||
261 | #define FTPGETPUT_OPT_USER 4 | ||
262 | #define FTPGETPUT_OPT_PASSWORD 8 | ||
263 | #define FTPGETPUT_OPT_PORT 16 | ||
264 | |||
265 | #if ENABLE_FEATURE_FTPGETPUT_LONG_OPTIONS | ||
266 | static const struct option ftpgetput_long_options[] = { | ||
267 | { "continue", 1, NULL, 'c' }, | ||
268 | { "verbose", 0, NULL, 'v' }, | ||
269 | { "username", 1, NULL, 'u' }, | ||
270 | { "password", 1, NULL, 'p' }, | ||
271 | { "port", 1, NULL, 'P' }, | ||
272 | { 0, 0, 0, 0 } | ||
273 | }; | ||
274 | #endif | ||
275 | |||
276 | int ftpgetput_main(int argc, char **argv) | ||
277 | { | ||
278 | /* content-length of the file */ | ||
279 | unsigned opt; | ||
280 | char *port = "ftp"; | ||
281 | |||
282 | /* socket to ftp server */ | ||
283 | FILE *control_stream; | ||
284 | struct sockaddr_in s_in; | ||
285 | |||
286 | /* continue a prev transfer (-c) */ | ||
287 | ftp_host_info_t *server; | ||
288 | |||
289 | int (*ftp_action)(ftp_host_info_t *, FILE *, const char *, char *) = NULL; | ||
290 | |||
291 | /* Check to see if the command is ftpget or ftput */ | ||
292 | if (ENABLE_FTPPUT && (!ENABLE_FTPGET || applet_name[3] == 'p')) { | ||
293 | ftp_action = ftp_send; | ||
294 | } | ||
295 | if (ENABLE_FTPGET && (!ENABLE_FTPPUT || applet_name[3] == 'g')) { | ||
296 | ftp_action = ftp_receive; | ||
297 | } | ||
298 | |||
299 | /* Set default values */ | ||
300 | server = xmalloc(sizeof(ftp_host_info_t)); | ||
301 | server->user = "anonymous"; | ||
302 | server->password = "busybox@"; | ||
303 | verbose_flag = 0; | ||
304 | |||
305 | /* | ||
306 | * Decipher the command line | ||
307 | */ | ||
308 | #if ENABLE_FEATURE_FTPGETPUT_LONG_OPTIONS | ||
309 | applet_long_options = ftpgetput_long_options; | ||
310 | #endif | ||
311 | opt = getopt32(argc, argv, "cvu:p:P:", &server->user, &server->password, &port); | ||
312 | |||
313 | /* Process the non-option command line arguments */ | ||
314 | if (argc - optind != 3) { | ||
315 | bb_show_usage(); | ||
316 | } | ||
317 | |||
318 | if (opt & FTPGETPUT_OPT_CONTINUE) { | ||
319 | do_continue = 1; | ||
320 | } | ||
321 | if (opt & FTPGETPUT_OPT_VERBOSE) { | ||
322 | verbose_flag = 1; | ||
323 | } | ||
324 | |||
325 | /* We want to do exactly _one_ DNS lookup, since some | ||
326 | * sites (i.e. ftp.us.debian.org) use round-robin DNS | ||
327 | * and we want to connect to only one IP... */ | ||
328 | server->s_in = &s_in; | ||
329 | bb_lookup_host(&s_in, argv[optind]); | ||
330 | s_in.sin_port = bb_lookup_port(port, "tcp", 21); | ||
331 | if (verbose_flag) { | ||
332 | printf("Connecting to %s[%s]:%d\n", | ||
333 | argv[optind], inet_ntoa(s_in.sin_addr), ntohs(s_in.sin_port)); | ||
334 | } | ||
335 | |||
336 | /* Connect/Setup/Configure the FTP session */ | ||
337 | control_stream = ftp_login(server); | ||
338 | |||
339 | return ftp_action(server, control_stream, argv[optind + 1], argv[optind + 2]); | ||
340 | } | ||
diff --git a/networking/hostname.c b/networking/hostname.c new file mode 100644 index 000000000..4fe28d740 --- /dev/null +++ b/networking/hostname.c | |||
@@ -0,0 +1,102 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * $Id: hostname.c,v 1.36 2003/07/14 21:21:01 andersen Exp $ | ||
4 | * Mini hostname implementation for busybox | ||
5 | * | ||
6 | * Copyright (C) 1999 by Randolph Chung <tausq@debian.org> | ||
7 | * | ||
8 | * adjusted by Erik Andersen <andersen@codepoet.org> to remove | ||
9 | * use of long options and GNU getopt. Improved the usage info. | ||
10 | * | ||
11 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
12 | * | ||
13 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | ||
14 | */ | ||
15 | |||
16 | #include "busybox.h" | ||
17 | |||
18 | static void do_sethostname(char *s, int isfile) | ||
19 | { | ||
20 | FILE *f; | ||
21 | char buf[256]; | ||
22 | |||
23 | if (!s) | ||
24 | return; | ||
25 | if (!isfile) { | ||
26 | if (sethostname(s, strlen(s)) < 0) { | ||
27 | if (errno == EPERM) | ||
28 | bb_error_msg_and_die("you must be root to change the hostname"); | ||
29 | else | ||
30 | bb_perror_msg_and_die("sethostname"); | ||
31 | } | ||
32 | } else { | ||
33 | f = xfopen(s, "r"); | ||
34 | while (fgets(buf, sizeof(buf), f) != NULL) { | ||
35 | if (buf[0] =='#') { | ||
36 | continue; | ||
37 | } | ||
38 | chomp(buf); | ||
39 | do_sethostname(buf, 0); | ||
40 | } | ||
41 | #ifdef CONFIG_FEATURE_CLEAN_UP | ||
42 | fclose(f); | ||
43 | #endif | ||
44 | } | ||
45 | } | ||
46 | |||
47 | int hostname_main(int argc, char **argv) | ||
48 | { | ||
49 | enum { | ||
50 | OPT_d = 0x1, | ||
51 | OPT_f = 0x2, | ||
52 | OPT_i = 0x4, | ||
53 | OPT_s = 0x8, | ||
54 | OPT_dfis = 0xf, | ||
55 | }; | ||
56 | |||
57 | char buf[256]; | ||
58 | unsigned opt; | ||
59 | char *hostname_str = NULL; | ||
60 | |||
61 | if (argc < 1) | ||
62 | bb_show_usage(); | ||
63 | |||
64 | opt = getopt32(argc, argv, "dfisF:", &hostname_str); | ||
65 | |||
66 | /* Output in desired format */ | ||
67 | if (opt & OPT_dfis) { | ||
68 | struct hostent *hp; | ||
69 | char *p; | ||
70 | gethostname(buf, sizeof(buf)); | ||
71 | hp = xgethostbyname(buf); | ||
72 | p = strchr(hp->h_name, '.'); | ||
73 | if (opt & OPT_f) { | ||
74 | puts(hp->h_name); | ||
75 | } else if (opt & OPT_s) { | ||
76 | if (p != NULL) { | ||
77 | *p = 0; | ||
78 | } | ||
79 | puts(hp->h_name); | ||
80 | } else if (opt & OPT_d) { | ||
81 | if (p) puts(p + 1); | ||
82 | } else if (opt & OPT_i) { | ||
83 | while (hp->h_addr_list[0]) { | ||
84 | printf("%s ", inet_ntoa(*(struct in_addr *) (*hp->h_addr_list++))); | ||
85 | } | ||
86 | puts(""); | ||
87 | } | ||
88 | } | ||
89 | /* Set the hostname */ | ||
90 | else if (hostname_str != NULL) { | ||
91 | do_sethostname(hostname_str, 1); | ||
92 | } else if (optind < argc) { | ||
93 | do_sethostname(argv[optind], 0); | ||
94 | } | ||
95 | /* Or if all else fails, | ||
96 | * just print the current hostname */ | ||
97 | else { | ||
98 | gethostname(buf, sizeof(buf)); | ||
99 | puts(buf); | ||
100 | } | ||
101 | return 0; | ||
102 | } | ||
diff --git a/networking/httpd.c b/networking/httpd.c new file mode 100644 index 000000000..49c2c76be --- /dev/null +++ b/networking/httpd.c | |||
@@ -0,0 +1,1970 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * httpd implementation for busybox | ||
4 | * | ||
5 | * Copyright (C) 2002,2003 Glenn Engel <glenne@engel.org> | ||
6 | * Copyright (C) 2003-2006 Vladimir Oleynik <dzo@simtreas.ru> | ||
7 | * | ||
8 | * simplify patch stolen from libbb without using strdup | ||
9 | * | ||
10 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | ||
11 | * | ||
12 | ***************************************************************************** | ||
13 | * | ||
14 | * Typical usage: | ||
15 | * for non root user | ||
16 | * httpd -p 8080 -h $HOME/public_html | ||
17 | * or for daemon start from rc script with uid=0: | ||
18 | * httpd -u www | ||
19 | * This is equivalent if www user have uid=80 to | ||
20 | * httpd -p 80 -u 80 -h /www -c /etc/httpd.conf -r "Web Server Authentication" | ||
21 | * | ||
22 | * | ||
23 | * When a url contains "cgi-bin" it is assumed to be a cgi script. The | ||
24 | * server changes directory to the location of the script and executes it | ||
25 | * after setting QUERY_STRING and other environment variables. | ||
26 | * | ||
27 | * Doc: | ||
28 | * "CGI Environment Variables": http://hoohoo.ncsa.uiuc.edu/cgi/env.html | ||
29 | * | ||
30 | * The server can also be invoked as a url arg decoder and html text encoder | ||
31 | * as follows: | ||
32 | * foo=`httpd -d $foo` # decode "Hello%20World" as "Hello World" | ||
33 | * bar=`httpd -e "<Hello World>"` # encode as "<Hello World>" | ||
34 | * Note that url encoding for arguments is not the same as html encoding for | ||
35 | * presentation. -d decodes a url-encoded argument while -e encodes in html | ||
36 | * for page display. | ||
37 | * | ||
38 | * httpd.conf has the following format: | ||
39 | * | ||
40 | * A:172.20. # Allow address from 172.20.0.0/16 | ||
41 | * A:10.0.0.0/25 # Allow any address from 10.0.0.0-10.0.0.127 | ||
42 | * A:10.0.0.0/255.255.255.128 # Allow any address that previous set | ||
43 | * A:127.0.0.1 # Allow local loopback connections | ||
44 | * D:* # Deny from other IP connections | ||
45 | * /cgi-bin:foo:bar # Require user foo, pwd bar on urls starting with /cgi-bin/ | ||
46 | * /adm:admin:setup # Require user admin, pwd setup on urls starting with /adm/ | ||
47 | * /adm:toor:PaSsWd # or user toor, pwd PaSsWd on urls starting with /adm/ | ||
48 | * .au:audio/basic # additional mime type for audio.au files | ||
49 | * *.php:/path/php # running cgi.php scripts through an interpreter | ||
50 | * | ||
51 | * A/D may be as a/d or allow/deny - first char case insensitive | ||
52 | * Deny IP rules take precedence over allow rules. | ||
53 | * | ||
54 | * | ||
55 | * The Deny/Allow IP logic: | ||
56 | * | ||
57 | * - Default is to allow all. No addresses are denied unless | ||
58 | * denied with a D: rule. | ||
59 | * - Order of Deny/Allow rules is significant | ||
60 | * - Deny rules take precedence over allow rules. | ||
61 | * - If a deny all rule (D:*) is used it acts as a catch-all for unmatched | ||
62 | * addresses. | ||
63 | * - Specification of Allow all (A:*) is a no-op | ||
64 | * | ||
65 | * Example: | ||
66 | * 1. Allow only specified addresses | ||
67 | * A:172.20 # Allow any address that begins with 172.20. | ||
68 | * A:10.10. # Allow any address that begins with 10.10. | ||
69 | * A:127.0.0.1 # Allow local loopback connections | ||
70 | * D:* # Deny from other IP connections | ||
71 | * | ||
72 | * 2. Only deny specified addresses | ||
73 | * D:1.2.3. # deny from 1.2.3.0 - 1.2.3.255 | ||
74 | * D:2.3.4. # deny from 2.3.4.0 - 2.3.4.255 | ||
75 | * A:* # (optional line added for clarity) | ||
76 | * | ||
77 | * If a sub directory contains a config file it is parsed and merged with | ||
78 | * any existing settings as if it was appended to the original configuration. | ||
79 | * | ||
80 | * subdir paths are relative to the containing subdir and thus cannot | ||
81 | * affect the parent rules. | ||
82 | * | ||
83 | * Note that since the sub dir is parsed in the forked thread servicing the | ||
84 | * subdir http request, any merge is discarded when the process exits. As a | ||
85 | * result, the subdir settings only have a lifetime of a single request. | ||
86 | * | ||
87 | * | ||
88 | * If -c is not set, an attempt will be made to open the default | ||
89 | * root configuration file. If -c is set and the file is not found, the | ||
90 | * server exits with an error. | ||
91 | * | ||
92 | */ | ||
93 | |||
94 | |||
95 | #include "busybox.h" | ||
96 | |||
97 | |||
98 | static const char httpdVersion[] = "busybox httpd/1.35 6-Oct-2004"; | ||
99 | static const char default_path_httpd_conf[] = "/etc"; | ||
100 | static const char httpd_conf[] = "httpd.conf"; | ||
101 | static const char home[] = "./"; | ||
102 | |||
103 | #define TIMEOUT 60 | ||
104 | |||
105 | // Note: busybox xfuncs are not used because we want the server to keep running | ||
106 | // if something bad happens due to a malformed user request. | ||
107 | // As a result, all memory allocation after daemonize | ||
108 | // is checked rigorously | ||
109 | |||
110 | //#define DEBUG 1 | ||
111 | |||
112 | #ifndef DEBUG | ||
113 | # define DEBUG 0 | ||
114 | #endif | ||
115 | |||
116 | #define MAX_MEMORY_BUFF 8192 /* IO buffer */ | ||
117 | |||
118 | typedef struct HT_ACCESS { | ||
119 | char *after_colon; | ||
120 | struct HT_ACCESS *next; | ||
121 | char before_colon[1]; /* really bigger, must last */ | ||
122 | } Htaccess; | ||
123 | |||
124 | typedef struct HT_ACCESS_IP { | ||
125 | unsigned int ip; | ||
126 | unsigned int mask; | ||
127 | int allow_deny; | ||
128 | struct HT_ACCESS_IP *next; | ||
129 | } Htaccess_IP; | ||
130 | |||
131 | typedef struct { | ||
132 | char buf[MAX_MEMORY_BUFF]; | ||
133 | |||
134 | USE_FEATURE_HTTPD_BASIC_AUTH(const char *realm;) | ||
135 | USE_FEATURE_HTTPD_BASIC_AUTH(char *remoteuser;) | ||
136 | |||
137 | const char *query; | ||
138 | |||
139 | USE_FEATURE_HTTPD_CGI(char *referer;) | ||
140 | |||
141 | const char *configFile; | ||
142 | |||
143 | unsigned int rmt_ip; | ||
144 | #if ENABLE_FEATURE_HTTPD_CGI || DEBUG | ||
145 | char rmt_ip_str[16]; /* for set env REMOTE_ADDR */ | ||
146 | #endif | ||
147 | unsigned port; /* server initial port and for | ||
148 | set env REMOTE_PORT */ | ||
149 | const char *found_mime_type; | ||
150 | const char *found_moved_temporarily; | ||
151 | |||
152 | off_t ContentLength; /* -1 - unknown */ | ||
153 | time_t last_mod; | ||
154 | |||
155 | Htaccess_IP *ip_a_d; /* config allow/deny lines */ | ||
156 | int flg_deny_all; | ||
157 | #if ENABLE_FEATURE_HTTPD_BASIC_AUTH | ||
158 | Htaccess *auth; /* config user:password lines */ | ||
159 | #endif | ||
160 | #if ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES | ||
161 | Htaccess *mime_a; /* config mime types */ | ||
162 | #endif | ||
163 | |||
164 | int server_socket; | ||
165 | int accepted_socket; | ||
166 | volatile int alarm_signaled; | ||
167 | |||
168 | #if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR | ||
169 | Htaccess *script_i; /* config script interpreters */ | ||
170 | #endif | ||
171 | } HttpdConfig; | ||
172 | |||
173 | static HttpdConfig *config; | ||
174 | |||
175 | static const char request_GET[] = "GET"; /* size algorithmic optimize */ | ||
176 | |||
177 | static const char* const suffixTable [] = { | ||
178 | /* Warning: shorted equivalent suffix in one line must be first */ | ||
179 | ".htm.html", "text/html", | ||
180 | ".jpg.jpeg", "image/jpeg", | ||
181 | ".gif", "image/gif", | ||
182 | ".png", "image/png", | ||
183 | ".txt.h.c.cc.cpp", "text/plain", | ||
184 | ".css", "text/css", | ||
185 | ".wav", "audio/wav", | ||
186 | ".avi", "video/x-msvideo", | ||
187 | ".qt.mov", "video/quicktime", | ||
188 | ".mpe.mpeg", "video/mpeg", | ||
189 | ".mid.midi", "audio/midi", | ||
190 | ".mp3", "audio/mpeg", | ||
191 | #if 0 /* unpopular */ | ||
192 | ".au", "audio/basic", | ||
193 | ".pac", "application/x-ns-proxy-autoconfig", | ||
194 | ".vrml.wrl", "model/vrml", | ||
195 | #endif | ||
196 | 0, "application/octet-stream" /* default */ | ||
197 | }; | ||
198 | |||
199 | typedef enum { | ||
200 | HTTP_OK = 200, | ||
201 | HTTP_MOVED_TEMPORARILY = 302, | ||
202 | HTTP_BAD_REQUEST = 400, /* malformed syntax */ | ||
203 | HTTP_UNAUTHORIZED = 401, /* authentication needed, respond with auth hdr */ | ||
204 | HTTP_NOT_FOUND = 404, | ||
205 | HTTP_FORBIDDEN = 403, | ||
206 | HTTP_REQUEST_TIMEOUT = 408, | ||
207 | HTTP_NOT_IMPLEMENTED = 501, /* used for unrecognized requests */ | ||
208 | HTTP_INTERNAL_SERVER_ERROR = 500, | ||
209 | #if 0 /* future use */ | ||
210 | HTTP_CONTINUE = 100, | ||
211 | HTTP_SWITCHING_PROTOCOLS = 101, | ||
212 | HTTP_CREATED = 201, | ||
213 | HTTP_ACCEPTED = 202, | ||
214 | HTTP_NON_AUTHORITATIVE_INFO = 203, | ||
215 | HTTP_NO_CONTENT = 204, | ||
216 | HTTP_MULTIPLE_CHOICES = 300, | ||
217 | HTTP_MOVED_PERMANENTLY = 301, | ||
218 | HTTP_NOT_MODIFIED = 304, | ||
219 | HTTP_PAYMENT_REQUIRED = 402, | ||
220 | HTTP_BAD_GATEWAY = 502, | ||
221 | HTTP_SERVICE_UNAVAILABLE = 503, /* overload, maintenance */ | ||
222 | HTTP_RESPONSE_SETSIZE = 0xffffffff | ||
223 | #endif | ||
224 | } HttpResponseNum; | ||
225 | |||
226 | typedef struct { | ||
227 | HttpResponseNum type; | ||
228 | const char *name; | ||
229 | const char *info; | ||
230 | } HttpEnumString; | ||
231 | |||
232 | static const HttpEnumString httpResponseNames[] = { | ||
233 | { HTTP_OK, "OK", NULL }, | ||
234 | { HTTP_MOVED_TEMPORARILY, "Found", "Directories must end with a slash." }, | ||
235 | { HTTP_REQUEST_TIMEOUT, "Request Timeout", | ||
236 | "No request appeared within a reasonable time period." }, | ||
237 | { HTTP_NOT_IMPLEMENTED, "Not Implemented", | ||
238 | "The requested method is not recognized by this server." }, | ||
239 | #if ENABLE_FEATURE_HTTPD_BASIC_AUTH | ||
240 | { HTTP_UNAUTHORIZED, "Unauthorized", "" }, | ||
241 | #endif | ||
242 | { HTTP_NOT_FOUND, "Not Found", | ||
243 | "The requested URL was not found on this server." }, | ||
244 | { HTTP_BAD_REQUEST, "Bad Request", "Unsupported method." }, | ||
245 | { HTTP_FORBIDDEN, "Forbidden", "" }, | ||
246 | { HTTP_INTERNAL_SERVER_ERROR, "Internal Server Error", | ||
247 | "Internal Server Error" }, | ||
248 | #if 0 /* not implemented */ | ||
249 | { HTTP_CREATED, "Created" }, | ||
250 | { HTTP_ACCEPTED, "Accepted" }, | ||
251 | { HTTP_NO_CONTENT, "No Content" }, | ||
252 | { HTTP_MULTIPLE_CHOICES, "Multiple Choices" }, | ||
253 | { HTTP_MOVED_PERMANENTLY, "Moved Permanently" }, | ||
254 | { HTTP_NOT_MODIFIED, "Not Modified" }, | ||
255 | { HTTP_BAD_GATEWAY, "Bad Gateway", "" }, | ||
256 | { HTTP_SERVICE_UNAVAILABLE, "Service Unavailable", "" }, | ||
257 | #endif | ||
258 | }; | ||
259 | |||
260 | |||
261 | static const char RFC1123FMT[] = "%a, %d %b %Y %H:%M:%S GMT"; | ||
262 | |||
263 | |||
264 | #define STRNCASECMP(a, str) strncasecmp((a), (str), sizeof(str)-1) | ||
265 | |||
266 | |||
267 | static int scan_ip(const char **ep, unsigned int *ip, unsigned char endc) | ||
268 | { | ||
269 | const char *p = *ep; | ||
270 | int auto_mask = 8; | ||
271 | int j; | ||
272 | |||
273 | *ip = 0; | ||
274 | for (j = 0; j < 4; j++) { | ||
275 | unsigned int octet; | ||
276 | |||
277 | if ((*p < '0' || *p > '9') && (*p != '/' || j == 0) && *p != 0) | ||
278 | return -auto_mask; | ||
279 | octet = 0; | ||
280 | while (*p >= '0' && *p <= '9') { | ||
281 | octet *= 10; | ||
282 | octet += *p - '0'; | ||
283 | if (octet > 255) | ||
284 | return -auto_mask; | ||
285 | p++; | ||
286 | } | ||
287 | if (*p == '.') | ||
288 | p++; | ||
289 | if (*p != '/' && *p != 0) | ||
290 | auto_mask += 8; | ||
291 | *ip = ((*ip) << 8) | octet; | ||
292 | } | ||
293 | if (*p != 0) { | ||
294 | if (*p != endc) | ||
295 | return -auto_mask; | ||
296 | p++; | ||
297 | if (*p == 0) | ||
298 | return -auto_mask; | ||
299 | } | ||
300 | *ep = p; | ||
301 | return auto_mask; | ||
302 | } | ||
303 | |||
304 | static int scan_ip_mask(const char *ipm, unsigned int *ip, unsigned int *mask) | ||
305 | { | ||
306 | int i; | ||
307 | unsigned int msk; | ||
308 | |||
309 | i = scan_ip(&ipm, ip, '/'); | ||
310 | if (i < 0) | ||
311 | return i; | ||
312 | if (*ipm) { | ||
313 | const char *p = ipm; | ||
314 | |||
315 | i = 0; | ||
316 | while (*p) { | ||
317 | if (*p < '0' || *p > '9') { | ||
318 | if (*p == '.') { | ||
319 | i = scan_ip(&ipm, mask, 0); | ||
320 | return i != 32; | ||
321 | } | ||
322 | return -1; | ||
323 | } | ||
324 | i *= 10; | ||
325 | i += *p - '0'; | ||
326 | p++; | ||
327 | } | ||
328 | } | ||
329 | if (i > 32 || i < 0) | ||
330 | return -1; | ||
331 | msk = 0x80000000; | ||
332 | *mask = 0; | ||
333 | while (i > 0) { | ||
334 | *mask |= msk; | ||
335 | msk >>= 1; | ||
336 | i--; | ||
337 | } | ||
338 | return 0; | ||
339 | } | ||
340 | |||
341 | #if ENABLE_FEATURE_HTTPD_BASIC_AUTH || ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES | ||
342 | static void free_config_lines(Htaccess **pprev) | ||
343 | { | ||
344 | Htaccess *prev = *pprev; | ||
345 | |||
346 | while (prev) { | ||
347 | Htaccess *cur = prev; | ||
348 | |||
349 | prev = cur->next; | ||
350 | free(cur); | ||
351 | } | ||
352 | *pprev = NULL; | ||
353 | } | ||
354 | #endif | ||
355 | |||
356 | /* flag */ | ||
357 | #define FIRST_PARSE 0 | ||
358 | #define SUBDIR_PARSE 1 | ||
359 | #define SIGNALED_PARSE 2 | ||
360 | #define FIND_FROM_HTTPD_ROOT 3 | ||
361 | /**************************************************************************** | ||
362 | * | ||
363 | > $Function: parse_conf() | ||
364 | * | ||
365 | * $Description: parse configuration file into in-memory linked list. | ||
366 | * | ||
367 | * The first non-white character is examined to determine if the config line | ||
368 | * is one of the following: | ||
369 | * .ext:mime/type # new mime type not compiled into httpd | ||
370 | * [adAD]:from # ip address allow/deny, * for wildcard | ||
371 | * /path:user:pass # username/password | ||
372 | * | ||
373 | * Any previous IP rules are discarded. | ||
374 | * If the flag argument is not SUBDIR_PARSE then all /path and mime rules | ||
375 | * are also discarded. That is, previous settings are retained if flag is | ||
376 | * SUBDIR_PARSE. | ||
377 | * | ||
378 | * $Parameters: | ||
379 | * (const char *) path . . null for ip address checks, path for password | ||
380 | * checks. | ||
381 | * (int) flag . . . . . . the source of the parse request. | ||
382 | * | ||
383 | * $Return: (None) | ||
384 | * | ||
385 | ****************************************************************************/ | ||
386 | static void parse_conf(const char *path, int flag) | ||
387 | { | ||
388 | FILE *f; | ||
389 | #if ENABLE_FEATURE_HTTPD_BASIC_AUTH | ||
390 | Htaccess *prev, *cur; | ||
391 | #elif ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES | ||
392 | Htaccess *cur; | ||
393 | #endif | ||
394 | |||
395 | const char *cf = config->configFile; | ||
396 | char buf[160]; | ||
397 | char *p0 = NULL; | ||
398 | char *c, *p; | ||
399 | |||
400 | /* free previous ip setup if present */ | ||
401 | Htaccess_IP *pip = config->ip_a_d; | ||
402 | |||
403 | while (pip) { | ||
404 | Htaccess_IP *cur_ipl = pip; | ||
405 | |||
406 | pip = cur_ipl->next; | ||
407 | free(cur_ipl); | ||
408 | } | ||
409 | config->ip_a_d = NULL; | ||
410 | |||
411 | config->flg_deny_all = 0; | ||
412 | |||
413 | #if ENABLE_FEATURE_HTTPD_BASIC_AUTH || ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES || ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR | ||
414 | /* retain previous auth and mime config only for subdir parse */ | ||
415 | if (flag != SUBDIR_PARSE) { | ||
416 | #if ENABLE_FEATURE_HTTPD_BASIC_AUTH | ||
417 | free_config_lines(&config->auth); | ||
418 | #endif | ||
419 | #if ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES | ||
420 | free_config_lines(&config->mime_a); | ||
421 | #endif | ||
422 | #if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR | ||
423 | free_config_lines(&config->script_i); | ||
424 | #endif | ||
425 | } | ||
426 | #endif | ||
427 | |||
428 | if (flag == SUBDIR_PARSE || cf == NULL) { | ||
429 | cf = alloca(strlen(path) + sizeof(httpd_conf) + 2); | ||
430 | if (cf == NULL) { | ||
431 | if (flag == FIRST_PARSE) | ||
432 | bb_error_msg_and_die(bb_msg_memory_exhausted); | ||
433 | return; | ||
434 | } | ||
435 | sprintf((char *)cf, "%s/%s", path, httpd_conf); | ||
436 | } | ||
437 | |||
438 | while ((f = fopen(cf, "r")) == NULL) { | ||
439 | if (flag == SUBDIR_PARSE || flag == FIND_FROM_HTTPD_ROOT) { | ||
440 | /* config file not found, no changes to config */ | ||
441 | return; | ||
442 | } | ||
443 | if (config->configFile && flag == FIRST_PARSE) /* if -c option given */ | ||
444 | bb_perror_msg_and_die("%s", cf); | ||
445 | flag = FIND_FROM_HTTPD_ROOT; | ||
446 | cf = httpd_conf; | ||
447 | } | ||
448 | |||
449 | #if ENABLE_FEATURE_HTTPD_BASIC_AUTH | ||
450 | prev = config->auth; | ||
451 | #endif | ||
452 | /* This could stand some work */ | ||
453 | while ((p0 = fgets(buf, sizeof(buf), f)) != NULL) { | ||
454 | c = NULL; | ||
455 | for (p = p0; *p0 != 0 && *p0 != '#'; p0++) { | ||
456 | if (!isspace(*p0)) { | ||
457 | *p++ = *p0; | ||
458 | if (*p0 == ':' && c == NULL) | ||
459 | c = p; | ||
460 | } | ||
461 | } | ||
462 | *p = 0; | ||
463 | |||
464 | /* test for empty or strange line */ | ||
465 | if (c == NULL || *c == 0) | ||
466 | continue; | ||
467 | p0 = buf; | ||
468 | if (*p0 == 'd') | ||
469 | *p0 = 'D'; | ||
470 | if (*c == '*') { | ||
471 | if (*p0 == 'D') { | ||
472 | /* memorize deny all */ | ||
473 | config->flg_deny_all++; | ||
474 | } | ||
475 | /* skip default other "word:*" config lines */ | ||
476 | continue; | ||
477 | } | ||
478 | |||
479 | if (*p0 == 'a') | ||
480 | *p0 = 'A'; | ||
481 | else if (*p0 != 'D' && *p0 != 'A' | ||
482 | #if ENABLE_FEATURE_HTTPD_BASIC_AUTH | ||
483 | && *p0 != '/' | ||
484 | #endif | ||
485 | #if ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES | ||
486 | && *p0 != '.' | ||
487 | #endif | ||
488 | #if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR | ||
489 | && *p0 != '*' | ||
490 | #endif | ||
491 | ) | ||
492 | continue; | ||
493 | if (*p0 == 'A' || *p0 == 'D') { | ||
494 | /* storing current config IP line */ | ||
495 | pip = calloc(1, sizeof(Htaccess_IP)); | ||
496 | if (pip) { | ||
497 | if (scan_ip_mask(c, &(pip->ip), &(pip->mask))) { | ||
498 | /* syntax IP{/mask} error detected, protect all */ | ||
499 | *p0 = 'D'; | ||
500 | pip->mask = 0; | ||
501 | } | ||
502 | pip->allow_deny = *p0; | ||
503 | if (*p0 == 'D') { | ||
504 | /* Deny:form_IP move top */ | ||
505 | pip->next = config->ip_a_d; | ||
506 | config->ip_a_d = pip; | ||
507 | } else { | ||
508 | /* add to bottom A:form_IP config line */ | ||
509 | Htaccess_IP *prev_IP = config->ip_a_d; | ||
510 | |||
511 | if (prev_IP == NULL) { | ||
512 | config->ip_a_d = pip; | ||
513 | } else { | ||
514 | while (prev_IP->next) | ||
515 | prev_IP = prev_IP->next; | ||
516 | prev_IP->next = pip; | ||
517 | } | ||
518 | } | ||
519 | } | ||
520 | continue; | ||
521 | } | ||
522 | #if ENABLE_FEATURE_HTTPD_BASIC_AUTH | ||
523 | if (*p0 == '/') { | ||
524 | /* make full path from httpd root / curent_path / config_line_path */ | ||
525 | cf = flag == SUBDIR_PARSE ? path : ""; | ||
526 | p0 = malloc(strlen(cf) + (c - buf) + 2 + strlen(c)); | ||
527 | if (p0 == NULL) | ||
528 | continue; | ||
529 | c[-1] = 0; | ||
530 | sprintf(p0, "/%s%s", cf, buf); | ||
531 | |||
532 | /* another call bb_simplify_path */ | ||
533 | cf = p = p0; | ||
534 | |||
535 | do { | ||
536 | if (*p == '/') { | ||
537 | if (*cf == '/') { /* skip duplicate (or initial) slash */ | ||
538 | continue; | ||
539 | } else if (*cf == '.') { | ||
540 | if (cf[1] == '/' || cf[1] == 0) { /* remove extra '.' */ | ||
541 | continue; | ||
542 | } else if ((cf[1] == '.') && (cf[2] == '/' || cf[2] == 0)) { | ||
543 | ++cf; | ||
544 | if (p > p0) { | ||
545 | while (*--p != '/') /* omit previous dir */; | ||
546 | } | ||
547 | continue; | ||
548 | } | ||
549 | } | ||
550 | } | ||
551 | *++p = *cf; | ||
552 | } while (*++cf); | ||
553 | |||
554 | if ((p == p0) || (*p != '/')) { /* not a trailing slash */ | ||
555 | ++p; /* so keep last character */ | ||
556 | } | ||
557 | *p = 0; | ||
558 | sprintf(p0, "%s:%s", p0, c); | ||
559 | } | ||
560 | #endif | ||
561 | |||
562 | #if ENABLE_FEATURE_HTTPD_BASIC_AUTH || ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES || ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR | ||
563 | /* storing current config line */ | ||
564 | cur = calloc(1, sizeof(Htaccess) + strlen(p0)); | ||
565 | if (cur) { | ||
566 | cf = strcpy(cur->before_colon, p0); | ||
567 | c = strchr(cf, ':'); | ||
568 | *c++ = 0; | ||
569 | cur->after_colon = c; | ||
570 | #if ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES | ||
571 | if (*cf == '.') { | ||
572 | /* config .mime line move top for overwrite previous */ | ||
573 | cur->next = config->mime_a; | ||
574 | config->mime_a = cur; | ||
575 | continue; | ||
576 | } | ||
577 | #endif | ||
578 | #if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR | ||
579 | if (*cf == '*' && cf[1] == '.') { | ||
580 | /* config script interpreter line move top for overwrite previous */ | ||
581 | cur->next = config->script_i; | ||
582 | config->script_i = cur; | ||
583 | continue; | ||
584 | } | ||
585 | #endif | ||
586 | #if ENABLE_FEATURE_HTTPD_BASIC_AUTH | ||
587 | free(p0); | ||
588 | if (prev == NULL) { | ||
589 | /* first line */ | ||
590 | config->auth = prev = cur; | ||
591 | } else { | ||
592 | /* sort path, if current lenght eq or bigger then move up */ | ||
593 | Htaccess *prev_hti = config->auth; | ||
594 | size_t l = strlen(cf); | ||
595 | Htaccess *hti; | ||
596 | |||
597 | for (hti = prev_hti; hti; hti = hti->next) { | ||
598 | if (l >= strlen(hti->before_colon)) { | ||
599 | /* insert before hti */ | ||
600 | cur->next = hti; | ||
601 | if (prev_hti != hti) { | ||
602 | prev_hti->next = cur; | ||
603 | } else { | ||
604 | /* insert as top */ | ||
605 | config->auth = cur; | ||
606 | } | ||
607 | break; | ||
608 | } | ||
609 | if (prev_hti != hti) | ||
610 | prev_hti = prev_hti->next; | ||
611 | } | ||
612 | if (!hti) { /* not inserted, add to bottom */ | ||
613 | prev->next = cur; | ||
614 | prev = cur; | ||
615 | } | ||
616 | } | ||
617 | #endif | ||
618 | } | ||
619 | #endif | ||
620 | } | ||
621 | fclose(f); | ||
622 | } | ||
623 | |||
624 | #if ENABLE_FEATURE_HTTPD_ENCODE_URL_STR | ||
625 | /**************************************************************************** | ||
626 | * | ||
627 | > $Function: encodeString() | ||
628 | * | ||
629 | * $Description: Given a string, html encode special characters. | ||
630 | * This is used for the -e command line option to provide an easy way | ||
631 | * for scripts to encode result data without confusing browsers. The | ||
632 | * returned string pointer is memory allocated by malloc(). | ||
633 | * | ||
634 | * $Parameters: | ||
635 | * (const char *) string . . The first string to encode. | ||
636 | * | ||
637 | * $Return: (char *) . . . .. . . A pointer to the encoded string. | ||
638 | * | ||
639 | * $Errors: Returns a null string ("") if memory is not available. | ||
640 | * | ||
641 | ****************************************************************************/ | ||
642 | static char *encodeString(const char *string) | ||
643 | { | ||
644 | /* take the simple route and encode everything */ | ||
645 | /* could possibly scan once to get length. */ | ||
646 | int len = strlen(string); | ||
647 | char *out = malloc(len * 6 + 1); | ||
648 | char *p = out; | ||
649 | char ch; | ||
650 | |||
651 | if (!out) return ""; | ||
652 | while ((ch = *string++)) { | ||
653 | // very simple check for what to encode | ||
654 | if (isalnum(ch)) *p++ = ch; | ||
655 | else p += sprintf(p, "&#%d;", (unsigned char) ch); | ||
656 | } | ||
657 | *p = 0; | ||
658 | return out; | ||
659 | } | ||
660 | #endif /* FEATURE_HTTPD_ENCODE_URL_STR */ | ||
661 | |||
662 | /**************************************************************************** | ||
663 | * | ||
664 | > $Function: decodeString() | ||
665 | * | ||
666 | * $Description: Given a URL encoded string, convert it to plain ascii. | ||
667 | * Since decoding always makes strings smaller, the decode is done in-place. | ||
668 | * Thus, callers should strdup() the argument if they do not want the | ||
669 | * argument modified. The return is the original pointer, allowing this | ||
670 | * function to be easily used as arguments to other functions. | ||
671 | * | ||
672 | * $Parameters: | ||
673 | * (char *) string . . . The first string to decode. | ||
674 | * (int) option_d . . 1 if called for httpd -d | ||
675 | * | ||
676 | * $Return: (char *) . . . . A pointer to the decoded string (same as input). | ||
677 | * | ||
678 | * $Errors: None | ||
679 | * | ||
680 | ****************************************************************************/ | ||
681 | static char *decodeString(char *orig, int option_d) | ||
682 | { | ||
683 | /* note that decoded string is always shorter than original */ | ||
684 | char *string = orig; | ||
685 | char *ptr = string; | ||
686 | char c; | ||
687 | |||
688 | while ((c = *ptr++) != '\0') { | ||
689 | unsigned value1, value2; | ||
690 | |||
691 | if (option_d && c == '+') { | ||
692 | *string++ = ' '; | ||
693 | continue; | ||
694 | } | ||
695 | if (c != '%') { | ||
696 | *string++ = c; | ||
697 | continue; | ||
698 | } | ||
699 | if (sscanf(ptr, "%1X", &value1) != 1 | ||
700 | || sscanf(ptr+1, "%1X", &value2) != 1 | ||
701 | ) { | ||
702 | if (!option_d) | ||
703 | return NULL; | ||
704 | *string++ = '%'; | ||
705 | continue; | ||
706 | } | ||
707 | value1 = value1 * 16 + value2; | ||
708 | if (!option_d && (value1 == '/' || value1 == '\0')) { | ||
709 | /* caller takes it as indication of invalid | ||
710 | * (dangerous wrt exploits) chars */ | ||
711 | return orig + 1; | ||
712 | } | ||
713 | *string++ = value1; | ||
714 | ptr += 2; | ||
715 | } | ||
716 | *string = '\0'; | ||
717 | return orig; | ||
718 | } | ||
719 | |||
720 | |||
721 | #if ENABLE_FEATURE_HTTPD_CGI | ||
722 | /**************************************************************************** | ||
723 | * setenv helpers | ||
724 | ****************************************************************************/ | ||
725 | static void setenv1(const char *name, const char *value) | ||
726 | { | ||
727 | if (!value) | ||
728 | value = ""; | ||
729 | setenv(name, value, 1); | ||
730 | } | ||
731 | static void setenv_long(const char *name, long value) | ||
732 | { | ||
733 | char buf[sizeof(value)*3 + 1]; | ||
734 | sprintf(buf, "%ld", value); | ||
735 | setenv(name, buf, 1); | ||
736 | } | ||
737 | #endif | ||
738 | |||
739 | #if ENABLE_FEATURE_HTTPD_BASIC_AUTH | ||
740 | /**************************************************************************** | ||
741 | * | ||
742 | > $Function: decodeBase64() | ||
743 | * | ||
744 | > $Description: Decode a base 64 data stream as per rfc1521. | ||
745 | * Note that the rfc states that none base64 chars are to be ignored. | ||
746 | * Since the decode always results in a shorter size than the input, it is | ||
747 | * OK to pass the input arg as an output arg. | ||
748 | * | ||
749 | * $Parameter: | ||
750 | * (char *) Data . . . . A pointer to a base64 encoded string. | ||
751 | * Where to place the decoded data. | ||
752 | * | ||
753 | * $Return: void | ||
754 | * | ||
755 | * $Errors: None | ||
756 | * | ||
757 | ****************************************************************************/ | ||
758 | static void decodeBase64(char *Data) | ||
759 | { | ||
760 | |||
761 | const unsigned char *in = (const unsigned char *)Data; | ||
762 | // The decoded size will be at most 3/4 the size of the encoded | ||
763 | unsigned long ch = 0; | ||
764 | int i = 0; | ||
765 | |||
766 | while (*in) { | ||
767 | int t = *in++; | ||
768 | |||
769 | if (t >= '0' && t <= '9') | ||
770 | t = t - '0' + 52; | ||
771 | else if (t >= 'A' && t <= 'Z') | ||
772 | t = t - 'A'; | ||
773 | else if (t >= 'a' && t <= 'z') | ||
774 | t = t - 'a' + 26; | ||
775 | else if (t == '+') | ||
776 | t = 62; | ||
777 | else if (t == '/') | ||
778 | t = 63; | ||
779 | else if (t == '=') | ||
780 | t = 0; | ||
781 | else | ||
782 | continue; | ||
783 | |||
784 | ch = (ch << 6) | t; | ||
785 | i++; | ||
786 | if (i == 4) { | ||
787 | *Data++ = (char) (ch >> 16); | ||
788 | *Data++ = (char) (ch >> 8); | ||
789 | *Data++ = (char) ch; | ||
790 | i = 0; | ||
791 | } | ||
792 | } | ||
793 | *Data = 0; | ||
794 | } | ||
795 | #endif | ||
796 | |||
797 | |||
798 | /**************************************************************************** | ||
799 | * | ||
800 | > $Function: openServer() | ||
801 | * | ||
802 | * $Description: create a listen server socket on the designated port. | ||
803 | * | ||
804 | * $Return: (int) . . . A connection socket. -1 for errors. | ||
805 | * | ||
806 | * $Errors: None | ||
807 | * | ||
808 | ****************************************************************************/ | ||
809 | static int openServer(void) | ||
810 | { | ||
811 | struct sockaddr_in lsocket; | ||
812 | int fd; | ||
813 | |||
814 | /* create the socket right now */ | ||
815 | /* inet_addr() returns a value that is already in network order */ | ||
816 | memset(&lsocket, 0, sizeof(lsocket)); | ||
817 | lsocket.sin_family = AF_INET; | ||
818 | lsocket.sin_addr.s_addr = INADDR_ANY; | ||
819 | lsocket.sin_port = htons(config->port); | ||
820 | fd = xsocket(AF_INET, SOCK_STREAM, 0); | ||
821 | /* tell the OS it's OK to reuse a previous address even though */ | ||
822 | /* it may still be in a close down state. Allows bind to succeed. */ | ||
823 | #ifdef SO_REUSEPORT | ||
824 | { | ||
825 | static const int on = 1; | ||
826 | setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, | ||
827 | (void *)&on, sizeof(on)); | ||
828 | } | ||
829 | #else | ||
830 | setsockopt_reuseaddr(fd); | ||
831 | #endif | ||
832 | xbind(fd, (struct sockaddr *)&lsocket, sizeof(lsocket)); | ||
833 | xlisten(fd, 9); | ||
834 | signal(SIGCHLD, SIG_IGN); /* prevent zombie (defunct) processes */ | ||
835 | return fd; | ||
836 | } | ||
837 | |||
838 | /**************************************************************************** | ||
839 | * | ||
840 | > $Function: sendHeaders() | ||
841 | * | ||
842 | * $Description: Create and send HTTP response headers. | ||
843 | * The arguments are combined and sent as one write operation. Note that | ||
844 | * IE will puke big-time if the headers are not sent in one packet and the | ||
845 | * second packet is delayed for any reason. | ||
846 | * | ||
847 | * $Parameter: | ||
848 | * (HttpResponseNum) responseNum . . . The result code to send. | ||
849 | * | ||
850 | * $Return: (int) . . . . writing errors | ||
851 | * | ||
852 | ****************************************************************************/ | ||
853 | static int sendHeaders(HttpResponseNum responseNum) | ||
854 | { | ||
855 | char *buf = config->buf; | ||
856 | const char *responseString = ""; | ||
857 | const char *infoString = 0; | ||
858 | const char *mime_type; | ||
859 | unsigned int i; | ||
860 | time_t timer = time(0); | ||
861 | char timeStr[80]; | ||
862 | int len; | ||
863 | enum { | ||
864 | numNames = sizeof(httpResponseNames) / sizeof(httpResponseNames[0]) | ||
865 | }; | ||
866 | |||
867 | for (i = 0; i < numNames; i++) { | ||
868 | if (httpResponseNames[i].type == responseNum) { | ||
869 | responseString = httpResponseNames[i].name; | ||
870 | infoString = httpResponseNames[i].info; | ||
871 | break; | ||
872 | } | ||
873 | } | ||
874 | /* error message is HTML */ | ||
875 | mime_type = responseNum == HTTP_OK ? | ||
876 | config->found_mime_type : "text/html"; | ||
877 | |||
878 | /* emit the current date */ | ||
879 | strftime(timeStr, sizeof(timeStr), RFC1123FMT, gmtime(&timer)); | ||
880 | len = sprintf(buf, | ||
881 | "HTTP/1.0 %d %s\r\nContent-type: %s\r\n" | ||
882 | "Date: %s\r\nConnection: close\r\n", | ||
883 | responseNum, responseString, mime_type, timeStr); | ||
884 | |||
885 | #if ENABLE_FEATURE_HTTPD_BASIC_AUTH | ||
886 | if (responseNum == HTTP_UNAUTHORIZED) { | ||
887 | len += sprintf(buf+len, "WWW-Authenticate: Basic realm=\"%s\"\r\n", | ||
888 | config->realm); | ||
889 | } | ||
890 | #endif | ||
891 | if (responseNum == HTTP_MOVED_TEMPORARILY) { | ||
892 | len += sprintf(buf+len, "Location: %s/%s%s\r\n", | ||
893 | config->found_moved_temporarily, | ||
894 | (config->query ? "?" : ""), | ||
895 | (config->query ? config->query : "")); | ||
896 | } | ||
897 | |||
898 | if (config->ContentLength != -1) { /* file */ | ||
899 | strftime(timeStr, sizeof(timeStr), RFC1123FMT, gmtime(&config->last_mod)); | ||
900 | len += sprintf(buf+len, "Last-Modified: %s\r\n%s %"OFF_FMT"d\r\n", | ||
901 | timeStr, "Content-length:", config->ContentLength); | ||
902 | } | ||
903 | strcat(buf, "\r\n"); | ||
904 | len += 2; | ||
905 | if (infoString) { | ||
906 | len += sprintf(buf+len, | ||
907 | "<HEAD><TITLE>%d %s</TITLE></HEAD>\n" | ||
908 | "<BODY><H1>%d %s</H1>\n%s\n</BODY>\n", | ||
909 | responseNum, responseString, | ||
910 | responseNum, responseString, infoString); | ||
911 | } | ||
912 | if (DEBUG) | ||
913 | fprintf(stderr, "headers: '%s'\n", buf); | ||
914 | return full_write(config->accepted_socket, buf, len); | ||
915 | } | ||
916 | |||
917 | /**************************************************************************** | ||
918 | * | ||
919 | > $Function: getLine() | ||
920 | * | ||
921 | * $Description: Read from the socket until an end of line char found. | ||
922 | * | ||
923 | * Characters are read one at a time until an eol sequence is found. | ||
924 | * | ||
925 | * $Return: (int) . . . . number of characters read. -1 if error. | ||
926 | * | ||
927 | ****************************************************************************/ | ||
928 | static int getLine(void) | ||
929 | { | ||
930 | int count = 0; | ||
931 | char *buf = config->buf; | ||
932 | |||
933 | while (read(config->accepted_socket, buf + count, 1) == 1) { | ||
934 | if (buf[count] == '\r') continue; | ||
935 | if (buf[count] == '\n') { | ||
936 | buf[count] = 0; | ||
937 | return count; | ||
938 | } | ||
939 | if (count < (MAX_MEMORY_BUFF-1)) /* check overflow */ | ||
940 | count++; | ||
941 | } | ||
942 | if (count) return count; | ||
943 | else return -1; | ||
944 | } | ||
945 | |||
946 | #if ENABLE_FEATURE_HTTPD_CGI | ||
947 | /**************************************************************************** | ||
948 | * | ||
949 | > $Function: sendCgi() | ||
950 | * | ||
951 | * $Description: Execute a CGI script and send it's stdout back | ||
952 | * | ||
953 | * Environment variables are set up and the script is invoked with pipes | ||
954 | * for stdin/stdout. If a post is being done the script is fed the POST | ||
955 | * data in addition to setting the QUERY_STRING variable (for GETs or POSTs). | ||
956 | * | ||
957 | * $Parameters: | ||
958 | * (const char *) url . . . . . . The requested URL (with leading /). | ||
959 | * (int bodyLen) . . . . . . . . Length of the post body. | ||
960 | * (const char *cookie) . . . . . For set HTTP_COOKIE. | ||
961 | * (const char *content_type) . . For set CONTENT_TYPE. | ||
962 | |||
963 | * | ||
964 | * $Return: (char *) . . . . A pointer to the decoded string (same as input). | ||
965 | * | ||
966 | * $Errors: None | ||
967 | * | ||
968 | ****************************************************************************/ | ||
969 | static int sendCgi(const char *url, | ||
970 | const char *request, int bodyLen, const char *cookie, | ||
971 | const char *content_type) | ||
972 | { | ||
973 | int fromCgi[2]; /* pipe for reading data from CGI */ | ||
974 | int toCgi[2]; /* pipe for sending data to CGI */ | ||
975 | |||
976 | static char * argp[] = { 0, 0 }; | ||
977 | int pid = 0; | ||
978 | int inFd; | ||
979 | int outFd; | ||
980 | int firstLine = 1; | ||
981 | int status; | ||
982 | size_t post_readed_size, post_readed_idx; | ||
983 | |||
984 | if (pipe(fromCgi) != 0) | ||
985 | return 0; | ||
986 | if (pipe(toCgi) != 0) | ||
987 | return 0; | ||
988 | |||
989 | pid = fork(); | ||
990 | if (pid < 0) | ||
991 | return 0; | ||
992 | |||
993 | if (!pid) { | ||
994 | /* child process */ | ||
995 | char *script; | ||
996 | char *purl = strdup(url); | ||
997 | char realpath_buff[MAXPATHLEN]; | ||
998 | |||
999 | if (purl == NULL) | ||
1000 | _exit(242); | ||
1001 | |||
1002 | inFd = toCgi[0]; | ||
1003 | outFd = fromCgi[1]; | ||
1004 | |||
1005 | dup2(inFd, 0); // replace stdin with the pipe | ||
1006 | dup2(outFd, 1); // replace stdout with the pipe | ||
1007 | if (!DEBUG) | ||
1008 | dup2(outFd, 2); // replace stderr with the pipe | ||
1009 | |||
1010 | close(toCgi[0]); | ||
1011 | close(toCgi[1]); | ||
1012 | close(fromCgi[0]); | ||
1013 | close(fromCgi[1]); | ||
1014 | |||
1015 | close(config->accepted_socket); | ||
1016 | close(config->server_socket); | ||
1017 | |||
1018 | /* | ||
1019 | * Find PATH_INFO. | ||
1020 | */ | ||
1021 | script = purl; | ||
1022 | while ((script = strchr(script + 1, '/')) != NULL) { | ||
1023 | /* have script.cgi/PATH_INFO or dirs/script.cgi[/PATH_INFO] */ | ||
1024 | struct stat sb; | ||
1025 | |||
1026 | *script = '\0'; | ||
1027 | if (is_directory(purl + 1, 1, &sb) == 0) { | ||
1028 | /* not directory, found script.cgi/PATH_INFO */ | ||
1029 | *script = '/'; | ||
1030 | break; | ||
1031 | } | ||
1032 | *script = '/'; /* is directory, find next '/' */ | ||
1033 | } | ||
1034 | setenv1("PATH_INFO", script); /* set /PATH_INFO or "" */ | ||
1035 | /* setenv1("PATH", getenv("PATH")); redundant */ | ||
1036 | setenv1("REQUEST_METHOD", request); | ||
1037 | if (config->query) { | ||
1038 | char *uri = alloca(strlen(purl) + 2 + strlen(config->query)); | ||
1039 | if (uri) | ||
1040 | sprintf(uri, "%s?%s", purl, config->query); | ||
1041 | setenv1("REQUEST_URI", uri); | ||
1042 | } else { | ||
1043 | setenv1("REQUEST_URI", purl); | ||
1044 | } | ||
1045 | if (script != NULL) | ||
1046 | *script = '\0'; /* cut off /PATH_INFO */ | ||
1047 | /* SCRIPT_FILENAME required by PHP in CGI mode */ | ||
1048 | if (!realpath(purl + 1, realpath_buff)) | ||
1049 | goto error_execing_cgi; | ||
1050 | setenv1("SCRIPT_FILENAME", realpath_buff); | ||
1051 | /* set SCRIPT_NAME as full path: /cgi-bin/dirs/script.cgi */ | ||
1052 | setenv1("SCRIPT_NAME", purl); | ||
1053 | /* http://hoohoo.ncsa.uiuc.edu/cgi/env.html: | ||
1054 | * QUERY_STRING: The information which follows the ? in the URL | ||
1055 | * which referenced this script. This is the query information. | ||
1056 | * It should not be decoded in any fashion. This variable | ||
1057 | * should always be set when there is query information, | ||
1058 | * regardless of command line decoding. */ | ||
1059 | /* (Older versions of bbox seemed to do some decoding) */ | ||
1060 | setenv1("QUERY_STRING", config->query); | ||
1061 | setenv1("SERVER_SOFTWARE", httpdVersion); | ||
1062 | putenv("SERVER_PROTOCOL=HTTP/1.0"); | ||
1063 | putenv("GATEWAY_INTERFACE=CGI/1.1"); | ||
1064 | setenv1("REMOTE_ADDR", config->rmt_ip_str); | ||
1065 | #if ENABLE_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV | ||
1066 | setenv_long("REMOTE_PORT", config->port); | ||
1067 | #endif | ||
1068 | if (bodyLen) | ||
1069 | setenv_long("CONTENT_LENGTH", bodyLen); | ||
1070 | if (cookie) | ||
1071 | setenv1("HTTP_COOKIE", cookie); | ||
1072 | if (content_type) | ||
1073 | setenv1("CONTENT_TYPE", content_type); | ||
1074 | #if ENABLE_FEATURE_HTTPD_BASIC_AUTH | ||
1075 | if (config->remoteuser) { | ||
1076 | setenv1("REMOTE_USER", config->remoteuser); | ||
1077 | putenv("AUTH_TYPE=Basic"); | ||
1078 | } | ||
1079 | #endif | ||
1080 | if (config->referer) | ||
1081 | setenv1("HTTP_REFERER", config->referer); | ||
1082 | |||
1083 | /* set execve argp[0] without path */ | ||
1084 | argp[0] = strrchr(purl, '/') + 1; | ||
1085 | /* but script argp[0] must have absolute path and chdiring to this */ | ||
1086 | script = strrchr(realpath_buff, '/'); | ||
1087 | if (!script) | ||
1088 | goto error_execing_cgi; | ||
1089 | *script = '\0'; | ||
1090 | if (chdir(realpath_buff) == 0) { | ||
1091 | // now run the program. If it fails, | ||
1092 | // use _exit() so no destructors | ||
1093 | // get called and make a mess. | ||
1094 | #if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR | ||
1095 | char *interpr = NULL; | ||
1096 | char *suffix = strrchr(purl, '.'); | ||
1097 | |||
1098 | if (suffix) { | ||
1099 | Htaccess *cur; | ||
1100 | for (cur = config->script_i; cur; cur = cur->next) { | ||
1101 | if (strcmp(cur->before_colon + 1, suffix) == 0) { | ||
1102 | interpr = cur->after_colon; | ||
1103 | break; | ||
1104 | } | ||
1105 | } | ||
1106 | } | ||
1107 | #endif | ||
1108 | *script = '/'; | ||
1109 | #if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR | ||
1110 | if (interpr) | ||
1111 | execv(interpr, argp); | ||
1112 | else | ||
1113 | #endif | ||
1114 | execv(realpath_buff, argp); | ||
1115 | } | ||
1116 | error_execing_cgi: | ||
1117 | /* send to stdout (even if we are not from inetd) */ | ||
1118 | config->accepted_socket = 1; | ||
1119 | sendHeaders(HTTP_NOT_FOUND); | ||
1120 | _exit(242); | ||
1121 | } /* end child */ | ||
1122 | |||
1123 | /* parent process */ | ||
1124 | |||
1125 | post_readed_size = 0; | ||
1126 | post_readed_idx = 0; | ||
1127 | inFd = fromCgi[0]; | ||
1128 | outFd = toCgi[1]; | ||
1129 | close(fromCgi[1]); | ||
1130 | close(toCgi[0]); | ||
1131 | signal(SIGPIPE, SIG_IGN); | ||
1132 | |||
1133 | while (1) { | ||
1134 | fd_set readSet; | ||
1135 | fd_set writeSet; | ||
1136 | char wbuf[128]; | ||
1137 | int nfound; | ||
1138 | int count; | ||
1139 | |||
1140 | FD_ZERO(&readSet); | ||
1141 | FD_ZERO(&writeSet); | ||
1142 | FD_SET(inFd, &readSet); | ||
1143 | if (bodyLen > 0 || post_readed_size > 0) { | ||
1144 | FD_SET(outFd, &writeSet); | ||
1145 | nfound = outFd > inFd ? outFd : inFd; | ||
1146 | if (post_readed_size == 0) { | ||
1147 | FD_SET(config->accepted_socket, &readSet); | ||
1148 | if (nfound < config->accepted_socket) | ||
1149 | nfound = config->accepted_socket; | ||
1150 | } | ||
1151 | /* Now wait on the set of sockets! */ | ||
1152 | nfound = select(nfound + 1, &readSet, &writeSet, 0, NULL); | ||
1153 | } else { | ||
1154 | if (!bodyLen) { | ||
1155 | close(outFd); | ||
1156 | bodyLen = -1; | ||
1157 | } | ||
1158 | nfound = select(inFd + 1, &readSet, 0, 0, NULL); | ||
1159 | } | ||
1160 | |||
1161 | if (nfound <= 0) { | ||
1162 | if (waitpid(pid, &status, WNOHANG) > 0) { | ||
1163 | close(inFd); | ||
1164 | if (DEBUG && WIFEXITED(status)) | ||
1165 | bb_error_msg("piped has exited with status=%d", WEXITSTATUS(status)); | ||
1166 | if (DEBUG && WIFSIGNALED(status)) | ||
1167 | bb_error_msg("piped has exited with signal=%d", WTERMSIG(status)); | ||
1168 | break; | ||
1169 | } | ||
1170 | } else if (post_readed_size > 0 && FD_ISSET(outFd, &writeSet)) { | ||
1171 | count = full_write(outFd, wbuf + post_readed_idx, post_readed_size); | ||
1172 | if (count > 0) { | ||
1173 | post_readed_size -= count; | ||
1174 | post_readed_idx += count; | ||
1175 | if (post_readed_size == 0) | ||
1176 | post_readed_idx = 0; | ||
1177 | } else { | ||
1178 | post_readed_size = post_readed_idx = bodyLen = 0; /* broken pipe to CGI */ | ||
1179 | } | ||
1180 | } else if (bodyLen > 0 && post_readed_size == 0 && FD_ISSET(config->accepted_socket, &readSet)) { | ||
1181 | count = bodyLen > (int)sizeof(wbuf) ? (int)sizeof(wbuf) : bodyLen; | ||
1182 | count = safe_read(config->accepted_socket, wbuf, count); | ||
1183 | if (count > 0) { | ||
1184 | post_readed_size += count; | ||
1185 | bodyLen -= count; | ||
1186 | } else { | ||
1187 | bodyLen = 0; /* closed */ | ||
1188 | } | ||
1189 | } | ||
1190 | if (FD_ISSET(inFd, &readSet)) { | ||
1191 | int s = config->accepted_socket; | ||
1192 | char *rbuf = config->buf; | ||
1193 | |||
1194 | #ifndef PIPE_BUF | ||
1195 | # define PIPESIZE 4096 /* amount of buffering in a pipe */ | ||
1196 | #else | ||
1197 | # define PIPESIZE PIPE_BUF | ||
1198 | #endif | ||
1199 | #if PIPESIZE >= MAX_MEMORY_BUFF | ||
1200 | # error "PIPESIZE >= MAX_MEMORY_BUFF" | ||
1201 | #endif | ||
1202 | |||
1203 | /* There is something to read */ | ||
1204 | count = safe_read(inFd, rbuf, PIPESIZE); | ||
1205 | if (count == 0) | ||
1206 | break; /* closed */ | ||
1207 | if (count > 0) { | ||
1208 | if (firstLine) { | ||
1209 | rbuf[count] = 0; | ||
1210 | /* check to see if the user script added headers */ | ||
1211 | if (strncmp(rbuf, "HTTP/1.0 200 OK\r\n", 4) != 0) { | ||
1212 | full_write(s, "HTTP/1.0 200 OK\r\n", 17); | ||
1213 | } | ||
1214 | /* Sometimes CGI is writing to pipe in small chunks | ||
1215 | * and we don't see Content-type (because the read | ||
1216 | * is too short) and we emit bogus "text/plain"! | ||
1217 | * Is it a bug or CGI *has to* write it in one piece? */ | ||
1218 | if (strstr(rbuf, "ontent-") == 0) { | ||
1219 | full_write(s, "Content-type: text/plain\r\n\r\n", 28); | ||
1220 | } | ||
1221 | firstLine = 0; | ||
1222 | } | ||
1223 | if (full_write(s, rbuf, count) != count) | ||
1224 | break; | ||
1225 | |||
1226 | if (DEBUG) | ||
1227 | fprintf(stderr, "cgi read %d bytes: '%.*s'\n", count, count, rbuf); | ||
1228 | } | ||
1229 | } | ||
1230 | } | ||
1231 | return 0; | ||
1232 | } | ||
1233 | #endif /* FEATURE_HTTPD_CGI */ | ||
1234 | |||
1235 | /**************************************************************************** | ||
1236 | * | ||
1237 | > $Function: sendFile() | ||
1238 | * | ||
1239 | * $Description: Send a file response to a HTTP request | ||
1240 | * | ||
1241 | * $Parameter: | ||
1242 | * (const char *) url . . The URL requested. | ||
1243 | * | ||
1244 | * $Return: (int) . . . . . . Always 0. | ||
1245 | * | ||
1246 | ****************************************************************************/ | ||
1247 | static int sendFile(const char *url) | ||
1248 | { | ||
1249 | char * suffix; | ||
1250 | int f; | ||
1251 | const char * const * table; | ||
1252 | const char * try_suffix; | ||
1253 | |||
1254 | suffix = strrchr(url, '.'); | ||
1255 | |||
1256 | for (table = suffixTable; *table; table += 2) | ||
1257 | if (suffix != NULL && (try_suffix = strstr(*table, suffix)) != 0) { | ||
1258 | try_suffix += strlen(suffix); | ||
1259 | if (*try_suffix == 0 || *try_suffix == '.') | ||
1260 | break; | ||
1261 | } | ||
1262 | /* also, if not found, set default as "application/octet-stream"; */ | ||
1263 | config->found_mime_type = table[1]; | ||
1264 | #if ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES | ||
1265 | if (suffix) { | ||
1266 | Htaccess * cur; | ||
1267 | |||
1268 | for (cur = config->mime_a; cur; cur = cur->next) { | ||
1269 | if (strcmp(cur->before_colon, suffix) == 0) { | ||
1270 | config->found_mime_type = cur->after_colon; | ||
1271 | break; | ||
1272 | } | ||
1273 | } | ||
1274 | } | ||
1275 | #endif /* FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES */ | ||
1276 | |||
1277 | if (DEBUG) | ||
1278 | fprintf(stderr, "sending file '%s' content-type: %s\n", | ||
1279 | url, config->found_mime_type); | ||
1280 | |||
1281 | f = open(url, O_RDONLY); | ||
1282 | if (f >= 0) { | ||
1283 | int count; | ||
1284 | char *buf = config->buf; | ||
1285 | |||
1286 | sendHeaders(HTTP_OK); | ||
1287 | /* TODO: sendfile() */ | ||
1288 | while ((count = full_read(f, buf, MAX_MEMORY_BUFF)) > 0) { | ||
1289 | if (full_write(config->accepted_socket, buf, count) != count) | ||
1290 | break; | ||
1291 | } | ||
1292 | close(f); | ||
1293 | } else { | ||
1294 | if (DEBUG) | ||
1295 | bb_perror_msg("cannot open '%s'", url); | ||
1296 | sendHeaders(HTTP_NOT_FOUND); | ||
1297 | } | ||
1298 | |||
1299 | return 0; | ||
1300 | } | ||
1301 | |||
1302 | static int checkPermIP(void) | ||
1303 | { | ||
1304 | Htaccess_IP * cur; | ||
1305 | |||
1306 | /* This could stand some work */ | ||
1307 | for (cur = config->ip_a_d; cur; cur = cur->next) { | ||
1308 | if (DEBUG) | ||
1309 | fprintf(stderr, "checkPermIP: '%s' ? ", config->rmt_ip_str); | ||
1310 | if (DEBUG) | ||
1311 | fprintf(stderr, "'%u.%u.%u.%u/%u.%u.%u.%u'\n", | ||
1312 | (unsigned char)(cur->ip >> 24), | ||
1313 | (unsigned char)(cur->ip >> 16), | ||
1314 | (unsigned char)(cur->ip >> 8), | ||
1315 | cur->ip & 0xff, | ||
1316 | (unsigned char)(cur->mask >> 24), | ||
1317 | (unsigned char)(cur->mask >> 16), | ||
1318 | (unsigned char)(cur->mask >> 8), | ||
1319 | cur->mask & 0xff); | ||
1320 | if ((config->rmt_ip & cur->mask) == cur->ip) | ||
1321 | return cur->allow_deny == 'A'; /* Allow/Deny */ | ||
1322 | } | ||
1323 | |||
1324 | /* if unconfigured, return 1 - access from all */ | ||
1325 | return !config->flg_deny_all; | ||
1326 | } | ||
1327 | |||
1328 | /**************************************************************************** | ||
1329 | * | ||
1330 | > $Function: checkPerm() | ||
1331 | * | ||
1332 | * $Description: Check the permission file for access password protected. | ||
1333 | * | ||
1334 | * If config file isn't present, everything is allowed. | ||
1335 | * Entries are of the form you can see example from header source | ||
1336 | * | ||
1337 | * $Parameters: | ||
1338 | * (const char *) path . . . . The file path. | ||
1339 | * (const char *) request . . . User information to validate. | ||
1340 | * | ||
1341 | * $Return: (int) . . . . . . . . . 1 if request OK, 0 otherwise. | ||
1342 | * | ||
1343 | ****************************************************************************/ | ||
1344 | |||
1345 | #if ENABLE_FEATURE_HTTPD_BASIC_AUTH | ||
1346 | static int checkPerm(const char *path, const char *request) | ||
1347 | { | ||
1348 | Htaccess * cur; | ||
1349 | const char *p; | ||
1350 | const char *p0; | ||
1351 | |||
1352 | const char *prev = NULL; | ||
1353 | |||
1354 | /* This could stand some work */ | ||
1355 | for (cur = config->auth; cur; cur = cur->next) { | ||
1356 | size_t l; | ||
1357 | |||
1358 | p0 = cur->before_colon; | ||
1359 | if (prev != NULL && strcmp(prev, p0) != 0) | ||
1360 | continue; /* find next identical */ | ||
1361 | p = cur->after_colon; | ||
1362 | if (DEBUG) | ||
1363 | fprintf(stderr, "checkPerm: '%s' ? '%s'\n", p0, request); | ||
1364 | |||
1365 | l = strlen(p0); | ||
1366 | if (strncmp(p0, path, l) == 0 | ||
1367 | && (l == 1 || path[l] == '/' || path[l] == '\0') | ||
1368 | ) { | ||
1369 | char *u; | ||
1370 | /* path match found. Check request */ | ||
1371 | /* for check next /path:user:password */ | ||
1372 | prev = p0; | ||
1373 | u = strchr(request, ':'); | ||
1374 | if (u == NULL) { | ||
1375 | /* bad request, ':' required */ | ||
1376 | break; | ||
1377 | } | ||
1378 | |||
1379 | if (ENABLE_FEATURE_HTTPD_AUTH_MD5) { | ||
1380 | char *cipher; | ||
1381 | char *pp; | ||
1382 | |||
1383 | if (strncmp(p, request, u-request) != 0) { | ||
1384 | /* user uncompared */ | ||
1385 | continue; | ||
1386 | } | ||
1387 | pp = strchr(p, ':'); | ||
1388 | if (pp && pp[1] == '$' && pp[2] == '1' && | ||
1389 | pp[3] == '$' && pp[4]) { | ||
1390 | pp++; | ||
1391 | cipher = pw_encrypt(u+1, pp); | ||
1392 | if (strcmp(cipher, pp) == 0) | ||
1393 | goto set_remoteuser_var; /* Ok */ | ||
1394 | /* unauthorized */ | ||
1395 | continue; | ||
1396 | } | ||
1397 | } | ||
1398 | |||
1399 | if (strcmp(p, request) == 0) { | ||
1400 | set_remoteuser_var: | ||
1401 | config->remoteuser = strdup(request); | ||
1402 | if (config->remoteuser) | ||
1403 | config->remoteuser[(u - request)] = 0; | ||
1404 | return 1; /* Ok */ | ||
1405 | } | ||
1406 | /* unauthorized */ | ||
1407 | } | ||
1408 | } /* for */ | ||
1409 | |||
1410 | return prev == NULL; | ||
1411 | } | ||
1412 | |||
1413 | #endif /* FEATURE_HTTPD_BASIC_AUTH */ | ||
1414 | |||
1415 | /**************************************************************************** | ||
1416 | * | ||
1417 | > $Function: handle_sigalrm() | ||
1418 | * | ||
1419 | * $Description: Handle timeouts | ||
1420 | * | ||
1421 | ****************************************************************************/ | ||
1422 | |||
1423 | static void handle_sigalrm(int sig) | ||
1424 | { | ||
1425 | sendHeaders(HTTP_REQUEST_TIMEOUT); | ||
1426 | config->alarm_signaled = sig; | ||
1427 | } | ||
1428 | |||
1429 | /**************************************************************************** | ||
1430 | * | ||
1431 | > $Function: handleIncoming() | ||
1432 | * | ||
1433 | * $Description: Handle an incoming http request. | ||
1434 | * | ||
1435 | ****************************************************************************/ | ||
1436 | static void handleIncoming(void) | ||
1437 | { | ||
1438 | char *buf = config->buf; | ||
1439 | char *url; | ||
1440 | char *purl; | ||
1441 | int blank = -1; | ||
1442 | char *test; | ||
1443 | struct stat sb; | ||
1444 | int ip_allowed; | ||
1445 | #if ENABLE_FEATURE_HTTPD_CGI | ||
1446 | const char *prequest = request_GET; | ||
1447 | unsigned long length = 0; | ||
1448 | char *cookie = 0; | ||
1449 | char *content_type = 0; | ||
1450 | #endif | ||
1451 | fd_set s_fd; | ||
1452 | struct timeval tv; | ||
1453 | int retval; | ||
1454 | struct sigaction sa; | ||
1455 | |||
1456 | #if ENABLE_FEATURE_HTTPD_BASIC_AUTH | ||
1457 | int credentials = -1; /* if not required this is Ok */ | ||
1458 | #endif | ||
1459 | |||
1460 | sa.sa_handler = handle_sigalrm; | ||
1461 | sigemptyset(&sa.sa_mask); | ||
1462 | sa.sa_flags = 0; /* no SA_RESTART */ | ||
1463 | sigaction(SIGALRM, &sa, NULL); | ||
1464 | |||
1465 | do { | ||
1466 | int count; | ||
1467 | |||
1468 | (void) alarm(TIMEOUT); | ||
1469 | if (getLine() <= 0) | ||
1470 | break; /* closed */ | ||
1471 | |||
1472 | purl = strpbrk(buf, " \t"); | ||
1473 | if (purl == NULL) { | ||
1474 | BAD_REQUEST: | ||
1475 | sendHeaders(HTTP_BAD_REQUEST); | ||
1476 | break; | ||
1477 | } | ||
1478 | *purl = '\0'; | ||
1479 | #if ENABLE_FEATURE_HTTPD_CGI | ||
1480 | if (strcasecmp(buf, prequest) != 0) { | ||
1481 | prequest = "POST"; | ||
1482 | if (strcasecmp(buf, prequest) != 0) { | ||
1483 | sendHeaders(HTTP_NOT_IMPLEMENTED); | ||
1484 | break; | ||
1485 | } | ||
1486 | } | ||
1487 | #else | ||
1488 | if (strcasecmp(buf, request_GET) != 0) { | ||
1489 | sendHeaders(HTTP_NOT_IMPLEMENTED); | ||
1490 | break; | ||
1491 | } | ||
1492 | #endif | ||
1493 | *purl = ' '; | ||
1494 | count = sscanf(purl, " %[^ ] HTTP/%d.%*d", buf, &blank); | ||
1495 | |||
1496 | if (count < 1 || buf[0] != '/') { | ||
1497 | /* Garbled request/URL */ | ||
1498 | goto BAD_REQUEST; | ||
1499 | } | ||
1500 | url = alloca(strlen(buf) + sizeof("/index.html")); | ||
1501 | if (url == NULL) { | ||
1502 | sendHeaders(HTTP_INTERNAL_SERVER_ERROR); | ||
1503 | break; | ||
1504 | } | ||
1505 | strcpy(url, buf); | ||
1506 | /* extract url args if present */ | ||
1507 | test = strchr(url, '?'); | ||
1508 | config->query = NULL; | ||
1509 | if (test) { | ||
1510 | *test++ = '\0'; | ||
1511 | config->query = test; | ||
1512 | } | ||
1513 | |||
1514 | test = decodeString(url, 0); | ||
1515 | if (test == NULL) | ||
1516 | goto BAD_REQUEST; | ||
1517 | if (test == url+1) { | ||
1518 | /* '/' or NUL is encoded */ | ||
1519 | sendHeaders(HTTP_NOT_FOUND); | ||
1520 | break; | ||
1521 | } | ||
1522 | |||
1523 | /* algorithm stolen from libbb bb_simplify_path(), | ||
1524 | but don't strdup and reducing trailing slash and protect out root */ | ||
1525 | purl = test = url; | ||
1526 | do { | ||
1527 | if (*purl == '/') { | ||
1528 | /* skip duplicate (or initial) slash */ | ||
1529 | if (*test == '/') { | ||
1530 | continue; | ||
1531 | } | ||
1532 | if (*test == '.') { | ||
1533 | /* skip extra '.' */ | ||
1534 | if (test[1] == '/' || test[1] == 0) { | ||
1535 | continue; | ||
1536 | } else | ||
1537 | /* '..': be careful */ | ||
1538 | if (test[1] == '.' && (test[2] == '/' || test[2] == 0)) { | ||
1539 | ++test; | ||
1540 | if (purl == url) { | ||
1541 | /* protect out root */ | ||
1542 | goto BAD_REQUEST; | ||
1543 | } | ||
1544 | while (*--purl != '/') /* omit previous dir */; | ||
1545 | continue; | ||
1546 | } | ||
1547 | } | ||
1548 | } | ||
1549 | *++purl = *test; | ||
1550 | } while (*++test); | ||
1551 | *++purl = '\0'; /* so keep last character */ | ||
1552 | test = purl; /* end ptr */ | ||
1553 | |||
1554 | /* If URL is directory, adding '/' */ | ||
1555 | if (test[-1] != '/') { | ||
1556 | if (is_directory(url + 1, 1, &sb)) { | ||
1557 | config->found_moved_temporarily = url; | ||
1558 | } | ||
1559 | } | ||
1560 | if (DEBUG) | ||
1561 | fprintf(stderr, "url='%s', args=%s\n", url, config->query); | ||
1562 | |||
1563 | test = url; | ||
1564 | ip_allowed = checkPermIP(); | ||
1565 | while (ip_allowed && (test = strchr(test + 1, '/')) != NULL) { | ||
1566 | /* have path1/path2 */ | ||
1567 | *test = '\0'; | ||
1568 | if (is_directory(url + 1, 1, &sb)) { | ||
1569 | /* may be having subdir config */ | ||
1570 | parse_conf(url + 1, SUBDIR_PARSE); | ||
1571 | ip_allowed = checkPermIP(); | ||
1572 | } | ||
1573 | *test = '/'; | ||
1574 | } | ||
1575 | if (blank >= 0) { | ||
1576 | /* read until blank line for HTTP version specified, else parse immediate */ | ||
1577 | while (1) { | ||
1578 | alarm(TIMEOUT); | ||
1579 | count = getLine(); | ||
1580 | if (count <= 0) | ||
1581 | break; | ||
1582 | |||
1583 | if (DEBUG) | ||
1584 | fprintf(stderr, "header: '%s'\n", buf); | ||
1585 | |||
1586 | #if ENABLE_FEATURE_HTTPD_CGI | ||
1587 | /* try and do our best to parse more lines */ | ||
1588 | if ((STRNCASECMP(buf, "Content-length:") == 0)) { | ||
1589 | /* extra read only for POST */ | ||
1590 | if (prequest != request_GET) { | ||
1591 | test = buf + sizeof("Content-length:")-1; | ||
1592 | if (!test[0]) goto bail_out; | ||
1593 | errno = 0; | ||
1594 | /* not using strtoul: it ignores leading munis! */ | ||
1595 | length = strtol(test, &test, 10); | ||
1596 | /* length is "ulong", but we need to pass it to int later */ | ||
1597 | /* so we check for negative or too large values in one go: */ | ||
1598 | /* (long -> ulong conv caused negatives to be seen as > INT_MAX) */ | ||
1599 | if (test[0] || errno || length > INT_MAX) | ||
1600 | goto bail_out; | ||
1601 | } | ||
1602 | } else if ((STRNCASECMP(buf, "Cookie:") == 0)) { | ||
1603 | cookie = strdup(skip_whitespace(buf + sizeof("Cookie:")-1)); | ||
1604 | } else if ((STRNCASECMP(buf, "Content-Type:") == 0)) { | ||
1605 | content_type = strdup(skip_whitespace(buf + sizeof("Content-Type:")-1)); | ||
1606 | } else if ((STRNCASECMP(buf, "Referer:") == 0)) { | ||
1607 | config->referer = strdup(skip_whitespace(buf + sizeof("Referer:")-1)); | ||
1608 | } | ||
1609 | #endif | ||
1610 | |||
1611 | #if ENABLE_FEATURE_HTTPD_BASIC_AUTH | ||
1612 | if (STRNCASECMP(buf, "Authorization:") == 0) { | ||
1613 | /* We only allow Basic credentials. | ||
1614 | * It shows up as "Authorization: Basic <userid:password>" where | ||
1615 | * the userid:password is base64 encoded. | ||
1616 | */ | ||
1617 | test = skip_whitespace(buf + sizeof("Authorization:")-1); | ||
1618 | if (STRNCASECMP(test, "Basic") != 0) | ||
1619 | continue; | ||
1620 | test += sizeof("Basic")-1; | ||
1621 | /* decodeBase64() skips whitespace itself */ | ||
1622 | decodeBase64(test); | ||
1623 | credentials = checkPerm(url, test); | ||
1624 | } | ||
1625 | #endif /* FEATURE_HTTPD_BASIC_AUTH */ | ||
1626 | |||
1627 | } /* while extra header reading */ | ||
1628 | } | ||
1629 | alarm(0); | ||
1630 | if (config->alarm_signaled) | ||
1631 | break; | ||
1632 | |||
1633 | if (strcmp(strrchr(url, '/') + 1, httpd_conf) == 0 || ip_allowed == 0) { | ||
1634 | /* protect listing [/path]/httpd_conf or IP deny */ | ||
1635 | #if ENABLE_FEATURE_HTTPD_CGI | ||
1636 | FORBIDDEN: /* protect listing /cgi-bin */ | ||
1637 | #endif | ||
1638 | sendHeaders(HTTP_FORBIDDEN); | ||
1639 | break; | ||
1640 | } | ||
1641 | |||
1642 | #if ENABLE_FEATURE_HTTPD_BASIC_AUTH | ||
1643 | if (credentials <= 0 && checkPerm(url, ":") == 0) { | ||
1644 | sendHeaders(HTTP_UNAUTHORIZED); | ||
1645 | break; | ||
1646 | } | ||
1647 | #endif | ||
1648 | |||
1649 | if (config->found_moved_temporarily) { | ||
1650 | sendHeaders(HTTP_MOVED_TEMPORARILY); | ||
1651 | /* clear unforked memory flag */ | ||
1652 | config->found_moved_temporarily = NULL; | ||
1653 | break; | ||
1654 | } | ||
1655 | |||
1656 | test = url + 1; /* skip first '/' */ | ||
1657 | |||
1658 | #if ENABLE_FEATURE_HTTPD_CGI | ||
1659 | if (strncmp(test, "cgi-bin", 7) == 0) { | ||
1660 | if (test[7] == '/' && test[8] == 0) | ||
1661 | goto FORBIDDEN; /* protect listing cgi-bin/ */ | ||
1662 | sendCgi(url, prequest, length, cookie, content_type); | ||
1663 | break; | ||
1664 | } | ||
1665 | if (prequest != request_GET) { | ||
1666 | sendHeaders(HTTP_NOT_IMPLEMENTED); | ||
1667 | break; | ||
1668 | } | ||
1669 | #endif /* FEATURE_HTTPD_CGI */ | ||
1670 | if (purl[-1] == '/') | ||
1671 | strcpy(purl, "index.html"); | ||
1672 | if (stat(test, &sb) == 0) { | ||
1673 | /* It's a dir URL and there is index.html */ | ||
1674 | config->ContentLength = sb.st_size; | ||
1675 | config->last_mod = sb.st_mtime; | ||
1676 | } | ||
1677 | #if ENABLE_FEATURE_HTTPD_CGI | ||
1678 | else if (purl[-1] == '/') { | ||
1679 | /* It's a dir URL and there is no index.html | ||
1680 | * Try cgi-bin/index.cgi */ | ||
1681 | if (access("/cgi-bin/index.cgi"+1, X_OK) == 0) { | ||
1682 | purl[0] = '\0'; | ||
1683 | config->query = url; | ||
1684 | sendCgi("/cgi-bin/index.cgi", prequest, length, cookie, content_type); | ||
1685 | break; | ||
1686 | } | ||
1687 | } | ||
1688 | #endif /* FEATURE_HTTPD_CGI */ | ||
1689 | sendFile(test); | ||
1690 | config->ContentLength = -1; | ||
1691 | } while (0); | ||
1692 | |||
1693 | bail_out: | ||
1694 | |||
1695 | if (DEBUG) | ||
1696 | fprintf(stderr, "closing socket\n\n"); | ||
1697 | #if ENABLE_FEATURE_HTTPD_CGI | ||
1698 | free(cookie); | ||
1699 | free(content_type); | ||
1700 | free(config->referer); | ||
1701 | config->referer = NULL; | ||
1702 | # if ENABLE_FEATURE_HTTPD_BASIC_AUTH | ||
1703 | free(config->remoteuser); | ||
1704 | config->remoteuser = NULL; | ||
1705 | # endif | ||
1706 | #endif | ||
1707 | shutdown(config->accepted_socket, SHUT_WR); | ||
1708 | |||
1709 | /* Properly wait for remote to closed */ | ||
1710 | FD_ZERO(&s_fd); | ||
1711 | FD_SET(config->accepted_socket, &s_fd); | ||
1712 | |||
1713 | do { | ||
1714 | tv.tv_sec = 2; | ||
1715 | tv.tv_usec = 0; | ||
1716 | retval = select(config->accepted_socket + 1, &s_fd, NULL, NULL, &tv); | ||
1717 | } while (retval > 0 && read(config->accepted_socket, buf, sizeof(config->buf) > 0)); | ||
1718 | |||
1719 | shutdown(config->accepted_socket, SHUT_RD); | ||
1720 | /* In inetd case, we close fd 1 (stdout) here. We will exit soon anyway */ | ||
1721 | close(config->accepted_socket); | ||
1722 | } | ||
1723 | |||
1724 | /**************************************************************************** | ||
1725 | * | ||
1726 | > $Function: miniHttpd() | ||
1727 | * | ||
1728 | * $Description: The main http server function. | ||
1729 | * | ||
1730 | * Given an open socket fildes, listen for new connections and farm out | ||
1731 | * the processing as a forked process. | ||
1732 | * | ||
1733 | * $Parameters: | ||
1734 | * (int) server. . . The server socket fildes. | ||
1735 | * | ||
1736 | * $Return: (int) . . . . Always 0. | ||
1737 | * | ||
1738 | ****************************************************************************/ | ||
1739 | static int miniHttpd(int server) | ||
1740 | { | ||
1741 | fd_set readfd, portfd; | ||
1742 | |||
1743 | FD_ZERO(&portfd); | ||
1744 | FD_SET(server, &portfd); | ||
1745 | |||
1746 | /* copy the ports we are watching to the readfd set */ | ||
1747 | while (1) { | ||
1748 | int on, s; | ||
1749 | socklen_t fromAddrLen; | ||
1750 | struct sockaddr_in fromAddr; | ||
1751 | |||
1752 | /* Now wait INDEFINITELY on the set of sockets! */ | ||
1753 | readfd = portfd; | ||
1754 | if (select(server + 1, &readfd, 0, 0, 0) <= 0) | ||
1755 | continue; | ||
1756 | if (!FD_ISSET(server, &readfd)) | ||
1757 | continue; | ||
1758 | fromAddrLen = sizeof(fromAddr); | ||
1759 | s = accept(server, (struct sockaddr *)&fromAddr, &fromAddrLen); | ||
1760 | if (s < 0) | ||
1761 | continue; | ||
1762 | config->accepted_socket = s; | ||
1763 | config->rmt_ip = ntohl(fromAddr.sin_addr.s_addr); | ||
1764 | #if ENABLE_FEATURE_HTTPD_CGI || DEBUG | ||
1765 | sprintf(config->rmt_ip_str, "%u.%u.%u.%u", | ||
1766 | (unsigned char)(config->rmt_ip >> 24), | ||
1767 | (unsigned char)(config->rmt_ip >> 16), | ||
1768 | (unsigned char)(config->rmt_ip >> 8), | ||
1769 | config->rmt_ip & 0xff); | ||
1770 | config->port = ntohs(fromAddr.sin_port); | ||
1771 | #if DEBUG | ||
1772 | bb_error_msg("connection from IP=%s, port %u", | ||
1773 | config->rmt_ip_str, config->port); | ||
1774 | #endif | ||
1775 | #endif /* FEATURE_HTTPD_CGI */ | ||
1776 | |||
1777 | /* set the KEEPALIVE option to cull dead connections */ | ||
1778 | on = 1; | ||
1779 | setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, (void *)&on, sizeof(on)); | ||
1780 | |||
1781 | if (DEBUG || fork() == 0) { | ||
1782 | /* child */ | ||
1783 | #if ENABLE_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP | ||
1784 | /* protect reload config, may be confuse checking */ | ||
1785 | signal(SIGHUP, SIG_IGN); | ||
1786 | #endif | ||
1787 | handleIncoming(); | ||
1788 | if (!DEBUG) | ||
1789 | exit(0); | ||
1790 | } | ||
1791 | close(s); | ||
1792 | } /* while (1) */ | ||
1793 | return 0; | ||
1794 | } | ||
1795 | |||
1796 | /* from inetd */ | ||
1797 | static int miniHttpd_inetd(void) | ||
1798 | { | ||
1799 | struct sockaddr_in fromAddrLen; | ||
1800 | socklen_t sinlen = sizeof(struct sockaddr_in); | ||
1801 | |||
1802 | getpeername(0, (struct sockaddr *)&fromAddrLen, &sinlen); | ||
1803 | config->rmt_ip = ntohl(fromAddrLen.sin_addr.s_addr); | ||
1804 | #if ENABLE_FEATURE_HTTPD_CGI | ||
1805 | sprintf(config->rmt_ip_str, "%u.%u.%u.%u", | ||
1806 | (unsigned char)(config->rmt_ip >> 24), | ||
1807 | (unsigned char)(config->rmt_ip >> 16), | ||
1808 | (unsigned char)(config->rmt_ip >> 8), | ||
1809 | config->rmt_ip & 0xff); | ||
1810 | #endif | ||
1811 | config->port = ntohs(fromAddrLen.sin_port); | ||
1812 | handleIncoming(); | ||
1813 | return 0; | ||
1814 | } | ||
1815 | |||
1816 | #if ENABLE_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP | ||
1817 | static void sighup_handler(int sig) | ||
1818 | { | ||
1819 | /* set and reset */ | ||
1820 | struct sigaction sa; | ||
1821 | |||
1822 | parse_conf(default_path_httpd_conf, sig == SIGHUP ? SIGNALED_PARSE : FIRST_PARSE); | ||
1823 | sa.sa_handler = sighup_handler; | ||
1824 | sigemptyset(&sa.sa_mask); | ||
1825 | sa.sa_flags = SA_RESTART; | ||
1826 | sigaction(SIGHUP, &sa, NULL); | ||
1827 | } | ||
1828 | #endif | ||
1829 | |||
1830 | enum { | ||
1831 | c_opt_config_file = 0, | ||
1832 | d_opt_decode_url, | ||
1833 | h_opt_home_httpd, | ||
1834 | USE_FEATURE_HTTPD_ENCODE_URL_STR(e_opt_encode_url,) | ||
1835 | USE_FEATURE_HTTPD_BASIC_AUTH( r_opt_realm ,) | ||
1836 | USE_FEATURE_HTTPD_AUTH_MD5( m_opt_md5 ,) | ||
1837 | USE_FEATURE_HTTPD_SETUID( u_opt_setuid ,) | ||
1838 | p_opt_port , | ||
1839 | p_opt_inetd , | ||
1840 | p_opt_foreground, | ||
1841 | OPT_CONFIG_FILE = 1 << c_opt_config_file, | ||
1842 | OPT_DECODE_URL = 1 << d_opt_decode_url, | ||
1843 | OPT_HOME_HTTPD = 1 << h_opt_home_httpd, | ||
1844 | OPT_ENCODE_URL = USE_FEATURE_HTTPD_ENCODE_URL_STR((1 << e_opt_encode_url)) + 0, | ||
1845 | OPT_REALM = USE_FEATURE_HTTPD_BASIC_AUTH( (1 << r_opt_realm )) + 0, | ||
1846 | OPT_MD5 = USE_FEATURE_HTTPD_AUTH_MD5( (1 << m_opt_md5 )) + 0, | ||
1847 | OPT_SETUID = USE_FEATURE_HTTPD_SETUID( (1 << u_opt_setuid )) + 0, | ||
1848 | OPT_PORT = 1 << p_opt_port, | ||
1849 | OPT_INETD = 1 << p_opt_inetd, | ||
1850 | OPT_FOREGROUND = 1 << p_opt_foreground, | ||
1851 | }; | ||
1852 | |||
1853 | static const char httpd_opts[] = "c:d:h:" | ||
1854 | USE_FEATURE_HTTPD_ENCODE_URL_STR("e:") | ||
1855 | USE_FEATURE_HTTPD_BASIC_AUTH("r:") | ||
1856 | USE_FEATURE_HTTPD_AUTH_MD5("m:") | ||
1857 | USE_FEATURE_HTTPD_SETUID("u:") | ||
1858 | "p:if"; | ||
1859 | |||
1860 | |||
1861 | int httpd_main(int argc, char *argv[]) | ||
1862 | { | ||
1863 | unsigned opt; | ||
1864 | const char *home_httpd = home; | ||
1865 | char *url_for_decode; | ||
1866 | USE_FEATURE_HTTPD_ENCODE_URL_STR(const char *url_for_encode;) | ||
1867 | const char *s_port; | ||
1868 | USE_FEATURE_HTTPD_SETUID(const char *s_ugid = NULL;) | ||
1869 | USE_FEATURE_HTTPD_SETUID(struct bb_uidgid_t ugid;) | ||
1870 | USE_FEATURE_HTTPD_AUTH_MD5(const char *pass;) | ||
1871 | |||
1872 | #if ENABLE_LOCALE_SUPPORT | ||
1873 | /* Undo busybox.c: we want to speak English in http (dates etc) */ | ||
1874 | setlocale(LC_TIME, "C"); | ||
1875 | #endif | ||
1876 | |||
1877 | config = xzalloc(sizeof(*config)); | ||
1878 | #if ENABLE_FEATURE_HTTPD_BASIC_AUTH | ||
1879 | config->realm = "Web Server Authentication"; | ||
1880 | #endif | ||
1881 | config->port = 80; | ||
1882 | config->ContentLength = -1; | ||
1883 | |||
1884 | opt = getopt32(argc, argv, httpd_opts, | ||
1885 | &(config->configFile), &url_for_decode, &home_httpd | ||
1886 | USE_FEATURE_HTTPD_ENCODE_URL_STR(, &url_for_encode) | ||
1887 | USE_FEATURE_HTTPD_BASIC_AUTH(, &(config->realm)) | ||
1888 | USE_FEATURE_HTTPD_AUTH_MD5(, &pass) | ||
1889 | USE_FEATURE_HTTPD_SETUID(, &s_ugid) | ||
1890 | , &s_port | ||
1891 | ); | ||
1892 | if (opt & OPT_DECODE_URL) { | ||
1893 | printf("%s", decodeString(url_for_decode, 1)); | ||
1894 | return 0; | ||
1895 | } | ||
1896 | #if ENABLE_FEATURE_HTTPD_ENCODE_URL_STR | ||
1897 | if (opt & OPT_ENCODE_URL) { | ||
1898 | printf("%s", encodeString(url_for_encode)); | ||
1899 | return 0; | ||
1900 | } | ||
1901 | #endif | ||
1902 | #if ENABLE_FEATURE_HTTPD_AUTH_MD5 | ||
1903 | if (opt & OPT_MD5) { | ||
1904 | puts(pw_encrypt(pass, "$1$")); | ||
1905 | return 0; | ||
1906 | } | ||
1907 | #endif | ||
1908 | if (opt & OPT_PORT) | ||
1909 | config->port = xatou16(s_port); | ||
1910 | |||
1911 | #if ENABLE_FEATURE_HTTPD_SETUID | ||
1912 | if (opt & OPT_SETUID) { | ||
1913 | char *e; | ||
1914 | // FIXME: what the default group should be? | ||
1915 | ugid.gid = -1; | ||
1916 | ugid.uid = bb_strtoul(s_ugid, &e, 0); | ||
1917 | if (*e == ':') { | ||
1918 | e++; | ||
1919 | ugid.gid = bb_strtoul(e, NULL, 0); | ||
1920 | } | ||
1921 | if (errno) { | ||
1922 | /* not integer */ | ||
1923 | if (!uidgid_get(&ugid, s_ugid)) | ||
1924 | bb_error_msg_and_die("unrecognized user[:group] " | ||
1925 | "name '%s'", s_ugid); | ||
1926 | } | ||
1927 | } | ||
1928 | #endif | ||
1929 | |||
1930 | xchdir(home_httpd); | ||
1931 | if (!(opt & OPT_INETD)) { | ||
1932 | config->server_socket = openServer(); | ||
1933 | #if ENABLE_FEATURE_HTTPD_SETUID | ||
1934 | /* drop privileges */ | ||
1935 | if (opt & OPT_SETUID) { | ||
1936 | if (ugid.gid != (gid_t)-1) { | ||
1937 | if (setgroups(1, &ugid.gid) == -1) | ||
1938 | bb_perror_msg_and_die("setgroups"); | ||
1939 | xsetgid(ugid.gid); | ||
1940 | } | ||
1941 | xsetuid(ugid.uid); | ||
1942 | } | ||
1943 | #endif | ||
1944 | } | ||
1945 | |||
1946 | #if ENABLE_FEATURE_HTTPD_CGI | ||
1947 | { | ||
1948 | char *p = getenv("PATH"); | ||
1949 | p = xstrdup(p); /* if gets NULL, returns NULL */ | ||
1950 | clearenv(); | ||
1951 | if (p) | ||
1952 | setenv1("PATH", p); | ||
1953 | if (!(opt & OPT_INETD)) | ||
1954 | setenv_long("SERVER_PORT", config->port); | ||
1955 | } | ||
1956 | #endif | ||
1957 | |||
1958 | #if ENABLE_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP | ||
1959 | sighup_handler(0); | ||
1960 | #else | ||
1961 | parse_conf(default_path_httpd_conf, FIRST_PARSE); | ||
1962 | #endif | ||
1963 | |||
1964 | if (opt & OPT_INETD) | ||
1965 | return miniHttpd_inetd(); | ||
1966 | |||
1967 | if (!(opt & OPT_FOREGROUND)) | ||
1968 | xdaemon(1, 0); /* don't change current directory */ | ||
1969 | return miniHttpd(config->server_socket); | ||
1970 | } | ||
diff --git a/networking/httpd_index_cgi_example b/networking/httpd_index_cgi_example new file mode 100644 index 000000000..31e768c70 --- /dev/null +++ b/networking/httpd_index_cgi_example | |||
@@ -0,0 +1,55 @@ | |||
1 | #!/bin/sh | ||
2 | # This CGI creates directory index. | ||
3 | # Put it into cgi-bin/index.cgi and chmod 0755. | ||
4 | # | ||
5 | # Problems: | ||
6 | # * Unsafe wrt weird filenames with <>"'& etc... | ||
7 | # * Not efficient: calls stat (program, not syscall) for each file | ||
8 | # * Probably requires bash | ||
9 | # | ||
10 | # If you want speed and safety, you need to code it in C | ||
11 | |||
12 | # Must start with '/' | ||
13 | test "${QUERY_STRING:0:1}" = "/" || exit 1 | ||
14 | # /../ is not allowed | ||
15 | test "${QUERY_STRING%/../*}" = "$QUERY_STRING" || exit 1 | ||
16 | test "${QUERY_STRING%/..}" = "$QUERY_STRING" || exit 1 | ||
17 | |||
18 | # Outta cgi-bin... | ||
19 | cd .. 2>/dev/null || exit 1 | ||
20 | # Strip leading '/', go to target dir | ||
21 | cd "${QUERY_STRING:1}" 2>/dev/null || exit 1 | ||
22 | |||
23 | f=`dirname "$QUERY_STRING"` | ||
24 | test "$f" = "/" && f="" | ||
25 | |||
26 | # Pipe thru dd (need to write header as single write(), | ||
27 | # or else httpd doesn't see "Content-type: text/html" | ||
28 | # in first read() and decides that it is not html) | ||
29 | { | ||
30 | printf "%s" \ | ||
31 | $'HTTP/1.0 200 OK\r\n'\ | ||
32 | $'Content-type: text/html\r\n\r\n'\ | ||
33 | "<html><head><title>Index of $QUERY_STRING</title></head>"$'\r\n'\ | ||
34 | "<body><h1>Index of $QUERY_STRING</h1><pre>"$'\r\n'\ | ||
35 | $'<table width=100%>\r\n'\ | ||
36 | $'<col><col><col width=0*>\r\n'\ | ||
37 | $'<tr><th>Name<th align=right>Last modified<th align=right>Size\r\n'\ | ||
38 | \ | ||
39 | "<tr><td><a href='$f/'>..</a><td><td>"$'\r\n' | ||
40 | |||
41 | IFS='#' | ||
42 | for f in *; do | ||
43 | # Guard against empty dirs... | ||
44 | test -e "$f" && \ | ||
45 | stat -c "%F#%s#%z" "$f" | { | ||
46 | read type size cdt junk | ||
47 | dir='' | ||
48 | test "$type" = "directory" && dir='/' | ||
49 | cdt="${cdt//.*}" # no fractional seconds | ||
50 | cdt="${cdt// / }" # prevent wrapping around space | ||
51 | printf "%s" "<tr><td><a href='$f$dir'>$f</a><td align=right>$cdt<td align=right>$size"$'\r\n' | ||
52 | } | ||
53 | done | ||
54 | printf "</table></pre><hr></body></html>"$'\r\n' | ||
55 | } | dd bs=4k | ||
diff --git a/networking/ifconfig.c b/networking/ifconfig.c new file mode 100644 index 000000000..ae5b468ce --- /dev/null +++ b/networking/ifconfig.c | |||
@@ -0,0 +1,548 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* ifconfig | ||
3 | * | ||
4 | * Similar to the standard Unix ifconfig, but with only the necessary | ||
5 | * parts for AF_INET, and without any printing of if info (for now). | ||
6 | * | ||
7 | * Bjorn Wesen, Axis Communications AB | ||
8 | * | ||
9 | * | ||
10 | * Authors of the original ifconfig was: | ||
11 | * Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org> | ||
12 | * | ||
13 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | ||
14 | */ | ||
15 | |||
16 | /* | ||
17 | * Heavily modified by Manuel Novoa III Mar 6, 2001 | ||
18 | * | ||
19 | * From initial port to busybox, removed most of the redundancy by | ||
20 | * converting to a table-driven approach. Added several (optional) | ||
21 | * args missing from initial port. | ||
22 | * | ||
23 | * Still missing: media, tunnel. | ||
24 | * | ||
25 | * 2002-04-20 | ||
26 | * IPV6 support added by Bart Visscher <magick@linux-fan.com> | ||
27 | */ | ||
28 | |||
29 | #include <net/if.h> | ||
30 | #include <net/if_arp.h> | ||
31 | #include <netinet/in.h> | ||
32 | #if __GLIBC__ >=2 && __GLIBC_MINOR__ >= 1 | ||
33 | #include <netpacket/packet.h> | ||
34 | #include <net/ethernet.h> | ||
35 | #else | ||
36 | #include <sys/types.h> | ||
37 | #include <netinet/if_ether.h> | ||
38 | #endif | ||
39 | #include "inet_common.h" | ||
40 | #include "busybox.h" | ||
41 | |||
42 | #if ENABLE_FEATURE_IFCONFIG_SLIP | ||
43 | # include <net/if_slip.h> | ||
44 | #endif | ||
45 | |||
46 | /* I don't know if this is needed for busybox or not. Anyone? */ | ||
47 | #define QUESTIONABLE_ALIAS_CASE | ||
48 | |||
49 | |||
50 | /* Defines for glibc2.0 users. */ | ||
51 | #ifndef SIOCSIFTXQLEN | ||
52 | # define SIOCSIFTXQLEN 0x8943 | ||
53 | # define SIOCGIFTXQLEN 0x8942 | ||
54 | #endif | ||
55 | |||
56 | /* ifr_qlen is ifru_ivalue, but it isn't present in 2.0 kernel headers */ | ||
57 | #ifndef ifr_qlen | ||
58 | # define ifr_qlen ifr_ifru.ifru_mtu | ||
59 | #endif | ||
60 | |||
61 | #ifndef IFF_DYNAMIC | ||
62 | # define IFF_DYNAMIC 0x8000 /* dialup device with changing addresses */ | ||
63 | #endif | ||
64 | |||
65 | #if ENABLE_FEATURE_IPV6 | ||
66 | struct in6_ifreq { | ||
67 | struct in6_addr ifr6_addr; | ||
68 | uint32_t ifr6_prefixlen; | ||
69 | int ifr6_ifindex; | ||
70 | }; | ||
71 | #endif | ||
72 | |||
73 | /* | ||
74 | * Here are the bit masks for the "flags" member of struct options below. | ||
75 | * N_ signifies no arg prefix; M_ signifies arg prefixed by '-'. | ||
76 | * CLR clears the flag; SET sets the flag; ARG signifies (optional) arg. | ||
77 | */ | ||
78 | #define N_CLR 0x01 | ||
79 | #define M_CLR 0x02 | ||
80 | #define N_SET 0x04 | ||
81 | #define M_SET 0x08 | ||
82 | #define N_ARG 0x10 | ||
83 | #define M_ARG 0x20 | ||
84 | |||
85 | #define M_MASK (M_CLR | M_SET | M_ARG) | ||
86 | #define N_MASK (N_CLR | N_SET | N_ARG) | ||
87 | #define SET_MASK (N_SET | M_SET) | ||
88 | #define CLR_MASK (N_CLR | M_CLR) | ||
89 | #define SET_CLR_MASK (SET_MASK | CLR_MASK) | ||
90 | #define ARG_MASK (M_ARG | N_ARG) | ||
91 | |||
92 | /* | ||
93 | * Here are the bit masks for the "arg_flags" member of struct options below. | ||
94 | */ | ||
95 | |||
96 | /* | ||
97 | * cast type: | ||
98 | * 00 int | ||
99 | * 01 char * | ||
100 | * 02 HOST_COPY in_ether | ||
101 | * 03 HOST_COPY INET_resolve | ||
102 | */ | ||
103 | #define A_CAST_TYPE 0x03 | ||
104 | /* | ||
105 | * map type: | ||
106 | * 00 not a map type (mem_start, io_addr, irq) | ||
107 | * 04 memstart (unsigned long) | ||
108 | * 08 io_addr (unsigned short) | ||
109 | * 0C irq (unsigned char) | ||
110 | */ | ||
111 | #define A_MAP_TYPE 0x0C | ||
112 | #define A_ARG_REQ 0x10 /* Set if an arg is required. */ | ||
113 | #define A_NETMASK 0x20 /* Set if netmask (check for multiple sets). */ | ||
114 | #define A_SET_AFTER 0x40 /* Set a flag at the end. */ | ||
115 | #define A_COLON_CHK 0x80 /* Is this needed? See below. */ | ||
116 | #if ENABLE_FEATURE_IFCONFIG_BROADCAST_PLUS | ||
117 | #define A_HOSTNAME 0x100 /* Set if it is ip addr. */ | ||
118 | #define A_BROADCAST 0x200 /* Set if it is broadcast addr. */ | ||
119 | #else | ||
120 | #define A_HOSTNAME 0 | ||
121 | #define A_BROADCAST 0 | ||
122 | #endif | ||
123 | |||
124 | /* | ||
125 | * These defines are for dealing with the A_CAST_TYPE field. | ||
126 | */ | ||
127 | #define A_CAST_CHAR_PTR 0x01 | ||
128 | #define A_CAST_RESOLVE 0x01 | ||
129 | #define A_CAST_HOST_COPY 0x02 | ||
130 | #define A_CAST_HOST_COPY_IN_ETHER A_CAST_HOST_COPY | ||
131 | #define A_CAST_HOST_COPY_RESOLVE (A_CAST_HOST_COPY | A_CAST_RESOLVE) | ||
132 | |||
133 | /* | ||
134 | * These defines are for dealing with the A_MAP_TYPE field. | ||
135 | */ | ||
136 | #define A_MAP_ULONG 0x04 /* memstart */ | ||
137 | #define A_MAP_USHORT 0x08 /* io_addr */ | ||
138 | #define A_MAP_UCHAR 0x0C /* irq */ | ||
139 | |||
140 | /* | ||
141 | * Define the bit masks signifying which operations to perform for each arg. | ||
142 | */ | ||
143 | |||
144 | #define ARG_METRIC (A_ARG_REQ /*| A_CAST_INT*/) | ||
145 | #define ARG_MTU (A_ARG_REQ /*| A_CAST_INT*/) | ||
146 | #define ARG_TXQUEUELEN (A_ARG_REQ /*| A_CAST_INT*/) | ||
147 | #define ARG_MEM_START (A_ARG_REQ | A_MAP_ULONG) | ||
148 | #define ARG_IO_ADDR (A_ARG_REQ | A_MAP_ULONG) | ||
149 | #define ARG_IRQ (A_ARG_REQ | A_MAP_UCHAR) | ||
150 | #define ARG_DSTADDR (A_ARG_REQ | A_CAST_HOST_COPY_RESOLVE) | ||
151 | #define ARG_NETMASK (A_ARG_REQ | A_CAST_HOST_COPY_RESOLVE | A_NETMASK) | ||
152 | #define ARG_BROADCAST (A_ARG_REQ | A_CAST_HOST_COPY_RESOLVE | A_SET_AFTER | A_BROADCAST) | ||
153 | #define ARG_HW (A_ARG_REQ | A_CAST_HOST_COPY_IN_ETHER) | ||
154 | #define ARG_POINTOPOINT (A_ARG_REQ | A_CAST_HOST_COPY_RESOLVE | A_SET_AFTER) | ||
155 | #define ARG_KEEPALIVE (A_ARG_REQ | A_CAST_CHAR_PTR) | ||
156 | #define ARG_OUTFILL (A_ARG_REQ | A_CAST_CHAR_PTR) | ||
157 | #define ARG_HOSTNAME (A_CAST_HOST_COPY_RESOLVE | A_SET_AFTER | A_COLON_CHK | A_HOSTNAME) | ||
158 | #define ARG_ADD_DEL (A_CAST_HOST_COPY_RESOLVE | A_SET_AFTER) | ||
159 | |||
160 | |||
161 | /* | ||
162 | * Set up the tables. Warning! They must have corresponding order! | ||
163 | */ | ||
164 | |||
165 | struct arg1opt { | ||
166 | const char *name; | ||
167 | int selector; | ||
168 | unsigned short ifr_offset; | ||
169 | }; | ||
170 | |||
171 | struct options { | ||
172 | const char *name; | ||
173 | #if ENABLE_FEATURE_IFCONFIG_BROADCAST_PLUS | ||
174 | const unsigned int flags:6; | ||
175 | const unsigned int arg_flags:10; | ||
176 | #else | ||
177 | const unsigned char flags; | ||
178 | const unsigned char arg_flags; | ||
179 | #endif | ||
180 | const unsigned short selector; | ||
181 | }; | ||
182 | |||
183 | #define ifreq_offsetof(x) offsetof(struct ifreq, x) | ||
184 | |||
185 | static const struct arg1opt Arg1Opt[] = { | ||
186 | {"SIOCSIFMETRIC", SIOCSIFMETRIC, ifreq_offsetof(ifr_metric)}, | ||
187 | {"SIOCSIFMTU", SIOCSIFMTU, ifreq_offsetof(ifr_mtu)}, | ||
188 | {"SIOCSIFTXQLEN", SIOCSIFTXQLEN, ifreq_offsetof(ifr_qlen)}, | ||
189 | {"SIOCSIFDSTADDR", SIOCSIFDSTADDR, ifreq_offsetof(ifr_dstaddr)}, | ||
190 | {"SIOCSIFNETMASK", SIOCSIFNETMASK, ifreq_offsetof(ifr_netmask)}, | ||
191 | {"SIOCSIFBRDADDR", SIOCSIFBRDADDR, ifreq_offsetof(ifr_broadaddr)}, | ||
192 | #if ENABLE_FEATURE_IFCONFIG_HW | ||
193 | {"SIOCSIFHWADDR", SIOCSIFHWADDR, ifreq_offsetof(ifr_hwaddr)}, | ||
194 | #endif | ||
195 | {"SIOCSIFDSTADDR", SIOCSIFDSTADDR, ifreq_offsetof(ifr_dstaddr)}, | ||
196 | #ifdef SIOCSKEEPALIVE | ||
197 | {"SIOCSKEEPALIVE", SIOCSKEEPALIVE, ifreq_offsetof(ifr_data)}, | ||
198 | #endif | ||
199 | #ifdef SIOCSOUTFILL | ||
200 | {"SIOCSOUTFILL", SIOCSOUTFILL, ifreq_offsetof(ifr_data)}, | ||
201 | #endif | ||
202 | #if ENABLE_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ | ||
203 | {"SIOCSIFMAP", SIOCSIFMAP, ifreq_offsetof(ifr_map.mem_start)}, | ||
204 | {"SIOCSIFMAP", SIOCSIFMAP, ifreq_offsetof(ifr_map.base_addr)}, | ||
205 | {"SIOCSIFMAP", SIOCSIFMAP, ifreq_offsetof(ifr_map.irq)}, | ||
206 | #endif | ||
207 | /* Last entry if for unmatched (possibly hostname) arg. */ | ||
208 | #if ENABLE_FEATURE_IPV6 | ||
209 | {"SIOCSIFADDR", SIOCSIFADDR, ifreq_offsetof(ifr_addr)}, /* IPv6 version ignores the offset */ | ||
210 | {"SIOCDIFADDR", SIOCDIFADDR, ifreq_offsetof(ifr_addr)}, /* IPv6 version ignores the offset */ | ||
211 | #endif | ||
212 | {"SIOCSIFADDR", SIOCSIFADDR, ifreq_offsetof(ifr_addr)}, | ||
213 | }; | ||
214 | |||
215 | static const struct options OptArray[] = { | ||
216 | {"metric", N_ARG, ARG_METRIC, 0}, | ||
217 | {"mtu", N_ARG, ARG_MTU, 0}, | ||
218 | {"txqueuelen", N_ARG, ARG_TXQUEUELEN, 0}, | ||
219 | {"dstaddr", N_ARG, ARG_DSTADDR, 0}, | ||
220 | {"netmask", N_ARG, ARG_NETMASK, 0}, | ||
221 | {"broadcast", N_ARG | M_CLR, ARG_BROADCAST, IFF_BROADCAST}, | ||
222 | #if ENABLE_FEATURE_IFCONFIG_HW | ||
223 | {"hw", N_ARG, ARG_HW, 0}, | ||
224 | #endif | ||
225 | {"pointopoint", N_ARG | M_CLR, ARG_POINTOPOINT, IFF_POINTOPOINT}, | ||
226 | #ifdef SIOCSKEEPALIVE | ||
227 | {"keepalive", N_ARG, ARG_KEEPALIVE, 0}, | ||
228 | #endif | ||
229 | #ifdef SIOCSOUTFILL | ||
230 | {"outfill", N_ARG, ARG_OUTFILL, 0}, | ||
231 | #endif | ||
232 | #if ENABLE_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ | ||
233 | {"mem_start", N_ARG, ARG_MEM_START, 0}, | ||
234 | {"io_addr", N_ARG, ARG_IO_ADDR, 0}, | ||
235 | {"irq", N_ARG, ARG_IRQ, 0}, | ||
236 | #endif | ||
237 | #if ENABLE_FEATURE_IPV6 | ||
238 | {"add", N_ARG, ARG_ADD_DEL, 0}, | ||
239 | {"del", N_ARG, ARG_ADD_DEL, 0}, | ||
240 | #endif | ||
241 | {"arp", N_CLR | M_SET, 0, IFF_NOARP}, | ||
242 | {"trailers", N_CLR | M_SET, 0, IFF_NOTRAILERS}, | ||
243 | {"promisc", N_SET | M_CLR, 0, IFF_PROMISC}, | ||
244 | {"multicast", N_SET | M_CLR, 0, IFF_MULTICAST}, | ||
245 | {"allmulti", N_SET | M_CLR, 0, IFF_ALLMULTI}, | ||
246 | {"dynamic", N_SET | M_CLR, 0, IFF_DYNAMIC}, | ||
247 | {"up", N_SET, 0, (IFF_UP | IFF_RUNNING)}, | ||
248 | {"down", N_CLR, 0, IFF_UP}, | ||
249 | {NULL, 0, ARG_HOSTNAME, (IFF_UP | IFF_RUNNING)} | ||
250 | }; | ||
251 | |||
252 | /* | ||
253 | * A couple of prototypes. | ||
254 | */ | ||
255 | |||
256 | #if ENABLE_FEATURE_IFCONFIG_HW | ||
257 | static int in_ether(const char *bufp, struct sockaddr *sap); | ||
258 | #endif | ||
259 | |||
260 | #if ENABLE_FEATURE_IFCONFIG_STATUS | ||
261 | extern int interface_opt_a; | ||
262 | extern int display_interfaces(char *ifname); | ||
263 | #endif | ||
264 | |||
265 | /* | ||
266 | * Our main function. | ||
267 | */ | ||
268 | |||
269 | int ifconfig_main(int argc, char **argv) | ||
270 | { | ||
271 | struct ifreq ifr; | ||
272 | struct sockaddr_in sai; | ||
273 | #if ENABLE_FEATURE_IPV6 | ||
274 | struct sockaddr_in6 sai6; | ||
275 | #endif | ||
276 | #if ENABLE_FEATURE_IFCONFIG_HW | ||
277 | struct sockaddr sa; | ||
278 | #endif | ||
279 | const struct arg1opt *a1op; | ||
280 | const struct options *op; | ||
281 | int sockfd; /* socket fd we use to manipulate stuff with */ | ||
282 | int selector; | ||
283 | #if ENABLE_FEATURE_IFCONFIG_BROADCAST_PLUS | ||
284 | unsigned int mask; | ||
285 | unsigned int did_flags; | ||
286 | unsigned int sai_hostname, sai_netmask; | ||
287 | #else | ||
288 | unsigned char mask; | ||
289 | unsigned char did_flags; | ||
290 | #endif | ||
291 | char *p; | ||
292 | /*char host[128];*/ | ||
293 | const char *host = NULL; /* make gcc happy */ | ||
294 | |||
295 | did_flags = 0; | ||
296 | #if ENABLE_FEATURE_IFCONFIG_BROADCAST_PLUS | ||
297 | sai_hostname = 0; | ||
298 | sai_netmask = 0; | ||
299 | #endif | ||
300 | |||
301 | /* skip argv[0] */ | ||
302 | ++argv; | ||
303 | --argc; | ||
304 | |||
305 | #if ENABLE_FEATURE_IFCONFIG_STATUS | ||
306 | if (argc > 0 && (argv[0][0] == '-' && argv[0][1] == 'a' && !argv[0][2])) { | ||
307 | interface_opt_a = 1; | ||
308 | --argc; | ||
309 | ++argv; | ||
310 | } | ||
311 | #endif | ||
312 | |||
313 | if (argc <= 1) { | ||
314 | #if ENABLE_FEATURE_IFCONFIG_STATUS | ||
315 | return display_interfaces(argc ? *argv : NULL); | ||
316 | #else | ||
317 | bb_error_msg_and_die("no support for status display"); | ||
318 | #endif | ||
319 | } | ||
320 | |||
321 | /* Create a channel to the NET kernel. */ | ||
322 | sockfd = xsocket(AF_INET, SOCK_DGRAM, 0); | ||
323 | |||
324 | /* get interface name */ | ||
325 | safe_strncpy(ifr.ifr_name, *argv, IFNAMSIZ); | ||
326 | |||
327 | /* Process the remaining arguments. */ | ||
328 | while (*++argv != (char *) NULL) { | ||
329 | p = *argv; | ||
330 | mask = N_MASK; | ||
331 | if (*p == '-') { /* If the arg starts with '-'... */ | ||
332 | ++p; /* advance past it and */ | ||
333 | mask = M_MASK; /* set the appropriate mask. */ | ||
334 | } | ||
335 | for (op = OptArray; op->name; op++) { /* Find table entry. */ | ||
336 | if (strcmp(p, op->name) == 0) { /* If name matches... */ | ||
337 | mask &= op->flags; | ||
338 | if (mask) /* set the mask and go. */ | ||
339 | goto FOUND_ARG; | ||
340 | /* If we get here, there was a valid arg with an */ | ||
341 | /* invalid '-' prefix. */ | ||
342 | bb_error_msg_and_die("bad: '%s'", p-1); | ||
343 | } | ||
344 | } | ||
345 | |||
346 | /* We fell through, so treat as possible hostname. */ | ||
347 | a1op = Arg1Opt + (sizeof(Arg1Opt) / sizeof(Arg1Opt[0])) - 1; | ||
348 | mask = op->arg_flags; | ||
349 | goto HOSTNAME; | ||
350 | |||
351 | FOUND_ARG: | ||
352 | if (mask & ARG_MASK) { | ||
353 | mask = op->arg_flags; | ||
354 | a1op = Arg1Opt + (op - OptArray); | ||
355 | if (mask & A_NETMASK & did_flags) | ||
356 | bb_show_usage(); | ||
357 | if (*++argv == NULL) { | ||
358 | if (mask & A_ARG_REQ) | ||
359 | bb_show_usage(); | ||
360 | --argv; | ||
361 | mask &= A_SET_AFTER; /* just for broadcast */ | ||
362 | } else { /* got an arg so process it */ | ||
363 | HOSTNAME: | ||
364 | did_flags |= (mask & (A_NETMASK|A_HOSTNAME)); | ||
365 | if (mask & A_CAST_HOST_COPY) { | ||
366 | #if ENABLE_FEATURE_IFCONFIG_HW | ||
367 | if (mask & A_CAST_RESOLVE) { | ||
368 | #endif | ||
369 | #if ENABLE_FEATURE_IPV6 | ||
370 | char *prefix; | ||
371 | int prefix_len = 0; | ||
372 | #endif | ||
373 | /*safe_strncpy(host, *argv, (sizeof host));*/ | ||
374 | host = *argv; | ||
375 | #if ENABLE_FEATURE_IPV6 | ||
376 | prefix = strchr(host, '/'); | ||
377 | if (prefix) { | ||
378 | prefix_len = xatou_range(prefix + 1, 0, 128); | ||
379 | *prefix = '\0'; | ||
380 | } | ||
381 | #endif | ||
382 | sai.sin_family = AF_INET; | ||
383 | sai.sin_port = 0; | ||
384 | if (!strcmp(host, bb_str_default)) { | ||
385 | /* Default is special, meaning 0.0.0.0. */ | ||
386 | sai.sin_addr.s_addr = INADDR_ANY; | ||
387 | } | ||
388 | #if ENABLE_FEATURE_IFCONFIG_BROADCAST_PLUS | ||
389 | else if ((host[0] == '+' && !host[1]) && (mask & A_BROADCAST) | ||
390 | && (did_flags & (A_NETMASK|A_HOSTNAME)) == (A_NETMASK|A_HOSTNAME) | ||
391 | ) { | ||
392 | /* + is special, meaning broadcast is derived. */ | ||
393 | sai.sin_addr.s_addr = (~sai_netmask) | (sai_hostname & sai_netmask); | ||
394 | } | ||
395 | #endif | ||
396 | #if ENABLE_FEATURE_IPV6 | ||
397 | else if (inet_pton(AF_INET6, host, &sai6.sin6_addr) > 0) { | ||
398 | int sockfd6; | ||
399 | struct in6_ifreq ifr6; | ||
400 | |||
401 | memcpy((char *) &ifr6.ifr6_addr, | ||
402 | (char *) &sai6.sin6_addr, | ||
403 | sizeof(struct in6_addr)); | ||
404 | |||
405 | /* Create a channel to the NET kernel. */ | ||
406 | sockfd6 = xsocket(AF_INET6, SOCK_DGRAM, 0); | ||
407 | if (ioctl(sockfd6, SIOGIFINDEX, &ifr) < 0) | ||
408 | bb_perror_msg_and_die("SIOGIFINDEX"); | ||
409 | ifr6.ifr6_ifindex = ifr.ifr_ifindex; | ||
410 | ifr6.ifr6_prefixlen = prefix_len; | ||
411 | if (ioctl(sockfd6, a1op->selector, &ifr6) < 0) | ||
412 | bb_perror_msg_and_die(a1op->name); | ||
413 | continue; | ||
414 | } | ||
415 | #endif | ||
416 | else if (inet_aton(host, &sai.sin_addr) == 0) { | ||
417 | /* It's not a dotted quad. */ | ||
418 | struct hostent *hp = xgethostbyname(host); | ||
419 | memcpy((char *) &sai.sin_addr, (char *) hp->h_addr_list[0], | ||
420 | sizeof(struct in_addr)); | ||
421 | } | ||
422 | #if ENABLE_FEATURE_IFCONFIG_BROADCAST_PLUS | ||
423 | if (mask & A_HOSTNAME) | ||
424 | sai_hostname = sai.sin_addr.s_addr; | ||
425 | if (mask & A_NETMASK) | ||
426 | sai_netmask = sai.sin_addr.s_addr; | ||
427 | #endif | ||
428 | p = (char *) &sai; | ||
429 | #if ENABLE_FEATURE_IFCONFIG_HW | ||
430 | } else { /* A_CAST_HOST_COPY_IN_ETHER */ | ||
431 | /* This is the "hw" arg case. */ | ||
432 | if (strcmp("ether", *argv) || !*++argv) | ||
433 | bb_show_usage(); | ||
434 | /*safe_strncpy(host, *argv, sizeof(host));*/ | ||
435 | host = *argv; | ||
436 | if (in_ether(host, &sa)) | ||
437 | bb_error_msg_and_die("invalid hw-addr %s", host); | ||
438 | p = (char *) &sa; | ||
439 | } | ||
440 | #endif | ||
441 | memcpy( (((char *)&ifr) + a1op->ifr_offset), | ||
442 | p, sizeof(struct sockaddr)); | ||
443 | } else { | ||
444 | /* FIXME: error check?? */ | ||
445 | unsigned long i = strtoul(*argv, NULL, 0); | ||
446 | p = ((char *)&ifr) + a1op->ifr_offset; | ||
447 | #if ENABLE_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ | ||
448 | if (mask & A_MAP_TYPE) { | ||
449 | if (ioctl(sockfd, SIOCGIFMAP, &ifr) < 0) | ||
450 | bb_perror_msg_and_die("SIOCGIFMAP"); | ||
451 | if ((mask & A_MAP_UCHAR) == A_MAP_UCHAR) | ||
452 | *((unsigned char *) p) = i; | ||
453 | else if (mask & A_MAP_USHORT) | ||
454 | *((unsigned short *) p) = i; | ||
455 | else | ||
456 | *((unsigned long *) p) = i; | ||
457 | } else | ||
458 | #endif | ||
459 | if (mask & A_CAST_CHAR_PTR) | ||
460 | *((caddr_t *) p) = (caddr_t) i; | ||
461 | else /* A_CAST_INT */ | ||
462 | *((int *) p) = i; | ||
463 | } | ||
464 | |||
465 | if (ioctl(sockfd, a1op->selector, &ifr) < 0) | ||
466 | bb_perror_msg_and_die(a1op->name); | ||
467 | #ifdef QUESTIONABLE_ALIAS_CASE | ||
468 | if (mask & A_COLON_CHK) { | ||
469 | /* | ||
470 | * Don't do the set_flag() if the address is an alias with | ||
471 | * a '-' at the end, since it's deleted already! - Roman | ||
472 | * | ||
473 | * Should really use regex.h here, not sure though how well | ||
474 | * it'll go with the cross-platform support etc. | ||
475 | */ | ||
476 | char *ptr; | ||
477 | short int found_colon = 0; | ||
478 | for (ptr = ifr.ifr_name; *ptr; ptr++) | ||
479 | if (*ptr == ':') | ||
480 | found_colon++; | ||
481 | if (found_colon && ptr[-1] == '-') | ||
482 | continue; | ||
483 | } | ||
484 | #endif | ||
485 | } | ||
486 | if (!(mask & A_SET_AFTER)) | ||
487 | continue; | ||
488 | mask = N_SET; | ||
489 | } | ||
490 | |||
491 | if (ioctl(sockfd, SIOCGIFFLAGS, &ifr) < 0) | ||
492 | bb_perror_msg_and_die("SIOCGIFFLAGS"); | ||
493 | selector = op->selector; | ||
494 | if (mask & SET_MASK) | ||
495 | ifr.ifr_flags |= selector; | ||
496 | else | ||
497 | ifr.ifr_flags &= ~selector; | ||
498 | if (ioctl(sockfd, SIOCSIFFLAGS, &ifr) < 0) | ||
499 | bb_perror_msg_and_die("SIOCSIFFLAGS"); | ||
500 | } /* while() */ | ||
501 | |||
502 | if (ENABLE_FEATURE_CLEAN_UP) | ||
503 | close(sockfd); | ||
504 | return 0; | ||
505 | } | ||
506 | |||
507 | #if ENABLE_FEATURE_IFCONFIG_HW | ||
508 | /* Input an Ethernet address and convert to binary. */ | ||
509 | static int in_ether(const char *bufp, struct sockaddr *sap) | ||
510 | { | ||
511 | char *ptr; | ||
512 | int i, j; | ||
513 | unsigned char val; | ||
514 | unsigned char c; | ||
515 | |||
516 | sap->sa_family = ARPHRD_ETHER; | ||
517 | ptr = sap->sa_data; | ||
518 | |||
519 | i = 0; | ||
520 | do { | ||
521 | j = val = 0; | ||
522 | |||
523 | /* We might get a semicolon here - not required. */ | ||
524 | if (i && (*bufp == ':')) { | ||
525 | bufp++; | ||
526 | } | ||
527 | |||
528 | do { | ||
529 | c = *bufp; | ||
530 | if (((unsigned char)(c - '0')) <= 9) { | ||
531 | c -= '0'; | ||
532 | } else if (((unsigned char)((c|0x20) - 'a')) <= 5) { | ||
533 | c = (c|0x20) - ('a'-10); | ||
534 | } else if (j && (c == ':' || c == 0)) { | ||
535 | break; | ||
536 | } else { | ||
537 | return -1; | ||
538 | } | ||
539 | ++bufp; | ||
540 | val <<= 4; | ||
541 | val += c; | ||
542 | } while (++j < 2); | ||
543 | *ptr++ = val; | ||
544 | } while (++i < ETH_ALEN); | ||
545 | |||
546 | return *bufp; /* Error if we don't end at end of string. */ | ||
547 | } | ||
548 | #endif | ||
diff --git a/networking/ifupdown.c b/networking/ifupdown.c new file mode 100644 index 000000000..76ff2a830 --- /dev/null +++ b/networking/ifupdown.c | |||
@@ -0,0 +1,1270 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * ifupdown for busybox | ||
4 | * Copyright (c) 2002 Glenn McGrath <bug1@iinet.net.au> | ||
5 | * Copyright (c) 2003-2004 Erik Andersen <andersen@codepoet.org> | ||
6 | * | ||
7 | * Based on ifupdown v 0.6.4 by Anthony Towns | ||
8 | * Copyright (c) 1999 Anthony Towns <aj@azure.humbug.org.au> | ||
9 | * | ||
10 | * Changes to upstream version | ||
11 | * Remove checks for kernel version, assume kernel version 2.2.0 or better. | ||
12 | * Lines in the interfaces file cannot wrap. | ||
13 | * To adhere to the FHS, the default state file is /var/run/ifstate. | ||
14 | * | ||
15 | * Licensed under the GPL v2 or later, see the file LICENSE in this tarball. | ||
16 | */ | ||
17 | |||
18 | #include "busybox.h" | ||
19 | #include <sys/utsname.h> | ||
20 | #include <fnmatch.h> | ||
21 | #include <getopt.h> | ||
22 | |||
23 | #define MAX_OPT_DEPTH 10 | ||
24 | #define EUNBALBRACK 10001 | ||
25 | #define EUNDEFVAR 10002 | ||
26 | #define EUNBALPER 10000 | ||
27 | |||
28 | #ifdef CONFIG_FEATURE_IFUPDOWN_MAPPING | ||
29 | #define MAX_INTERFACE_LENGTH 10 | ||
30 | #endif | ||
31 | |||
32 | #define debug_noise(fmt, args...) | ||
33 | |||
34 | /* Forward declaration */ | ||
35 | struct interface_defn_t; | ||
36 | |||
37 | typedef int execfn(char *command); | ||
38 | |||
39 | struct method_t | ||
40 | { | ||
41 | char *name; | ||
42 | int (*up)(struct interface_defn_t *ifd, execfn *e); | ||
43 | int (*down)(struct interface_defn_t *ifd, execfn *e); | ||
44 | }; | ||
45 | |||
46 | struct address_family_t | ||
47 | { | ||
48 | char *name; | ||
49 | int n_methods; | ||
50 | const struct method_t *method; | ||
51 | }; | ||
52 | |||
53 | struct mapping_defn_t | ||
54 | { | ||
55 | struct mapping_defn_t *next; | ||
56 | |||
57 | int max_matches; | ||
58 | int n_matches; | ||
59 | char **match; | ||
60 | |||
61 | char *script; | ||
62 | |||
63 | int max_mappings; | ||
64 | int n_mappings; | ||
65 | char **mapping; | ||
66 | }; | ||
67 | |||
68 | struct variable_t | ||
69 | { | ||
70 | char *name; | ||
71 | char *value; | ||
72 | }; | ||
73 | |||
74 | struct interface_defn_t | ||
75 | { | ||
76 | const struct address_family_t *address_family; | ||
77 | const struct method_t *method; | ||
78 | |||
79 | char *iface; | ||
80 | int max_options; | ||
81 | int n_options; | ||
82 | struct variable_t *option; | ||
83 | }; | ||
84 | |||
85 | struct interfaces_file_t | ||
86 | { | ||
87 | llist_t *autointerfaces; | ||
88 | llist_t *ifaces; | ||
89 | struct mapping_defn_t *mappings; | ||
90 | }; | ||
91 | |||
92 | #define OPTION_STR "anvf" USE_FEATURE_IFUPDOWN_MAPPING("m") "i:" | ||
93 | enum { | ||
94 | OPT_do_all = 0x1, | ||
95 | OPT_no_act = 0x2, | ||
96 | OPT_verbose = 0x4, | ||
97 | OPT_force = 0x8, | ||
98 | OPT_no_mappings = 0x10, | ||
99 | }; | ||
100 | #define DO_ALL (option_mask32 & OPT_do_all) | ||
101 | #define NO_ACT (option_mask32 & OPT_no_act) | ||
102 | #define VERBOSE (option_mask32 & OPT_verbose) | ||
103 | #define FORCE (option_mask32 & OPT_force) | ||
104 | #define NO_MAPPINGS (option_mask32 & OPT_no_mappings) | ||
105 | |||
106 | static char **__myenviron; | ||
107 | |||
108 | static char *startup_PATH; | ||
109 | |||
110 | #if ENABLE_FEATURE_IFUPDOWN_IPV4 || ENABLE_FEATURE_IFUPDOWN_IPV6 | ||
111 | |||
112 | #ifdef CONFIG_FEATURE_IFUPDOWN_IP | ||
113 | |||
114 | static unsigned count_bits(unsigned a) | ||
115 | { | ||
116 | unsigned result; | ||
117 | result = (a & 0x55) + ((a >> 1) & 0x55); | ||
118 | result = (result & 0x33) + ((result >> 2) & 0x33); | ||
119 | return (result & 0x0F) + ((result >> 4) & 0x0F); | ||
120 | } | ||
121 | |||
122 | static int count_netmask_bits(char *dotted_quad) | ||
123 | { | ||
124 | unsigned result, a, b, c, d; | ||
125 | /* Found a netmask... Check if it is dotted quad */ | ||
126 | if (sscanf(dotted_quad, "%u.%u.%u.%u", &a, &b, &c, &d) != 4) | ||
127 | return -1; | ||
128 | // FIXME: will be confused by e.g. 255.0.255.0 | ||
129 | result = count_bits(a); | ||
130 | result += count_bits(b); | ||
131 | result += count_bits(c); | ||
132 | result += count_bits(d); | ||
133 | return (int)result; | ||
134 | } | ||
135 | #endif | ||
136 | |||
137 | static void addstr(char **bufp, const char *str, size_t str_length) | ||
138 | { | ||
139 | /* xasprintf trick will be smaller, but we are often | ||
140 | * called with str_length == 1 - don't want to have | ||
141 | * THAT much of malloc/freeing! */ | ||
142 | char *buf = *bufp; | ||
143 | int len = (buf ? strlen(buf) : 0); | ||
144 | str_length++; | ||
145 | buf = xrealloc(buf, len + str_length); | ||
146 | /* copies at most str_length-1 chars! */ | ||
147 | safe_strncpy(buf + len, str, str_length); | ||
148 | *bufp = buf; | ||
149 | } | ||
150 | |||
151 | static int strncmpz(const char *l, const char *r, size_t llen) | ||
152 | { | ||
153 | int i = strncmp(l, r, llen); | ||
154 | |||
155 | if (i == 0) | ||
156 | return -r[llen]; | ||
157 | return i; | ||
158 | } | ||
159 | |||
160 | static char *get_var(const char *id, size_t idlen, struct interface_defn_t *ifd) | ||
161 | { | ||
162 | int i; | ||
163 | |||
164 | if (strncmpz(id, "iface", idlen) == 0) { | ||
165 | char *result; | ||
166 | static char label_buf[20]; | ||
167 | safe_strncpy(label_buf, ifd->iface, sizeof(label_buf)); | ||
168 | result = strchr(label_buf, ':'); | ||
169 | if (result) { | ||
170 | *result = '\0'; | ||
171 | } | ||
172 | return label_buf; | ||
173 | } | ||
174 | if (strncmpz(id, "label", idlen) == 0) { | ||
175 | return ifd->iface; | ||
176 | } | ||
177 | for (i = 0; i < ifd->n_options; i++) { | ||
178 | if (strncmpz(id, ifd->option[i].name, idlen) == 0) { | ||
179 | return ifd->option[i].value; | ||
180 | } | ||
181 | } | ||
182 | return NULL; | ||
183 | } | ||
184 | |||
185 | static char *parse(const char *command, struct interface_defn_t *ifd) | ||
186 | { | ||
187 | char *result = NULL; | ||
188 | size_t old_pos[MAX_OPT_DEPTH] = { 0 }; | ||
189 | int okay[MAX_OPT_DEPTH] = { 1 }; | ||
190 | int opt_depth = 1; | ||
191 | |||
192 | while (*command) { | ||
193 | switch (*command) { | ||
194 | default: | ||
195 | addstr(&result, command, 1); | ||
196 | command++; | ||
197 | break; | ||
198 | case '\\': | ||
199 | if (command[1]) { | ||
200 | addstr(&result, command + 1, 1); | ||
201 | command += 2; | ||
202 | } else { | ||
203 | addstr(&result, command, 1); | ||
204 | command++; | ||
205 | } | ||
206 | break; | ||
207 | case '[': | ||
208 | if (command[1] == '[' && opt_depth < MAX_OPT_DEPTH) { | ||
209 | old_pos[opt_depth] = strlen(result); | ||
210 | okay[opt_depth] = 1; | ||
211 | opt_depth++; | ||
212 | command += 2; | ||
213 | } else { | ||
214 | addstr(&result, "[", 1); | ||
215 | command++; | ||
216 | } | ||
217 | break; | ||
218 | case ']': | ||
219 | if (command[1] == ']' && opt_depth > 1) { | ||
220 | opt_depth--; | ||
221 | if (!okay[opt_depth]) { | ||
222 | result[old_pos[opt_depth]] = '\0'; | ||
223 | } | ||
224 | command += 2; | ||
225 | } else { | ||
226 | addstr(&result, "]", 1); | ||
227 | command++; | ||
228 | } | ||
229 | break; | ||
230 | case '%': | ||
231 | { | ||
232 | char *nextpercent; | ||
233 | char *varvalue; | ||
234 | |||
235 | command++; | ||
236 | nextpercent = strchr(command, '%'); | ||
237 | if (!nextpercent) { | ||
238 | errno = EUNBALPER; | ||
239 | free(result); | ||
240 | return NULL; | ||
241 | } | ||
242 | |||
243 | varvalue = get_var(command, nextpercent - command, ifd); | ||
244 | |||
245 | if (varvalue) { | ||
246 | addstr(&result, varvalue, strlen(varvalue)); | ||
247 | } else { | ||
248 | #ifdef CONFIG_FEATURE_IFUPDOWN_IP | ||
249 | /* Sigh... Add a special case for 'ip' to convert from | ||
250 | * dotted quad to bit count style netmasks. */ | ||
251 | if (strncmp(command, "bnmask", 6)==0) { | ||
252 | unsigned res; | ||
253 | varvalue = get_var("netmask", 7, ifd); | ||
254 | if (varvalue && (res = count_netmask_bits(varvalue)) > 0) { | ||
255 | const char *argument = utoa(res); | ||
256 | addstr(&result, argument, strlen(argument)); | ||
257 | command = nextpercent + 1; | ||
258 | break; | ||
259 | } | ||
260 | } | ||
261 | #endif | ||
262 | okay[opt_depth - 1] = 0; | ||
263 | } | ||
264 | |||
265 | command = nextpercent + 1; | ||
266 | } | ||
267 | break; | ||
268 | } | ||
269 | } | ||
270 | |||
271 | if (opt_depth > 1) { | ||
272 | errno = EUNBALBRACK; | ||
273 | free(result); | ||
274 | return NULL; | ||
275 | } | ||
276 | |||
277 | if (!okay[0]) { | ||
278 | errno = EUNDEFVAR; | ||
279 | free(result); | ||
280 | return NULL; | ||
281 | } | ||
282 | |||
283 | return result; | ||
284 | } | ||
285 | |||
286 | /* execute() returns 1 for success and 0 for failure */ | ||
287 | static int execute(const char *command, struct interface_defn_t *ifd, execfn *exec) | ||
288 | { | ||
289 | char *out; | ||
290 | int ret; | ||
291 | |||
292 | out = parse(command, ifd); | ||
293 | if (!out) { | ||
294 | return 0; | ||
295 | } | ||
296 | ret = (*exec)(out); | ||
297 | |||
298 | free(out); | ||
299 | if (ret != 1) { | ||
300 | return 0; | ||
301 | } | ||
302 | return 1; | ||
303 | } | ||
304 | #endif | ||
305 | |||
306 | #ifdef CONFIG_FEATURE_IFUPDOWN_IPV6 | ||
307 | static int loopback_up6(struct interface_defn_t *ifd, execfn *exec) | ||
308 | { | ||
309 | #ifdef CONFIG_FEATURE_IFUPDOWN_IP | ||
310 | int result; | ||
311 | result = execute("ip addr add ::1 dev %iface%", ifd, exec); | ||
312 | result += execute("ip link set %iface% up", ifd, exec); | ||
313 | return ((result == 2) ? 2 : 0); | ||
314 | #else | ||
315 | return execute("ifconfig %iface% add ::1", ifd, exec); | ||
316 | #endif | ||
317 | } | ||
318 | |||
319 | static int loopback_down6(struct interface_defn_t *ifd, execfn *exec) | ||
320 | { | ||
321 | #ifdef CONFIG_FEATURE_IFUPDOWN_IP | ||
322 | return execute("ip link set %iface% down", ifd, exec); | ||
323 | #else | ||
324 | return execute("ifconfig %iface% del ::1", ifd, exec); | ||
325 | #endif | ||
326 | } | ||
327 | |||
328 | static int static_up6(struct interface_defn_t *ifd, execfn *exec) | ||
329 | { | ||
330 | int result; | ||
331 | #ifdef CONFIG_FEATURE_IFUPDOWN_IP | ||
332 | result = execute("ip addr add %address%/%netmask% dev %iface% [[label %label%]]", ifd, exec); | ||
333 | result += execute("ip link set [[mtu %mtu%]] [[address %hwaddress%]] %iface% up", ifd, exec); | ||
334 | result += execute("[[ ip route add ::/0 via %gateway% ]]", ifd, exec); | ||
335 | #else | ||
336 | result = execute("ifconfig %iface% [[media %media%]] [[hw %hwaddress%]] [[mtu %mtu%]] up", ifd, exec); | ||
337 | result += execute("ifconfig %iface% add %address%/%netmask%", ifd, exec); | ||
338 | result += execute("[[ route -A inet6 add ::/0 gw %gateway% ]]", ifd, exec); | ||
339 | #endif | ||
340 | return ((result == 3) ? 3 : 0); | ||
341 | } | ||
342 | |||
343 | static int static_down6(struct interface_defn_t *ifd, execfn *exec) | ||
344 | { | ||
345 | #ifdef CONFIG_FEATURE_IFUPDOWN_IP | ||
346 | return execute("ip link set %iface% down", ifd, exec); | ||
347 | #else | ||
348 | return execute("ifconfig %iface% down", ifd, exec); | ||
349 | #endif | ||
350 | } | ||
351 | |||
352 | #ifdef CONFIG_FEATURE_IFUPDOWN_IP | ||
353 | static int v4tunnel_up(struct interface_defn_t *ifd, execfn *exec) | ||
354 | { | ||
355 | int result; | ||
356 | result = execute("ip tunnel add %iface% mode sit remote " | ||
357 | "%endpoint% [[local %local%]] [[ttl %ttl%]]", ifd, exec); | ||
358 | result += execute("ip link set %iface% up", ifd, exec); | ||
359 | result += execute("ip addr add %address%/%netmask% dev %iface%", ifd, exec); | ||
360 | result += execute("[[ ip route add ::/0 via %gateway% ]]", ifd, exec); | ||
361 | return ((result == 4) ? 4 : 0); | ||
362 | } | ||
363 | |||
364 | static int v4tunnel_down(struct interface_defn_t * ifd, execfn * exec) | ||
365 | { | ||
366 | return execute("ip tunnel del %iface%", ifd, exec); | ||
367 | } | ||
368 | #endif | ||
369 | |||
370 | static const struct method_t methods6[] = { | ||
371 | #ifdef CONFIG_FEATURE_IFUPDOWN_IP | ||
372 | { "v4tunnel", v4tunnel_up, v4tunnel_down, }, | ||
373 | #endif | ||
374 | { "static", static_up6, static_down6, }, | ||
375 | { "loopback", loopback_up6, loopback_down6, }, | ||
376 | }; | ||
377 | |||
378 | static const struct address_family_t addr_inet6 = { | ||
379 | "inet6", | ||
380 | sizeof(methods6) / sizeof(struct method_t), | ||
381 | methods6 | ||
382 | }; | ||
383 | #endif /* CONFIG_FEATURE_IFUPDOWN_IPV6 */ | ||
384 | |||
385 | #ifdef CONFIG_FEATURE_IFUPDOWN_IPV4 | ||
386 | static int loopback_up(struct interface_defn_t *ifd, execfn *exec) | ||
387 | { | ||
388 | #ifdef CONFIG_FEATURE_IFUPDOWN_IP | ||
389 | int result; | ||
390 | result = execute("ip addr add 127.0.0.1/8 dev %iface%", ifd, exec); | ||
391 | result += execute("ip link set %iface% up", ifd, exec); | ||
392 | return ((result == 2) ? 2 : 0); | ||
393 | #else | ||
394 | return execute("ifconfig %iface% 127.0.0.1 up", ifd, exec); | ||
395 | #endif | ||
396 | } | ||
397 | |||
398 | static int loopback_down(struct interface_defn_t *ifd, execfn *exec) | ||
399 | { | ||
400 | #ifdef CONFIG_FEATURE_IFUPDOWN_IP | ||
401 | int result; | ||
402 | result = execute("ip addr flush dev %iface%", ifd, exec); | ||
403 | result += execute("ip link set %iface% down", ifd, exec); | ||
404 | return ((result == 2) ? 2 : 0); | ||
405 | #else | ||
406 | return execute("ifconfig %iface% 127.0.0.1 down", ifd, exec); | ||
407 | #endif | ||
408 | } | ||
409 | |||
410 | static int static_up(struct interface_defn_t *ifd, execfn *exec) | ||
411 | { | ||
412 | int result; | ||
413 | #ifdef CONFIG_FEATURE_IFUPDOWN_IP | ||
414 | result = execute("ip addr add %address%/%bnmask% [[broadcast %broadcast%]] " | ||
415 | "dev %iface% [[peer %pointopoint%]] [[label %label%]]", ifd, exec); | ||
416 | result += execute("ip link set [[mtu %mtu%]] [[address %hwaddress%]] %iface% up", ifd, exec); | ||
417 | result += execute("[[ ip route add default via %gateway% dev %iface% ]]", ifd, exec); | ||
418 | return ((result == 3) ? 3 : 0); | ||
419 | #else | ||
420 | /* ifconfig said to set iface up before it processes hw %hwaddress%, | ||
421 | * which then of course fails. Thus we run two separate ifconfig */ | ||
422 | result = execute("ifconfig %iface% [[hw %hwaddress%]] [[media %media%]] [[mtu %mtu%]] up", | ||
423 | ifd, exec); | ||
424 | result += execute("ifconfig %iface% %address% netmask %netmask% " | ||
425 | "[[broadcast %broadcast%]] [[pointopoint %pointopoint%]] ", | ||
426 | ifd, exec); | ||
427 | result += execute("[[ route add default gw %gateway% %iface% ]]", ifd, exec); | ||
428 | return ((result == 3) ? 3 : 0); | ||
429 | #endif | ||
430 | } | ||
431 | |||
432 | static int static_down(struct interface_defn_t *ifd, execfn *exec) | ||
433 | { | ||
434 | int result; | ||
435 | #ifdef CONFIG_FEATURE_IFUPDOWN_IP | ||
436 | result = execute("ip addr flush dev %iface%", ifd, exec); | ||
437 | result += execute("ip link set %iface% down", ifd, exec); | ||
438 | #else | ||
439 | result = execute("[[ route del default gw %gateway% %iface% ]]", ifd, exec); | ||
440 | result += execute("ifconfig %iface% down", ifd, exec); | ||
441 | #endif | ||
442 | return ((result == 2) ? 2 : 0); | ||
443 | } | ||
444 | |||
445 | #ifndef CONFIG_APP_UDHCPC | ||
446 | struct dhcp_client_t | ||
447 | { | ||
448 | const char *name; | ||
449 | const char *startcmd; | ||
450 | const char *stopcmd; | ||
451 | }; | ||
452 | |||
453 | static const struct dhcp_client_t ext_dhcp_clients[] = { | ||
454 | { "udhcpc", | ||
455 | "udhcpc -R -n -p /var/run/udhcpc.%iface%.pid -i %iface% [[-H %hostname%]] [[-c %clientid%]] [[-s %script%]]", | ||
456 | "kill -TERM `cat /var/run/udhcpc.%iface%.pid` 2>/dev/null", | ||
457 | }, | ||
458 | { "pump", | ||
459 | "pump -i %iface% [[-h %hostname%]] [[-l %leasehours%]]", | ||
460 | "pump -i %iface% -k", | ||
461 | }, | ||
462 | { "dhclient", | ||
463 | "dhclient -pf /var/run/dhclient.%iface%.pid %iface%", | ||
464 | "kill -9 `cat /var/run/dhclient.%iface%.pid` 2>/dev/null", | ||
465 | }, | ||
466 | { "dhcpcd", | ||
467 | "dhcpcd [[-h %hostname%]] [[-i %vendor%]] [[-I %clientid%]] [[-l %leasetime%]] %iface%", | ||
468 | "dhcpcd -k %iface%", | ||
469 | }, | ||
470 | }; | ||
471 | #endif | ||
472 | |||
473 | static int dhcp_up(struct interface_defn_t *ifd, execfn *exec) | ||
474 | { | ||
475 | #ifdef CONFIG_APP_UDHCPC | ||
476 | return execute("udhcpc -R -n -p /var/run/udhcpc.%iface%.pid " | ||
477 | "-i %iface% [[-H %hostname%]] [[-c %clientid%]] [[-s %script%]]", | ||
478 | ifd, exec); | ||
479 | #else | ||
480 | int i, nclients = sizeof(ext_dhcp_clients) / sizeof(ext_dhcp_clients[0]); | ||
481 | for (i = 0; i < nclients; i++) { | ||
482 | if (exists_execable(ext_dhcp_clients[i].name)) | ||
483 | return execute(ext_dhcp_clients[i].startcmd, ifd, exec); | ||
484 | } | ||
485 | bb_error_msg("no dhcp clients found"); | ||
486 | return 0; | ||
487 | #endif | ||
488 | } | ||
489 | |||
490 | static int dhcp_down(struct interface_defn_t *ifd, execfn *exec) | ||
491 | { | ||
492 | #ifdef CONFIG_APP_UDHCPC | ||
493 | return execute("kill -TERM " | ||
494 | "`cat /var/run/udhcpc.%iface%.pid` 2>/dev/null", ifd, exec); | ||
495 | #else | ||
496 | int i, nclients = sizeof(ext_dhcp_clients) / sizeof(ext_dhcp_clients[0]); | ||
497 | for (i = 0; i < nclients; i++) { | ||
498 | if (exists_execable(ext_dhcp_clients[i].name)) | ||
499 | return execute(ext_dhcp_clients[i].stopcmd, ifd, exec); | ||
500 | } | ||
501 | bb_error_msg("no dhcp clients found, using static interface shutdown"); | ||
502 | return static_down(ifd, exec); | ||
503 | #endif | ||
504 | } | ||
505 | |||
506 | static int manual_up_down(struct interface_defn_t *ifd, execfn *exec) | ||
507 | { | ||
508 | return 1; | ||
509 | } | ||
510 | |||
511 | static int bootp_up(struct interface_defn_t *ifd, execfn *exec) | ||
512 | { | ||
513 | return execute("bootpc [[--bootfile %bootfile%]] --dev %iface% " | ||
514 | "[[--server %server%]] [[--hwaddr %hwaddr%]] " | ||
515 | "--returniffail --serverbcast", ifd, exec); | ||
516 | } | ||
517 | |||
518 | static int ppp_up(struct interface_defn_t *ifd, execfn *exec) | ||
519 | { | ||
520 | return execute("pon [[%provider%]]", ifd, exec); | ||
521 | } | ||
522 | |||
523 | static int ppp_down(struct interface_defn_t *ifd, execfn *exec) | ||
524 | { | ||
525 | return execute("poff [[%provider%]]", ifd, exec); | ||
526 | } | ||
527 | |||
528 | static int wvdial_up(struct interface_defn_t *ifd, execfn *exec) | ||
529 | { | ||
530 | return execute("/sbin/start-stop-daemon --start -x /usr/bin/wvdial " | ||
531 | "-p /var/run/wvdial.%iface% -b -m -- [[ %provider% ]]", ifd, exec); | ||
532 | } | ||
533 | |||
534 | static int wvdial_down(struct interface_defn_t *ifd, execfn *exec) | ||
535 | { | ||
536 | return execute("/sbin/start-stop-daemon --stop -x /usr/bin/wvdial " | ||
537 | "-p /var/run/wvdial.%iface% -s 2", ifd, exec); | ||
538 | } | ||
539 | |||
540 | static const struct method_t methods[] = { | ||
541 | { "manual", manual_up_down, manual_up_down, }, | ||
542 | { "wvdial", wvdial_up, wvdial_down, }, | ||
543 | { "ppp", ppp_up, ppp_down, }, | ||
544 | { "static", static_up, static_down, }, | ||
545 | { "bootp", bootp_up, static_down, }, | ||
546 | { "dhcp", dhcp_up, dhcp_down, }, | ||
547 | { "loopback", loopback_up, loopback_down, }, | ||
548 | }; | ||
549 | |||
550 | static const struct address_family_t addr_inet = { | ||
551 | "inet", | ||
552 | sizeof(methods) / sizeof(struct method_t), | ||
553 | methods | ||
554 | }; | ||
555 | |||
556 | #endif /* ifdef CONFIG_FEATURE_IFUPDOWN_IPV4 */ | ||
557 | |||
558 | static char *next_word(char **buf) | ||
559 | { | ||
560 | unsigned short length; | ||
561 | char *word; | ||
562 | |||
563 | if (!buf || !*buf || !**buf) { | ||
564 | return NULL; | ||
565 | } | ||
566 | |||
567 | /* Skip over leading whitespace */ | ||
568 | word = skip_whitespace(*buf); | ||
569 | |||
570 | /* Skip over comments */ | ||
571 | if (*word == '#') { | ||
572 | return NULL; | ||
573 | } | ||
574 | |||
575 | /* Find the length of this word */ | ||
576 | length = strcspn(word, " \t\n"); | ||
577 | if (length == 0) { | ||
578 | return NULL; | ||
579 | } | ||
580 | *buf = word + length; | ||
581 | /*DBU:[dave@cray.com] if we are already at EOL dont't increment beyond it */ | ||
582 | if (**buf) { | ||
583 | **buf = '\0'; | ||
584 | (*buf)++; | ||
585 | } | ||
586 | |||
587 | return word; | ||
588 | } | ||
589 | |||
590 | static const struct address_family_t *get_address_family(const struct address_family_t *const af[], char *name) | ||
591 | { | ||
592 | int i; | ||
593 | |||
594 | if (!name) | ||
595 | return NULL; | ||
596 | |||
597 | for (i = 0; af[i]; i++) { | ||
598 | if (strcmp(af[i]->name, name) == 0) { | ||
599 | return af[i]; | ||
600 | } | ||
601 | } | ||
602 | return NULL; | ||
603 | } | ||
604 | |||
605 | static const struct method_t *get_method(const struct address_family_t *af, char *name) | ||
606 | { | ||
607 | int i; | ||
608 | |||
609 | if (!name) | ||
610 | return NULL; | ||
611 | |||
612 | for (i = 0; i < af->n_methods; i++) { | ||
613 | if (strcmp(af->method[i].name, name) == 0) { | ||
614 | return &af->method[i]; | ||
615 | } | ||
616 | } | ||
617 | return NULL; | ||
618 | } | ||
619 | |||
620 | static const llist_t *find_list_string(const llist_t *list, const char *string) | ||
621 | { | ||
622 | if (string == NULL) | ||
623 | return NULL; | ||
624 | |||
625 | while (list) { | ||
626 | if (strcmp(list->data, string) == 0) { | ||
627 | return list; | ||
628 | } | ||
629 | list = list->link; | ||
630 | } | ||
631 | return NULL; | ||
632 | } | ||
633 | |||
634 | static struct interfaces_file_t *read_interfaces(const char *filename) | ||
635 | { | ||
636 | #ifdef CONFIG_FEATURE_IFUPDOWN_MAPPING | ||
637 | struct mapping_defn_t *currmap = NULL; | ||
638 | #endif | ||
639 | struct interface_defn_t *currif = NULL; | ||
640 | struct interfaces_file_t *defn; | ||
641 | FILE *f; | ||
642 | char *firstword; | ||
643 | char *buf; | ||
644 | |||
645 | enum { NONE, IFACE, MAPPING } currently_processing = NONE; | ||
646 | |||
647 | defn = xzalloc(sizeof(struct interfaces_file_t)); | ||
648 | |||
649 | f = xfopen(filename, "r"); | ||
650 | |||
651 | while ((buf = xmalloc_getline(f)) != NULL) { | ||
652 | char *buf_ptr = buf; | ||
653 | |||
654 | firstword = next_word(&buf_ptr); | ||
655 | if (firstword == NULL) { | ||
656 | free(buf); | ||
657 | continue; /* blank line */ | ||
658 | } | ||
659 | |||
660 | if (strcmp(firstword, "mapping") == 0) { | ||
661 | #ifdef CONFIG_FEATURE_IFUPDOWN_MAPPING | ||
662 | currmap = xzalloc(sizeof(struct mapping_defn_t)); | ||
663 | |||
664 | while ((firstword = next_word(&buf_ptr)) != NULL) { | ||
665 | if (currmap->max_matches == currmap->n_matches) { | ||
666 | currmap->max_matches = currmap->max_matches * 2 + 1; | ||
667 | currmap->match = xrealloc(currmap->match, sizeof(currmap->match) * currmap->max_matches); | ||
668 | } | ||
669 | |||
670 | currmap->match[currmap->n_matches++] = xstrdup(firstword); | ||
671 | } | ||
672 | currmap->max_mappings = 0; | ||
673 | currmap->n_mappings = 0; | ||
674 | currmap->mapping = NULL; | ||
675 | currmap->script = NULL; | ||
676 | { | ||
677 | struct mapping_defn_t **where = &defn->mappings; | ||
678 | while (*where != NULL) { | ||
679 | where = &(*where)->next; | ||
680 | } | ||
681 | *where = currmap; | ||
682 | currmap->next = NULL; | ||
683 | } | ||
684 | debug_noise("Added mapping\n"); | ||
685 | #endif | ||
686 | currently_processing = MAPPING; | ||
687 | } else if (strcmp(firstword, "iface") == 0) { | ||
688 | static const struct address_family_t *const addr_fams[] = { | ||
689 | #ifdef CONFIG_FEATURE_IFUPDOWN_IPV4 | ||
690 | &addr_inet, | ||
691 | #endif | ||
692 | #ifdef CONFIG_FEATURE_IFUPDOWN_IPV6 | ||
693 | &addr_inet6, | ||
694 | #endif | ||
695 | NULL | ||
696 | }; | ||
697 | |||
698 | char *iface_name; | ||
699 | char *address_family_name; | ||
700 | char *method_name; | ||
701 | llist_t *iface_list; | ||
702 | |||
703 | currif = xzalloc(sizeof(struct interface_defn_t)); | ||
704 | iface_name = next_word(&buf_ptr); | ||
705 | address_family_name = next_word(&buf_ptr); | ||
706 | method_name = next_word(&buf_ptr); | ||
707 | |||
708 | if (buf_ptr == NULL) { | ||
709 | bb_error_msg("too few parameters for line \"%s\"", buf); | ||
710 | return NULL; | ||
711 | } | ||
712 | |||
713 | /* ship any trailing whitespace */ | ||
714 | buf_ptr = skip_whitespace(buf_ptr); | ||
715 | |||
716 | if (buf_ptr[0] != '\0') { | ||
717 | bb_error_msg("too many parameters \"%s\"", buf); | ||
718 | return NULL; | ||
719 | } | ||
720 | |||
721 | currif->iface = xstrdup(iface_name); | ||
722 | |||
723 | currif->address_family = get_address_family(addr_fams, address_family_name); | ||
724 | if (!currif->address_family) { | ||
725 | bb_error_msg("unknown address type \"%s\"", address_family_name); | ||
726 | return NULL; | ||
727 | } | ||
728 | |||
729 | currif->method = get_method(currif->address_family, method_name); | ||
730 | if (!currif->method) { | ||
731 | bb_error_msg("unknown method \"%s\"", method_name); | ||
732 | return NULL; | ||
733 | } | ||
734 | |||
735 | for (iface_list = defn->ifaces; iface_list; iface_list = iface_list->link) { | ||
736 | struct interface_defn_t *tmp = (struct interface_defn_t *) iface_list->data; | ||
737 | if ((strcmp(tmp->iface, currif->iface) == 0) && | ||
738 | (tmp->address_family == currif->address_family)) { | ||
739 | bb_error_msg("duplicate interface \"%s\"", tmp->iface); | ||
740 | return NULL; | ||
741 | } | ||
742 | } | ||
743 | llist_add_to_end(&(defn->ifaces), (char*)currif); | ||
744 | |||
745 | debug_noise("iface %s %s %s\n", currif->iface, address_family_name, method_name); | ||
746 | currently_processing = IFACE; | ||
747 | } else if (strcmp(firstword, "auto") == 0) { | ||
748 | while ((firstword = next_word(&buf_ptr)) != NULL) { | ||
749 | |||
750 | /* Check the interface isnt already listed */ | ||
751 | if (find_list_string(defn->autointerfaces, firstword)) { | ||
752 | bb_perror_msg_and_die("interface declared auto twice \"%s\"", buf); | ||
753 | } | ||
754 | |||
755 | /* Add the interface to the list */ | ||
756 | llist_add_to_end(&(defn->autointerfaces), xstrdup(firstword)); | ||
757 | debug_noise("\nauto %s\n", firstword); | ||
758 | } | ||
759 | currently_processing = NONE; | ||
760 | } else { | ||
761 | switch (currently_processing) { | ||
762 | case IFACE: | ||
763 | { | ||
764 | int i; | ||
765 | |||
766 | if (strlen(buf_ptr) == 0) { | ||
767 | bb_error_msg("option with empty value \"%s\"", buf); | ||
768 | return NULL; | ||
769 | } | ||
770 | |||
771 | if (strcmp(firstword, "up") != 0 | ||
772 | && strcmp(firstword, "down") != 0 | ||
773 | && strcmp(firstword, "pre-up") != 0 | ||
774 | && strcmp(firstword, "post-down") != 0) { | ||
775 | for (i = 0; i < currif->n_options; i++) { | ||
776 | if (strcmp(currif->option[i].name, firstword) == 0) { | ||
777 | bb_error_msg("duplicate option \"%s\"", buf); | ||
778 | return NULL; | ||
779 | } | ||
780 | } | ||
781 | } | ||
782 | } | ||
783 | if (currif->n_options >= currif->max_options) { | ||
784 | struct variable_t *opt; | ||
785 | |||
786 | currif->max_options = currif->max_options + 10; | ||
787 | opt = xrealloc(currif->option, sizeof(*opt) * currif->max_options); | ||
788 | currif->option = opt; | ||
789 | } | ||
790 | currif->option[currif->n_options].name = xstrdup(firstword); | ||
791 | currif->option[currif->n_options].value = xstrdup(buf_ptr); | ||
792 | if (!currif->option[currif->n_options].name) { | ||
793 | perror(filename); | ||
794 | return NULL; | ||
795 | } | ||
796 | if (!currif->option[currif->n_options].value) { | ||
797 | perror(filename); | ||
798 | return NULL; | ||
799 | } | ||
800 | debug_noise("\t%s=%s\n", currif->option[currif->n_options].name, | ||
801 | currif->option[currif->n_options].value); | ||
802 | currif->n_options++; | ||
803 | break; | ||
804 | case MAPPING: | ||
805 | #ifdef CONFIG_FEATURE_IFUPDOWN_MAPPING | ||
806 | if (strcmp(firstword, "script") == 0) { | ||
807 | if (currmap->script != NULL) { | ||
808 | bb_error_msg("duplicate script in mapping \"%s\"", buf); | ||
809 | return NULL; | ||
810 | } else { | ||
811 | currmap->script = xstrdup(next_word(&buf_ptr)); | ||
812 | } | ||
813 | } else if (strcmp(firstword, "map") == 0) { | ||
814 | if (currmap->max_mappings == currmap->n_mappings) { | ||
815 | currmap->max_mappings = currmap->max_mappings * 2 + 1; | ||
816 | currmap->mapping = xrealloc(currmap->mapping, sizeof(char *) * currmap->max_mappings); | ||
817 | } | ||
818 | currmap->mapping[currmap->n_mappings] = xstrdup(next_word(&buf_ptr)); | ||
819 | currmap->n_mappings++; | ||
820 | } else { | ||
821 | bb_error_msg("misplaced option \"%s\"", buf); | ||
822 | return NULL; | ||
823 | } | ||
824 | #endif | ||
825 | break; | ||
826 | case NONE: | ||
827 | default: | ||
828 | bb_error_msg("misplaced option \"%s\"", buf); | ||
829 | return NULL; | ||
830 | } | ||
831 | } | ||
832 | free(buf); | ||
833 | } | ||
834 | if (ferror(f) != 0) { | ||
835 | bb_perror_msg_and_die("%s", filename); | ||
836 | } | ||
837 | fclose(f); | ||
838 | |||
839 | return defn; | ||
840 | } | ||
841 | |||
842 | static char *setlocalenv(char *format, const char *name, const char *value) | ||
843 | { | ||
844 | char *result; | ||
845 | char *here; | ||
846 | char *there; | ||
847 | |||
848 | result = xasprintf(format, name, value); | ||
849 | |||
850 | for (here = there = result; *there != '=' && *there; there++) { | ||
851 | if (*there == '-') | ||
852 | *there = '_'; | ||
853 | if (isalpha(*there)) | ||
854 | *there = toupper(*there); | ||
855 | |||
856 | if (isalnum(*there) || *there == '_') { | ||
857 | *here = *there; | ||
858 | here++; | ||
859 | } | ||
860 | } | ||
861 | memmove(here, there, strlen(there) + 1); | ||
862 | |||
863 | return result; | ||
864 | } | ||
865 | |||
866 | static void set_environ(struct interface_defn_t *iface, const char *mode) | ||
867 | { | ||
868 | char **environend; | ||
869 | int i; | ||
870 | const int n_env_entries = iface->n_options + 5; | ||
871 | char **ppch; | ||
872 | |||
873 | if (__myenviron != NULL) { | ||
874 | for (ppch = __myenviron; *ppch; ppch++) { | ||
875 | free(*ppch); | ||
876 | *ppch = NULL; | ||
877 | } | ||
878 | free(__myenviron); | ||
879 | } | ||
880 | __myenviron = xzalloc(sizeof(char *) * (n_env_entries + 1 /* for final NULL */ )); | ||
881 | environend = __myenviron; | ||
882 | |||
883 | for (i = 0; i < iface->n_options; i++) { | ||
884 | if (strcmp(iface->option[i].name, "up") == 0 | ||
885 | || strcmp(iface->option[i].name, "down") == 0 | ||
886 | || strcmp(iface->option[i].name, "pre-up") == 0 | ||
887 | || strcmp(iface->option[i].name, "post-down") == 0) { | ||
888 | continue; | ||
889 | } | ||
890 | *(environend++) = setlocalenv("IF_%s=%s", iface->option[i].name, iface->option[i].value); | ||
891 | } | ||
892 | |||
893 | *(environend++) = setlocalenv("%s=%s", "IFACE", iface->iface); | ||
894 | *(environend++) = setlocalenv("%s=%s", "ADDRFAM", iface->address_family->name); | ||
895 | *(environend++) = setlocalenv("%s=%s", "METHOD", iface->method->name); | ||
896 | *(environend++) = setlocalenv("%s=%s", "MODE", mode); | ||
897 | *(environend++) = setlocalenv("%s=%s", "PATH", startup_PATH); | ||
898 | } | ||
899 | |||
900 | static int doit(char *str) | ||
901 | { | ||
902 | if (option_mask32 & (OPT_no_act|OPT_verbose)) { | ||
903 | puts(str); | ||
904 | } | ||
905 | if (!(option_mask32 & OPT_no_act)) { | ||
906 | pid_t child; | ||
907 | int status; | ||
908 | |||
909 | fflush(NULL); | ||
910 | switch (child = fork()) { | ||
911 | case -1: /* failure */ | ||
912 | return 0; | ||
913 | case 0: /* child */ | ||
914 | execle(DEFAULT_SHELL, DEFAULT_SHELL, "-c", str, NULL, __myenviron); | ||
915 | exit(127); | ||
916 | } | ||
917 | waitpid(child, &status, 0); | ||
918 | if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { | ||
919 | return 0; | ||
920 | } | ||
921 | } | ||
922 | return 1; | ||
923 | } | ||
924 | |||
925 | static int execute_all(struct interface_defn_t *ifd, const char *opt) | ||
926 | { | ||
927 | int i; | ||
928 | char *buf; | ||
929 | for (i = 0; i < ifd->n_options; i++) { | ||
930 | if (strcmp(ifd->option[i].name, opt) == 0) { | ||
931 | if (!doit(ifd->option[i].value)) { | ||
932 | return 0; | ||
933 | } | ||
934 | } | ||
935 | } | ||
936 | |||
937 | buf = xasprintf("run-parts /etc/network/if-%s.d", opt); | ||
938 | if (doit(buf) != 1) { | ||
939 | return 0; | ||
940 | } | ||
941 | return 1; | ||
942 | } | ||
943 | |||
944 | static int check(char *str) { | ||
945 | return str != NULL; | ||
946 | } | ||
947 | |||
948 | static int iface_up(struct interface_defn_t *iface) | ||
949 | { | ||
950 | if (!iface->method->up(iface,check)) return -1; | ||
951 | set_environ(iface, "start"); | ||
952 | if (!execute_all(iface, "pre-up")) return 0; | ||
953 | if (!iface->method->up(iface, doit)) return 0; | ||
954 | if (!execute_all(iface, "up")) return 0; | ||
955 | return 1; | ||
956 | } | ||
957 | |||
958 | static int iface_down(struct interface_defn_t *iface) | ||
959 | { | ||
960 | if (!iface->method->down(iface,check)) return -1; | ||
961 | set_environ(iface, "stop"); | ||
962 | if (!execute_all(iface, "down")) return 0; | ||
963 | if (!iface->method->down(iface, doit)) return 0; | ||
964 | if (!execute_all(iface, "post-down")) return 0; | ||
965 | return 1; | ||
966 | } | ||
967 | |||
968 | #ifdef CONFIG_FEATURE_IFUPDOWN_MAPPING | ||
969 | static int popen2(FILE **in, FILE **out, char *command, ...) | ||
970 | { | ||
971 | va_list ap; | ||
972 | char *argv[11] = { command }; | ||
973 | int argc; | ||
974 | int infd[2], outfd[2]; | ||
975 | pid_t pid; | ||
976 | |||
977 | argc = 1; | ||
978 | va_start(ap, command); | ||
979 | while ((argc < 10) && (argv[argc] = va_arg(ap, char *))) { | ||
980 | argc++; | ||
981 | } | ||
982 | argv[argc] = NULL; /* make sure */ | ||
983 | va_end(ap); | ||
984 | |||
985 | if (pipe(infd) != 0) { | ||
986 | return 0; | ||
987 | } | ||
988 | |||
989 | if (pipe(outfd) != 0) { | ||
990 | close(infd[0]); | ||
991 | close(infd[1]); | ||
992 | return 0; | ||
993 | } | ||
994 | |||
995 | fflush(NULL); | ||
996 | switch (pid = fork()) { | ||
997 | case -1: /* failure */ | ||
998 | close(infd[0]); | ||
999 | close(infd[1]); | ||
1000 | close(outfd[0]); | ||
1001 | close(outfd[1]); | ||
1002 | return 0; | ||
1003 | case 0: /* child */ | ||
1004 | dup2(infd[0], 0); | ||
1005 | dup2(outfd[1], 1); | ||
1006 | close(infd[0]); | ||
1007 | close(infd[1]); | ||
1008 | close(outfd[0]); | ||
1009 | close(outfd[1]); | ||
1010 | execvp(command, argv); | ||
1011 | exit(127); | ||
1012 | default: /* parent */ | ||
1013 | *in = fdopen(infd[1], "w"); | ||
1014 | *out = fdopen(outfd[0], "r"); | ||
1015 | close(infd[0]); | ||
1016 | close(outfd[1]); | ||
1017 | return pid; | ||
1018 | } | ||
1019 | /* unreached */ | ||
1020 | } | ||
1021 | |||
1022 | static char *run_mapping(char *physical, struct mapping_defn_t * map) | ||
1023 | { | ||
1024 | FILE *in, *out; | ||
1025 | int i, status; | ||
1026 | pid_t pid; | ||
1027 | |||
1028 | char *logical = xstrdup(physical); | ||
1029 | |||
1030 | /* Run the mapping script. */ | ||
1031 | pid = popen2(&in, &out, map->script, physical, NULL); | ||
1032 | |||
1033 | /* popen2() returns 0 on failure. */ | ||
1034 | if (pid == 0) | ||
1035 | return logical; | ||
1036 | |||
1037 | /* Write mappings to stdin of mapping script. */ | ||
1038 | for (i = 0; i < map->n_mappings; i++) { | ||
1039 | fprintf(in, "%s\n", map->mapping[i]); | ||
1040 | } | ||
1041 | fclose(in); | ||
1042 | waitpid(pid, &status, 0); | ||
1043 | |||
1044 | if (WIFEXITED(status) && WEXITSTATUS(status) == 0) { | ||
1045 | /* If the mapping script exited successfully, try to | ||
1046 | * grab a line of output and use that as the name of the | ||
1047 | * logical interface. */ | ||
1048 | char *new_logical = (char *)xmalloc(MAX_INTERFACE_LENGTH); | ||
1049 | |||
1050 | if (fgets(new_logical, MAX_INTERFACE_LENGTH, out)) { | ||
1051 | /* If we are able to read a line of output from the script, | ||
1052 | * remove any trailing whitespace and use this value | ||
1053 | * as the name of the logical interface. */ | ||
1054 | char *pch = new_logical + strlen(new_logical) - 1; | ||
1055 | |||
1056 | while (pch >= new_logical && isspace(*pch)) | ||
1057 | *(pch--) = '\0'; | ||
1058 | |||
1059 | free(logical); | ||
1060 | logical = new_logical; | ||
1061 | } else { | ||
1062 | /* If we are UNABLE to read a line of output, discard our | ||
1063 | * freshly allocated memory. */ | ||
1064 | free(new_logical); | ||
1065 | } | ||
1066 | } | ||
1067 | |||
1068 | fclose(out); | ||
1069 | |||
1070 | return logical; | ||
1071 | } | ||
1072 | #endif /* CONFIG_FEATURE_IFUPDOWN_MAPPING */ | ||
1073 | |||
1074 | static llist_t *find_iface_state(llist_t *state_list, const char *iface) | ||
1075 | { | ||
1076 | unsigned short iface_len = strlen(iface); | ||
1077 | llist_t *search = state_list; | ||
1078 | |||
1079 | while (search) { | ||
1080 | if ((strncmp(search->data, iface, iface_len) == 0) && | ||
1081 | (search->data[iface_len] == '=')) { | ||
1082 | return search; | ||
1083 | } | ||
1084 | search = search->link; | ||
1085 | } | ||
1086 | return NULL; | ||
1087 | } | ||
1088 | |||
1089 | int ifupdown_main(int argc, char **argv) | ||
1090 | { | ||
1091 | static const char statefile[] = "/var/run/ifstate"; | ||
1092 | |||
1093 | int (*cmds)(struct interface_defn_t *) = NULL; | ||
1094 | struct interfaces_file_t *defn; | ||
1095 | llist_t *state_list = NULL; | ||
1096 | llist_t *target_list = NULL; | ||
1097 | const char *interfaces = "/etc/network/interfaces"; | ||
1098 | int any_failures = 0; | ||
1099 | |||
1100 | if (applet_name[2] == 'u') { | ||
1101 | /* ifup command */ | ||
1102 | cmds = iface_up; | ||
1103 | } else { | ||
1104 | /* ifdown command */ | ||
1105 | cmds = iface_down; | ||
1106 | } | ||
1107 | |||
1108 | getopt32(argc, argv, OPTION_STR, &interfaces); | ||
1109 | if (argc - optind > 0) { | ||
1110 | if (DO_ALL) bb_show_usage(); | ||
1111 | } else | ||
1112 | if (!DO_ALL) bb_show_usage(); | ||
1113 | |||
1114 | debug_noise("reading %s file:\n", interfaces); | ||
1115 | defn = read_interfaces(interfaces); | ||
1116 | debug_noise("\ndone reading %s\n\n", interfaces); | ||
1117 | |||
1118 | if (!defn) { | ||
1119 | exit(EXIT_FAILURE); | ||
1120 | } | ||
1121 | |||
1122 | startup_PATH = getenv("PATH"); | ||
1123 | if (!startup_PATH) startup_PATH = ""; | ||
1124 | |||
1125 | /* Create a list of interfaces to work on */ | ||
1126 | if (DO_ALL) { | ||
1127 | if (cmds == iface_up) { | ||
1128 | target_list = defn->autointerfaces; | ||
1129 | } else { | ||
1130 | /* iface_down */ | ||
1131 | const llist_t *list = state_list; | ||
1132 | while (list) { | ||
1133 | llist_add_to_end(&target_list, xstrdup(list->data)); | ||
1134 | list = list->link; | ||
1135 | } | ||
1136 | target_list = defn->autointerfaces; | ||
1137 | } | ||
1138 | } else { | ||
1139 | llist_add_to_end(&target_list, argv[optind]); | ||
1140 | } | ||
1141 | |||
1142 | |||
1143 | /* Update the interfaces */ | ||
1144 | while (target_list) { | ||
1145 | llist_t *iface_list; | ||
1146 | struct interface_defn_t *currif; | ||
1147 | char *iface; | ||
1148 | char *liface; | ||
1149 | char *pch; | ||
1150 | int okay = 0; | ||
1151 | int cmds_ret; | ||
1152 | |||
1153 | iface = xstrdup(target_list->data); | ||
1154 | target_list = target_list->link; | ||
1155 | |||
1156 | pch = strchr(iface, '='); | ||
1157 | if (pch) { | ||
1158 | *pch = '\0'; | ||
1159 | liface = xstrdup(pch + 1); | ||
1160 | } else { | ||
1161 | liface = xstrdup(iface); | ||
1162 | } | ||
1163 | |||
1164 | if (!FORCE) { | ||
1165 | const llist_t *iface_state = find_iface_state(state_list, iface); | ||
1166 | |||
1167 | if (cmds == iface_up) { | ||
1168 | /* ifup */ | ||
1169 | if (iface_state) { | ||
1170 | bb_error_msg("interface %s already configured", iface); | ||
1171 | continue; | ||
1172 | } | ||
1173 | } else { | ||
1174 | /* ifdown */ | ||
1175 | if (iface_state) { | ||
1176 | bb_error_msg("interface %s not configured", iface); | ||
1177 | continue; | ||
1178 | } | ||
1179 | } | ||
1180 | } | ||
1181 | |||
1182 | #ifdef CONFIG_FEATURE_IFUPDOWN_MAPPING | ||
1183 | if ((cmds == iface_up) && !NO_MAPPINGS) { | ||
1184 | struct mapping_defn_t *currmap; | ||
1185 | |||
1186 | for (currmap = defn->mappings; currmap; currmap = currmap->next) { | ||
1187 | int i; | ||
1188 | for (i = 0; i < currmap->n_matches; i++) { | ||
1189 | if (fnmatch(currmap->match[i], liface, 0) != 0) | ||
1190 | continue; | ||
1191 | if (VERBOSE) { | ||
1192 | printf("Running mapping script %s on %s\n", currmap->script, liface); | ||
1193 | } | ||
1194 | liface = run_mapping(iface, currmap); | ||
1195 | break; | ||
1196 | } | ||
1197 | } | ||
1198 | } | ||
1199 | #endif | ||
1200 | |||
1201 | |||
1202 | iface_list = defn->ifaces; | ||
1203 | while (iface_list) { | ||
1204 | currif = (struct interface_defn_t *) iface_list->data; | ||
1205 | if (strcmp(liface, currif->iface) == 0) { | ||
1206 | char *oldiface = currif->iface; | ||
1207 | |||
1208 | okay = 1; | ||
1209 | currif->iface = iface; | ||
1210 | |||
1211 | debug_noise("\nConfiguring interface %s (%s)\n", liface, currif->address_family->name); | ||
1212 | |||
1213 | /* Call the cmds function pointer, does either iface_up() or iface_down() */ | ||
1214 | cmds_ret = cmds(currif); | ||
1215 | if (cmds_ret == -1) { | ||
1216 | bb_error_msg("don't seem to have all the variables for %s/%s", | ||
1217 | liface, currif->address_family->name); | ||
1218 | any_failures += 1; | ||
1219 | } else if (cmds_ret == 0) { | ||
1220 | any_failures += 1; | ||
1221 | } | ||
1222 | |||
1223 | currif->iface = oldiface; | ||
1224 | } | ||
1225 | iface_list = iface_list->link; | ||
1226 | } | ||
1227 | if (VERBOSE) { | ||
1228 | puts(""); | ||
1229 | } | ||
1230 | |||
1231 | if (!okay && !FORCE) { | ||
1232 | bb_error_msg("ignoring unknown interface %s", liface); | ||
1233 | any_failures += 1; | ||
1234 | } else { | ||
1235 | llist_t *iface_state = find_iface_state(state_list, iface); | ||
1236 | |||
1237 | if (cmds == iface_up) { | ||
1238 | char *newiface = xasprintf("%s=%s", iface, liface); | ||
1239 | if (iface_state == NULL) { | ||
1240 | llist_add_to_end(&state_list, newiface); | ||
1241 | } else { | ||
1242 | free(iface_state->data); | ||
1243 | iface_state->data = newiface; | ||
1244 | } | ||
1245 | } else { | ||
1246 | /* Remove an interface from the linked list */ | ||
1247 | free(llist_pop(&iface_state)); | ||
1248 | } | ||
1249 | } | ||
1250 | } | ||
1251 | |||
1252 | /* Actually write the new state */ | ||
1253 | if (!NO_ACT) { | ||
1254 | FILE *state_fp = NULL; | ||
1255 | |||
1256 | state_fp = xfopen(statefile, "w"); | ||
1257 | while (state_list) { | ||
1258 | if (state_list->data) { | ||
1259 | fputs(state_list->data, state_fp); | ||
1260 | fputc('\n', state_fp); | ||
1261 | } | ||
1262 | state_list = state_list->link; | ||
1263 | } | ||
1264 | fclose(state_fp); | ||
1265 | } | ||
1266 | |||
1267 | if (any_failures) | ||
1268 | return 1; | ||
1269 | return 0; | ||
1270 | } | ||
diff --git a/networking/inetd.c b/networking/inetd.c new file mode 100644 index 000000000..ec7b2e8f7 --- /dev/null +++ b/networking/inetd.c | |||
@@ -0,0 +1,1767 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* $Slackware: inetd.c 1.79s 2001/02/06 13:18:00 volkerdi Exp $ */ | ||
3 | /* $OpenBSD: inetd.c,v 1.79 2001/01/30 08:30:57 deraadt Exp $ */ | ||
4 | /* $NetBSD: inetd.c,v 1.11 1996/02/22 11:14:41 mycroft Exp $ */ | ||
5 | /* Busybox port by Vladimir Oleynik (C) 2001-2005 <dzo@simtreas.ru> */ | ||
6 | /* | ||
7 | * Copyright (c) 1983,1991 The Regents of the University of California. | ||
8 | * All rights reserved. | ||
9 | * | ||
10 | * Redistribution and use in source and binary forms, with or without | ||
11 | * modification, are permitted provided that the following conditions | ||
12 | * are met: | ||
13 | * 1. Redistributions of source code must retain the above copyright | ||
14 | * notice, this list of conditions and the following disclaimer. | ||
15 | * 2. Redistributions in binary form must reproduce the above copyright | ||
16 | * notice, this list of conditions and the following disclaimer in the | ||
17 | * documentation and/or other materials provided with the distribution. | ||
18 | * 3. All advertising materials mentioning features or use of this software | ||
19 | * must display the following acknowledgement: | ||
20 | * This product includes software developed by the University of | ||
21 | * California, Berkeley and its contributors. | ||
22 | * 4. Neither the name of the University nor the names of its contributors | ||
23 | * may be used to endorse or promote products derived from this software | ||
24 | * without specific prior written permission. | ||
25 | * | ||
26 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | ||
27 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||
28 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | ||
29 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | ||
30 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||
31 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | ||
32 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | ||
33 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | ||
34 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | ||
35 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | ||
36 | * SUCH DAMAGE. | ||
37 | */ | ||
38 | |||
39 | /* | ||
40 | * Inetd - Internet super-server | ||
41 | * | ||
42 | * This program invokes all internet services as needed. | ||
43 | * connection-oriented services are invoked each time a | ||
44 | * connection is made, by creating a process. This process | ||
45 | * is passed the connection as file descriptor 0 and is | ||
46 | * expected to do a getpeername to find out the source host | ||
47 | * and port. | ||
48 | * | ||
49 | * Datagram oriented services are invoked when a datagram | ||
50 | * arrives; a process is created and passed a pending message | ||
51 | * on file descriptor 0. Datagram servers may either connect | ||
52 | * to their peer, freeing up the original socket for inetd | ||
53 | * to receive further messages on, or ``take over the socket'', | ||
54 | * processing all arriving datagrams and, eventually, timing | ||
55 | * out. The first type of server is said to be ``multi-threaded''; | ||
56 | * the second type of server ``single-threaded''. | ||
57 | * | ||
58 | * Inetd uses a configuration file which is read at startup | ||
59 | * and, possibly, at some later time in response to a hangup signal. | ||
60 | * The configuration file is ``free format'' with fields given in the | ||
61 | * order shown below. Continuation lines for an entry must begin with | ||
62 | * a space or tab. All fields must be present in each entry. | ||
63 | * | ||
64 | * service name must be in /etc/services | ||
65 | * socket type stream/dgram/raw/rdm/seqpacket | ||
66 | * protocol must be in /etc/protocols | ||
67 | * wait/nowait[.max] single-threaded/multi-threaded, max # | ||
68 | * user[.group] or user[:group] user/group to run daemon as | ||
69 | * server program full path name | ||
70 | * server program arguments maximum of MAXARGS (20) | ||
71 | * | ||
72 | * For RPC services | ||
73 | * service name/version must be in /etc/rpc | ||
74 | * socket type stream/dgram/raw/rdm/seqpacket | ||
75 | * protocol must be in /etc/protocols | ||
76 | * wait/nowait[.max] single-threaded/multi-threaded | ||
77 | * user[.group] or user[:group] user to run daemon as | ||
78 | * server program full path name | ||
79 | * server program arguments maximum of MAXARGS (20) | ||
80 | * | ||
81 | * For non-RPC services, the "service name" can be of the form | ||
82 | * hostaddress:servicename, in which case the hostaddress is used | ||
83 | * as the host portion of the address to listen on. If hostaddress | ||
84 | * consists of a single `*' character, INADDR_ANY is used. | ||
85 | * | ||
86 | * A line can also consist of just | ||
87 | * hostaddress: | ||
88 | * where hostaddress is as in the preceding paragraph. Such a line must | ||
89 | * have no further fields; the specified hostaddress is remembered and | ||
90 | * used for all further lines that have no hostaddress specified, | ||
91 | * until the next such line (or EOF). (This is why * is provided to | ||
92 | * allow explicit specification of INADDR_ANY.) A line | ||
93 | * *: | ||
94 | * is implicitly in effect at the beginning of the file. | ||
95 | * | ||
96 | * The hostaddress specifier may (and often will) contain dots; | ||
97 | * the service name must not. | ||
98 | * | ||
99 | * For RPC services, host-address specifiers are accepted and will | ||
100 | * work to some extent; however, because of limitations in the | ||
101 | * portmapper interface, it will not work to try to give more than | ||
102 | * one line for any given RPC service, even if the host-address | ||
103 | * specifiers are different. | ||
104 | * | ||
105 | * Comment lines are indicated by a `#' in column 1. | ||
106 | */ | ||
107 | |||
108 | /* | ||
109 | * Here's the scoop concerning the user[.:]group feature: | ||
110 | * | ||
111 | * 1) set-group-option off. | ||
112 | * | ||
113 | * a) user = root: NO setuid() or setgid() is done | ||
114 | * | ||
115 | * b) other: setgid(primary group as found in passwd) | ||
116 | * initgroups(name, primary group) | ||
117 | * setuid() | ||
118 | * | ||
119 | * 2) set-group-option on. | ||
120 | * | ||
121 | * a) user = root: setgid(specified group) | ||
122 | * NO initgroups() | ||
123 | * NO setuid() | ||
124 | * | ||
125 | * b) other: setgid(specified group) | ||
126 | * initgroups(name, specified group) | ||
127 | * setuid() | ||
128 | * | ||
129 | */ | ||
130 | |||
131 | #include "busybox.h" | ||
132 | #include <syslog.h> | ||
133 | #include <sys/un.h> | ||
134 | |||
135 | //#define CONFIG_FEATURE_INETD_RPC | ||
136 | //#define CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_ECHO | ||
137 | //#define CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DISCARD | ||
138 | //#define CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_TIME | ||
139 | //#define CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME | ||
140 | //#define CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN | ||
141 | //#define CONFIG_FEATURE_IPV6 | ||
142 | |||
143 | #ifdef CONFIG_FEATURE_INETD_RPC | ||
144 | #include <rpc/rpc.h> | ||
145 | #include <rpc/pmap_clnt.h> | ||
146 | #endif | ||
147 | |||
148 | #define _PATH_INETDCONF "/etc/inetd.conf" | ||
149 | #define _PATH_INETDPID "/var/run/inetd.pid" | ||
150 | |||
151 | |||
152 | #define CNT_INTVL 60 /* servers in CNT_INTVL sec. */ | ||
153 | #define RETRYTIME (60*10) /* retry after bind or server fail */ | ||
154 | |||
155 | #ifndef RLIMIT_NOFILE | ||
156 | #define RLIMIT_NOFILE RLIMIT_OFILE | ||
157 | #endif | ||
158 | |||
159 | #ifndef OPEN_MAX | ||
160 | #define OPEN_MAX 64 | ||
161 | #endif | ||
162 | |||
163 | /* Reserve some descriptors, 3 stdio + at least: 1 log, 1 conf. file */ | ||
164 | #define FD_MARGIN (8) | ||
165 | static rlim_t rlim_ofile_cur = OPEN_MAX; | ||
166 | static struct rlimit rlim_ofile; | ||
167 | |||
168 | |||
169 | /* Check unsupporting builtin */ | ||
170 | #if defined CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_ECHO || \ | ||
171 | defined CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DISCARD || \ | ||
172 | defined CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_TIME || \ | ||
173 | defined CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME || \ | ||
174 | defined CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN | ||
175 | # define INETD_FEATURE_ENABLED | ||
176 | #endif | ||
177 | |||
178 | #if defined CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_ECHO || \ | ||
179 | defined CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DISCARD || \ | ||
180 | defined CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN | ||
181 | # define INETD_SETPROCTITLE | ||
182 | #endif | ||
183 | |||
184 | typedef struct servtab { | ||
185 | char *se_hostaddr; /* host address to listen on */ | ||
186 | char *se_service; /* name of service */ | ||
187 | int se_socktype; /* type of socket to use */ | ||
188 | int se_family; /* address family */ | ||
189 | char *se_proto; /* protocol used */ | ||
190 | #ifdef CONFIG_FEATURE_INETD_RPC | ||
191 | int se_rpcprog; /* rpc program number */ | ||
192 | int se_rpcversl; /* rpc program lowest version */ | ||
193 | int se_rpcversh; /* rpc program highest version */ | ||
194 | #define isrpcservice(sep) ((sep)->se_rpcversl != 0) | ||
195 | #else | ||
196 | #define isrpcservice(sep) 0 | ||
197 | #endif | ||
198 | pid_t se_wait; /* single threaded server */ | ||
199 | short se_checked; /* looked at during merge */ | ||
200 | char *se_user; /* user name to run as */ | ||
201 | char *se_group; /* group name to run as */ | ||
202 | #ifdef INETD_FEATURE_ENABLED | ||
203 | const struct builtin *se_bi; /* if built-in, description */ | ||
204 | #endif | ||
205 | char *se_server; /* server program */ | ||
206 | #define MAXARGV 20 | ||
207 | char *se_argv[MAXARGV + 1]; /* program arguments */ | ||
208 | int se_fd; /* open descriptor */ | ||
209 | union { | ||
210 | struct sockaddr se_un_ctrladdr; | ||
211 | struct sockaddr_in se_un_ctrladdr_in; | ||
212 | #ifdef CONFIG_FEATURE_IPV6 | ||
213 | struct sockaddr_in6 se_un_ctrladdr_in6; | ||
214 | #endif | ||
215 | struct sockaddr_un se_un_ctrladdr_un; | ||
216 | } se_un; /* bound address */ | ||
217 | #define se_ctrladdr se_un.se_un_ctrladdr | ||
218 | #define se_ctrladdr_in se_un.se_un_ctrladdr_in | ||
219 | #define se_ctrladdr_in6 se_un.se_un_ctrladdr_in6 | ||
220 | #define se_ctrladdr_un se_un.se_un_ctrladdr_un | ||
221 | int se_ctrladdr_size; | ||
222 | int se_max; /* max # of instances of this service */ | ||
223 | int se_count; /* number started since se_time */ | ||
224 | struct timeval se_time; /* start of se_count */ | ||
225 | struct servtab *se_next; | ||
226 | } servtab_t; | ||
227 | |||
228 | static servtab_t *servtab; | ||
229 | |||
230 | #ifdef INETD_FEATURE_ENABLED | ||
231 | struct builtin { | ||
232 | const char *bi_service; /* internally provided service name */ | ||
233 | int bi_socktype; /* type of socket supported */ | ||
234 | short bi_fork; /* 1 if should fork before call */ | ||
235 | short bi_wait; /* 1 if should wait for child */ | ||
236 | void (*bi_fn) (int, servtab_t *); | ||
237 | }; | ||
238 | |||
239 | /* Echo received data */ | ||
240 | #ifdef CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_ECHO | ||
241 | static void echo_stream(int, servtab_t *); | ||
242 | static void echo_dg(int, servtab_t *); | ||
243 | #endif | ||
244 | /* Internet /dev/null */ | ||
245 | #ifdef CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DISCARD | ||
246 | static void discard_stream(int, servtab_t *); | ||
247 | static void discard_dg(int, servtab_t *); | ||
248 | #endif | ||
249 | /* Return 32 bit time since 1900 */ | ||
250 | #ifdef CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_TIME | ||
251 | static void machtime_stream(int, servtab_t *); | ||
252 | static void machtime_dg(int, servtab_t *); | ||
253 | #endif | ||
254 | /* Return human-readable time */ | ||
255 | #ifdef CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME | ||
256 | static void daytime_stream(int, servtab_t *); | ||
257 | static void daytime_dg(int, servtab_t *); | ||
258 | #endif | ||
259 | /* Familiar character generator */ | ||
260 | #ifdef CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN | ||
261 | static void chargen_stream(int, servtab_t *); | ||
262 | static void chargen_dg(int, servtab_t *); | ||
263 | #endif | ||
264 | |||
265 | static const struct builtin builtins[] = { | ||
266 | #ifdef CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_ECHO | ||
267 | /* Echo received data */ | ||
268 | {"echo", SOCK_STREAM, 1, 0, echo_stream,}, | ||
269 | {"echo", SOCK_DGRAM, 0, 0, echo_dg,}, | ||
270 | #endif | ||
271 | #ifdef CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DISCARD | ||
272 | /* Internet /dev/null */ | ||
273 | {"discard", SOCK_STREAM, 1, 0, discard_stream,}, | ||
274 | {"discard", SOCK_DGRAM, 0, 0, discard_dg,}, | ||
275 | #endif | ||
276 | #ifdef CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_TIME | ||
277 | /* Return 32 bit time since 1900 */ | ||
278 | {"time", SOCK_STREAM, 0, 0, machtime_stream,}, | ||
279 | {"time", SOCK_DGRAM, 0, 0, machtime_dg,}, | ||
280 | #endif | ||
281 | #ifdef CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME | ||
282 | /* Return human-readable time */ | ||
283 | {"daytime", SOCK_STREAM, 0, 0, daytime_stream,}, | ||
284 | {"daytime", SOCK_DGRAM, 0, 0, daytime_dg,}, | ||
285 | #endif | ||
286 | #ifdef CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN | ||
287 | /* Familiar character generator */ | ||
288 | {"chargen", SOCK_STREAM, 1, 0, chargen_stream,}, | ||
289 | {"chargen", SOCK_DGRAM, 0, 0, chargen_dg,}, | ||
290 | #endif | ||
291 | {NULL, 0, 0, 0, NULL} | ||
292 | }; | ||
293 | #endif /* INETD_FEATURE_ENABLED */ | ||
294 | |||
295 | static int global_queuelen = 128; | ||
296 | static int nsock, maxsock; | ||
297 | static fd_set allsock; | ||
298 | static int toomany; | ||
299 | static int timingout; | ||
300 | static struct servent *sp; | ||
301 | static uid_t uid; | ||
302 | |||
303 | static char *CONFIG = _PATH_INETDCONF; | ||
304 | |||
305 | static FILE *fconfig; | ||
306 | static char line[1024]; | ||
307 | static char *defhost; | ||
308 | |||
309 | /* xstrdup(NULL) returns NULL, but this one | ||
310 | * will return newly-allocated "" if called with NULL arg | ||
311 | * TODO: audit whether this makes any real difference | ||
312 | */ | ||
313 | static char *xxstrdup(char *cp) | ||
314 | { | ||
315 | return xstrdup(cp ? cp : ""); | ||
316 | } | ||
317 | |||
318 | static int setconfig(void) | ||
319 | { | ||
320 | free(defhost); | ||
321 | defhost = xstrdup("*"); | ||
322 | if (fconfig != NULL) { | ||
323 | fseek(fconfig, 0L, SEEK_SET); | ||
324 | return 1; | ||
325 | } | ||
326 | fconfig = fopen(CONFIG, "r"); | ||
327 | return (fconfig != NULL); | ||
328 | } | ||
329 | |||
330 | static void endconfig(void) | ||
331 | { | ||
332 | if (fconfig) { | ||
333 | (void) fclose(fconfig); | ||
334 | fconfig = NULL; | ||
335 | } | ||
336 | free(defhost); | ||
337 | defhost = 0; | ||
338 | } | ||
339 | |||
340 | #ifdef CONFIG_FEATURE_INETD_RPC | ||
341 | static void register_rpc(servtab_t *sep) | ||
342 | { | ||
343 | int n; | ||
344 | struct sockaddr_in ir_sin; | ||
345 | struct protoent *pp; | ||
346 | socklen_t size; | ||
347 | |||
348 | if ((pp = getprotobyname(sep->se_proto + 4)) == NULL) { | ||
349 | bb_perror_msg("%s: getproto", sep->se_proto); | ||
350 | return; | ||
351 | } | ||
352 | size = sizeof ir_sin; | ||
353 | if (getsockname(sep->se_fd, (struct sockaddr *) &ir_sin, &size) < 0) { | ||
354 | bb_perror_msg("%s/%s: getsockname", | ||
355 | sep->se_service, sep->se_proto); | ||
356 | return; | ||
357 | } | ||
358 | |||
359 | for (n = sep->se_rpcversl; n <= sep->se_rpcversh; n++) { | ||
360 | (void) pmap_unset(sep->se_rpcprog, n); | ||
361 | if (!pmap_set(sep->se_rpcprog, n, pp->p_proto, ntohs(ir_sin.sin_port))) | ||
362 | bb_perror_msg("%s %s: pmap_set: %u %u %u %u", | ||
363 | sep->se_service, sep->se_proto, | ||
364 | sep->se_rpcprog, n, pp->p_proto, ntohs(ir_sin.sin_port)); | ||
365 | } | ||
366 | } | ||
367 | |||
368 | static void unregister_rpc(servtab_t *sep) | ||
369 | { | ||
370 | int n; | ||
371 | |||
372 | for (n = sep->se_rpcversl; n <= sep->se_rpcversh; n++) { | ||
373 | if (!pmap_unset(sep->se_rpcprog, n)) | ||
374 | bb_error_msg("pmap_unset(%u, %u)", sep->se_rpcprog, n); | ||
375 | } | ||
376 | } | ||
377 | #endif /* CONFIG_FEATURE_INETD_RPC */ | ||
378 | |||
379 | static void freeconfig(servtab_t *cp) | ||
380 | { | ||
381 | int i; | ||
382 | |||
383 | free(cp->se_hostaddr); | ||
384 | free(cp->se_service); | ||
385 | free(cp->se_proto); | ||
386 | free(cp->se_user); | ||
387 | free(cp->se_group); | ||
388 | free(cp->se_server); | ||
389 | for (i = 0; i < MAXARGV; i++) | ||
390 | free(cp->se_argv[i]); | ||
391 | } | ||
392 | |||
393 | static int bump_nofile (void) | ||
394 | { | ||
395 | #define FD_CHUNK 32 | ||
396 | |||
397 | struct rlimit rl; | ||
398 | |||
399 | if (getrlimit (RLIMIT_NOFILE, &rl) < 0) { | ||
400 | bb_perror_msg("getrlimit"); | ||
401 | return -1; | ||
402 | } | ||
403 | rl.rlim_cur = MIN(rl.rlim_max, rl.rlim_cur + FD_CHUNK); | ||
404 | rl.rlim_cur = MIN(FD_SETSIZE, rl.rlim_cur + FD_CHUNK); | ||
405 | if (rl.rlim_cur <= rlim_ofile_cur) { | ||
406 | bb_error_msg("bump_nofile: cannot extend file limit, max = %d", | ||
407 | (int) rl.rlim_cur); | ||
408 | return -1; | ||
409 | } | ||
410 | |||
411 | if (setrlimit(RLIMIT_NOFILE, &rl) < 0) { | ||
412 | bb_perror_msg("setrlimit"); | ||
413 | return -1; | ||
414 | } | ||
415 | |||
416 | rlim_ofile_cur = rl.rlim_cur; | ||
417 | return 0; | ||
418 | } | ||
419 | |||
420 | static void setup(servtab_t *sep) | ||
421 | { | ||
422 | int r; | ||
423 | |||
424 | sep->se_fd = socket(sep->se_family, sep->se_socktype, 0); | ||
425 | if (sep->se_fd < 0) { | ||
426 | bb_perror_msg("%s/%s: socket", sep->se_service, sep->se_proto); | ||
427 | return; | ||
428 | } | ||
429 | if (setsockopt_reuseaddr(sep->se_fd) < 0) | ||
430 | bb_perror_msg("setsockopt(SO_REUSEADDR)"); | ||
431 | |||
432 | #ifdef CONFIG_FEATURE_INETD_RPC | ||
433 | if (isrpcservice(sep)) { | ||
434 | struct passwd *pwd; | ||
435 | |||
436 | /* | ||
437 | * for RPC services, attempt to use a reserved port | ||
438 | * if they are going to be running as root. | ||
439 | * | ||
440 | * Also, zero out the port for all RPC services; let bind() | ||
441 | * find one. | ||
442 | */ | ||
443 | sep->se_ctrladdr_in.sin_port = 0; | ||
444 | if (sep->se_user && (pwd = getpwnam(sep->se_user)) && | ||
445 | pwd->pw_uid == 0 && uid == 0) | ||
446 | r = bindresvport(sep->se_fd, &sep->se_ctrladdr_in); | ||
447 | else { | ||
448 | r = bind(sep->se_fd, &sep->se_ctrladdr, sep->se_ctrladdr_size); | ||
449 | if (r == 0) { | ||
450 | socklen_t len = sep->se_ctrladdr_size; | ||
451 | int saveerrno = errno; | ||
452 | |||
453 | /* update se_ctrladdr_in.sin_port */ | ||
454 | r = getsockname(sep->se_fd, &sep->se_ctrladdr, &len); | ||
455 | if (r <= 0) | ||
456 | errno = saveerrno; | ||
457 | } | ||
458 | } | ||
459 | } else | ||
460 | #endif | ||
461 | r = bind(sep->se_fd, &sep->se_ctrladdr, sep->se_ctrladdr_size); | ||
462 | if (r < 0) { | ||
463 | bb_perror_msg("%s/%s (%d): bind", | ||
464 | sep->se_service, sep->se_proto, sep->se_ctrladdr.sa_family); | ||
465 | close(sep->se_fd); | ||
466 | sep->se_fd = -1; | ||
467 | if (!timingout) { | ||
468 | timingout = 1; | ||
469 | alarm(RETRYTIME); | ||
470 | } | ||
471 | return; | ||
472 | } | ||
473 | if (sep->se_socktype == SOCK_STREAM) | ||
474 | listen(sep->se_fd, global_queuelen); | ||
475 | |||
476 | FD_SET(sep->se_fd, &allsock); | ||
477 | nsock++; | ||
478 | if (sep->se_fd > maxsock) { | ||
479 | maxsock = sep->se_fd; | ||
480 | if ((rlim_t)maxsock > rlim_ofile_cur - FD_MARGIN) | ||
481 | bump_nofile(); | ||
482 | } | ||
483 | } | ||
484 | |||
485 | static char *nextline(void) | ||
486 | { | ||
487 | char *cp; | ||
488 | FILE *fd = fconfig; | ||
489 | |||
490 | if (fgets(line, sizeof(line), fd) == NULL) | ||
491 | return NULL; | ||
492 | cp = strchr(line, '\n'); | ||
493 | if (cp) | ||
494 | *cp = '\0'; | ||
495 | return line; | ||
496 | } | ||
497 | |||
498 | static char *skip(char **cpp) /* int report; */ | ||
499 | { | ||
500 | char *cp = *cpp; | ||
501 | char *start; | ||
502 | |||
503 | /* erp: */ | ||
504 | if (*cpp == NULL) { | ||
505 | /* if (report) */ | ||
506 | /* bb_error_msg("syntax error in inetd config file"); */ | ||
507 | return NULL; | ||
508 | } | ||
509 | |||
510 | again: | ||
511 | while (*cp == ' ' || *cp == '\t') | ||
512 | cp++; | ||
513 | if (*cp == '\0') { | ||
514 | int c; | ||
515 | |||
516 | c = getc(fconfig); | ||
517 | (void) ungetc(c, fconfig); | ||
518 | if (c == ' ' || c == '\t') | ||
519 | if ((cp = nextline())) | ||
520 | goto again; | ||
521 | *cpp = NULL; | ||
522 | /* goto erp; */ | ||
523 | return NULL; | ||
524 | } | ||
525 | start = cp; | ||
526 | while (*cp && *cp != ' ' && *cp != '\t') | ||
527 | cp++; | ||
528 | if (*cp != '\0') | ||
529 | *cp++ = '\0'; | ||
530 | /* if ((*cpp = cp) == NULL) */ | ||
531 | /* goto erp; */ | ||
532 | |||
533 | *cpp = cp; | ||
534 | return start; | ||
535 | } | ||
536 | |||
537 | static servtab_t *new_servtab(void) | ||
538 | { | ||
539 | return xmalloc(sizeof(servtab_t)); | ||
540 | } | ||
541 | |||
542 | static servtab_t *dupconfig(servtab_t *sep) | ||
543 | { | ||
544 | servtab_t *newtab; | ||
545 | int argc; | ||
546 | |||
547 | newtab = new_servtab(); | ||
548 | memset(newtab, 0, sizeof(servtab_t)); | ||
549 | newtab->se_service = xstrdup(sep->se_service); | ||
550 | newtab->se_socktype = sep->se_socktype; | ||
551 | newtab->se_family = sep->se_family; | ||
552 | newtab->se_proto = xstrdup(sep->se_proto); | ||
553 | #ifdef CONFIG_FEATURE_INETD_RPC | ||
554 | newtab->se_rpcprog = sep->se_rpcprog; | ||
555 | newtab->se_rpcversl = sep->se_rpcversl; | ||
556 | newtab->se_rpcversh = sep->se_rpcversh; | ||
557 | #endif | ||
558 | newtab->se_wait = sep->se_wait; | ||
559 | newtab->se_user = xstrdup(sep->se_user); | ||
560 | newtab->se_group = xstrdup(sep->se_group); | ||
561 | #ifdef INETD_FEATURE_ENABLED | ||
562 | newtab->se_bi = sep->se_bi; | ||
563 | #endif | ||
564 | newtab->se_server = xstrdup(sep->se_server); | ||
565 | |||
566 | for (argc = 0; argc <= MAXARGV; argc++) | ||
567 | newtab->se_argv[argc] = xstrdup(sep->se_argv[argc]); | ||
568 | newtab->se_max = sep->se_max; | ||
569 | |||
570 | return newtab; | ||
571 | } | ||
572 | |||
573 | static servtab_t *getconfigent(void) | ||
574 | { | ||
575 | servtab_t *sep; | ||
576 | int argc; | ||
577 | char *cp, *arg; | ||
578 | char *hostdelim; | ||
579 | servtab_t *nsep; | ||
580 | servtab_t *psep; | ||
581 | |||
582 | sep = new_servtab(); | ||
583 | |||
584 | /* memset(sep, 0, sizeof *sep); */ | ||
585 | more: | ||
586 | /* freeconfig(sep); */ | ||
587 | |||
588 | while ((cp = nextline()) && *cp == '#') /* skip comment line */; | ||
589 | if (cp == NULL) { | ||
590 | /* free(sep); */ | ||
591 | return NULL; | ||
592 | } | ||
593 | |||
594 | memset((char *) sep, 0, sizeof *sep); | ||
595 | arg = skip(&cp); | ||
596 | if (arg == NULL) { | ||
597 | /* A blank line. */ | ||
598 | goto more; | ||
599 | } | ||
600 | |||
601 | /* Check for a host name. */ | ||
602 | hostdelim = strrchr(arg, ':'); | ||
603 | if (hostdelim) { | ||
604 | *hostdelim = '\0'; | ||
605 | sep->se_hostaddr = xstrdup(arg); | ||
606 | arg = hostdelim + 1; | ||
607 | /* | ||
608 | * If the line is of the form `host:', then just change the | ||
609 | * default host for the following lines. | ||
610 | */ | ||
611 | if (*arg == '\0') { | ||
612 | arg = skip(&cp); | ||
613 | if (cp == NULL) { | ||
614 | free(defhost); | ||
615 | defhost = sep->se_hostaddr; | ||
616 | goto more; | ||
617 | } | ||
618 | } | ||
619 | } else | ||
620 | sep->se_hostaddr = xxstrdup(defhost); | ||
621 | |||
622 | sep->se_service = xxstrdup(arg); | ||
623 | arg = skip(&cp); | ||
624 | |||
625 | if (strcmp(arg, "stream") == 0) | ||
626 | sep->se_socktype = SOCK_STREAM; | ||
627 | else if (strcmp(arg, "dgram") == 0) | ||
628 | sep->se_socktype = SOCK_DGRAM; | ||
629 | else if (strcmp(arg, "rdm") == 0) | ||
630 | sep->se_socktype = SOCK_RDM; | ||
631 | else if (strcmp(arg, "seqpacket") == 0) | ||
632 | sep->se_socktype = SOCK_SEQPACKET; | ||
633 | else if (strcmp(arg, "raw") == 0) | ||
634 | sep->se_socktype = SOCK_RAW; | ||
635 | else | ||
636 | sep->se_socktype = -1; | ||
637 | |||
638 | sep->se_proto = xxstrdup(skip(&cp)); | ||
639 | |||
640 | if (strcmp(sep->se_proto, "unix") == 0) { | ||
641 | sep->se_family = AF_UNIX; | ||
642 | } else { | ||
643 | sep->se_family = AF_INET; | ||
644 | if (sep->se_proto[strlen(sep->se_proto) - 1] == '6') | ||
645 | #ifdef CONFIG_FEATURE_IPV6 | ||
646 | sep->se_family = AF_INET6; | ||
647 | #else | ||
648 | bb_error_msg("%s: IPV6 not supported", sep->se_proto); | ||
649 | #endif | ||
650 | if (strncmp(sep->se_proto, "rpc/", 4) == 0) { | ||
651 | #ifdef CONFIG_FEATURE_INETD_RPC | ||
652 | char *p, *ccp; | ||
653 | long l; | ||
654 | |||
655 | p = strchr(sep->se_service, '/'); | ||
656 | if (p == 0) { | ||
657 | bb_error_msg("%s: no rpc version", sep->se_service); | ||
658 | goto more; | ||
659 | } | ||
660 | *p++ = '\0'; | ||
661 | l = strtol(p, &ccp, 0); | ||
662 | if (ccp == p || l < 0 || l > INT_MAX) { | ||
663 | badafterall: | ||
664 | bb_error_msg("%s/%s: bad rpc version", sep->se_service, p); | ||
665 | goto more; | ||
666 | } | ||
667 | sep->se_rpcversl = sep->se_rpcversh = l; | ||
668 | if (*ccp == '-') { | ||
669 | p = ccp + 1; | ||
670 | l = strtol(p, &ccp, 0); | ||
671 | if (ccp == p || l < 0 || l > INT_MAX || l < sep->se_rpcversl || *ccp) | ||
672 | goto badafterall; | ||
673 | sep->se_rpcversh = l; | ||
674 | } else if (*ccp != '\0') | ||
675 | goto badafterall; | ||
676 | #else | ||
677 | bb_error_msg("%s: rpc services not supported", sep->se_service); | ||
678 | #endif | ||
679 | } | ||
680 | } | ||
681 | arg = skip(&cp); | ||
682 | if (arg == NULL) | ||
683 | goto more; | ||
684 | |||
685 | { | ||
686 | char *s = strchr(arg, '.'); | ||
687 | if (s) { | ||
688 | *s++ = '\0'; | ||
689 | sep->se_max = xatoi(s); | ||
690 | } else | ||
691 | sep->se_max = toomany; | ||
692 | } | ||
693 | sep->se_wait = strcmp(arg, "wait") == 0; | ||
694 | /* if ((arg = skip(&cp, 1)) == NULL) */ | ||
695 | /* goto more; */ | ||
696 | sep->se_user = xxstrdup(skip(&cp)); | ||
697 | arg = strchr(sep->se_user, '.'); | ||
698 | if (arg == NULL) | ||
699 | arg = strchr(sep->se_user, ':'); | ||
700 | if (arg) { | ||
701 | *arg++ = '\0'; | ||
702 | sep->se_group = xstrdup(arg); | ||
703 | } | ||
704 | /* if ((arg = skip(&cp, 1)) == NULL) */ | ||
705 | /* goto more; */ | ||
706 | |||
707 | sep->se_server = xxstrdup(skip(&cp)); | ||
708 | if (strcmp(sep->se_server, "internal") == 0) { | ||
709 | #ifdef INETD_FEATURE_ENABLED | ||
710 | const struct builtin *bi; | ||
711 | |||
712 | for (bi = builtins; bi->bi_service; bi++) | ||
713 | if (bi->bi_socktype == sep->se_socktype && | ||
714 | strcmp(bi->bi_service, sep->se_service) == 0) | ||
715 | break; | ||
716 | if (bi->bi_service == 0) { | ||
717 | bb_error_msg("internal service %s unknown", sep->se_service); | ||
718 | goto more; | ||
719 | } | ||
720 | sep->se_bi = bi; | ||
721 | sep->se_wait = bi->bi_wait; | ||
722 | #else | ||
723 | bb_perror_msg("internal service %s unknown", sep->se_service); | ||
724 | goto more; | ||
725 | #endif | ||
726 | } | ||
727 | #ifdef INETD_FEATURE_ENABLED | ||
728 | else | ||
729 | sep->se_bi = NULL; | ||
730 | #endif | ||
731 | argc = 0; | ||
732 | for (arg = skip(&cp); cp; arg = skip(&cp)) { | ||
733 | if (argc < MAXARGV) | ||
734 | sep->se_argv[argc++] = xxstrdup(arg); | ||
735 | } | ||
736 | while (argc <= MAXARGV) | ||
737 | sep->se_argv[argc++] = NULL; | ||
738 | |||
739 | /* | ||
740 | * Now that we've processed the entire line, check if the hostname | ||
741 | * specifier was a comma separated list of hostnames. If so | ||
742 | * we'll make new entries for each address. | ||
743 | */ | ||
744 | while ((hostdelim = strrchr(sep->se_hostaddr, ',')) != NULL) { | ||
745 | nsep = dupconfig(sep); | ||
746 | |||
747 | /* | ||
748 | * NULL terminate the hostname field of the existing entry, | ||
749 | * and make a dup for the new entry. | ||
750 | */ | ||
751 | *hostdelim++ = '\0'; | ||
752 | nsep->se_hostaddr = xstrdup(hostdelim); | ||
753 | |||
754 | nsep->se_next = sep->se_next; | ||
755 | sep->se_next = nsep; | ||
756 | } | ||
757 | |||
758 | nsep = sep; | ||
759 | while (nsep != NULL) { | ||
760 | nsep->se_checked = 1; | ||
761 | if (nsep->se_family == AF_INET) { | ||
762 | if (!strcmp(nsep->se_hostaddr, "*")) | ||
763 | nsep->se_ctrladdr_in.sin_addr.s_addr = INADDR_ANY; | ||
764 | else if (!inet_aton(nsep->se_hostaddr, &nsep->se_ctrladdr_in.sin_addr)) { | ||
765 | struct hostent *hp; | ||
766 | |||
767 | hp = gethostbyname(nsep->se_hostaddr); | ||
768 | if (hp == 0) { | ||
769 | bb_error_msg("%s: unknown host", nsep->se_hostaddr); | ||
770 | nsep->se_checked = 0; | ||
771 | goto skip; | ||
772 | } else if (hp->h_addrtype != AF_INET) { | ||
773 | bb_error_msg("%s: address isn't an Internet " | ||
774 | "address", nsep->se_hostaddr); | ||
775 | nsep->se_checked = 0; | ||
776 | goto skip; | ||
777 | } else { | ||
778 | int i = 1; | ||
779 | |||
780 | memmove(&nsep->se_ctrladdr_in.sin_addr, | ||
781 | hp->h_addr_list[0], sizeof(struct in_addr)); | ||
782 | while (hp->h_addr_list[i] != NULL) { | ||
783 | psep = dupconfig(nsep); | ||
784 | psep->se_hostaddr = xxstrdup(nsep->se_hostaddr); | ||
785 | psep->se_checked = 1; | ||
786 | memmove(&psep->se_ctrladdr_in.sin_addr, | ||
787 | hp->h_addr_list[i], sizeof(struct in_addr)); | ||
788 | psep->se_ctrladdr_size = sizeof(psep->se_ctrladdr_in); | ||
789 | i++; | ||
790 | /* Prepend to list, don't want to look up */ | ||
791 | /* its hostname again. */ | ||
792 | psep->se_next = sep; | ||
793 | sep = psep; | ||
794 | } | ||
795 | } | ||
796 | } | ||
797 | } | ||
798 | /* XXX BUG?: is this skip: label supposed to remain? */ | ||
799 | skip: | ||
800 | nsep = nsep->se_next; | ||
801 | } | ||
802 | |||
803 | /* | ||
804 | * Finally, free any entries which failed the gethostbyname | ||
805 | * check. | ||
806 | */ | ||
807 | psep = NULL; | ||
808 | nsep = sep; | ||
809 | while (nsep != NULL) { | ||
810 | servtab_t *tsep; | ||
811 | |||
812 | if (nsep->se_checked == 0) { | ||
813 | tsep = nsep; | ||
814 | if (psep == NULL) { | ||
815 | sep = nsep->se_next; | ||
816 | nsep = sep; | ||
817 | } else { | ||
818 | nsep = nsep->se_next; | ||
819 | psep->se_next = nsep; | ||
820 | } | ||
821 | freeconfig(tsep); | ||
822 | } else { | ||
823 | nsep->se_checked = 0; | ||
824 | psep = nsep; | ||
825 | nsep = nsep->se_next; | ||
826 | } | ||
827 | } | ||
828 | |||
829 | return sep; | ||
830 | } | ||
831 | |||
832 | #define Block_Using_Signals(m) do { \ | ||
833 | sigemptyset(&m); \ | ||
834 | sigaddset(&m, SIGCHLD); \ | ||
835 | sigaddset(&m, SIGHUP); \ | ||
836 | sigaddset(&m, SIGALRM); \ | ||
837 | sigprocmask(SIG_BLOCK, &m, NULL); \ | ||
838 | } while(0) | ||
839 | |||
840 | static servtab_t *enter(servtab_t *cp) | ||
841 | { | ||
842 | servtab_t *sep; | ||
843 | sigset_t omask; | ||
844 | |||
845 | sep = new_servtab(); | ||
846 | *sep = *cp; | ||
847 | sep->se_fd = -1; | ||
848 | #ifdef CONFIG_FEATURE_INETD_RPC | ||
849 | sep->se_rpcprog = -1; | ||
850 | #endif | ||
851 | Block_Using_Signals(omask); | ||
852 | sep->se_next = servtab; | ||
853 | servtab = sep; | ||
854 | sigprocmask(SIG_UNBLOCK, &omask, NULL); | ||
855 | return sep; | ||
856 | } | ||
857 | |||
858 | static int matchconf(servtab_t *old, servtab_t *new) | ||
859 | { | ||
860 | if (strcmp(old->se_service, new->se_service) != 0) | ||
861 | return 0; | ||
862 | |||
863 | if (strcmp(old->se_hostaddr, new->se_hostaddr) != 0) | ||
864 | return 0; | ||
865 | |||
866 | if (strcmp(old->se_proto, new->se_proto) != 0) | ||
867 | return 0; | ||
868 | |||
869 | /* | ||
870 | * If the new servtab is bound to a specific address, check that the | ||
871 | * old servtab is bound to the same entry. If the new service is not | ||
872 | * bound to a specific address then the check of se_hostaddr above | ||
873 | * is sufficient. | ||
874 | */ | ||
875 | |||
876 | if (old->se_family == AF_INET && new->se_family == AF_INET && | ||
877 | memcmp(&old->se_ctrladdr_in.sin_addr, | ||
878 | &new->se_ctrladdr_in.sin_addr, | ||
879 | sizeof(new->se_ctrladdr_in.sin_addr)) != 0) | ||
880 | return 0; | ||
881 | |||
882 | #ifdef CONFIG_FEATURE_IPV6 | ||
883 | if (old->se_family == AF_INET6 && new->se_family == AF_INET6 && | ||
884 | memcmp(&old->se_ctrladdr_in6.sin6_addr, | ||
885 | &new->se_ctrladdr_in6.sin6_addr, | ||
886 | sizeof(new->se_ctrladdr_in6.sin6_addr)) != 0) | ||
887 | return 0; | ||
888 | #endif | ||
889 | return 1; | ||
890 | } | ||
891 | |||
892 | static void config(int sig ATTRIBUTE_UNUSED) | ||
893 | { | ||
894 | servtab_t *sep, *cp, **sepp; | ||
895 | sigset_t omask; | ||
896 | size_t n; | ||
897 | char protoname[10]; | ||
898 | |||
899 | if (!setconfig()) { | ||
900 | bb_perror_msg("%s", CONFIG); | ||
901 | return; | ||
902 | } | ||
903 | for (sep = servtab; sep; sep = sep->se_next) | ||
904 | sep->se_checked = 0; | ||
905 | cp = getconfigent(); | ||
906 | while (cp != NULL) { | ||
907 | for (sep = servtab; sep; sep = sep->se_next) | ||
908 | if (matchconf(sep, cp)) | ||
909 | break; | ||
910 | |||
911 | if (sep != 0) { | ||
912 | int i; | ||
913 | |||
914 | #define SWAP(type, a, b) do {type c=(type)a; a=(type)b; b=(type)c;} while (0) | ||
915 | |||
916 | Block_Using_Signals(omask); | ||
917 | /* | ||
918 | * sep->se_wait may be holding the pid of a daemon | ||
919 | * that we're waiting for. If so, don't overwrite | ||
920 | * it unless the config file explicitly says don't | ||
921 | * wait. | ||
922 | */ | ||
923 | if ( | ||
924 | #ifdef INETD_FEATURE_ENABLED | ||
925 | cp->se_bi == 0 && | ||
926 | #endif | ||
927 | (sep->se_wait == 1 || cp->se_wait == 0)) | ||
928 | sep->se_wait = cp->se_wait; | ||
929 | SWAP(int, cp->se_max, sep->se_max); | ||
930 | SWAP(char *, sep->se_user, cp->se_user); | ||
931 | SWAP(char *, sep->se_group, cp->se_group); | ||
932 | SWAP(char *, sep->se_server, cp->se_server); | ||
933 | for (i = 0; i < MAXARGV; i++) | ||
934 | SWAP(char *, sep->se_argv[i], cp->se_argv[i]); | ||
935 | #undef SWAP | ||
936 | |||
937 | #ifdef CONFIG_FEATURE_INETD_RPC | ||
938 | if (isrpcservice(sep)) | ||
939 | unregister_rpc(sep); | ||
940 | sep->se_rpcversl = cp->se_rpcversl; | ||
941 | sep->se_rpcversh = cp->se_rpcversh; | ||
942 | #endif | ||
943 | sigprocmask(SIG_UNBLOCK, &omask, NULL); | ||
944 | freeconfig(cp); | ||
945 | } else { | ||
946 | sep = enter(cp); | ||
947 | } | ||
948 | sep->se_checked = 1; | ||
949 | |||
950 | switch (sep->se_family) { | ||
951 | case AF_UNIX: | ||
952 | if (sep->se_fd != -1) | ||
953 | break; | ||
954 | (void) unlink(sep->se_service); | ||
955 | n = strlen(sep->se_service); | ||
956 | if (n > sizeof sep->se_ctrladdr_un.sun_path - 1) | ||
957 | n = sizeof sep->se_ctrladdr_un.sun_path - 1; | ||
958 | safe_strncpy(sep->se_ctrladdr_un.sun_path, sep->se_service, n + 1); | ||
959 | sep->se_ctrladdr_un.sun_family = AF_UNIX; | ||
960 | sep->se_ctrladdr_size = n + sizeof sep->se_ctrladdr_un.sun_family; | ||
961 | setup(sep); | ||
962 | break; | ||
963 | case AF_INET: | ||
964 | sep->se_ctrladdr_in.sin_family = AF_INET; | ||
965 | /* se_ctrladdr_in was set in getconfigent */ | ||
966 | sep->se_ctrladdr_size = sizeof sep->se_ctrladdr_in; | ||
967 | |||
968 | #ifdef CONFIG_FEATURE_INETD_RPC | ||
969 | if (isrpcservice(sep)) { | ||
970 | struct rpcent *rp; | ||
971 | // FIXME: atoi_or_else(str, 0) would be handy here | ||
972 | sep->se_rpcprog = atoi(sep->se_service); | ||
973 | if (sep->se_rpcprog == 0) { | ||
974 | rp = getrpcbyname(sep->se_service); | ||
975 | if (rp == 0) { | ||
976 | bb_error_msg("%s: unknown rpc service", sep->se_service); | ||
977 | goto serv_unknown; | ||
978 | } | ||
979 | sep->se_rpcprog = rp->r_number; | ||
980 | } | ||
981 | if (sep->se_fd == -1) | ||
982 | setup(sep); | ||
983 | if (sep->se_fd != -1) | ||
984 | register_rpc(sep); | ||
985 | } else | ||
986 | #endif | ||
987 | { | ||
988 | u_short port = htons(atoi(sep->se_service)); | ||
989 | // FIXME: atoi_or_else(str, 0) would be handy here | ||
990 | if (!port) { | ||
991 | /*XXX*/ strncpy(protoname, sep->se_proto, sizeof(protoname)); | ||
992 | if (isdigit(protoname[strlen(protoname) - 1])) | ||
993 | protoname[strlen(protoname) - 1] = '\0'; | ||
994 | sp = getservbyname(sep->se_service, protoname); | ||
995 | if (sp == 0) { | ||
996 | bb_error_msg("%s/%s: unknown service", | ||
997 | sep->se_service, sep->se_proto); | ||
998 | goto serv_unknown; | ||
999 | } | ||
1000 | port = sp->s_port; | ||
1001 | } | ||
1002 | if (port != sep->se_ctrladdr_in.sin_port) { | ||
1003 | sep->se_ctrladdr_in.sin_port = port; | ||
1004 | if (sep->se_fd != -1) { | ||
1005 | FD_CLR(sep->se_fd, &allsock); | ||
1006 | nsock--; | ||
1007 | (void) close(sep->se_fd); | ||
1008 | } | ||
1009 | sep->se_fd = -1; | ||
1010 | } | ||
1011 | if (sep->se_fd == -1) | ||
1012 | setup(sep); | ||
1013 | } | ||
1014 | break; | ||
1015 | #ifdef CONFIG_FEATURE_IPV6 | ||
1016 | case AF_INET6: | ||
1017 | sep->se_ctrladdr_in6.sin6_family = AF_INET6; | ||
1018 | /* se_ctrladdr_in was set in getconfigent */ | ||
1019 | sep->se_ctrladdr_size = sizeof sep->se_ctrladdr_in6; | ||
1020 | |||
1021 | #ifdef CONFIG_FEATURE_INETD_RPC | ||
1022 | if (isrpcservice(sep)) { | ||
1023 | struct rpcent *rp; | ||
1024 | |||
1025 | sep->se_rpcprog = atoi(sep->se_service); | ||
1026 | if (sep->se_rpcprog == 0) { | ||
1027 | rp = getrpcbyname(sep->se_service); | ||
1028 | if (rp == 0) { | ||
1029 | bb_error_msg("%s: unknown rpc service", sep->se_service); | ||
1030 | goto serv_unknown; | ||
1031 | } | ||
1032 | sep->se_rpcprog = rp->r_number; | ||
1033 | } | ||
1034 | if (sep->se_fd == -1) | ||
1035 | setup(sep); | ||
1036 | if (sep->se_fd != -1) | ||
1037 | register_rpc(sep); | ||
1038 | } else | ||
1039 | #endif | ||
1040 | { | ||
1041 | u_short port = htons(atoi(sep->se_service)); | ||
1042 | |||
1043 | if (!port) { | ||
1044 | /*XXX*/ strncpy(protoname, sep->se_proto, sizeof(protoname)); | ||
1045 | if (isdigit(protoname[strlen(protoname) - 1])) | ||
1046 | protoname[strlen(protoname) - 1] = '\0'; | ||
1047 | sp = getservbyname(sep->se_service, protoname); | ||
1048 | if (sp == 0) { | ||
1049 | bb_error_msg("%s/%s: unknown service", | ||
1050 | sep->se_service, sep->se_proto); | ||
1051 | goto serv_unknown; | ||
1052 | } | ||
1053 | port = sp->s_port; | ||
1054 | } | ||
1055 | if (port != sep->se_ctrladdr_in6.sin6_port) { | ||
1056 | sep->se_ctrladdr_in6.sin6_port = port; | ||
1057 | if (sep->se_fd != -1) { | ||
1058 | FD_CLR(sep->se_fd, &allsock); | ||
1059 | nsock--; | ||
1060 | (void) close(sep->se_fd); | ||
1061 | } | ||
1062 | sep->se_fd = -1; | ||
1063 | } | ||
1064 | if (sep->se_fd == -1) | ||
1065 | setup(sep); | ||
1066 | } | ||
1067 | break; | ||
1068 | #endif /* CONFIG_FEATURE_IPV6 */ | ||
1069 | } | ||
1070 | serv_unknown: | ||
1071 | if (cp->se_next != NULL) { | ||
1072 | servtab_t *tmp = cp; | ||
1073 | |||
1074 | cp = cp->se_next; | ||
1075 | free(tmp); | ||
1076 | } else { | ||
1077 | free(cp); | ||
1078 | cp = getconfigent(); | ||
1079 | } | ||
1080 | } | ||
1081 | endconfig(); | ||
1082 | /* | ||
1083 | * Purge anything not looked at above. | ||
1084 | */ | ||
1085 | Block_Using_Signals(omask); | ||
1086 | sepp = &servtab; | ||
1087 | while ((sep = *sepp)) { | ||
1088 | if (sep->se_checked) { | ||
1089 | sepp = &sep->se_next; | ||
1090 | continue; | ||
1091 | } | ||
1092 | *sepp = sep->se_next; | ||
1093 | if (sep->se_fd != -1) { | ||
1094 | FD_CLR(sep->se_fd, &allsock); | ||
1095 | nsock--; | ||
1096 | (void) close(sep->se_fd); | ||
1097 | } | ||
1098 | #ifdef CONFIG_FEATURE_INETD_RPC | ||
1099 | if (isrpcservice(sep)) | ||
1100 | unregister_rpc(sep); | ||
1101 | #endif | ||
1102 | if (sep->se_family == AF_UNIX) | ||
1103 | (void) unlink(sep->se_service); | ||
1104 | freeconfig(sep); | ||
1105 | free(sep); | ||
1106 | } | ||
1107 | sigprocmask(SIG_UNBLOCK, &omask, NULL); | ||
1108 | } | ||
1109 | |||
1110 | |||
1111 | static void reapchild(int sig ATTRIBUTE_UNUSED) | ||
1112 | { | ||
1113 | pid_t pid; | ||
1114 | int save_errno = errno, status; | ||
1115 | servtab_t *sep; | ||
1116 | |||
1117 | for (;;) { | ||
1118 | pid = wait3(&status, WNOHANG, NULL); | ||
1119 | if (pid <= 0) | ||
1120 | break; | ||
1121 | for (sep = servtab; sep; sep = sep->se_next) | ||
1122 | if (sep->se_wait == pid) { | ||
1123 | if (WIFEXITED(status) && WEXITSTATUS(status)) | ||
1124 | bb_error_msg("%s: exit status 0x%x", | ||
1125 | sep->se_server, WEXITSTATUS(status)); | ||
1126 | else if (WIFSIGNALED(status)) | ||
1127 | bb_error_msg("%s: exit signal 0x%x", | ||
1128 | sep->se_server, WTERMSIG(status)); | ||
1129 | sep->se_wait = 1; | ||
1130 | FD_SET(sep->se_fd, &allsock); | ||
1131 | nsock++; | ||
1132 | } | ||
1133 | } | ||
1134 | errno = save_errno; | ||
1135 | } | ||
1136 | |||
1137 | static void retry(int sig ATTRIBUTE_UNUSED) | ||
1138 | { | ||
1139 | servtab_t *sep; | ||
1140 | |||
1141 | timingout = 0; | ||
1142 | for (sep = servtab; sep; sep = sep->se_next) { | ||
1143 | if (sep->se_fd == -1) { | ||
1144 | switch (sep->se_family) { | ||
1145 | case AF_UNIX: | ||
1146 | case AF_INET: | ||
1147 | #ifdef CONFIG_FEATURE_IPV6 | ||
1148 | case AF_INET6: | ||
1149 | #endif | ||
1150 | setup(sep); | ||
1151 | #ifdef CONFIG_FEATURE_INETD_RPC | ||
1152 | if (sep->se_fd != -1 && isrpcservice(sep)) | ||
1153 | register_rpc(sep); | ||
1154 | #endif | ||
1155 | break; | ||
1156 | } | ||
1157 | } | ||
1158 | } | ||
1159 | } | ||
1160 | |||
1161 | static void goaway(int sig ATTRIBUTE_UNUSED) | ||
1162 | { | ||
1163 | servtab_t *sep; | ||
1164 | |||
1165 | /* XXX signal race walking sep list */ | ||
1166 | for (sep = servtab; sep; sep = sep->se_next) { | ||
1167 | if (sep->se_fd == -1) | ||
1168 | continue; | ||
1169 | |||
1170 | switch (sep->se_family) { | ||
1171 | case AF_UNIX: | ||
1172 | (void) unlink(sep->se_service); | ||
1173 | break; | ||
1174 | case AF_INET: | ||
1175 | #ifdef CONFIG_FEATURE_IPV6 | ||
1176 | case AF_INET6: | ||
1177 | #endif | ||
1178 | #ifdef CONFIG_FEATURE_INETD_RPC | ||
1179 | if (sep->se_wait == 1 && isrpcservice(sep)) | ||
1180 | unregister_rpc(sep); /* XXX signal race */ | ||
1181 | #endif | ||
1182 | break; | ||
1183 | } | ||
1184 | (void) close(sep->se_fd); | ||
1185 | } | ||
1186 | (void) unlink(_PATH_INETDPID); | ||
1187 | exit(0); | ||
1188 | } | ||
1189 | |||
1190 | |||
1191 | #ifdef INETD_SETPROCTITLE | ||
1192 | static char **Argv; | ||
1193 | static char *LastArg; | ||
1194 | |||
1195 | static void | ||
1196 | inetd_setproctitle(char *a, int s) | ||
1197 | { | ||
1198 | socklen_t size; | ||
1199 | char *cp; | ||
1200 | struct sockaddr_in prt_sin; | ||
1201 | char buf[80]; | ||
1202 | |||
1203 | cp = Argv[0]; | ||
1204 | size = sizeof(prt_sin); | ||
1205 | (void) snprintf(buf, sizeof buf, "-%s", a); | ||
1206 | if (getpeername(s, (struct sockaddr *) &prt_sin, &size) == 0) { | ||
1207 | char *sa = inet_ntoa(prt_sin.sin_addr); | ||
1208 | |||
1209 | buf[sizeof(buf) - 1 - strlen(sa) - 3] = '\0'; | ||
1210 | strcat(buf, " ["); | ||
1211 | strcat(buf, sa); | ||
1212 | strcat(buf, "]"); | ||
1213 | } | ||
1214 | strncpy(cp, buf, LastArg - cp); | ||
1215 | cp += strlen(cp); | ||
1216 | while (cp < LastArg) | ||
1217 | *cp++ = ' '; | ||
1218 | } | ||
1219 | #endif | ||
1220 | |||
1221 | |||
1222 | int | ||
1223 | inetd_main(int argc, char *argv[]) | ||
1224 | { | ||
1225 | servtab_t *sep; | ||
1226 | struct passwd *pwd; | ||
1227 | struct group *grp = NULL; | ||
1228 | int tmpint; | ||
1229 | struct sigaction sa, sapipe; | ||
1230 | int opt; | ||
1231 | pid_t pid; | ||
1232 | char buf[50]; | ||
1233 | char *stoomany; | ||
1234 | sigset_t omask, wait_mask; | ||
1235 | |||
1236 | #ifdef INETD_SETPROCTITLE | ||
1237 | extern char **environ; | ||
1238 | char **envp = environ; | ||
1239 | |||
1240 | Argv = argv; | ||
1241 | if (envp == 0 || *envp == 0) | ||
1242 | envp = argv; | ||
1243 | while (*envp) | ||
1244 | envp++; | ||
1245 | LastArg = envp[-1] + strlen(envp[-1]); | ||
1246 | #endif | ||
1247 | |||
1248 | openlog(applet_name, LOG_PID | LOG_NOWAIT, LOG_DAEMON); | ||
1249 | |||
1250 | opt = getopt32(argc, argv, "R:f", &stoomany); | ||
1251 | if(opt & 1) { | ||
1252 | toomany = xatoi_u(stoomany); | ||
1253 | } | ||
1254 | argc -= optind; | ||
1255 | argv += optind; | ||
1256 | |||
1257 | uid = getuid(); | ||
1258 | if (uid != 0) | ||
1259 | CONFIG = NULL; | ||
1260 | if (argc > 0) | ||
1261 | CONFIG = argv[0]; | ||
1262 | if (CONFIG == NULL) | ||
1263 | bb_error_msg_and_die("non-root must specify a config file"); | ||
1264 | |||
1265 | if (!(opt & 2)) { | ||
1266 | #ifdef BB_NOMMU | ||
1267 | /* reexec for vfork() do continue parent */ | ||
1268 | vfork_daemon_rexec(0, 0, argc, argv, "-f"); | ||
1269 | #else | ||
1270 | xdaemon(0, 0); | ||
1271 | #endif | ||
1272 | } else { | ||
1273 | setsid(); | ||
1274 | } | ||
1275 | logmode = LOGMODE_SYSLOG; | ||
1276 | |||
1277 | if (uid == 0) { | ||
1278 | gid_t gid = getgid(); | ||
1279 | |||
1280 | /* If run by hand, ensure groups vector gets trashed */ | ||
1281 | setgroups(1, &gid); | ||
1282 | } | ||
1283 | |||
1284 | { | ||
1285 | FILE *fp = fopen(_PATH_INETDPID, "w"); | ||
1286 | |||
1287 | if (fp != NULL) { | ||
1288 | fprintf(fp, "%u\n", getpid()); | ||
1289 | (void) fclose(fp); | ||
1290 | } | ||
1291 | } | ||
1292 | |||
1293 | if (getrlimit(RLIMIT_NOFILE, &rlim_ofile) < 0) { | ||
1294 | bb_perror_msg("getrlimit"); | ||
1295 | } else { | ||
1296 | rlim_ofile_cur = rlim_ofile.rlim_cur; | ||
1297 | if (rlim_ofile_cur == RLIM_INFINITY) /* ! */ | ||
1298 | rlim_ofile_cur = OPEN_MAX; | ||
1299 | } | ||
1300 | |||
1301 | memset((char *) &sa, 0, sizeof(sa)); | ||
1302 | sigemptyset(&sa.sa_mask); | ||
1303 | sigaddset(&sa.sa_mask, SIGALRM); | ||
1304 | sigaddset(&sa.sa_mask, SIGCHLD); | ||
1305 | sigaddset(&sa.sa_mask, SIGHUP); | ||
1306 | sa.sa_handler = retry; | ||
1307 | sigaction(SIGALRM, &sa, NULL); | ||
1308 | config(SIGHUP); | ||
1309 | sa.sa_handler = config; | ||
1310 | sigaction(SIGHUP, &sa, NULL); | ||
1311 | sa.sa_handler = reapchild; | ||
1312 | sigaction(SIGCHLD, &sa, NULL); | ||
1313 | sa.sa_handler = goaway; | ||
1314 | sigaction(SIGTERM, &sa, NULL); | ||
1315 | sa.sa_handler = goaway; | ||
1316 | sigaction(SIGINT, &sa, NULL); | ||
1317 | sa.sa_handler = SIG_IGN; | ||
1318 | sigaction(SIGPIPE, &sa, &sapipe); | ||
1319 | memset(&wait_mask, 0, sizeof(wait_mask)); | ||
1320 | { | ||
1321 | /* space for daemons to overwrite environment for ps */ | ||
1322 | #define DUMMYSIZE 100 | ||
1323 | char dummy[DUMMYSIZE]; | ||
1324 | |||
1325 | (void) memset(dummy, 'x', DUMMYSIZE - 1); | ||
1326 | dummy[DUMMYSIZE - 1] = '\0'; | ||
1327 | |||
1328 | (void) setenv("inetd_dummy", dummy, 1); | ||
1329 | } | ||
1330 | |||
1331 | for (;;) { | ||
1332 | int n, ctrl = -1; | ||
1333 | fd_set readable; | ||
1334 | |||
1335 | if (nsock == 0) { | ||
1336 | Block_Using_Signals(omask); | ||
1337 | while (nsock == 0) | ||
1338 | sigsuspend(&wait_mask); | ||
1339 | sigprocmask(SIG_UNBLOCK, &omask, NULL); | ||
1340 | } | ||
1341 | |||
1342 | readable = allsock; | ||
1343 | n = select(maxsock + 1, &readable, NULL, NULL, NULL); | ||
1344 | if (n <= 0) { | ||
1345 | if (n < 0 && errno != EINTR) { | ||
1346 | bb_perror_msg("select"); | ||
1347 | sleep(1); | ||
1348 | } | ||
1349 | continue; | ||
1350 | } | ||
1351 | |||
1352 | for (sep = servtab; n && sep; sep = sep->se_next) { | ||
1353 | if (sep->se_fd == -1 || !FD_ISSET(sep->se_fd, &readable)) | ||
1354 | continue; | ||
1355 | |||
1356 | n--; | ||
1357 | if (!sep->se_wait && sep->se_socktype == SOCK_STREAM) { | ||
1358 | ctrl = accept(sep->se_fd, NULL, NULL); | ||
1359 | if (ctrl < 0) { | ||
1360 | if (errno == EINTR) | ||
1361 | continue; | ||
1362 | bb_perror_msg("accept (for %s)", sep->se_service); | ||
1363 | continue; | ||
1364 | } | ||
1365 | if (sep->se_family == AF_INET && sep->se_socktype == SOCK_STREAM) { | ||
1366 | struct sockaddr_in peer; | ||
1367 | socklen_t plen = sizeof(peer); | ||
1368 | |||
1369 | if (getpeername(ctrl, (struct sockaddr *) &peer, &plen) < 0) { | ||
1370 | bb_error_msg("cannot getpeername"); | ||
1371 | close(ctrl); | ||
1372 | continue; | ||
1373 | } | ||
1374 | if (ntohs(peer.sin_port) == 20) { | ||
1375 | /* XXX ftp bounce */ | ||
1376 | close(ctrl); | ||
1377 | continue; | ||
1378 | } | ||
1379 | } | ||
1380 | } else | ||
1381 | ctrl = sep->se_fd; | ||
1382 | |||
1383 | Block_Using_Signals(omask); | ||
1384 | pid = 0; | ||
1385 | #ifdef INETD_FEATURE_ENABLED | ||
1386 | if (sep->se_bi == 0 || sep->se_bi->bi_fork) | ||
1387 | #endif | ||
1388 | { | ||
1389 | if (sep->se_count++ == 0) | ||
1390 | (void) gettimeofday(&sep->se_time, NULL); | ||
1391 | else if (toomany > 0 && sep->se_count >= sep->se_max) { | ||
1392 | struct timeval now; | ||
1393 | |||
1394 | (void) gettimeofday(&now, NULL); | ||
1395 | if (now.tv_sec - sep->se_time.tv_sec > CNT_INTVL) { | ||
1396 | sep->se_time = now; | ||
1397 | sep->se_count = 1; | ||
1398 | } else { | ||
1399 | if (!sep->se_wait && sep->se_socktype == SOCK_STREAM) | ||
1400 | close(ctrl); | ||
1401 | if (sep->se_family == AF_INET && | ||
1402 | ntohs(sep->se_ctrladdr_in.sin_port) >= IPPORT_RESERVED) { | ||
1403 | /* | ||
1404 | * Cannot close it -- there are | ||
1405 | * thieves on the system. | ||
1406 | * Simply ignore the connection. | ||
1407 | */ | ||
1408 | --sep->se_count; | ||
1409 | continue; | ||
1410 | } | ||
1411 | bb_error_msg("%s/%s server failing (looping), service terminated", | ||
1412 | sep->se_service, sep->se_proto); | ||
1413 | if (!sep->se_wait && sep->se_socktype == SOCK_STREAM) | ||
1414 | close(ctrl); | ||
1415 | FD_CLR(sep->se_fd, &allsock); | ||
1416 | (void) close(sep->se_fd); | ||
1417 | sep->se_fd = -1; | ||
1418 | sep->se_count = 0; | ||
1419 | nsock--; | ||
1420 | sigprocmask(SIG_UNBLOCK, &omask, NULL); | ||
1421 | if (!timingout) { | ||
1422 | timingout = 1; | ||
1423 | alarm(RETRYTIME); | ||
1424 | } | ||
1425 | continue; | ||
1426 | } | ||
1427 | } | ||
1428 | pid = fork(); | ||
1429 | } | ||
1430 | if (pid < 0) { | ||
1431 | bb_perror_msg("fork"); | ||
1432 | if (!sep->se_wait && sep->se_socktype == SOCK_STREAM) | ||
1433 | close(ctrl); | ||
1434 | sigprocmask(SIG_UNBLOCK, &omask, NULL); | ||
1435 | sleep(1); | ||
1436 | continue; | ||
1437 | } | ||
1438 | if (pid && sep->se_wait) { | ||
1439 | sep->se_wait = pid; | ||
1440 | FD_CLR(sep->se_fd, &allsock); | ||
1441 | nsock--; | ||
1442 | } | ||
1443 | sigprocmask(SIG_UNBLOCK, &omask, NULL); | ||
1444 | if (pid == 0) { | ||
1445 | #ifdef INETD_FEATURE_ENABLED | ||
1446 | if (sep->se_bi) { | ||
1447 | (*sep->se_bi->bi_fn)(ctrl, sep); | ||
1448 | } else | ||
1449 | #endif | ||
1450 | { | ||
1451 | pwd = getpwnam(sep->se_user); | ||
1452 | if (pwd == NULL) { | ||
1453 | bb_error_msg("getpwnam: %s: no such user", sep->se_user); | ||
1454 | goto do_exit1; | ||
1455 | } | ||
1456 | if (setsid() < 0) | ||
1457 | bb_perror_msg("%s: setsid", sep->se_service); | ||
1458 | if (sep->se_group && (grp = getgrnam(sep->se_group)) == NULL) { | ||
1459 | bb_error_msg("getgrnam: %s: no such group", sep->se_group); | ||
1460 | goto do_exit1; | ||
1461 | } | ||
1462 | if (uid != 0) { | ||
1463 | /* a user running private inetd */ | ||
1464 | if (uid != pwd->pw_uid) | ||
1465 | _exit(1); | ||
1466 | } else if (pwd->pw_uid) { | ||
1467 | if (sep->se_group) | ||
1468 | pwd->pw_gid = grp->gr_gid; | ||
1469 | xsetgid((gid_t) pwd->pw_gid); | ||
1470 | initgroups(pwd->pw_name, pwd->pw_gid); | ||
1471 | xsetuid((uid_t) pwd->pw_uid); | ||
1472 | } else if (sep->se_group) { | ||
1473 | xsetgid(grp->gr_gid); | ||
1474 | setgroups(1, &grp->gr_gid); | ||
1475 | } | ||
1476 | dup2(ctrl, 0); | ||
1477 | if (ctrl) close(ctrl); | ||
1478 | dup2(0, 1); | ||
1479 | dup2(0, 2); | ||
1480 | if (rlim_ofile.rlim_cur != rlim_ofile_cur) | ||
1481 | if (setrlimit(RLIMIT_NOFILE, &rlim_ofile) < 0) | ||
1482 | bb_perror_msg("setrlimit"); | ||
1483 | closelog(); | ||
1484 | for (tmpint = rlim_ofile_cur - 1; --tmpint > 2;) | ||
1485 | (void) close(tmpint); | ||
1486 | sigaction(SIGPIPE, &sapipe, NULL); | ||
1487 | execv(sep->se_server, sep->se_argv); | ||
1488 | bb_perror_msg("execv %s", sep->se_server); | ||
1489 | do_exit1: | ||
1490 | if (sep->se_socktype != SOCK_STREAM) | ||
1491 | recv(0, buf, sizeof(buf), 0); | ||
1492 | _exit(1); | ||
1493 | } | ||
1494 | } | ||
1495 | if (!sep->se_wait && sep->se_socktype == SOCK_STREAM) | ||
1496 | close(ctrl); | ||
1497 | } /* for (sep = servtab...) */ | ||
1498 | } /* for(;;) */ | ||
1499 | } | ||
1500 | |||
1501 | /* | ||
1502 | * Internet services provided internally by inetd: | ||
1503 | */ | ||
1504 | #define BUFSIZE 4096 | ||
1505 | |||
1506 | #if defined(CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_ECHO) || \ | ||
1507 | defined(CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN) || \ | ||
1508 | defined(CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME) | ||
1509 | static int dg_badinput(struct sockaddr_in *dg_sin) | ||
1510 | { | ||
1511 | if (ntohs(dg_sin->sin_port) < IPPORT_RESERVED) | ||
1512 | return 1; | ||
1513 | if (dg_sin->sin_addr.s_addr == htonl(INADDR_BROADCAST)) | ||
1514 | return 1; | ||
1515 | /* XXX compare against broadcast addresses in SIOCGIFCONF list? */ | ||
1516 | return 0; | ||
1517 | } | ||
1518 | #endif | ||
1519 | |||
1520 | #ifdef CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_ECHO | ||
1521 | /* Echo service -- echo data back */ | ||
1522 | /* ARGSUSED */ | ||
1523 | static void | ||
1524 | echo_stream(int s, servtab_t *sep) | ||
1525 | { | ||
1526 | char buffer[BUFSIZE]; | ||
1527 | int i; | ||
1528 | |||
1529 | inetd_setproctitle(sep->se_service, s); | ||
1530 | while (1) { | ||
1531 | i = read(s, buffer, sizeof(buffer)); | ||
1532 | if (i <= 0) break; | ||
1533 | /* FIXME: this isnt correct - safe_write()? */ | ||
1534 | if (write(s, buffer, i) <= 0) break; | ||
1535 | } | ||
1536 | exit(0); | ||
1537 | } | ||
1538 | |||
1539 | /* Echo service -- echo data back */ | ||
1540 | /* ARGSUSED */ | ||
1541 | static void | ||
1542 | echo_dg(int s, servtab_t *sep ATTRIBUTE_UNUSED) | ||
1543 | { | ||
1544 | char buffer[BUFSIZE]; | ||
1545 | int i; | ||
1546 | socklen_t size; | ||
1547 | /* struct sockaddr_storage ss; */ | ||
1548 | struct sockaddr sa; | ||
1549 | |||
1550 | size = sizeof(sa); | ||
1551 | i = recvfrom(s, buffer, sizeof(buffer), 0, &sa, &size); | ||
1552 | if (i < 0) | ||
1553 | return; | ||
1554 | if (dg_badinput((struct sockaddr_in *) &sa)) | ||
1555 | return; | ||
1556 | (void) sendto(s, buffer, i, 0, &sa, sizeof(sa)); | ||
1557 | } | ||
1558 | #endif /* CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_ECHO */ | ||
1559 | |||
1560 | #ifdef CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DISCARD | ||
1561 | /* Discard service -- ignore data */ | ||
1562 | /* ARGSUSED */ | ||
1563 | static void | ||
1564 | discard_stream(int s, servtab_t *sep) | ||
1565 | { | ||
1566 | char buffer[BUFSIZE]; | ||
1567 | |||
1568 | inetd_setproctitle(sep->se_service, s); | ||
1569 | while (1) { | ||
1570 | errno = 0; | ||
1571 | if (read(s, buffer, sizeof(buffer)) <= 0 && errno != EINTR) | ||
1572 | exit(0); | ||
1573 | } | ||
1574 | } | ||
1575 | |||
1576 | /* Discard service -- ignore data */ | ||
1577 | /* ARGSUSED */ | ||
1578 | static void | ||
1579 | discard_dg(int s, servtab_t *sep ATTRIBUTE_UNUSED) | ||
1580 | { | ||
1581 | char buffer[BUFSIZE]; | ||
1582 | |||
1583 | (void) read(s, buffer, sizeof(buffer)); | ||
1584 | } | ||
1585 | #endif /* CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DISCARD */ | ||
1586 | |||
1587 | |||
1588 | #ifdef CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN | ||
1589 | #define LINESIZ 72 | ||
1590 | static char ring[128]; | ||
1591 | static char *endring; | ||
1592 | |||
1593 | static void | ||
1594 | initring(void) | ||
1595 | { | ||
1596 | int i; | ||
1597 | |||
1598 | endring = ring; | ||
1599 | |||
1600 | for (i = 0; i <= 128; ++i) | ||
1601 | if (isprint(i)) | ||
1602 | *endring++ = i; | ||
1603 | } | ||
1604 | |||
1605 | /* Character generator */ | ||
1606 | /* ARGSUSED */ | ||
1607 | static void | ||
1608 | chargen_stream(int s, servtab_t *sep) | ||
1609 | { | ||
1610 | char *rs; | ||
1611 | int len; | ||
1612 | char text[LINESIZ + 2]; | ||
1613 | |||
1614 | inetd_setproctitle(sep->se_service, s); | ||
1615 | |||
1616 | if (!endring) { | ||
1617 | initring(); | ||
1618 | rs = ring; | ||
1619 | } | ||
1620 | |||
1621 | text[LINESIZ] = '\r'; | ||
1622 | text[LINESIZ + 1] = '\n'; | ||
1623 | rs = ring; | ||
1624 | for (;;) { | ||
1625 | len = endring - rs; | ||
1626 | if (len >= LINESIZ) | ||
1627 | memmove(text, rs, LINESIZ); | ||
1628 | else { | ||
1629 | memmove(text, rs, len); | ||
1630 | memmove(text + len, ring, LINESIZ - len); | ||
1631 | } | ||
1632 | if (++rs == endring) | ||
1633 | rs = ring; | ||
1634 | if (write(s, text, sizeof(text)) != sizeof(text)) | ||
1635 | break; | ||
1636 | } | ||
1637 | exit(0); | ||
1638 | } | ||
1639 | |||
1640 | /* Character generator */ | ||
1641 | /* ARGSUSED */ | ||
1642 | static void | ||
1643 | chargen_dg(int s, servtab_t *sep ATTRIBUTE_UNUSED) | ||
1644 | { | ||
1645 | /* struct sockaddr_storage ss; */ | ||
1646 | struct sockaddr sa; | ||
1647 | static char *rs; | ||
1648 | int len; | ||
1649 | char text[LINESIZ + 2]; | ||
1650 | socklen_t size; | ||
1651 | |||
1652 | if (endring == 0) { | ||
1653 | initring(); | ||
1654 | rs = ring; | ||
1655 | } | ||
1656 | |||
1657 | size = sizeof(sa); | ||
1658 | if (recvfrom(s, text, sizeof(text), 0, &sa, &size) < 0) | ||
1659 | return; | ||
1660 | if (dg_badinput((struct sockaddr_in *) &sa)) | ||
1661 | return; | ||
1662 | |||
1663 | if ((len = endring - rs) >= LINESIZ) | ||
1664 | memmove(text, rs, LINESIZ); | ||
1665 | else { | ||
1666 | memmove(text, rs, len); | ||
1667 | memmove(text + len, ring, LINESIZ - len); | ||
1668 | } | ||
1669 | if (++rs == endring) | ||
1670 | rs = ring; | ||
1671 | text[LINESIZ] = '\r'; | ||
1672 | text[LINESIZ + 1] = '\n'; | ||
1673 | (void) sendto(s, text, sizeof(text), 0, &sa, sizeof(sa)); | ||
1674 | } | ||
1675 | #endif /* CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN */ | ||
1676 | |||
1677 | |||
1678 | #ifdef CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_TIME | ||
1679 | /* | ||
1680 | * Return a machine readable date and time, in the form of the | ||
1681 | * number of seconds since midnight, Jan 1, 1900. Since gettimeofday | ||
1682 | * returns the number of seconds since midnight, Jan 1, 1970, | ||
1683 | * we must add 2208988800 seconds to this figure to make up for | ||
1684 | * some seventy years Bell Labs was asleep. | ||
1685 | */ | ||
1686 | |||
1687 | static u_int machtime(void) | ||
1688 | { | ||
1689 | struct timeval tv; | ||
1690 | |||
1691 | if (gettimeofday(&tv, NULL) < 0) { | ||
1692 | fprintf(stderr, "Unable to get time of day\n"); | ||
1693 | return 0L; | ||
1694 | } | ||
1695 | return htonl((u_int) tv.tv_sec + 2208988800UL); | ||
1696 | } | ||
1697 | |||
1698 | /* ARGSUSED */ | ||
1699 | static void | ||
1700 | machtime_stream(int s, servtab_t *sep ATTRIBUTE_UNUSED) | ||
1701 | { | ||
1702 | u_int result; | ||
1703 | |||
1704 | result = machtime(); | ||
1705 | (void) write(s, (char *) &result, sizeof(result)); | ||
1706 | } | ||
1707 | |||
1708 | /* ARGSUSED */ | ||
1709 | static void | ||
1710 | machtime_dg(int s, servtab_t *sep ATTRIBUTE_UNUSED) | ||
1711 | { | ||
1712 | u_int result; | ||
1713 | /* struct sockaddr_storage ss; */ | ||
1714 | struct sockaddr sa; | ||
1715 | struct sockaddr_in *dg_sin; | ||
1716 | socklen_t size; | ||
1717 | |||
1718 | size = sizeof(sa); | ||
1719 | if (recvfrom(s, (char *) &result, sizeof(result), 0, &sa, &size) < 0) | ||
1720 | return; | ||
1721 | /* if (dg_badinput((struct sockaddr *)&ss)) */ | ||
1722 | dg_sin = (struct sockaddr_in *) &sa; | ||
1723 | if (dg_sin->sin_addr.s_addr == htonl(INADDR_BROADCAST) || | ||
1724 | ntohs(dg_sin->sin_port) < IPPORT_RESERVED / 2) | ||
1725 | return; | ||
1726 | result = machtime(); | ||
1727 | (void) sendto(s, (char *) &result, sizeof(result), 0, &sa, sizeof(sa)); | ||
1728 | } | ||
1729 | #endif /* CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_TIME */ | ||
1730 | |||
1731 | |||
1732 | #ifdef CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME | ||
1733 | /* Return human-readable time of day */ | ||
1734 | /* ARGSUSED */ | ||
1735 | static void daytime_stream(int s, servtab_t *sep ATTRIBUTE_UNUSED) | ||
1736 | { | ||
1737 | char buffer[256]; | ||
1738 | time_t t; | ||
1739 | |||
1740 | t = time(NULL); | ||
1741 | |||
1742 | (void) sprintf(buffer, "%.24s\r\n", ctime(&t)); | ||
1743 | (void) write(s, buffer, strlen(buffer)); | ||
1744 | } | ||
1745 | |||
1746 | /* Return human-readable time of day */ | ||
1747 | /* ARGSUSED */ | ||
1748 | void | ||
1749 | daytime_dg(int s, servtab_t *sep ATTRIBUTE_UNUSED) | ||
1750 | { | ||
1751 | char buffer[256]; | ||
1752 | time_t t; | ||
1753 | /* struct sockaddr_storage ss; */ | ||
1754 | struct sockaddr sa; | ||
1755 | socklen_t size; | ||
1756 | |||
1757 | t = time(NULL); | ||
1758 | |||
1759 | size = sizeof(sa); | ||
1760 | if (recvfrom(s, buffer, sizeof(buffer), 0, &sa, &size) < 0) | ||
1761 | return; | ||
1762 | if (dg_badinput((struct sockaddr_in *) &sa)) | ||
1763 | return; | ||
1764 | (void) sprintf(buffer, "%.24s\r\n", ctime(&t)); | ||
1765 | (void) sendto(s, buffer, strlen(buffer), 0, &sa, sizeof(sa)); | ||
1766 | } | ||
1767 | #endif /* CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME */ | ||
diff --git a/networking/interface.c b/networking/interface.c new file mode 100644 index 000000000..dd455823b --- /dev/null +++ b/networking/interface.c | |||
@@ -0,0 +1,1152 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * stolen from net-tools-1.59 and stripped down for busybox by | ||
4 | * Erik Andersen <andersen@codepoet.org> | ||
5 | * | ||
6 | * Heavily modified by Manuel Novoa III Mar 12, 2001 | ||
7 | * | ||
8 | * Added print_bytes_scaled function to reduce code size. | ||
9 | * Added some (potentially) missing defines. | ||
10 | * Improved display support for -a and for a named interface. | ||
11 | * | ||
12 | * ----------------------------------------------------------- | ||
13 | * | ||
14 | * ifconfig This file contains an implementation of the command | ||
15 | * that either displays or sets the characteristics of | ||
16 | * one or more of the system's networking interfaces. | ||
17 | * | ||
18 | * Version: $Id: interface.c,v 1.25 2004/08/26 21:45:21 andersen Exp $ | ||
19 | * | ||
20 | * Author: Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org> | ||
21 | * and others. Copyright 1993 MicroWalt Corporation | ||
22 | * | ||
23 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | ||
24 | * | ||
25 | * Patched to support 'add' and 'del' keywords for INET(4) addresses | ||
26 | * by Mrs. Brisby <mrs.brisby@nimh.org> | ||
27 | * | ||
28 | * {1.34} - 19980630 - Arnaldo Carvalho de Melo <acme@conectiva.com.br> | ||
29 | * - gettext instead of catgets for i18n | ||
30 | * 10/1998 - Andi Kleen. Use interface list primitives. | ||
31 | * 20001008 - Bernd Eckenfels, Patch from RH for setting mtu | ||
32 | * (default AF was wrong) | ||
33 | */ | ||
34 | |||
35 | #include "inet_common.h" | ||
36 | #include <stdio.h> | ||
37 | #include <errno.h> | ||
38 | #include <stdlib.h> | ||
39 | #include <string.h> | ||
40 | #include <unistd.h> | ||
41 | #include <fcntl.h> | ||
42 | #include <ctype.h> | ||
43 | #include <sys/ioctl.h> | ||
44 | #include <sys/types.h> | ||
45 | #include <net/if.h> | ||
46 | #include <net/if_arp.h> | ||
47 | #include "busybox.h" | ||
48 | |||
49 | #ifdef CONFIG_FEATURE_IPV6 | ||
50 | # define HAVE_AFINET6 1 | ||
51 | #else | ||
52 | # undef HAVE_AFINET6 | ||
53 | #endif | ||
54 | |||
55 | #define _PATH_PROCNET_DEV "/proc/net/dev" | ||
56 | #define _PATH_PROCNET_IFINET6 "/proc/net/if_inet6" | ||
57 | |||
58 | #ifdef HAVE_AFINET6 | ||
59 | |||
60 | #ifndef _LINUX_IN6_H | ||
61 | /* | ||
62 | * This is in linux/include/net/ipv6.h. | ||
63 | */ | ||
64 | |||
65 | struct in6_ifreq { | ||
66 | struct in6_addr ifr6_addr; | ||
67 | uint32_t ifr6_prefixlen; | ||
68 | unsigned int ifr6_ifindex; | ||
69 | }; | ||
70 | |||
71 | #endif | ||
72 | |||
73 | #endif /* HAVE_AFINET6 */ | ||
74 | |||
75 | /* Defines for glibc2.0 users. */ | ||
76 | #ifndef SIOCSIFTXQLEN | ||
77 | #define SIOCSIFTXQLEN 0x8943 | ||
78 | #define SIOCGIFTXQLEN 0x8942 | ||
79 | #endif | ||
80 | |||
81 | /* ifr_qlen is ifru_ivalue, but it isn't present in 2.0 kernel headers */ | ||
82 | #ifndef ifr_qlen | ||
83 | #define ifr_qlen ifr_ifru.ifru_mtu | ||
84 | #endif | ||
85 | |||
86 | #ifndef HAVE_TXQUEUELEN | ||
87 | #define HAVE_TXQUEUELEN 1 | ||
88 | #endif | ||
89 | |||
90 | #ifndef IFF_DYNAMIC | ||
91 | #define IFF_DYNAMIC 0x8000 /* dialup device with changing addresses */ | ||
92 | #endif | ||
93 | |||
94 | /* This structure defines protocol families and their handlers. */ | ||
95 | struct aftype { | ||
96 | const char *name; | ||
97 | const char *title; | ||
98 | int af; | ||
99 | int alen; | ||
100 | char *(*print) (unsigned char *); | ||
101 | char *(*sprint) (struct sockaddr *, int numeric); | ||
102 | int (*input) (int type, char *bufp, struct sockaddr *); | ||
103 | void (*herror) (char *text); | ||
104 | int (*rprint) (int options); | ||
105 | int (*rinput) (int typ, int ext, char **argv); | ||
106 | |||
107 | /* may modify src */ | ||
108 | int (*getmask) (char *src, struct sockaddr * mask, char *name); | ||
109 | |||
110 | int fd; | ||
111 | char *flag_file; | ||
112 | }; | ||
113 | |||
114 | /* Display an Internet socket address. */ | ||
115 | static char *INET_sprint(struct sockaddr *sap, int numeric) | ||
116 | { | ||
117 | static char buff[128]; | ||
118 | |||
119 | if (sap->sa_family == 0xFFFF || sap->sa_family == 0) | ||
120 | return safe_strncpy(buff, "[NONE SET]", sizeof(buff)); | ||
121 | |||
122 | if (INET_rresolve(buff, sizeof(buff), (struct sockaddr_in *) sap, | ||
123 | numeric, 0xffffff00) != 0) | ||
124 | return NULL; | ||
125 | |||
126 | return buff; | ||
127 | } | ||
128 | |||
129 | static struct aftype inet_aftype = { | ||
130 | .name = "inet", | ||
131 | .title = "DARPA Internet", | ||
132 | .af = AF_INET, | ||
133 | .alen = 4, | ||
134 | .sprint = INET_sprint, | ||
135 | .fd = -1 | ||
136 | }; | ||
137 | |||
138 | #ifdef HAVE_AFINET6 | ||
139 | |||
140 | /* Display an Internet socket address. */ | ||
141 | /* dirty! struct sockaddr usually doesn't suffer for inet6 addresses, fst. */ | ||
142 | static char *INET6_sprint(struct sockaddr *sap, int numeric) | ||
143 | { | ||
144 | static char buff[128]; | ||
145 | |||
146 | if (sap->sa_family == 0xFFFF || sap->sa_family == 0) | ||
147 | return safe_strncpy(buff, "[NONE SET]", sizeof(buff)); | ||
148 | if (INET6_rresolve | ||
149 | (buff, sizeof(buff), (struct sockaddr_in6 *) sap, numeric) != 0) | ||
150 | return safe_strncpy(buff, "[UNKNOWN]", sizeof(buff)); | ||
151 | return buff; | ||
152 | } | ||
153 | |||
154 | static struct aftype inet6_aftype = { | ||
155 | .name = "inet6", | ||
156 | .title = "IPv6", | ||
157 | .af = AF_INET6, | ||
158 | .alen = sizeof(struct in6_addr), | ||
159 | .sprint = INET6_sprint, | ||
160 | .fd = -1 | ||
161 | }; | ||
162 | |||
163 | #endif /* HAVE_AFINET6 */ | ||
164 | |||
165 | /* Display an UNSPEC address. */ | ||
166 | static char *UNSPEC_print(unsigned char *ptr) | ||
167 | { | ||
168 | static char buff[sizeof(struct sockaddr) * 3 + 1]; | ||
169 | char *pos; | ||
170 | unsigned int i; | ||
171 | |||
172 | pos = buff; | ||
173 | for (i = 0; i < sizeof(struct sockaddr); i++) { | ||
174 | /* careful -- not every libc's sprintf returns # bytes written */ | ||
175 | sprintf(pos, "%02X-", (*ptr++ & 0377)); | ||
176 | pos += 3; | ||
177 | } | ||
178 | /* Erase trailing "-". Works as long as sizeof(struct sockaddr) != 0 */ | ||
179 | *--pos = '\0'; | ||
180 | return buff; | ||
181 | } | ||
182 | |||
183 | /* Display an UNSPEC socket address. */ | ||
184 | static char *UNSPEC_sprint(struct sockaddr *sap, int numeric) | ||
185 | { | ||
186 | static char buf[64]; | ||
187 | |||
188 | if (sap->sa_family == 0xFFFF || sap->sa_family == 0) | ||
189 | return safe_strncpy(buf, "[NONE SET]", sizeof(buf)); | ||
190 | return UNSPEC_print((unsigned char *)sap->sa_data); | ||
191 | } | ||
192 | |||
193 | static struct aftype unspec_aftype = { | ||
194 | "unspec", "UNSPEC", AF_UNSPEC, 0, | ||
195 | UNSPEC_print, UNSPEC_sprint, NULL, NULL, | ||
196 | NULL, | ||
197 | }; | ||
198 | |||
199 | static struct aftype * const aftypes[] = { | ||
200 | &inet_aftype, | ||
201 | #ifdef HAVE_AFINET6 | ||
202 | &inet6_aftype, | ||
203 | #endif | ||
204 | &unspec_aftype, | ||
205 | NULL | ||
206 | }; | ||
207 | |||
208 | /* Check our protocol family table for this family. */ | ||
209 | static struct aftype *get_afntype(int af) | ||
210 | { | ||
211 | struct aftype * const *afp; | ||
212 | |||
213 | afp = aftypes; | ||
214 | while (*afp != NULL) { | ||
215 | if ((*afp)->af == af) | ||
216 | return *afp; | ||
217 | afp++; | ||
218 | } | ||
219 | return NULL; | ||
220 | } | ||
221 | |||
222 | /* Check our protocol family table for this family and return its socket */ | ||
223 | static int get_socket_for_af(int af) | ||
224 | { | ||
225 | struct aftype * const *afp; | ||
226 | |||
227 | afp = aftypes; | ||
228 | while (*afp != NULL) { | ||
229 | if ((*afp)->af == af) | ||
230 | return (*afp)->fd; | ||
231 | afp++; | ||
232 | } | ||
233 | return -1; | ||
234 | } | ||
235 | |||
236 | struct user_net_device_stats { | ||
237 | unsigned long long rx_packets; /* total packets received */ | ||
238 | unsigned long long tx_packets; /* total packets transmitted */ | ||
239 | unsigned long long rx_bytes; /* total bytes received */ | ||
240 | unsigned long long tx_bytes; /* total bytes transmitted */ | ||
241 | unsigned long rx_errors; /* bad packets received */ | ||
242 | unsigned long tx_errors; /* packet transmit problems */ | ||
243 | unsigned long rx_dropped; /* no space in linux buffers */ | ||
244 | unsigned long tx_dropped; /* no space available in linux */ | ||
245 | unsigned long rx_multicast; /* multicast packets received */ | ||
246 | unsigned long rx_compressed; | ||
247 | unsigned long tx_compressed; | ||
248 | unsigned long collisions; | ||
249 | |||
250 | /* detailed rx_errors: */ | ||
251 | unsigned long rx_length_errors; | ||
252 | unsigned long rx_over_errors; /* receiver ring buff overflow */ | ||
253 | unsigned long rx_crc_errors; /* recved pkt with crc error */ | ||
254 | unsigned long rx_frame_errors; /* recv'd frame alignment error */ | ||
255 | unsigned long rx_fifo_errors; /* recv'r fifo overrun */ | ||
256 | unsigned long rx_missed_errors; /* receiver missed packet */ | ||
257 | /* detailed tx_errors */ | ||
258 | unsigned long tx_aborted_errors; | ||
259 | unsigned long tx_carrier_errors; | ||
260 | unsigned long tx_fifo_errors; | ||
261 | unsigned long tx_heartbeat_errors; | ||
262 | unsigned long tx_window_errors; | ||
263 | }; | ||
264 | |||
265 | struct interface { | ||
266 | struct interface *next, *prev; | ||
267 | char name[IFNAMSIZ]; /* interface name */ | ||
268 | short type; /* if type */ | ||
269 | short flags; /* various flags */ | ||
270 | int metric; /* routing metric */ | ||
271 | int mtu; /* MTU value */ | ||
272 | int tx_queue_len; /* transmit queue length */ | ||
273 | struct ifmap map; /* hardware setup */ | ||
274 | struct sockaddr addr; /* IP address */ | ||
275 | struct sockaddr dstaddr; /* P-P IP address */ | ||
276 | struct sockaddr broadaddr; /* IP broadcast address */ | ||
277 | struct sockaddr netmask; /* IP network mask */ | ||
278 | int has_ip; | ||
279 | char hwaddr[32]; /* HW address */ | ||
280 | int statistics_valid; | ||
281 | struct user_net_device_stats stats; /* statistics */ | ||
282 | int keepalive; /* keepalive value for SLIP */ | ||
283 | int outfill; /* outfill value for SLIP */ | ||
284 | }; | ||
285 | |||
286 | |||
287 | int interface_opt_a; /* show all interfaces */ | ||
288 | |||
289 | static struct interface *int_list, *int_last; | ||
290 | static int skfd = -1; /* generic raw socket desc. */ | ||
291 | |||
292 | |||
293 | static int sockets_open(int family) | ||
294 | { | ||
295 | struct aftype * const *aft; | ||
296 | int sfd = -1; | ||
297 | static int force = -1; | ||
298 | |||
299 | if (force < 0) { | ||
300 | force = 0; | ||
301 | if (get_linux_version_code() < KERNEL_VERSION(2,1,0)) | ||
302 | force = 1; | ||
303 | if (access("/proc/net", R_OK)) | ||
304 | force = 1; | ||
305 | } | ||
306 | for (aft = aftypes; *aft; aft++) { | ||
307 | struct aftype *af = *aft; | ||
308 | int type = SOCK_DGRAM; | ||
309 | |||
310 | if (af->af == AF_UNSPEC) | ||
311 | continue; | ||
312 | if (family && family != af->af) | ||
313 | continue; | ||
314 | if (af->fd != -1) { | ||
315 | sfd = af->fd; | ||
316 | continue; | ||
317 | } | ||
318 | /* Check some /proc file first to not stress kmod */ | ||
319 | if (!family && !force && af->flag_file) { | ||
320 | if (access(af->flag_file, R_OK)) | ||
321 | continue; | ||
322 | } | ||
323 | af->fd = socket(af->af, type, 0); | ||
324 | if (af->fd >= 0) | ||
325 | sfd = af->fd; | ||
326 | } | ||
327 | if (sfd < 0) { | ||
328 | bb_error_msg("no usable address families found"); | ||
329 | } | ||
330 | return sfd; | ||
331 | } | ||
332 | |||
333 | #ifdef CONFIG_FEATURE_CLEAN_UP | ||
334 | static void sockets_close(void) | ||
335 | { | ||
336 | struct aftype * const *aft; | ||
337 | for (aft = aftypes; *aft != NULL; aft++) { | ||
338 | struct aftype *af = *aft; | ||
339 | if( af->fd != -1 ) { | ||
340 | close(af->fd); | ||
341 | af->fd = -1; | ||
342 | } | ||
343 | } | ||
344 | } | ||
345 | #endif | ||
346 | #if 0 | ||
347 | /* like strcmp(), but knows about numbers */ | ||
348 | except that the freshly added calls to xatoul() brf on ethernet aliases with | ||
349 | uClibc with e.g.: ife->name='lo' name='eth0:1' | ||
350 | static int nstrcmp(const char *a, const char *b) | ||
351 | { | ||
352 | const char *a_ptr = a; | ||
353 | const char *b_ptr = b; | ||
354 | |||
355 | while (*a == *b) { | ||
356 | if (*a == '\0') { | ||
357 | return 0; | ||
358 | } | ||
359 | if (!isdigit(*a) && isdigit(*(a+1))) { | ||
360 | a_ptr = a+1; | ||
361 | b_ptr = b+1; | ||
362 | } | ||
363 | a++; | ||
364 | b++; | ||
365 | } | ||
366 | |||
367 | if (isdigit(*a) && isdigit(*b)) { | ||
368 | return xatoul(a_ptr) > xatoul(b_ptr) ? 1 : -1; | ||
369 | } | ||
370 | return *a - *b; | ||
371 | } | ||
372 | #endif | ||
373 | |||
374 | static struct interface *add_interface(char *name) | ||
375 | { | ||
376 | struct interface *ife, **nextp, *new; | ||
377 | |||
378 | for (ife = int_last; ife; ife = ife->prev) { | ||
379 | int n = /*n*/strcmp(ife->name, name); | ||
380 | |||
381 | if (n == 0) | ||
382 | return ife; | ||
383 | if (n < 0) | ||
384 | break; | ||
385 | } | ||
386 | |||
387 | new = xzalloc(sizeof(*new)); | ||
388 | safe_strncpy(new->name, name, IFNAMSIZ); | ||
389 | nextp = ife ? &ife->next : &int_list; | ||
390 | new->prev = ife; | ||
391 | new->next = *nextp; | ||
392 | if (new->next) | ||
393 | new->next->prev = new; | ||
394 | else | ||
395 | int_last = new; | ||
396 | *nextp = new; | ||
397 | return new; | ||
398 | } | ||
399 | |||
400 | |||
401 | static int if_readconf(void) | ||
402 | { | ||
403 | int numreqs = 30; | ||
404 | struct ifconf ifc; | ||
405 | struct ifreq *ifr; | ||
406 | int n, err = -1; | ||
407 | int skfd2; | ||
408 | |||
409 | /* SIOCGIFCONF currently seems to only work properly on AF_INET sockets | ||
410 | (as of 2.1.128) */ | ||
411 | skfd2 = get_socket_for_af(AF_INET); | ||
412 | if (skfd2 < 0) { | ||
413 | bb_perror_msg(("warning: no inet socket available")); | ||
414 | /* Try to soldier on with whatever socket we can get hold of. */ | ||
415 | skfd2 = sockets_open(0); | ||
416 | if (skfd2 < 0) | ||
417 | return -1; | ||
418 | } | ||
419 | |||
420 | ifc.ifc_buf = NULL; | ||
421 | for (;;) { | ||
422 | ifc.ifc_len = sizeof(struct ifreq) * numreqs; | ||
423 | ifc.ifc_buf = xrealloc(ifc.ifc_buf, ifc.ifc_len); | ||
424 | |||
425 | if (ioctl(skfd2, SIOCGIFCONF, &ifc) < 0) { | ||
426 | perror("SIOCGIFCONF"); | ||
427 | goto out; | ||
428 | } | ||
429 | if (ifc.ifc_len == sizeof(struct ifreq) * numreqs) { | ||
430 | /* assume it overflowed and try again */ | ||
431 | numreqs += 10; | ||
432 | continue; | ||
433 | } | ||
434 | break; | ||
435 | } | ||
436 | |||
437 | ifr = ifc.ifc_req; | ||
438 | for (n = 0; n < ifc.ifc_len; n += sizeof(struct ifreq)) { | ||
439 | add_interface(ifr->ifr_name); | ||
440 | ifr++; | ||
441 | } | ||
442 | err = 0; | ||
443 | |||
444 | out: | ||
445 | free(ifc.ifc_buf); | ||
446 | return err; | ||
447 | } | ||
448 | |||
449 | static char *get_name(char *name, char *p) | ||
450 | { | ||
451 | /* Extract <name> from nul-terminated p where p matches | ||
452 | <name>: after leading whitespace. | ||
453 | If match is not made, set name empty and return unchanged p */ | ||
454 | int namestart=0, nameend=0; | ||
455 | while (isspace(p[namestart])) | ||
456 | namestart++; | ||
457 | nameend=namestart; | ||
458 | while (p[nameend] && p[nameend]!=':' && !isspace(p[nameend])) | ||
459 | nameend++; | ||
460 | if (p[nameend]==':') { | ||
461 | if ((nameend-namestart)<IFNAMSIZ) { | ||
462 | memcpy(name,&p[namestart],nameend-namestart); | ||
463 | name[nameend-namestart]='\0'; | ||
464 | p=&p[nameend]; | ||
465 | } else { | ||
466 | /* Interface name too large */ | ||
467 | name[0]='\0'; | ||
468 | } | ||
469 | } else { | ||
470 | /* trailing ':' not found - return empty */ | ||
471 | name[0]='\0'; | ||
472 | } | ||
473 | return p + 1; | ||
474 | } | ||
475 | |||
476 | /* If scanf supports size qualifiers for %n conversions, then we can | ||
477 | * use a modified fmt that simply stores the position in the fields | ||
478 | * having no associated fields in the proc string. Of course, we need | ||
479 | * to zero them again when we're done. But that is smaller than the | ||
480 | * old approach of multiple scanf occurrences with large numbers of | ||
481 | * args. */ | ||
482 | |||
483 | /* static const char * const ss_fmt[] = { */ | ||
484 | /* "%lln%llu%lu%lu%lu%lu%ln%ln%lln%llu%lu%lu%lu%lu%lu", */ | ||
485 | /* "%llu%llu%lu%lu%lu%lu%ln%ln%llu%llu%lu%lu%lu%lu%lu", */ | ||
486 | /* "%llu%llu%lu%lu%lu%lu%lu%lu%llu%llu%lu%lu%lu%lu%lu%lu" */ | ||
487 | /* }; */ | ||
488 | |||
489 | /* Lie about the size of the int pointed to for %n. */ | ||
490 | #if INT_MAX == LONG_MAX | ||
491 | static const char * const ss_fmt[] = { | ||
492 | "%n%llu%u%u%u%u%n%n%n%llu%u%u%u%u%u", | ||
493 | "%llu%llu%u%u%u%u%n%n%llu%llu%u%u%u%u%u", | ||
494 | "%llu%llu%u%u%u%u%u%u%llu%llu%u%u%u%u%u%u" | ||
495 | }; | ||
496 | #else | ||
497 | static const char * const ss_fmt[] = { | ||
498 | "%n%llu%lu%lu%lu%lu%n%n%n%llu%lu%lu%lu%lu%lu", | ||
499 | "%llu%llu%lu%lu%lu%lu%n%n%llu%llu%lu%lu%lu%lu%lu", | ||
500 | "%llu%llu%lu%lu%lu%lu%lu%lu%llu%llu%lu%lu%lu%lu%lu%lu" | ||
501 | }; | ||
502 | |||
503 | #endif | ||
504 | |||
505 | static void get_dev_fields(char *bp, struct interface *ife, int procnetdev_vsn) | ||
506 | { | ||
507 | memset(&ife->stats, 0, sizeof(struct user_net_device_stats)); | ||
508 | |||
509 | sscanf(bp, ss_fmt[procnetdev_vsn], | ||
510 | &ife->stats.rx_bytes, /* missing for 0 */ | ||
511 | &ife->stats.rx_packets, | ||
512 | &ife->stats.rx_errors, | ||
513 | &ife->stats.rx_dropped, | ||
514 | &ife->stats.rx_fifo_errors, | ||
515 | &ife->stats.rx_frame_errors, | ||
516 | &ife->stats.rx_compressed, /* missing for <= 1 */ | ||
517 | &ife->stats.rx_multicast, /* missing for <= 1 */ | ||
518 | &ife->stats.tx_bytes, /* missing for 0 */ | ||
519 | &ife->stats.tx_packets, | ||
520 | &ife->stats.tx_errors, | ||
521 | &ife->stats.tx_dropped, | ||
522 | &ife->stats.tx_fifo_errors, | ||
523 | &ife->stats.collisions, | ||
524 | &ife->stats.tx_carrier_errors, | ||
525 | &ife->stats.tx_compressed /* missing for <= 1 */ | ||
526 | ); | ||
527 | |||
528 | if (procnetdev_vsn <= 1) { | ||
529 | if (procnetdev_vsn == 0) { | ||
530 | ife->stats.rx_bytes = 0; | ||
531 | ife->stats.tx_bytes = 0; | ||
532 | } | ||
533 | ife->stats.rx_multicast = 0; | ||
534 | ife->stats.rx_compressed = 0; | ||
535 | ife->stats.tx_compressed = 0; | ||
536 | } | ||
537 | } | ||
538 | |||
539 | static inline int procnetdev_version(char *buf) | ||
540 | { | ||
541 | if (strstr(buf, "compressed")) | ||
542 | return 2; | ||
543 | if (strstr(buf, "bytes")) | ||
544 | return 1; | ||
545 | return 0; | ||
546 | } | ||
547 | |||
548 | static int if_readlist_proc(char *target) | ||
549 | { | ||
550 | static int proc_read; | ||
551 | FILE *fh; | ||
552 | char buf[512]; | ||
553 | struct interface *ife; | ||
554 | int err, procnetdev_vsn; | ||
555 | |||
556 | if (proc_read) | ||
557 | return 0; | ||
558 | if (!target) | ||
559 | proc_read = 1; | ||
560 | |||
561 | fh = fopen(_PATH_PROCNET_DEV, "r"); | ||
562 | if (!fh) { | ||
563 | bb_perror_msg("warning: cannot open %s, limiting output", _PATH_PROCNET_DEV); | ||
564 | return if_readconf(); | ||
565 | } | ||
566 | fgets(buf, sizeof buf, fh); /* eat line */ | ||
567 | fgets(buf, sizeof buf, fh); | ||
568 | |||
569 | procnetdev_vsn = procnetdev_version(buf); | ||
570 | |||
571 | err = 0; | ||
572 | while (fgets(buf, sizeof buf, fh)) { | ||
573 | char *s, name[128]; | ||
574 | |||
575 | s = get_name(name, buf); | ||
576 | ife = add_interface(name); | ||
577 | get_dev_fields(s, ife, procnetdev_vsn); | ||
578 | ife->statistics_valid = 1; | ||
579 | if (target && !strcmp(target, name)) | ||
580 | break; | ||
581 | } | ||
582 | if (ferror(fh)) { | ||
583 | perror(_PATH_PROCNET_DEV); | ||
584 | err = -1; | ||
585 | proc_read = 0; | ||
586 | } | ||
587 | fclose(fh); | ||
588 | return err; | ||
589 | } | ||
590 | |||
591 | static int if_readlist(void) | ||
592 | { | ||
593 | int err = if_readlist_proc(NULL); | ||
594 | |||
595 | if (!err) | ||
596 | err = if_readconf(); | ||
597 | return err; | ||
598 | } | ||
599 | |||
600 | static int for_all_interfaces(int (*doit) (struct interface *, void *), | ||
601 | void *cookie) | ||
602 | { | ||
603 | struct interface *ife; | ||
604 | |||
605 | if (!int_list && (if_readlist() < 0)) | ||
606 | return -1; | ||
607 | for (ife = int_list; ife; ife = ife->next) { | ||
608 | int err = doit(ife, cookie); | ||
609 | |||
610 | if (err) | ||
611 | return err; | ||
612 | } | ||
613 | return 0; | ||
614 | } | ||
615 | |||
616 | /* Fetch the interface configuration from the kernel. */ | ||
617 | static int if_fetch(struct interface *ife) | ||
618 | { | ||
619 | struct ifreq ifr; | ||
620 | int fd; | ||
621 | char *ifname = ife->name; | ||
622 | |||
623 | strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); | ||
624 | if (ioctl(skfd, SIOCGIFFLAGS, &ifr) < 0) | ||
625 | return -1; | ||
626 | ife->flags = ifr.ifr_flags; | ||
627 | |||
628 | strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); | ||
629 | if (ioctl(skfd, SIOCGIFHWADDR, &ifr) < 0) | ||
630 | memset(ife->hwaddr, 0, 32); | ||
631 | else | ||
632 | memcpy(ife->hwaddr, ifr.ifr_hwaddr.sa_data, 8); | ||
633 | |||
634 | ife->type = ifr.ifr_hwaddr.sa_family; | ||
635 | |||
636 | strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); | ||
637 | if (ioctl(skfd, SIOCGIFMETRIC, &ifr) < 0) | ||
638 | ife->metric = 0; | ||
639 | else | ||
640 | ife->metric = ifr.ifr_metric; | ||
641 | |||
642 | strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); | ||
643 | if (ioctl(skfd, SIOCGIFMTU, &ifr) < 0) | ||
644 | ife->mtu = 0; | ||
645 | else | ||
646 | ife->mtu = ifr.ifr_mtu; | ||
647 | |||
648 | #ifdef SIOCGIFMAP | ||
649 | strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); | ||
650 | if (ioctl(skfd, SIOCGIFMAP, &ifr) == 0) | ||
651 | ife->map = ifr.ifr_map; | ||
652 | else | ||
653 | #endif | ||
654 | memset(&ife->map, 0, sizeof(struct ifmap)); | ||
655 | |||
656 | #ifdef HAVE_TXQUEUELEN | ||
657 | strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); | ||
658 | if (ioctl(skfd, SIOCGIFTXQLEN, &ifr) < 0) | ||
659 | ife->tx_queue_len = -1; /* unknown value */ | ||
660 | else | ||
661 | ife->tx_queue_len = ifr.ifr_qlen; | ||
662 | #else | ||
663 | ife->tx_queue_len = -1; /* unknown value */ | ||
664 | #endif | ||
665 | |||
666 | /* IPv4 address? */ | ||
667 | fd = get_socket_for_af(AF_INET); | ||
668 | if (fd >= 0) { | ||
669 | strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); | ||
670 | ifr.ifr_addr.sa_family = AF_INET; | ||
671 | if (ioctl(fd, SIOCGIFADDR, &ifr) == 0) { | ||
672 | ife->has_ip = 1; | ||
673 | ife->addr = ifr.ifr_addr; | ||
674 | strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); | ||
675 | if (ioctl(fd, SIOCGIFDSTADDR, &ifr) < 0) | ||
676 | memset(&ife->dstaddr, 0, sizeof(struct sockaddr)); | ||
677 | else | ||
678 | ife->dstaddr = ifr.ifr_dstaddr; | ||
679 | |||
680 | strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); | ||
681 | if (ioctl(fd, SIOCGIFBRDADDR, &ifr) < 0) | ||
682 | memset(&ife->broadaddr, 0, sizeof(struct sockaddr)); | ||
683 | else | ||
684 | ife->broadaddr = ifr.ifr_broadaddr; | ||
685 | |||
686 | strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); | ||
687 | if (ioctl(fd, SIOCGIFNETMASK, &ifr) < 0) | ||
688 | memset(&ife->netmask, 0, sizeof(struct sockaddr)); | ||
689 | else | ||
690 | ife->netmask = ifr.ifr_netmask; | ||
691 | } else | ||
692 | memset(&ife->addr, 0, sizeof(struct sockaddr)); | ||
693 | } | ||
694 | |||
695 | return 0; | ||
696 | } | ||
697 | |||
698 | |||
699 | static int do_if_fetch(struct interface *ife) | ||
700 | { | ||
701 | if (if_fetch(ife) < 0) { | ||
702 | char *errmsg; | ||
703 | |||
704 | if (errno == ENODEV) { | ||
705 | /* Give better error message for this case. */ | ||
706 | errmsg = "Device not found"; | ||
707 | } else { | ||
708 | errmsg = strerror(errno); | ||
709 | } | ||
710 | bb_error_msg("%s: error fetching interface information: %s", | ||
711 | ife->name, errmsg); | ||
712 | return -1; | ||
713 | } | ||
714 | return 0; | ||
715 | } | ||
716 | |||
717 | /* This structure defines hardware protocols and their handlers. */ | ||
718 | struct hwtype { | ||
719 | const char * const name; | ||
720 | const char *title; | ||
721 | int type; | ||
722 | int alen; | ||
723 | char *(*print) (unsigned char *); | ||
724 | int (*input) (char *, struct sockaddr *); | ||
725 | int (*activate) (int fd); | ||
726 | int suppress_null_addr; | ||
727 | }; | ||
728 | |||
729 | static const struct hwtype unspec_hwtype = { | ||
730 | .name = "unspec", | ||
731 | .title = "UNSPEC", | ||
732 | .type = -1, | ||
733 | .print = UNSPEC_print | ||
734 | }; | ||
735 | |||
736 | static const struct hwtype loop_hwtype = { | ||
737 | .name = "loop", | ||
738 | .title = "Local Loopback", | ||
739 | .type = ARPHRD_LOOPBACK | ||
740 | }; | ||
741 | |||
742 | #include <net/if_arp.h> | ||
743 | |||
744 | #if (__GLIBC__ >=2 && __GLIBC_MINOR__ >= 1) || defined(_NEWLIB_VERSION) | ||
745 | #include <net/ethernet.h> | ||
746 | #else | ||
747 | #include <linux/if_ether.h> | ||
748 | #endif | ||
749 | |||
750 | /* Display an Ethernet address in readable format. */ | ||
751 | static char *pr_ether(unsigned char *ptr) | ||
752 | { | ||
753 | static char buff[64]; | ||
754 | |||
755 | snprintf(buff, sizeof(buff), "%02X:%02X:%02X:%02X:%02X:%02X", | ||
756 | (ptr[0] & 0377), (ptr[1] & 0377), (ptr[2] & 0377), | ||
757 | (ptr[3] & 0377), (ptr[4] & 0377), (ptr[5] & 0377) | ||
758 | ); | ||
759 | return buff; | ||
760 | } | ||
761 | |||
762 | static const struct hwtype ether_hwtype = { | ||
763 | .name = "ether", | ||
764 | .title = "Ethernet", | ||
765 | .type = ARPHRD_ETHER, | ||
766 | .alen = ETH_ALEN, | ||
767 | .print = pr_ether | ||
768 | }; | ||
769 | |||
770 | #include <net/if_arp.h> | ||
771 | |||
772 | static const struct hwtype ppp_hwtype = { | ||
773 | .name = "ppp", | ||
774 | .title = "Point-to-Point Protocol", | ||
775 | .type = ARPHRD_PPP | ||
776 | }; | ||
777 | |||
778 | #ifdef CONFIG_FEATURE_IPV6 | ||
779 | static const struct hwtype sit_hwtype = { | ||
780 | .name = "sit", | ||
781 | .title = "IPv6-in-IPv4", | ||
782 | .type = ARPHRD_SIT, | ||
783 | .print = UNSPEC_print, | ||
784 | .suppress_null_addr = 1 | ||
785 | } ; | ||
786 | #endif | ||
787 | |||
788 | static const struct hwtype * const hwtypes[] = { | ||
789 | &loop_hwtype, | ||
790 | ðer_hwtype, | ||
791 | &ppp_hwtype, | ||
792 | &unspec_hwtype, | ||
793 | #ifdef CONFIG_FEATURE_IPV6 | ||
794 | &sit_hwtype, | ||
795 | #endif | ||
796 | NULL | ||
797 | }; | ||
798 | |||
799 | #ifdef IFF_PORTSEL | ||
800 | static const char * const if_port_text[] = { | ||
801 | /* Keep in step with <linux/netdevice.h> */ | ||
802 | "unknown", | ||
803 | "10base2", | ||
804 | "10baseT", | ||
805 | "AUI", | ||
806 | "100baseT", | ||
807 | "100baseTX", | ||
808 | "100baseFX", | ||
809 | NULL | ||
810 | }; | ||
811 | #endif | ||
812 | |||
813 | /* Check our hardware type table for this type. */ | ||
814 | static const struct hwtype *get_hwntype(int type) | ||
815 | { | ||
816 | const struct hwtype * const *hwp; | ||
817 | |||
818 | hwp = hwtypes; | ||
819 | while (*hwp != NULL) { | ||
820 | if ((*hwp)->type == type) | ||
821 | return *hwp; | ||
822 | hwp++; | ||
823 | } | ||
824 | return NULL; | ||
825 | } | ||
826 | |||
827 | /* return 1 if address is all zeros */ | ||
828 | static int hw_null_address(const struct hwtype *hw, void *ap) | ||
829 | { | ||
830 | unsigned int i; | ||
831 | unsigned char *address = (unsigned char *) ap; | ||
832 | |||
833 | for (i = 0; i < hw->alen; i++) | ||
834 | if (address[i]) | ||
835 | return 0; | ||
836 | return 1; | ||
837 | } | ||
838 | |||
839 | static const char TRext[] = "\0\0\0Ki\0Mi\0Gi\0Ti"; | ||
840 | |||
841 | static void print_bytes_scaled(unsigned long long ull, const char *end) | ||
842 | { | ||
843 | unsigned long long int_part; | ||
844 | const char *ext; | ||
845 | unsigned int frac_part; | ||
846 | int i; | ||
847 | |||
848 | frac_part = 0; | ||
849 | ext = TRext; | ||
850 | int_part = ull; | ||
851 | i = 4; | ||
852 | do { | ||
853 | if (int_part >= 1024) { | ||
854 | frac_part = ((((unsigned int) int_part) & (1024-1)) * 10) / 1024; | ||
855 | int_part /= 1024; | ||
856 | ext += 3; /* KiB, MiB, GiB, TiB */ | ||
857 | } | ||
858 | --i; | ||
859 | } while (i); | ||
860 | |||
861 | printf("X bytes:%llu (%llu.%u %sB)%s", ull, int_part, frac_part, ext, end); | ||
862 | } | ||
863 | |||
864 | static const char * const ife_print_flags_strs[] = { | ||
865 | "UP ", | ||
866 | "BROADCAST ", | ||
867 | "DEBUG ", | ||
868 | "LOOPBACK ", | ||
869 | "POINTOPOINT ", | ||
870 | "NOTRAILERS ", | ||
871 | "RUNNING ", | ||
872 | "NOARP ", | ||
873 | "PROMISC ", | ||
874 | "ALLMULTI ", | ||
875 | "SLAVE ", | ||
876 | "MASTER ", | ||
877 | "MULTICAST ", | ||
878 | #ifdef HAVE_DYNAMIC | ||
879 | "DYNAMIC " | ||
880 | #endif | ||
881 | }; | ||
882 | |||
883 | static const unsigned short ife_print_flags_mask[] = { | ||
884 | IFF_UP, | ||
885 | IFF_BROADCAST, | ||
886 | IFF_DEBUG, | ||
887 | IFF_LOOPBACK, | ||
888 | IFF_POINTOPOINT, | ||
889 | IFF_NOTRAILERS, | ||
890 | IFF_RUNNING, | ||
891 | IFF_NOARP, | ||
892 | IFF_PROMISC, | ||
893 | IFF_ALLMULTI, | ||
894 | IFF_SLAVE, | ||
895 | IFF_MASTER, | ||
896 | IFF_MULTICAST, | ||
897 | #ifdef HAVE_DYNAMIC | ||
898 | IFF_DYNAMIC | ||
899 | #endif | ||
900 | 0 | ||
901 | }; | ||
902 | |||
903 | static void ife_print(struct interface *ptr) | ||
904 | { | ||
905 | struct aftype *ap; | ||
906 | const struct hwtype *hw; | ||
907 | int hf; | ||
908 | int can_compress = 0; | ||
909 | |||
910 | #ifdef HAVE_AFINET6 | ||
911 | FILE *f; | ||
912 | char addr6[40], devname[20]; | ||
913 | struct sockaddr_in6 sap; | ||
914 | int plen, scope, dad_status, if_idx; | ||
915 | char addr6p[8][5]; | ||
916 | #endif | ||
917 | |||
918 | ap = get_afntype(ptr->addr.sa_family); | ||
919 | if (ap == NULL) | ||
920 | ap = get_afntype(0); | ||
921 | |||
922 | hf = ptr->type; | ||
923 | |||
924 | if (hf == ARPHRD_CSLIP || hf == ARPHRD_CSLIP6) | ||
925 | can_compress = 1; | ||
926 | |||
927 | hw = get_hwntype(hf); | ||
928 | if (hw == NULL) | ||
929 | hw = get_hwntype(-1); | ||
930 | |||
931 | printf("%-9.9s Link encap:%s ", ptr->name, hw->title); | ||
932 | /* For some hardware types (eg Ash, ATM) we don't print the | ||
933 | hardware address if it's null. */ | ||
934 | if (hw->print != NULL && (!(hw_null_address(hw, ptr->hwaddr) && | ||
935 | hw->suppress_null_addr))) | ||
936 | printf("HWaddr %s ", hw->print((unsigned char *)ptr->hwaddr)); | ||
937 | #ifdef IFF_PORTSEL | ||
938 | if (ptr->flags & IFF_PORTSEL) { | ||
939 | printf("Media:%s", if_port_text[ptr->map.port] /* [0] */); | ||
940 | if (ptr->flags & IFF_AUTOMEDIA) | ||
941 | printf("(auto)"); | ||
942 | } | ||
943 | #endif | ||
944 | puts(""); | ||
945 | |||
946 | if (ptr->has_ip) { | ||
947 | printf(" %s addr:%s ", ap->name, | ||
948 | ap->sprint(&ptr->addr, 1)); | ||
949 | if (ptr->flags & IFF_POINTOPOINT) { | ||
950 | printf(" P-t-P:%s ", ap->sprint(&ptr->dstaddr, 1)); | ||
951 | } | ||
952 | if (ptr->flags & IFF_BROADCAST) { | ||
953 | printf(" Bcast:%s ", ap->sprint(&ptr->broadaddr, 1)); | ||
954 | } | ||
955 | printf(" Mask:%s\n", ap->sprint(&ptr->netmask, 1)); | ||
956 | } | ||
957 | |||
958 | #ifdef HAVE_AFINET6 | ||
959 | |||
960 | #define IPV6_ADDR_ANY 0x0000U | ||
961 | |||
962 | #define IPV6_ADDR_UNICAST 0x0001U | ||
963 | #define IPV6_ADDR_MULTICAST 0x0002U | ||
964 | #define IPV6_ADDR_ANYCAST 0x0004U | ||
965 | |||
966 | #define IPV6_ADDR_LOOPBACK 0x0010U | ||
967 | #define IPV6_ADDR_LINKLOCAL 0x0020U | ||
968 | #define IPV6_ADDR_SITELOCAL 0x0040U | ||
969 | |||
970 | #define IPV6_ADDR_COMPATv4 0x0080U | ||
971 | |||
972 | #define IPV6_ADDR_SCOPE_MASK 0x00f0U | ||
973 | |||
974 | #define IPV6_ADDR_MAPPED 0x1000U | ||
975 | #define IPV6_ADDR_RESERVED 0x2000U /* reserved address space */ | ||
976 | |||
977 | if ((f = fopen(_PATH_PROCNET_IFINET6, "r")) != NULL) { | ||
978 | while (fscanf | ||
979 | (f, "%4s%4s%4s%4s%4s%4s%4s%4s %02x %02x %02x %02x %20s\n", | ||
980 | addr6p[0], addr6p[1], addr6p[2], addr6p[3], addr6p[4], | ||
981 | addr6p[5], addr6p[6], addr6p[7], &if_idx, &plen, &scope, | ||
982 | &dad_status, devname) != EOF) { | ||
983 | if (!strcmp(devname, ptr->name)) { | ||
984 | sprintf(addr6, "%s:%s:%s:%s:%s:%s:%s:%s", | ||
985 | addr6p[0], addr6p[1], addr6p[2], addr6p[3], | ||
986 | addr6p[4], addr6p[5], addr6p[6], addr6p[7]); | ||
987 | inet_pton(AF_INET6, addr6, | ||
988 | (struct sockaddr *) &sap.sin6_addr); | ||
989 | sap.sin6_family = AF_INET6; | ||
990 | printf(" inet6 addr: %s/%d", | ||
991 | inet6_aftype.sprint((struct sockaddr *) &sap, 1), | ||
992 | plen); | ||
993 | printf(" Scope:"); | ||
994 | switch (scope & IPV6_ADDR_SCOPE_MASK) { | ||
995 | case 0: | ||
996 | printf("Global"); | ||
997 | break; | ||
998 | case IPV6_ADDR_LINKLOCAL: | ||
999 | printf("Link"); | ||
1000 | break; | ||
1001 | case IPV6_ADDR_SITELOCAL: | ||
1002 | printf("Site"); | ||
1003 | break; | ||
1004 | case IPV6_ADDR_COMPATv4: | ||
1005 | printf("Compat"); | ||
1006 | break; | ||
1007 | case IPV6_ADDR_LOOPBACK: | ||
1008 | printf("Host"); | ||
1009 | break; | ||
1010 | default: | ||
1011 | printf("Unknown"); | ||
1012 | } | ||
1013 | puts(""); | ||
1014 | } | ||
1015 | } | ||
1016 | fclose(f); | ||
1017 | } | ||
1018 | #endif | ||
1019 | |||
1020 | printf(" "); | ||
1021 | /* DONT FORGET TO ADD THE FLAGS IN ife_print_short, too */ | ||
1022 | |||
1023 | if (ptr->flags == 0) { | ||
1024 | printf("[NO FLAGS] "); | ||
1025 | } else { | ||
1026 | int i = 0; | ||
1027 | do { | ||
1028 | if (ptr->flags & ife_print_flags_mask[i]) { | ||
1029 | printf(ife_print_flags_strs[i]); | ||
1030 | } | ||
1031 | } while (ife_print_flags_mask[++i]); | ||
1032 | } | ||
1033 | |||
1034 | /* DONT FORGET TO ADD THE FLAGS IN ife_print_short */ | ||
1035 | printf(" MTU:%d Metric:%d", ptr->mtu, ptr->metric ? ptr->metric : 1); | ||
1036 | #ifdef SIOCSKEEPALIVE | ||
1037 | if (ptr->outfill || ptr->keepalive) | ||
1038 | printf(" Outfill:%d Keepalive:%d", ptr->outfill, ptr->keepalive); | ||
1039 | #endif | ||
1040 | puts(""); | ||
1041 | |||
1042 | /* If needed, display the interface statistics. */ | ||
1043 | |||
1044 | if (ptr->statistics_valid) { | ||
1045 | /* XXX: statistics are currently only printed for the primary address, | ||
1046 | * not for the aliases, although strictly speaking they're shared | ||
1047 | * by all addresses. | ||
1048 | */ | ||
1049 | printf(" "); | ||
1050 | |||
1051 | printf("RX packets:%llu errors:%lu dropped:%lu overruns:%lu frame:%lu\n", | ||
1052 | ptr->stats.rx_packets, ptr->stats.rx_errors, | ||
1053 | ptr->stats.rx_dropped, ptr->stats.rx_fifo_errors, | ||
1054 | ptr->stats.rx_frame_errors); | ||
1055 | if (can_compress) | ||
1056 | printf(" compressed:%lu\n", | ||
1057 | ptr->stats.rx_compressed); | ||
1058 | printf(" "); | ||
1059 | printf("TX packets:%llu errors:%lu dropped:%lu overruns:%lu carrier:%lu\n", | ||
1060 | ptr->stats.tx_packets, ptr->stats.tx_errors, | ||
1061 | ptr->stats.tx_dropped, ptr->stats.tx_fifo_errors, | ||
1062 | ptr->stats.tx_carrier_errors); | ||
1063 | printf(" collisions:%lu ", ptr->stats.collisions); | ||
1064 | if (can_compress) | ||
1065 | printf("compressed:%lu ", ptr->stats.tx_compressed); | ||
1066 | if (ptr->tx_queue_len != -1) | ||
1067 | printf("txqueuelen:%d ", ptr->tx_queue_len); | ||
1068 | printf("\n R"); | ||
1069 | print_bytes_scaled(ptr->stats.rx_bytes, " T"); | ||
1070 | print_bytes_scaled(ptr->stats.tx_bytes, "\n"); | ||
1071 | |||
1072 | } | ||
1073 | |||
1074 | if ((ptr->map.irq || ptr->map.mem_start || ptr->map.dma || | ||
1075 | ptr->map.base_addr)) { | ||
1076 | printf(" "); | ||
1077 | if (ptr->map.irq) | ||
1078 | printf("Interrupt:%d ", ptr->map.irq); | ||
1079 | if (ptr->map.base_addr >= 0x100) /* Only print devices using it for | ||
1080 | I/O maps */ | ||
1081 | printf("Base address:0x%lx ", | ||
1082 | (unsigned long) ptr->map.base_addr); | ||
1083 | if (ptr->map.mem_start) { | ||
1084 | printf("Memory:%lx-%lx ", ptr->map.mem_start, | ||
1085 | ptr->map.mem_end); | ||
1086 | } | ||
1087 | if (ptr->map.dma) | ||
1088 | printf("DMA chan:%x ", ptr->map.dma); | ||
1089 | puts(""); | ||
1090 | } | ||
1091 | puts(""); | ||
1092 | } | ||
1093 | |||
1094 | |||
1095 | static int do_if_print(struct interface *ife, void *cookie) | ||
1096 | { | ||
1097 | int *opt_a = (int *) cookie; | ||
1098 | int res; | ||
1099 | |||
1100 | res = do_if_fetch(ife); | ||
1101 | if (res >= 0) { | ||
1102 | if ((ife->flags & IFF_UP) || *opt_a) | ||
1103 | ife_print(ife); | ||
1104 | } | ||
1105 | return res; | ||
1106 | } | ||
1107 | |||
1108 | static struct interface *lookup_interface(char *name) | ||
1109 | { | ||
1110 | struct interface *ife = NULL; | ||
1111 | |||
1112 | if (if_readlist_proc(name) < 0) | ||
1113 | return NULL; | ||
1114 | ife = add_interface(name); | ||
1115 | return ife; | ||
1116 | } | ||
1117 | |||
1118 | /* for ipv4 add/del modes */ | ||
1119 | static int if_print(char *ifname) | ||
1120 | { | ||
1121 | int res; | ||
1122 | |||
1123 | if (!ifname) { | ||
1124 | res = for_all_interfaces(do_if_print, &interface_opt_a); | ||
1125 | } else { | ||
1126 | struct interface *ife; | ||
1127 | |||
1128 | ife = lookup_interface(ifname); | ||
1129 | res = do_if_fetch(ife); | ||
1130 | if (res >= 0) | ||
1131 | ife_print(ife); | ||
1132 | } | ||
1133 | return res; | ||
1134 | } | ||
1135 | |||
1136 | int display_interfaces(char *ifname); | ||
1137 | int display_interfaces(char *ifname) | ||
1138 | { | ||
1139 | int status; | ||
1140 | |||
1141 | /* Create a channel to the NET kernel. */ | ||
1142 | if ((skfd = sockets_open(0)) < 0) { | ||
1143 | bb_perror_msg_and_die("socket"); | ||
1144 | } | ||
1145 | |||
1146 | /* Do we have to show the current setup? */ | ||
1147 | status = if_print(ifname); | ||
1148 | #ifdef CONFIG_FEATURE_CLEAN_UP | ||
1149 | sockets_close(); | ||
1150 | #endif | ||
1151 | exit(status < 0); | ||
1152 | } | ||
diff --git a/networking/ip.c b/networking/ip.c new file mode 100644 index 000000000..4c8b89e2f --- /dev/null +++ b/networking/ip.c | |||
@@ -0,0 +1,48 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * ip.c "ip" utility frontend. | ||
4 | * | ||
5 | * Licensed under the GPL v2 or later, see the file LICENSE in this tarball. | ||
6 | * | ||
7 | * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> | ||
8 | * | ||
9 | * | ||
10 | * Changes: | ||
11 | * | ||
12 | * Rani Assaf <rani@magic.metawire.com> 980929: resolve addresses | ||
13 | */ | ||
14 | |||
15 | #include "busybox.h" | ||
16 | |||
17 | #include "libiproute/utils.h" | ||
18 | #include "libiproute/ip_common.h" | ||
19 | |||
20 | int ip_main(int argc, char **argv) | ||
21 | { | ||
22 | int ret = EXIT_FAILURE; | ||
23 | |||
24 | ip_parse_common_args(&argc, &argv); | ||
25 | |||
26 | if (argc > 1) { | ||
27 | if (ENABLE_FEATURE_IP_ADDRESS && matches(argv[1], "address") == 0) { | ||
28 | ret = do_ipaddr(argc-2, argv+2); | ||
29 | } | ||
30 | if (ENABLE_FEATURE_IP_ROUTE && matches(argv[1], "route") == 0) { | ||
31 | ret = do_iproute(argc-2, argv+2); | ||
32 | } | ||
33 | if (ENABLE_FEATURE_IP_LINK && matches(argv[1], "link") == 0) { | ||
34 | ret = do_iplink(argc-2, argv+2); | ||
35 | } | ||
36 | if (ENABLE_FEATURE_IP_TUNNEL && | ||
37 | (matches(argv[1], "tunnel") == 0 || strcmp(argv[1], "tunl") == 0)) { | ||
38 | ret = do_iptunnel(argc-2, argv+2); | ||
39 | } | ||
40 | if (ENABLE_FEATURE_IP_RULE && matches(argv[1], "rule") == 0) { | ||
41 | ret = do_iprule(argc-2, argv+2); | ||
42 | } | ||
43 | } | ||
44 | if (ret) { | ||
45 | bb_show_usage(); | ||
46 | } | ||
47 | return EXIT_SUCCESS; | ||
48 | } | ||
diff --git a/networking/ipaddr.c b/networking/ipaddr.c new file mode 100644 index 000000000..c90780879 --- /dev/null +++ b/networking/ipaddr.c | |||
@@ -0,0 +1,25 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * ip.c "ip" utility frontend. | ||
4 | * | ||
5 | * Licensed under the GPL v2 or later, see the file LICENSE in this tarball. | ||
6 | * | ||
7 | * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> | ||
8 | * | ||
9 | * | ||
10 | * Changes: | ||
11 | * | ||
12 | * Rani Assaf <rani@magic.metawire.com> 980929: resolve addresses | ||
13 | */ | ||
14 | |||
15 | #include "libiproute/utils.h" | ||
16 | #include "libiproute/ip_common.h" | ||
17 | |||
18 | #include "busybox.h" | ||
19 | |||
20 | int ipaddr_main(int argc, char **argv) | ||
21 | { | ||
22 | ip_parse_common_args(&argc, &argv); | ||
23 | |||
24 | return do_ipaddr(argc-1, argv+1); | ||
25 | } | ||
diff --git a/networking/ipcalc.c b/networking/ipcalc.c new file mode 100644 index 000000000..0ee9646c2 --- /dev/null +++ b/networking/ipcalc.c | |||
@@ -0,0 +1,194 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * Mini ipcalc implementation for busybox | ||
4 | * | ||
5 | * By Jordan Crouse <jordan@cosmicpenguin.net> | ||
6 | * Stephan Linz <linz@li-pro.net> | ||
7 | * | ||
8 | * This is a complete reimplementation of the ipcalc program | ||
9 | * from Red Hat. I didn't look at their source code, but there | ||
10 | * is no denying that this is a loving reimplementation | ||
11 | * | ||
12 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | ||
13 | */ | ||
14 | |||
15 | #include "busybox.h" | ||
16 | #include <ctype.h> | ||
17 | #include <getopt.h> | ||
18 | #include <sys/socket.h> | ||
19 | #include <arpa/inet.h> | ||
20 | |||
21 | #define CLASS_A_NETMASK ntohl(0xFF000000) | ||
22 | #define CLASS_B_NETMASK ntohl(0xFFFF0000) | ||
23 | #define CLASS_C_NETMASK ntohl(0xFFFFFF00) | ||
24 | |||
25 | static unsigned long get_netmask(unsigned long ipaddr) | ||
26 | { | ||
27 | ipaddr = htonl(ipaddr); | ||
28 | |||
29 | if ((ipaddr & 0xC0000000) == 0xC0000000) | ||
30 | return CLASS_C_NETMASK; | ||
31 | else if ((ipaddr & 0x80000000) == 0x80000000) | ||
32 | return CLASS_B_NETMASK; | ||
33 | else if ((ipaddr & 0x80000000) == 0) | ||
34 | return CLASS_A_NETMASK; | ||
35 | else | ||
36 | return 0; | ||
37 | } | ||
38 | |||
39 | #ifdef CONFIG_FEATURE_IPCALC_FANCY | ||
40 | static int get_prefix(unsigned long netmask) | ||
41 | { | ||
42 | unsigned long msk = 0x80000000; | ||
43 | int ret = 0; | ||
44 | |||
45 | netmask = htonl(netmask); | ||
46 | while(msk) { | ||
47 | if (netmask & msk) | ||
48 | ret++; | ||
49 | msk >>= 1; | ||
50 | } | ||
51 | return ret; | ||
52 | } | ||
53 | #else | ||
54 | int get_prefix(unsigned long netmask); | ||
55 | #endif | ||
56 | |||
57 | |||
58 | #define NETMASK 0x01 | ||
59 | #define BROADCAST 0x02 | ||
60 | #define NETWORK 0x04 | ||
61 | #define NETPREFIX 0x08 | ||
62 | #define HOSTNAME 0x10 | ||
63 | #define SILENT 0x20 | ||
64 | |||
65 | #if ENABLE_FEATURE_IPCALC_LONG_OPTIONS | ||
66 | static const struct option long_options[] = { | ||
67 | { "netmask", no_argument, NULL, 'm' }, | ||
68 | { "broadcast", no_argument, NULL, 'b' }, | ||
69 | { "network", no_argument, NULL, 'n' }, | ||
70 | # if ENABLE_FEATURE_IPCALC_FANCY | ||
71 | { "prefix", no_argument, NULL, 'p' }, | ||
72 | { "hostname", no_argument, NULL, 'h' }, | ||
73 | { "silent", no_argument, NULL, 's' }, | ||
74 | # endif | ||
75 | { NULL, 0, NULL, 0 } | ||
76 | }; | ||
77 | #endif | ||
78 | |||
79 | int ipcalc_main(int argc, char **argv) | ||
80 | { | ||
81 | unsigned opt; | ||
82 | int have_netmask = 0; | ||
83 | in_addr_t netmask, broadcast, network, ipaddr; | ||
84 | struct in_addr a; | ||
85 | char *ipstr; | ||
86 | |||
87 | #if ENABLE_FEATURE_IPCALC_LONG_OPTIONS | ||
88 | applet_long_options = long_options; | ||
89 | #endif | ||
90 | opt = getopt32(argc, argv, "mbn" USE_FEATURE_IPCALC_FANCY("phs")); | ||
91 | argc -= optind; | ||
92 | argv += optind; | ||
93 | if (opt & (BROADCAST | NETWORK | NETPREFIX)) { | ||
94 | if (argc > 2 || argc <= 0) | ||
95 | bb_show_usage(); | ||
96 | } else { | ||
97 | if (argc != 1) | ||
98 | bb_show_usage(); | ||
99 | } | ||
100 | if (opt & SILENT) | ||
101 | logmode = LOGMODE_NONE; /* Suppress error_msg() output */ | ||
102 | |||
103 | ipstr = argv[0]; | ||
104 | if (ENABLE_FEATURE_IPCALC_FANCY) { | ||
105 | unsigned long netprefix = 0; | ||
106 | char *prefixstr; | ||
107 | |||
108 | prefixstr = ipstr; | ||
109 | |||
110 | while (*prefixstr) { | ||
111 | if (*prefixstr == '/') { | ||
112 | *prefixstr = (char)0; | ||
113 | prefixstr++; | ||
114 | if (*prefixstr) { | ||
115 | unsigned msk; | ||
116 | netprefix = xatoul_range(prefixstr, 0, 32); | ||
117 | netmask = 0; | ||
118 | msk = 0x80000000; | ||
119 | while (netprefix > 0) { | ||
120 | netmask |= msk; | ||
121 | msk >>= 1; | ||
122 | netprefix--; | ||
123 | } | ||
124 | netmask = htonl(netmask); | ||
125 | /* Even if it was 0, we will signify that we have a netmask. This allows */ | ||
126 | /* for specification of default routes, etc which have a 0 netmask/prefix */ | ||
127 | have_netmask = 1; | ||
128 | } | ||
129 | break; | ||
130 | } | ||
131 | prefixstr++; | ||
132 | } | ||
133 | } | ||
134 | ipaddr = inet_aton(ipstr, &a); | ||
135 | |||
136 | if (ipaddr == 0) { | ||
137 | bb_error_msg_and_die("bad IP address: %s", argv[0]); | ||
138 | } | ||
139 | ipaddr = a.s_addr; | ||
140 | |||
141 | if (argc == 2) { | ||
142 | if (ENABLE_FEATURE_IPCALC_FANCY && have_netmask) { | ||
143 | bb_error_msg_and_die("use prefix or netmask, not both"); | ||
144 | } | ||
145 | |||
146 | netmask = inet_aton(argv[1], &a); | ||
147 | if (netmask == 0) { | ||
148 | bb_error_msg_and_die("bad netmask: %s", argv[1]); | ||
149 | } | ||
150 | netmask = a.s_addr; | ||
151 | } else { | ||
152 | |||
153 | /* JHC - If the netmask wasn't provided then calculate it */ | ||
154 | if (!ENABLE_FEATURE_IPCALC_FANCY || !have_netmask) | ||
155 | netmask = get_netmask(ipaddr); | ||
156 | } | ||
157 | |||
158 | if (opt & NETMASK) { | ||
159 | printf("NETMASK=%s\n", inet_ntoa((*(struct in_addr *) &netmask))); | ||
160 | } | ||
161 | |||
162 | if (opt & BROADCAST) { | ||
163 | broadcast = (ipaddr & netmask) | ~netmask; | ||
164 | printf("BROADCAST=%s\n", inet_ntoa((*(struct in_addr *) &broadcast))); | ||
165 | } | ||
166 | |||
167 | if (opt & NETWORK) { | ||
168 | network = ipaddr & netmask; | ||
169 | printf("NETWORK=%s\n", inet_ntoa((*(struct in_addr *) &network))); | ||
170 | } | ||
171 | |||
172 | if (ENABLE_FEATURE_IPCALC_FANCY) { | ||
173 | if (opt & NETPREFIX) { | ||
174 | printf("PREFIX=%i\n", get_prefix(netmask)); | ||
175 | } | ||
176 | |||
177 | if (opt & HOSTNAME) { | ||
178 | struct hostent *hostinfo; | ||
179 | int x; | ||
180 | |||
181 | hostinfo = gethostbyaddr((char *) &ipaddr, sizeof(ipaddr), AF_INET); | ||
182 | if (!hostinfo) { | ||
183 | bb_herror_msg_and_die("cannot find hostname for %s", argv[0]); | ||
184 | } | ||
185 | for (x = 0; hostinfo->h_name[x]; x++) { | ||
186 | hostinfo->h_name[x] = tolower(hostinfo->h_name[x]); | ||
187 | } | ||
188 | |||
189 | printf("HOSTNAME=%s\n", hostinfo->h_name); | ||
190 | } | ||
191 | } | ||
192 | |||
193 | return EXIT_SUCCESS; | ||
194 | } | ||
diff --git a/networking/iplink.c b/networking/iplink.c new file mode 100644 index 000000000..4a28c74a9 --- /dev/null +++ b/networking/iplink.c | |||
@@ -0,0 +1,25 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * ip.c "ip" utility frontend. | ||
4 | * | ||
5 | * Licensed under the GPL v2 or later, see the file LICENSE in this tarball. | ||
6 | * | ||
7 | * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> | ||
8 | * | ||
9 | * | ||
10 | * Changes: | ||
11 | * | ||
12 | * Rani Assaf <rani@magic.metawire.com> 980929: resolve addresses | ||
13 | */ | ||
14 | |||
15 | #include "libiproute/utils.h" | ||
16 | #include "libiproute/ip_common.h" | ||
17 | |||
18 | #include "busybox.h" | ||
19 | |||
20 | int iplink_main(int argc, char **argv) | ||
21 | { | ||
22 | ip_parse_common_args(&argc, &argv); | ||
23 | |||
24 | return do_iplink(argc-1, argv+1); | ||
25 | } | ||
diff --git a/networking/iproute.c b/networking/iproute.c new file mode 100644 index 000000000..62335a8b3 --- /dev/null +++ b/networking/iproute.c | |||
@@ -0,0 +1,25 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * ip.c "ip" utility frontend. | ||
4 | * | ||
5 | * Licensed under the GPL v2 or later, see the file LICENSE in this tarball. | ||
6 | * | ||
7 | * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> | ||
8 | * | ||
9 | * | ||
10 | * Changes: | ||
11 | * | ||
12 | * Rani Assaf <rani@magic.metawire.com> 980929: resolve addresses | ||
13 | */ | ||
14 | |||
15 | #include "libiproute/utils.h" | ||
16 | #include "libiproute/ip_common.h" | ||
17 | |||
18 | #include "busybox.h" | ||
19 | |||
20 | int iproute_main(int argc, char **argv) | ||
21 | { | ||
22 | ip_parse_common_args(&argc, &argv); | ||
23 | |||
24 | return do_iproute(argc-1, argv+1); | ||
25 | } | ||
diff --git a/networking/iprule.c b/networking/iprule.c new file mode 100644 index 000000000..43eaea633 --- /dev/null +++ b/networking/iprule.c | |||
@@ -0,0 +1,25 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * ip.c "ip" utility frontend. | ||
4 | * | ||
5 | * Licensed under the GPL v2 or later, see the file LICENSE in this tarball. | ||
6 | * | ||
7 | * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> | ||
8 | * | ||
9 | * | ||
10 | * Changes: | ||
11 | * | ||
12 | * Rani Assaf <rani@magic.metawire.com> 980929: resolve addresses | ||
13 | */ | ||
14 | |||
15 | #include "libiproute/utils.h" | ||
16 | #include "libiproute/ip_common.h" | ||
17 | |||
18 | #include "busybox.h" | ||
19 | |||
20 | int iprule_main(int argc, char **argv) | ||
21 | { | ||
22 | ip_parse_common_args(&argc, &argv); | ||
23 | |||
24 | return do_iprule(argc-1, argv+1); | ||
25 | } | ||
diff --git a/networking/iptunnel.c b/networking/iptunnel.c new file mode 100644 index 000000000..9ae734a21 --- /dev/null +++ b/networking/iptunnel.c | |||
@@ -0,0 +1,25 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * ip.c "ip" utility frontend. | ||
4 | * | ||
5 | * Licensed under the GPL v2 or later, see the file LICENSE in this tarball. | ||
6 | * | ||
7 | * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> | ||
8 | * | ||
9 | * | ||
10 | * Changes: | ||
11 | * | ||
12 | * Rani Assaf <rani@magic.metawire.com> 980929: resolve addresses | ||
13 | */ | ||
14 | |||
15 | #include "libiproute/utils.h" | ||
16 | #include "libiproute/ip_common.h" | ||
17 | |||
18 | #include "busybox.h" | ||
19 | |||
20 | int iptunnel_main(int argc, char **argv) | ||
21 | { | ||
22 | ip_parse_common_args(&argc, &argv); | ||
23 | |||
24 | return do_iptunnel(argc-1, argv+1); | ||
25 | } | ||
diff --git a/networking/libiproute/Kbuild b/networking/libiproute/Kbuild new file mode 100644 index 000000000..8383630f2 --- /dev/null +++ b/networking/libiproute/Kbuild | |||
@@ -0,0 +1,66 @@ | |||
1 | # Makefile for busybox | ||
2 | # | ||
3 | # Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org> | ||
4 | # | ||
5 | # Licensed under the GPL v2 or later, see the file LICENSE in this tarball. | ||
6 | # | ||
7 | |||
8 | lib-y:= | ||
9 | lib-$(CONFIG_IP) += \ | ||
10 | ip_parse_common_args.o \ | ||
11 | ipaddress.o \ | ||
12 | iplink.o \ | ||
13 | iproute.o \ | ||
14 | iptunnel.o \ | ||
15 | iprule.o \ | ||
16 | libnetlink.o \ | ||
17 | ll_addr.o \ | ||
18 | ll_map.o \ | ||
19 | ll_proto.o \ | ||
20 | ll_types.o \ | ||
21 | rt_names.o \ | ||
22 | rtm_map.o \ | ||
23 | utils.o | ||
24 | |||
25 | lib-$(CONFIG_IPADDR) += \ | ||
26 | ip_parse_common_args.o \ | ||
27 | ipaddress.o \ | ||
28 | libnetlink.o \ | ||
29 | ll_addr.o \ | ||
30 | ll_map.o \ | ||
31 | ll_types.o \ | ||
32 | rt_names.o \ | ||
33 | utils.o | ||
34 | |||
35 | lib-$(CONFIG_IPLINK) += \ | ||
36 | ip_parse_common_args.o \ | ||
37 | ipaddress.o \ | ||
38 | iplink.o \ | ||
39 | libnetlink.o \ | ||
40 | ll_addr.o \ | ||
41 | ll_map.o \ | ||
42 | ll_types.o \ | ||
43 | rt_names.o \ | ||
44 | utils.o | ||
45 | |||
46 | lib-$(CONFIG_IPROUTE) += \ | ||
47 | ip_parse_common_args.o \ | ||
48 | iproute.o \ | ||
49 | libnetlink.o \ | ||
50 | ll_map.o \ | ||
51 | rt_names.o \ | ||
52 | rtm_map.o \ | ||
53 | utils.o | ||
54 | |||
55 | lib-$(CONFIG_IPTUNNEL) += \ | ||
56 | ip_parse_common_args.o \ | ||
57 | iptunnel.o \ | ||
58 | rt_names.o \ | ||
59 | utils.o | ||
60 | |||
61 | lib-$(CONFIG_IPRULE) += \ | ||
62 | ip_parse_common_args.o \ | ||
63 | iprule.o \ | ||
64 | rt_names.o \ | ||
65 | utils.o | ||
66 | |||
diff --git a/networking/libiproute/ip_common.h b/networking/libiproute/ip_common.h new file mode 100644 index 000000000..15291780e --- /dev/null +++ b/networking/libiproute/ip_common.h | |||
@@ -0,0 +1,34 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | #ifndef _IP_COMMON_H | ||
3 | #define _IP_COMMON_H 1 | ||
4 | |||
5 | #include "busybox.h" | ||
6 | #include <asm/types.h> | ||
7 | #include <linux/netlink.h> | ||
8 | #include <linux/rtnetlink.h> | ||
9 | #if !defined IFA_RTA | ||
10 | #include <linux/if_addr.h> | ||
11 | #endif | ||
12 | #if !defined IFLA_RTA | ||
13 | #include <linux/if_link.h> | ||
14 | #endif | ||
15 | |||
16 | extern int preferred_family; | ||
17 | extern char * _SL_; | ||
18 | |||
19 | extern void ip_parse_common_args(int *argcp, char ***argvp); | ||
20 | extern int print_neigh(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg); | ||
21 | extern int ipaddr_list_or_flush(int argc, char **argv, int flush); | ||
22 | extern int iproute_monitor(int argc, char **argv); | ||
23 | extern void iplink_usage(void) ATTRIBUTE_NORETURN; | ||
24 | extern void ipneigh_reset_filter(void); | ||
25 | extern int do_ipaddr(int argc, char **argv); | ||
26 | extern int do_iproute(int argc, char **argv); | ||
27 | extern int do_iprule(int argc, char **argv); | ||
28 | extern int do_ipneigh(int argc, char **argv); | ||
29 | extern int do_iptunnel(int argc, char **argv); | ||
30 | extern int do_iplink(int argc, char **argv); | ||
31 | extern int do_ipmonitor(int argc, char **argv); | ||
32 | extern int do_multiaddr(int argc, char **argv); | ||
33 | extern int do_multiroute(int argc, char **argv); | ||
34 | #endif /* ip_common.h */ | ||
diff --git a/networking/libiproute/ip_parse_common_args.c b/networking/libiproute/ip_parse_common_args.c new file mode 100644 index 000000000..fee6e5e95 --- /dev/null +++ b/networking/libiproute/ip_parse_common_args.c | |||
@@ -0,0 +1,77 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * ip.c "ip" utility frontend. | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or | ||
6 | * modify it under the terms of the GNU General Public License | ||
7 | * as published by the Free Software Foundation; either version | ||
8 | * 2 of the License, or (at your option) any later version. | ||
9 | * | ||
10 | * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> | ||
11 | * | ||
12 | * | ||
13 | * Changes: | ||
14 | * | ||
15 | * Rani Assaf <rani@magic.metawire.com> 980929: resolve addresses | ||
16 | */ | ||
17 | |||
18 | #include <string.h> | ||
19 | |||
20 | #include "libbb.h" | ||
21 | #include "utils.h" | ||
22 | #include "ip_common.h" | ||
23 | |||
24 | |||
25 | int preferred_family = AF_UNSPEC; | ||
26 | int oneline = 0; | ||
27 | char * _SL_ = NULL; | ||
28 | |||
29 | void ip_parse_common_args(int *argcp, char ***argvp) | ||
30 | { | ||
31 | int argc = *argcp; | ||
32 | char **argv = *argvp; | ||
33 | |||
34 | while (argc > 1) { | ||
35 | char *opt = argv[1]; | ||
36 | |||
37 | if (strcmp(opt,"--") == 0) { | ||
38 | argc--; argv++; | ||
39 | break; | ||
40 | } | ||
41 | |||
42 | if (opt[0] != '-') | ||
43 | break; | ||
44 | |||
45 | if (opt[1] == '-') | ||
46 | opt++; | ||
47 | |||
48 | if (matches(opt, "-family") == 0) { | ||
49 | argc--; | ||
50 | argv++; | ||
51 | if (!argv[1]) | ||
52 | bb_show_usage(); | ||
53 | if (strcmp(argv[1], "inet") == 0) | ||
54 | preferred_family = AF_INET; | ||
55 | else if (strcmp(argv[1], "inet6") == 0) | ||
56 | preferred_family = AF_INET6; | ||
57 | else if (strcmp(argv[1], "link") == 0) | ||
58 | preferred_family = AF_PACKET; | ||
59 | else | ||
60 | invarg(argv[1], "protocol family"); | ||
61 | } else if (strcmp(opt, "-4") == 0) { | ||
62 | preferred_family = AF_INET; | ||
63 | } else if (strcmp(opt, "-6") == 0) { | ||
64 | preferred_family = AF_INET6; | ||
65 | } else if (strcmp(opt, "-0") == 0) { | ||
66 | preferred_family = AF_PACKET; | ||
67 | } else if (matches(opt, "-oneline") == 0) { | ||
68 | ++oneline; | ||
69 | } else { | ||
70 | bb_show_usage(); | ||
71 | } | ||
72 | argc--; argv++; | ||
73 | } | ||
74 | _SL_ = oneline ? "\\" : "\n" ; | ||
75 | *argcp = argc; | ||
76 | *argvp = argv; | ||
77 | } | ||
diff --git a/networking/libiproute/ipaddress.c b/networking/libiproute/ipaddress.c new file mode 100644 index 000000000..2a267fef6 --- /dev/null +++ b/networking/libiproute/ipaddress.c | |||
@@ -0,0 +1,824 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * ipaddress.c "ip address". | ||
4 | * | ||
5 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | ||
6 | * | ||
7 | * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> | ||
8 | * | ||
9 | * Changes: | ||
10 | * Laszlo Valko <valko@linux.karinthy.hu> 990223: address label must be zero terminated | ||
11 | */ | ||
12 | |||
13 | #include "libbb.h" | ||
14 | #include <sys/socket.h> | ||
15 | #include <sys/ioctl.h> | ||
16 | |||
17 | #include <fnmatch.h> | ||
18 | #include <string.h> | ||
19 | #include <unistd.h> | ||
20 | |||
21 | #include <net/if.h> | ||
22 | #include <net/if_arp.h> | ||
23 | |||
24 | #include "rt_names.h" | ||
25 | #include "utils.h" | ||
26 | #include "ip_common.h" | ||
27 | |||
28 | |||
29 | static struct | ||
30 | { | ||
31 | int ifindex; | ||
32 | int family; | ||
33 | int oneline; | ||
34 | int showqueue; | ||
35 | inet_prefix pfx; | ||
36 | int scope, scopemask; | ||
37 | int flags, flagmask; | ||
38 | int up; | ||
39 | char *label; | ||
40 | int flushed; | ||
41 | char *flushb; | ||
42 | int flushp; | ||
43 | int flushe; | ||
44 | struct rtnl_handle *rth; | ||
45 | } filter; | ||
46 | |||
47 | static void print_link_flags(FILE *fp, unsigned flags, unsigned mdown) | ||
48 | { | ||
49 | fprintf(fp, "<"); | ||
50 | flags &= ~IFF_RUNNING; | ||
51 | #define _PF(f) if (flags&IFF_##f) { \ | ||
52 | flags &= ~IFF_##f ; \ | ||
53 | fprintf(fp, #f "%s", flags ? "," : ""); } | ||
54 | _PF(LOOPBACK); | ||
55 | _PF(BROADCAST); | ||
56 | _PF(POINTOPOINT); | ||
57 | _PF(MULTICAST); | ||
58 | _PF(NOARP); | ||
59 | #if 0 | ||
60 | _PF(ALLMULTI); | ||
61 | _PF(PROMISC); | ||
62 | _PF(MASTER); | ||
63 | _PF(SLAVE); | ||
64 | _PF(DEBUG); | ||
65 | _PF(DYNAMIC); | ||
66 | _PF(AUTOMEDIA); | ||
67 | _PF(PORTSEL); | ||
68 | _PF(NOTRAILERS); | ||
69 | #endif | ||
70 | _PF(UP); | ||
71 | #undef _PF | ||
72 | if (flags) | ||
73 | fprintf(fp, "%x", flags); | ||
74 | if (mdown) | ||
75 | fprintf(fp, ",M-DOWN"); | ||
76 | fprintf(fp, "> "); | ||
77 | } | ||
78 | |||
79 | static void print_queuelen(char *name) | ||
80 | { | ||
81 | struct ifreq ifr; | ||
82 | int s; | ||
83 | |||
84 | s = socket(AF_INET, SOCK_STREAM, 0); | ||
85 | if (s < 0) | ||
86 | return; | ||
87 | |||
88 | memset(&ifr, 0, sizeof(ifr)); | ||
89 | strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); | ||
90 | if (ioctl(s, SIOCGIFTXQLEN, &ifr) < 0) { | ||
91 | perror("SIOCGIFXQLEN"); | ||
92 | close(s); | ||
93 | return; | ||
94 | } | ||
95 | close(s); | ||
96 | |||
97 | if (ifr.ifr_qlen) | ||
98 | printf("qlen %d", ifr.ifr_qlen); | ||
99 | } | ||
100 | |||
101 | static int print_linkinfo(struct sockaddr_nl ATTRIBUTE_UNUSED *who, | ||
102 | const struct nlmsghdr *n, void ATTRIBUTE_UNUSED *arg) | ||
103 | { | ||
104 | FILE *fp = (FILE*)arg; | ||
105 | struct ifinfomsg *ifi = NLMSG_DATA(n); | ||
106 | struct rtattr * tb[IFLA_MAX+1]; | ||
107 | int len = n->nlmsg_len; | ||
108 | unsigned m_flag = 0; | ||
109 | |||
110 | if (n->nlmsg_type != RTM_NEWLINK && n->nlmsg_type != RTM_DELLINK) | ||
111 | return 0; | ||
112 | |||
113 | len -= NLMSG_LENGTH(sizeof(*ifi)); | ||
114 | if (len < 0) | ||
115 | return -1; | ||
116 | |||
117 | if (filter.ifindex && ifi->ifi_index != filter.ifindex) | ||
118 | return 0; | ||
119 | if (filter.up && !(ifi->ifi_flags&IFF_UP)) | ||
120 | return 0; | ||
121 | |||
122 | memset(tb, 0, sizeof(tb)); | ||
123 | parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), len); | ||
124 | if (tb[IFLA_IFNAME] == NULL) { | ||
125 | bb_error_msg("nil ifname"); | ||
126 | return -1; | ||
127 | } | ||
128 | if (filter.label && | ||
129 | (!filter.family || filter.family == AF_PACKET) && | ||
130 | fnmatch(filter.label, RTA_DATA(tb[IFLA_IFNAME]), 0)) | ||
131 | return 0; | ||
132 | |||
133 | if (n->nlmsg_type == RTM_DELLINK) | ||
134 | fprintf(fp, "Deleted "); | ||
135 | |||
136 | fprintf(fp, "%d: %s", ifi->ifi_index, | ||
137 | tb[IFLA_IFNAME] ? (char*)RTA_DATA(tb[IFLA_IFNAME]) : "<nil>"); | ||
138 | |||
139 | if (tb[IFLA_LINK]) { | ||
140 | SPRINT_BUF(b1); | ||
141 | int iflink = *(int*)RTA_DATA(tb[IFLA_LINK]); | ||
142 | if (iflink == 0) | ||
143 | fprintf(fp, "@NONE: "); | ||
144 | else { | ||
145 | fprintf(fp, "@%s: ", ll_idx_n2a(iflink, b1)); | ||
146 | m_flag = ll_index_to_flags(iflink); | ||
147 | m_flag = !(m_flag & IFF_UP); | ||
148 | } | ||
149 | } else { | ||
150 | fprintf(fp, ": "); | ||
151 | } | ||
152 | print_link_flags(fp, ifi->ifi_flags, m_flag); | ||
153 | |||
154 | if (tb[IFLA_MTU]) | ||
155 | fprintf(fp, "mtu %u ", *(int*)RTA_DATA(tb[IFLA_MTU])); | ||
156 | if (tb[IFLA_QDISC]) | ||
157 | fprintf(fp, "qdisc %s ", (char*)RTA_DATA(tb[IFLA_QDISC])); | ||
158 | #ifdef IFLA_MASTER | ||
159 | if (tb[IFLA_MASTER]) { | ||
160 | SPRINT_BUF(b1); | ||
161 | fprintf(fp, "master %s ", ll_idx_n2a(*(int*)RTA_DATA(tb[IFLA_MASTER]), b1)); | ||
162 | } | ||
163 | #endif | ||
164 | if (filter.showqueue) | ||
165 | print_queuelen((char*)RTA_DATA(tb[IFLA_IFNAME])); | ||
166 | |||
167 | if (!filter.family || filter.family == AF_PACKET) { | ||
168 | SPRINT_BUF(b1); | ||
169 | fprintf(fp, "%s", _SL_); | ||
170 | fprintf(fp, " link/%s ", ll_type_n2a(ifi->ifi_type, b1, sizeof(b1))); | ||
171 | |||
172 | if (tb[IFLA_ADDRESS]) { | ||
173 | fprintf(fp, "%s", ll_addr_n2a(RTA_DATA(tb[IFLA_ADDRESS]), | ||
174 | RTA_PAYLOAD(tb[IFLA_ADDRESS]), | ||
175 | ifi->ifi_type, | ||
176 | b1, sizeof(b1))); | ||
177 | } | ||
178 | if (tb[IFLA_BROADCAST]) { | ||
179 | if (ifi->ifi_flags&IFF_POINTOPOINT) | ||
180 | fprintf(fp, " peer "); | ||
181 | else | ||
182 | fprintf(fp, " brd "); | ||
183 | fprintf(fp, "%s", ll_addr_n2a(RTA_DATA(tb[IFLA_BROADCAST]), | ||
184 | RTA_PAYLOAD(tb[IFLA_BROADCAST]), | ||
185 | ifi->ifi_type, | ||
186 | b1, sizeof(b1))); | ||
187 | } | ||
188 | } | ||
189 | fprintf(fp, "\n"); | ||
190 | fflush(fp); | ||
191 | return 0; | ||
192 | } | ||
193 | |||
194 | static int flush_update(void) | ||
195 | { | ||
196 | if (rtnl_send(filter.rth, filter.flushb, filter.flushp) < 0) { | ||
197 | perror("Failed to send flush request\n"); | ||
198 | return -1; | ||
199 | } | ||
200 | filter.flushp = 0; | ||
201 | return 0; | ||
202 | } | ||
203 | |||
204 | static int print_addrinfo(struct sockaddr_nl ATTRIBUTE_UNUSED *who, | ||
205 | struct nlmsghdr *n, void ATTRIBUTE_UNUSED *arg) | ||
206 | { | ||
207 | FILE *fp = (FILE*)arg; | ||
208 | struct ifaddrmsg *ifa = NLMSG_DATA(n); | ||
209 | int len = n->nlmsg_len; | ||
210 | struct rtattr * rta_tb[IFA_MAX+1]; | ||
211 | char abuf[256]; | ||
212 | SPRINT_BUF(b1); | ||
213 | |||
214 | if (n->nlmsg_type != RTM_NEWADDR && n->nlmsg_type != RTM_DELADDR) | ||
215 | return 0; | ||
216 | len -= NLMSG_LENGTH(sizeof(*ifa)); | ||
217 | if (len < 0) { | ||
218 | bb_error_msg("wrong nlmsg len %d", len); | ||
219 | return -1; | ||
220 | } | ||
221 | |||
222 | if (filter.flushb && n->nlmsg_type != RTM_NEWADDR) | ||
223 | return 0; | ||
224 | |||
225 | memset(rta_tb, 0, sizeof(rta_tb)); | ||
226 | parse_rtattr(rta_tb, IFA_MAX, IFA_RTA(ifa), n->nlmsg_len - NLMSG_LENGTH(sizeof(*ifa))); | ||
227 | |||
228 | if (!rta_tb[IFA_LOCAL]) | ||
229 | rta_tb[IFA_LOCAL] = rta_tb[IFA_ADDRESS]; | ||
230 | if (!rta_tb[IFA_ADDRESS]) | ||
231 | rta_tb[IFA_ADDRESS] = rta_tb[IFA_LOCAL]; | ||
232 | |||
233 | if (filter.ifindex && filter.ifindex != ifa->ifa_index) | ||
234 | return 0; | ||
235 | if ((filter.scope^ifa->ifa_scope)&filter.scopemask) | ||
236 | return 0; | ||
237 | if ((filter.flags^ifa->ifa_flags)&filter.flagmask) | ||
238 | return 0; | ||
239 | if (filter.label) { | ||
240 | const char *label; | ||
241 | if (rta_tb[IFA_LABEL]) | ||
242 | label = RTA_DATA(rta_tb[IFA_LABEL]); | ||
243 | else | ||
244 | label = ll_idx_n2a(ifa->ifa_index, b1); | ||
245 | if (fnmatch(filter.label, label, 0) != 0) | ||
246 | return 0; | ||
247 | } | ||
248 | if (filter.pfx.family) { | ||
249 | if (rta_tb[IFA_LOCAL]) { | ||
250 | inet_prefix dst; | ||
251 | memset(&dst, 0, sizeof(dst)); | ||
252 | dst.family = ifa->ifa_family; | ||
253 | memcpy(&dst.data, RTA_DATA(rta_tb[IFA_LOCAL]), RTA_PAYLOAD(rta_tb[IFA_LOCAL])); | ||
254 | if (inet_addr_match(&dst, &filter.pfx, filter.pfx.bitlen)) | ||
255 | return 0; | ||
256 | } | ||
257 | } | ||
258 | |||
259 | if (filter.flushb) { | ||
260 | struct nlmsghdr *fn; | ||
261 | if (NLMSG_ALIGN(filter.flushp) + n->nlmsg_len > filter.flushe) { | ||
262 | if (flush_update()) | ||
263 | return -1; | ||
264 | } | ||
265 | fn = (struct nlmsghdr*)(filter.flushb + NLMSG_ALIGN(filter.flushp)); | ||
266 | memcpy(fn, n, n->nlmsg_len); | ||
267 | fn->nlmsg_type = RTM_DELADDR; | ||
268 | fn->nlmsg_flags = NLM_F_REQUEST; | ||
269 | fn->nlmsg_seq = ++filter.rth->seq; | ||
270 | filter.flushp = (((char*)fn) + n->nlmsg_len) - filter.flushb; | ||
271 | filter.flushed++; | ||
272 | return 0; | ||
273 | } | ||
274 | |||
275 | if (n->nlmsg_type == RTM_DELADDR) | ||
276 | fprintf(fp, "Deleted "); | ||
277 | |||
278 | if (filter.oneline) | ||
279 | fprintf(fp, "%u: %s", ifa->ifa_index, ll_index_to_name(ifa->ifa_index)); | ||
280 | if (ifa->ifa_family == AF_INET) | ||
281 | fprintf(fp, " inet "); | ||
282 | else if (ifa->ifa_family == AF_INET6) | ||
283 | fprintf(fp, " inet6 "); | ||
284 | else | ||
285 | fprintf(fp, " family %d ", ifa->ifa_family); | ||
286 | |||
287 | if (rta_tb[IFA_LOCAL]) { | ||
288 | fprintf(fp, "%s", rt_addr_n2a(ifa->ifa_family, | ||
289 | RTA_PAYLOAD(rta_tb[IFA_LOCAL]), | ||
290 | RTA_DATA(rta_tb[IFA_LOCAL]), | ||
291 | abuf, sizeof(abuf))); | ||
292 | |||
293 | if (rta_tb[IFA_ADDRESS] == NULL || | ||
294 | memcmp(RTA_DATA(rta_tb[IFA_ADDRESS]), RTA_DATA(rta_tb[IFA_LOCAL]), 4) == 0) { | ||
295 | fprintf(fp, "/%d ", ifa->ifa_prefixlen); | ||
296 | } else { | ||
297 | fprintf(fp, " peer %s/%d ", | ||
298 | rt_addr_n2a(ifa->ifa_family, | ||
299 | RTA_PAYLOAD(rta_tb[IFA_ADDRESS]), | ||
300 | RTA_DATA(rta_tb[IFA_ADDRESS]), | ||
301 | abuf, sizeof(abuf)), | ||
302 | ifa->ifa_prefixlen); | ||
303 | } | ||
304 | } | ||
305 | |||
306 | if (rta_tb[IFA_BROADCAST]) { | ||
307 | fprintf(fp, "brd %s ", | ||
308 | rt_addr_n2a(ifa->ifa_family, | ||
309 | RTA_PAYLOAD(rta_tb[IFA_BROADCAST]), | ||
310 | RTA_DATA(rta_tb[IFA_BROADCAST]), | ||
311 | abuf, sizeof(abuf))); | ||
312 | } | ||
313 | if (rta_tb[IFA_ANYCAST]) { | ||
314 | fprintf(fp, "any %s ", | ||
315 | rt_addr_n2a(ifa->ifa_family, | ||
316 | RTA_PAYLOAD(rta_tb[IFA_ANYCAST]), | ||
317 | RTA_DATA(rta_tb[IFA_ANYCAST]), | ||
318 | abuf, sizeof(abuf))); | ||
319 | } | ||
320 | fprintf(fp, "scope %s ", rtnl_rtscope_n2a(ifa->ifa_scope, b1, sizeof(b1))); | ||
321 | if (ifa->ifa_flags&IFA_F_SECONDARY) { | ||
322 | ifa->ifa_flags &= ~IFA_F_SECONDARY; | ||
323 | fprintf(fp, "secondary "); | ||
324 | } | ||
325 | if (ifa->ifa_flags&IFA_F_TENTATIVE) { | ||
326 | ifa->ifa_flags &= ~IFA_F_TENTATIVE; | ||
327 | fprintf(fp, "tentative "); | ||
328 | } | ||
329 | if (ifa->ifa_flags&IFA_F_DEPRECATED) { | ||
330 | ifa->ifa_flags &= ~IFA_F_DEPRECATED; | ||
331 | fprintf(fp, "deprecated "); | ||
332 | } | ||
333 | if (!(ifa->ifa_flags&IFA_F_PERMANENT)) { | ||
334 | fprintf(fp, "dynamic "); | ||
335 | } else | ||
336 | ifa->ifa_flags &= ~IFA_F_PERMANENT; | ||
337 | if (ifa->ifa_flags) | ||
338 | fprintf(fp, "flags %02x ", ifa->ifa_flags); | ||
339 | if (rta_tb[IFA_LABEL]) | ||
340 | fprintf(fp, "%s", (char*)RTA_DATA(rta_tb[IFA_LABEL])); | ||
341 | if (rta_tb[IFA_CACHEINFO]) { | ||
342 | struct ifa_cacheinfo *ci = RTA_DATA(rta_tb[IFA_CACHEINFO]); | ||
343 | char buf[128]; | ||
344 | fprintf(fp, "%s", _SL_); | ||
345 | if (ci->ifa_valid == 0xFFFFFFFFU) | ||
346 | sprintf(buf, "valid_lft forever"); | ||
347 | else | ||
348 | sprintf(buf, "valid_lft %dsec", ci->ifa_valid); | ||
349 | if (ci->ifa_prefered == 0xFFFFFFFFU) | ||
350 | sprintf(buf+strlen(buf), " preferred_lft forever"); | ||
351 | else | ||
352 | sprintf(buf+strlen(buf), " preferred_lft %dsec", ci->ifa_prefered); | ||
353 | fprintf(fp, " %s", buf); | ||
354 | } | ||
355 | fprintf(fp, "\n"); | ||
356 | fflush(fp); | ||
357 | return 0; | ||
358 | } | ||
359 | |||
360 | |||
361 | struct nlmsg_list | ||
362 | { | ||
363 | struct nlmsg_list *next; | ||
364 | struct nlmsghdr h; | ||
365 | }; | ||
366 | |||
367 | static int print_selected_addrinfo(int ifindex, struct nlmsg_list *ainfo, FILE *fp) | ||
368 | { | ||
369 | for ( ;ainfo ; ainfo = ainfo->next) { | ||
370 | struct nlmsghdr *n = &ainfo->h; | ||
371 | struct ifaddrmsg *ifa = NLMSG_DATA(n); | ||
372 | |||
373 | if (n->nlmsg_type != RTM_NEWADDR) | ||
374 | continue; | ||
375 | |||
376 | if (n->nlmsg_len < NLMSG_LENGTH(sizeof(ifa))) | ||
377 | return -1; | ||
378 | |||
379 | if (ifa->ifa_index != ifindex || | ||
380 | (filter.family && filter.family != ifa->ifa_family)) | ||
381 | continue; | ||
382 | |||
383 | print_addrinfo(NULL, n, fp); | ||
384 | } | ||
385 | return 0; | ||
386 | } | ||
387 | |||
388 | |||
389 | static int store_nlmsg(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) | ||
390 | { | ||
391 | struct nlmsg_list **linfo = (struct nlmsg_list**)arg; | ||
392 | struct nlmsg_list *h; | ||
393 | struct nlmsg_list **lp; | ||
394 | |||
395 | h = malloc(n->nlmsg_len+sizeof(void*)); | ||
396 | if (h == NULL) | ||
397 | return -1; | ||
398 | |||
399 | memcpy(&h->h, n, n->nlmsg_len); | ||
400 | h->next = NULL; | ||
401 | |||
402 | for (lp = linfo; *lp; lp = &(*lp)->next) /* NOTHING */; | ||
403 | *lp = h; | ||
404 | |||
405 | ll_remember_index(who, n, NULL); | ||
406 | return 0; | ||
407 | } | ||
408 | |||
409 | static void ipaddr_reset_filter(int _oneline) | ||
410 | { | ||
411 | memset(&filter, 0, sizeof(filter)); | ||
412 | filter.oneline = _oneline; | ||
413 | } | ||
414 | |||
415 | int ipaddr_list_or_flush(int argc, char **argv, int flush) | ||
416 | { | ||
417 | static const char *const option[] = { "to", "scope", "up", "label", "dev", 0 }; | ||
418 | |||
419 | struct nlmsg_list *linfo = NULL; | ||
420 | struct nlmsg_list *ainfo = NULL; | ||
421 | struct nlmsg_list *l; | ||
422 | struct rtnl_handle rth; | ||
423 | char *filter_dev = NULL; | ||
424 | int no_link = 0; | ||
425 | |||
426 | ipaddr_reset_filter(oneline); | ||
427 | filter.showqueue = 1; | ||
428 | |||
429 | if (filter.family == AF_UNSPEC) | ||
430 | filter.family = preferred_family; | ||
431 | |||
432 | if (flush) { | ||
433 | if (argc <= 0) { | ||
434 | bb_error_msg(bb_msg_requires_arg, "flush"); | ||
435 | return -1; | ||
436 | } | ||
437 | if (filter.family == AF_PACKET) { | ||
438 | bb_error_msg("cannot flush link addresses"); | ||
439 | return -1; | ||
440 | } | ||
441 | } | ||
442 | |||
443 | while (argc > 0) { | ||
444 | const int option_num = index_in_str_array(option, *argv); | ||
445 | switch (option_num) { | ||
446 | case 0: /* to */ | ||
447 | NEXT_ARG(); | ||
448 | get_prefix(&filter.pfx, *argv, filter.family); | ||
449 | if (filter.family == AF_UNSPEC) { | ||
450 | filter.family = filter.pfx.family; | ||
451 | } | ||
452 | break; | ||
453 | case 1: /* scope */ | ||
454 | { | ||
455 | uint32_t scope = 0; | ||
456 | NEXT_ARG(); | ||
457 | filter.scopemask = -1; | ||
458 | if (rtnl_rtscope_a2n(&scope, *argv)) { | ||
459 | if (strcmp(*argv, "all") != 0) { | ||
460 | invarg(*argv, "scope"); | ||
461 | } | ||
462 | scope = RT_SCOPE_NOWHERE; | ||
463 | filter.scopemask = 0; | ||
464 | } | ||
465 | filter.scope = scope; | ||
466 | break; | ||
467 | } | ||
468 | case 2: /* up */ | ||
469 | filter.up = 1; | ||
470 | break; | ||
471 | case 3: /* label */ | ||
472 | NEXT_ARG(); | ||
473 | filter.label = *argv; | ||
474 | break; | ||
475 | case 4: /* dev */ | ||
476 | NEXT_ARG(); | ||
477 | default: | ||
478 | if (filter_dev) { | ||
479 | duparg2("dev", *argv); | ||
480 | } | ||
481 | filter_dev = *argv; | ||
482 | } | ||
483 | argv++; | ||
484 | argc--; | ||
485 | } | ||
486 | |||
487 | if (rtnl_open(&rth, 0) < 0) | ||
488 | exit(1); | ||
489 | |||
490 | if (rtnl_wilddump_request(&rth, preferred_family, RTM_GETLINK) < 0) { | ||
491 | bb_perror_msg_and_die("cannot send dump request"); | ||
492 | } | ||
493 | |||
494 | if (rtnl_dump_filter(&rth, store_nlmsg, &linfo, NULL, NULL) < 0) { | ||
495 | bb_error_msg_and_die("dump terminated"); | ||
496 | } | ||
497 | |||
498 | if (filter_dev) { | ||
499 | filter.ifindex = ll_name_to_index(filter_dev); | ||
500 | if (filter.ifindex <= 0) { | ||
501 | bb_error_msg("device \"%s\" does not exist", filter_dev); | ||
502 | return -1; | ||
503 | } | ||
504 | } | ||
505 | |||
506 | if (flush) { | ||
507 | char flushb[4096-512]; | ||
508 | |||
509 | filter.flushb = flushb; | ||
510 | filter.flushp = 0; | ||
511 | filter.flushe = sizeof(flushb); | ||
512 | filter.rth = &rth; | ||
513 | |||
514 | for (;;) { | ||
515 | if (rtnl_wilddump_request(&rth, filter.family, RTM_GETADDR) < 0) { | ||
516 | perror("Cannot send dump request"); | ||
517 | exit(1); | ||
518 | } | ||
519 | filter.flushed = 0; | ||
520 | if (rtnl_dump_filter(&rth, print_addrinfo, stdout, NULL, NULL) < 0) { | ||
521 | fprintf(stderr, "Flush terminated\n"); | ||
522 | exit(1); | ||
523 | } | ||
524 | if (filter.flushed == 0) { | ||
525 | fflush(stdout); | ||
526 | return 0; | ||
527 | } | ||
528 | if (flush_update() < 0) | ||
529 | exit(1); | ||
530 | } | ||
531 | } | ||
532 | |||
533 | if (filter.family != AF_PACKET) { | ||
534 | if (rtnl_wilddump_request(&rth, filter.family, RTM_GETADDR) < 0) { | ||
535 | bb_perror_msg_and_die("cannot send dump request"); | ||
536 | } | ||
537 | |||
538 | if (rtnl_dump_filter(&rth, store_nlmsg, &ainfo, NULL, NULL) < 0) { | ||
539 | bb_error_msg_and_die("dump terminated"); | ||
540 | } | ||
541 | } | ||
542 | |||
543 | |||
544 | if (filter.family && filter.family != AF_PACKET) { | ||
545 | struct nlmsg_list **lp; | ||
546 | lp=&linfo; | ||
547 | |||
548 | if (filter.oneline) | ||
549 | no_link = 1; | ||
550 | |||
551 | while ((l=*lp)!=NULL) { | ||
552 | int ok = 0; | ||
553 | struct ifinfomsg *ifi = NLMSG_DATA(&l->h); | ||
554 | struct nlmsg_list *a; | ||
555 | |||
556 | for (a=ainfo; a; a=a->next) { | ||
557 | struct nlmsghdr *n = &a->h; | ||
558 | struct ifaddrmsg *ifa = NLMSG_DATA(n); | ||
559 | |||
560 | if (ifa->ifa_index != ifi->ifi_index || | ||
561 | (filter.family && filter.family != ifa->ifa_family)) | ||
562 | continue; | ||
563 | if ((filter.scope^ifa->ifa_scope)&filter.scopemask) | ||
564 | continue; | ||
565 | if ((filter.flags^ifa->ifa_flags)&filter.flagmask) | ||
566 | continue; | ||
567 | if (filter.pfx.family || filter.label) { | ||
568 | struct rtattr *tb[IFA_MAX+1]; | ||
569 | memset(tb, 0, sizeof(tb)); | ||
570 | parse_rtattr(tb, IFA_MAX, IFA_RTA(ifa), IFA_PAYLOAD(n)); | ||
571 | if (!tb[IFA_LOCAL]) | ||
572 | tb[IFA_LOCAL] = tb[IFA_ADDRESS]; | ||
573 | |||
574 | if (filter.pfx.family && tb[IFA_LOCAL]) { | ||
575 | inet_prefix dst; | ||
576 | memset(&dst, 0, sizeof(dst)); | ||
577 | dst.family = ifa->ifa_family; | ||
578 | memcpy(&dst.data, RTA_DATA(tb[IFA_LOCAL]), RTA_PAYLOAD(tb[IFA_LOCAL])); | ||
579 | if (inet_addr_match(&dst, &filter.pfx, filter.pfx.bitlen)) | ||
580 | continue; | ||
581 | } | ||
582 | if (filter.label) { | ||
583 | SPRINT_BUF(b1); | ||
584 | const char *label; | ||
585 | if (tb[IFA_LABEL]) | ||
586 | label = RTA_DATA(tb[IFA_LABEL]); | ||
587 | else | ||
588 | label = ll_idx_n2a(ifa->ifa_index, b1); | ||
589 | if (fnmatch(filter.label, label, 0) != 0) | ||
590 | continue; | ||
591 | } | ||
592 | } | ||
593 | |||
594 | ok = 1; | ||
595 | break; | ||
596 | } | ||
597 | if (!ok) | ||
598 | *lp = l->next; | ||
599 | else | ||
600 | lp = &l->next; | ||
601 | } | ||
602 | } | ||
603 | |||
604 | for (l=linfo; l; l = l->next) { | ||
605 | if (no_link || print_linkinfo(NULL, &l->h, stdout) == 0) { | ||
606 | struct ifinfomsg *ifi = NLMSG_DATA(&l->h); | ||
607 | if (filter.family != AF_PACKET) | ||
608 | print_selected_addrinfo(ifi->ifi_index, ainfo, stdout); | ||
609 | } | ||
610 | fflush(stdout); | ||
611 | } | ||
612 | |||
613 | exit(0); | ||
614 | } | ||
615 | |||
616 | static int default_scope(inet_prefix *lcl) | ||
617 | { | ||
618 | if (lcl->family == AF_INET) { | ||
619 | if (lcl->bytelen >= 1 && *(__u8*)&lcl->data == 127) | ||
620 | return RT_SCOPE_HOST; | ||
621 | } | ||
622 | return 0; | ||
623 | } | ||
624 | |||
625 | static int ipaddr_modify(int cmd, int argc, char **argv) | ||
626 | { | ||
627 | static const char *const option[] = { | ||
628 | "peer", "remote", "broadcast", "brd", | ||
629 | "anycast", "scope", "dev", "label", "local", 0 | ||
630 | }; | ||
631 | |||
632 | struct rtnl_handle rth; | ||
633 | struct { | ||
634 | struct nlmsghdr n; | ||
635 | struct ifaddrmsg ifa; | ||
636 | char buf[256]; | ||
637 | } req; | ||
638 | char *d = NULL; | ||
639 | char *l = NULL; | ||
640 | inet_prefix lcl; | ||
641 | inet_prefix peer; | ||
642 | int local_len = 0; | ||
643 | int peer_len = 0; | ||
644 | int brd_len = 0; | ||
645 | int any_len = 0; | ||
646 | int scoped = 0; | ||
647 | |||
648 | memset(&req, 0, sizeof(req)); | ||
649 | |||
650 | req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg)); | ||
651 | req.n.nlmsg_flags = NLM_F_REQUEST; | ||
652 | req.n.nlmsg_type = cmd; | ||
653 | req.ifa.ifa_family = preferred_family; | ||
654 | |||
655 | while (argc > 0) { | ||
656 | const int option_num = index_in_str_array(option, *argv); | ||
657 | switch (option_num) { | ||
658 | case 0: /* peer */ | ||
659 | case 1: /* remote */ | ||
660 | NEXT_ARG(); | ||
661 | |||
662 | if (peer_len) { | ||
663 | duparg("peer", *argv); | ||
664 | } | ||
665 | get_prefix(&peer, *argv, req.ifa.ifa_family); | ||
666 | peer_len = peer.bytelen; | ||
667 | if (req.ifa.ifa_family == AF_UNSPEC) { | ||
668 | req.ifa.ifa_family = peer.family; | ||
669 | } | ||
670 | addattr_l(&req.n, sizeof(req), IFA_ADDRESS, &peer.data, peer.bytelen); | ||
671 | req.ifa.ifa_prefixlen = peer.bitlen; | ||
672 | break; | ||
673 | case 2: /* broadcast */ | ||
674 | case 3: /* brd */ | ||
675 | { | ||
676 | inet_prefix addr; | ||
677 | NEXT_ARG(); | ||
678 | if (brd_len) { | ||
679 | duparg("broadcast", *argv); | ||
680 | } | ||
681 | if (strcmp(*argv, "+") == 0) { | ||
682 | brd_len = -1; | ||
683 | } | ||
684 | else if (strcmp(*argv, "-") == 0) { | ||
685 | brd_len = -2; | ||
686 | } else { | ||
687 | get_addr(&addr, *argv, req.ifa.ifa_family); | ||
688 | if (req.ifa.ifa_family == AF_UNSPEC) | ||
689 | req.ifa.ifa_family = addr.family; | ||
690 | addattr_l(&req.n, sizeof(req), IFA_BROADCAST, &addr.data, addr.bytelen); | ||
691 | brd_len = addr.bytelen; | ||
692 | } | ||
693 | break; | ||
694 | } | ||
695 | case 4: /* anycast */ | ||
696 | { | ||
697 | inet_prefix addr; | ||
698 | NEXT_ARG(); | ||
699 | if (any_len) { | ||
700 | duparg("anycast", *argv); | ||
701 | } | ||
702 | get_addr(&addr, *argv, req.ifa.ifa_family); | ||
703 | if (req.ifa.ifa_family == AF_UNSPEC) { | ||
704 | req.ifa.ifa_family = addr.family; | ||
705 | } | ||
706 | addattr_l(&req.n, sizeof(req), IFA_ANYCAST, &addr.data, addr.bytelen); | ||
707 | any_len = addr.bytelen; | ||
708 | break; | ||
709 | } | ||
710 | case 5: /* scope */ | ||
711 | { | ||
712 | uint32_t scope = 0; | ||
713 | NEXT_ARG(); | ||
714 | if (rtnl_rtscope_a2n(&scope, *argv)) { | ||
715 | invarg(*argv, "scope"); | ||
716 | } | ||
717 | req.ifa.ifa_scope = scope; | ||
718 | scoped = 1; | ||
719 | break; | ||
720 | } | ||
721 | case 6: /* dev */ | ||
722 | NEXT_ARG(); | ||
723 | d = *argv; | ||
724 | break; | ||
725 | case 7: /* label */ | ||
726 | NEXT_ARG(); | ||
727 | l = *argv; | ||
728 | addattr_l(&req.n, sizeof(req), IFA_LABEL, l, strlen(l)+1); | ||
729 | break; | ||
730 | case 8: /* local */ | ||
731 | NEXT_ARG(); | ||
732 | default: | ||
733 | if (local_len) { | ||
734 | duparg2("local", *argv); | ||
735 | } | ||
736 | get_prefix(&lcl, *argv, req.ifa.ifa_family); | ||
737 | if (req.ifa.ifa_family == AF_UNSPEC) { | ||
738 | req.ifa.ifa_family = lcl.family; | ||
739 | } | ||
740 | addattr_l(&req.n, sizeof(req), IFA_LOCAL, &lcl.data, lcl.bytelen); | ||
741 | local_len = lcl.bytelen; | ||
742 | } | ||
743 | argc--; | ||
744 | argv++; | ||
745 | } | ||
746 | |||
747 | if (d == NULL) { | ||
748 | bb_error_msg(bb_msg_requires_arg,"\"dev\""); | ||
749 | return -1; | ||
750 | } | ||
751 | if (l && matches(d, l) != 0) { | ||
752 | bb_error_msg_and_die("\"dev\" (%s) must match \"label\" (%s)", d, l); | ||
753 | } | ||
754 | |||
755 | if (peer_len == 0 && local_len && cmd != RTM_DELADDR) { | ||
756 | peer = lcl; | ||
757 | addattr_l(&req.n, sizeof(req), IFA_ADDRESS, &lcl.data, lcl.bytelen); | ||
758 | } | ||
759 | if (req.ifa.ifa_prefixlen == 0) | ||
760 | req.ifa.ifa_prefixlen = lcl.bitlen; | ||
761 | |||
762 | if (brd_len < 0 && cmd != RTM_DELADDR) { | ||
763 | inet_prefix brd; | ||
764 | int i; | ||
765 | if (req.ifa.ifa_family != AF_INET) { | ||
766 | bb_error_msg("broadcast can be set only for IPv4 addresses"); | ||
767 | return -1; | ||
768 | } | ||
769 | brd = peer; | ||
770 | if (brd.bitlen <= 30) { | ||
771 | for (i=31; i>=brd.bitlen; i--) { | ||
772 | if (brd_len == -1) | ||
773 | brd.data[0] |= htonl(1<<(31-i)); | ||
774 | else | ||
775 | brd.data[0] &= ~htonl(1<<(31-i)); | ||
776 | } | ||
777 | addattr_l(&req.n, sizeof(req), IFA_BROADCAST, &brd.data, brd.bytelen); | ||
778 | brd_len = brd.bytelen; | ||
779 | } | ||
780 | } | ||
781 | if (!scoped && cmd != RTM_DELADDR) | ||
782 | req.ifa.ifa_scope = default_scope(&lcl); | ||
783 | |||
784 | if (rtnl_open(&rth, 0) < 0) | ||
785 | exit(1); | ||
786 | |||
787 | ll_init_map(&rth); | ||
788 | |||
789 | if ((req.ifa.ifa_index = ll_name_to_index(d)) == 0) { | ||
790 | bb_error_msg("cannot find device \"%s\"", d); | ||
791 | return -1; | ||
792 | } | ||
793 | |||
794 | if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0) | ||
795 | exit(2); | ||
796 | |||
797 | exit(0); | ||
798 | } | ||
799 | |||
800 | int do_ipaddr(int argc, char **argv) | ||
801 | { | ||
802 | static const char *const commands[] = { | ||
803 | "add", "delete", "list", "show", "lst", "flush", 0 | ||
804 | }; | ||
805 | |||
806 | int command_num = 2; | ||
807 | |||
808 | if (*argv) { | ||
809 | command_num = index_in_substr_array(commands, *argv); | ||
810 | } | ||
811 | switch (command_num) { | ||
812 | case 0: /* add */ | ||
813 | return ipaddr_modify(RTM_NEWADDR, argc-1, argv+1); | ||
814 | case 1: /* delete */ | ||
815 | return ipaddr_modify(RTM_DELADDR, argc-1, argv+1); | ||
816 | case 2: /* list */ | ||
817 | case 3: /* show */ | ||
818 | case 4: /* lst */ | ||
819 | return ipaddr_list_or_flush(argc-1, argv+1, 0); | ||
820 | case 5: /* flush */ | ||
821 | return ipaddr_list_or_flush(argc-1, argv+1, 1); | ||
822 | } | ||
823 | bb_error_msg_and_die("unknown command %s", *argv); | ||
824 | } | ||
diff --git a/networking/libiproute/iplink.c b/networking/libiproute/iplink.c new file mode 100644 index 000000000..1ea11f60b --- /dev/null +++ b/networking/libiproute/iplink.c | |||
@@ -0,0 +1,354 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * iplink.c "ip link". | ||
4 | * | ||
5 | * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> | ||
6 | * | ||
7 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | ||
8 | */ | ||
9 | |||
10 | #include "libbb.h" | ||
11 | |||
12 | #include <sys/ioctl.h> | ||
13 | #include <sys/socket.h> | ||
14 | |||
15 | #include <errno.h> | ||
16 | #include <string.h> | ||
17 | #include <unistd.h> | ||
18 | |||
19 | #include <net/if.h> | ||
20 | #include <net/if_packet.h> | ||
21 | #include <netpacket/packet.h> | ||
22 | |||
23 | #include <net/ethernet.h> | ||
24 | |||
25 | #include "rt_names.h" | ||
26 | #include "utils.h" | ||
27 | #include "ip_common.h" | ||
28 | |||
29 | /* take from linux/sockios.h */ | ||
30 | #define SIOCSIFNAME 0x8923 /* set interface name */ | ||
31 | |||
32 | static int on_off(char *msg) | ||
33 | { | ||
34 | bb_error_msg("error: argument of \"%s\" must be \"on\" or \"off\"", msg); | ||
35 | return -1; | ||
36 | } | ||
37 | |||
38 | static int get_ctl_fd(void) | ||
39 | { | ||
40 | int s_errno; | ||
41 | int fd; | ||
42 | |||
43 | fd = socket(PF_INET, SOCK_DGRAM, 0); | ||
44 | if (fd >= 0) | ||
45 | return fd; | ||
46 | s_errno = errno; | ||
47 | fd = socket(PF_PACKET, SOCK_DGRAM, 0); | ||
48 | if (fd >= 0) | ||
49 | return fd; | ||
50 | fd = socket(PF_INET6, SOCK_DGRAM, 0); | ||
51 | if (fd >= 0) | ||
52 | return fd; | ||
53 | errno = s_errno; | ||
54 | perror("Cannot create control socket"); | ||
55 | return -1; | ||
56 | } | ||
57 | |||
58 | static int do_chflags(char *dev, __u32 flags, __u32 mask) | ||
59 | { | ||
60 | struct ifreq ifr; | ||
61 | int fd; | ||
62 | int err; | ||
63 | |||
64 | strncpy(ifr.ifr_name, dev, sizeof(ifr.ifr_name)); | ||
65 | fd = get_ctl_fd(); | ||
66 | if (fd < 0) | ||
67 | return -1; | ||
68 | err = ioctl(fd, SIOCGIFFLAGS, &ifr); | ||
69 | if (err) { | ||
70 | perror("SIOCGIFFLAGS"); | ||
71 | close(fd); | ||
72 | return -1; | ||
73 | } | ||
74 | if ((ifr.ifr_flags^flags)&mask) { | ||
75 | ifr.ifr_flags &= ~mask; | ||
76 | ifr.ifr_flags |= mask&flags; | ||
77 | err = ioctl(fd, SIOCSIFFLAGS, &ifr); | ||
78 | if (err) | ||
79 | perror("SIOCSIFFLAGS"); | ||
80 | } | ||
81 | close(fd); | ||
82 | return err; | ||
83 | } | ||
84 | |||
85 | static int do_changename(char *dev, char *newdev) | ||
86 | { | ||
87 | struct ifreq ifr; | ||
88 | int fd; | ||
89 | int err; | ||
90 | |||
91 | strncpy(ifr.ifr_name, dev, sizeof(ifr.ifr_name)); | ||
92 | strncpy(ifr.ifr_newname, newdev, sizeof(ifr.ifr_newname)); | ||
93 | fd = get_ctl_fd(); | ||
94 | if (fd < 0) | ||
95 | return -1; | ||
96 | err = ioctl(fd, SIOCSIFNAME, &ifr); | ||
97 | if (err) { | ||
98 | perror("SIOCSIFNAME"); | ||
99 | close(fd); | ||
100 | return -1; | ||
101 | } | ||
102 | close(fd); | ||
103 | return err; | ||
104 | } | ||
105 | |||
106 | static int set_qlen(char *dev, int qlen) | ||
107 | { | ||
108 | struct ifreq ifr; | ||
109 | int s; | ||
110 | |||
111 | s = get_ctl_fd(); | ||
112 | if (s < 0) | ||
113 | return -1; | ||
114 | |||
115 | memset(&ifr, 0, sizeof(ifr)); | ||
116 | strncpy(ifr.ifr_name, dev, sizeof(ifr.ifr_name)); | ||
117 | ifr.ifr_qlen = qlen; | ||
118 | if (ioctl(s, SIOCSIFTXQLEN, &ifr) < 0) { | ||
119 | perror("SIOCSIFXQLEN"); | ||
120 | close(s); | ||
121 | return -1; | ||
122 | } | ||
123 | close(s); | ||
124 | |||
125 | return 0; | ||
126 | } | ||
127 | |||
128 | static int set_mtu(char *dev, int mtu) | ||
129 | { | ||
130 | struct ifreq ifr; | ||
131 | int s; | ||
132 | |||
133 | s = get_ctl_fd(); | ||
134 | if (s < 0) | ||
135 | return -1; | ||
136 | |||
137 | memset(&ifr, 0, sizeof(ifr)); | ||
138 | strncpy(ifr.ifr_name, dev, sizeof(ifr.ifr_name)); | ||
139 | ifr.ifr_mtu = mtu; | ||
140 | if (ioctl(s, SIOCSIFMTU, &ifr) < 0) { | ||
141 | perror("SIOCSIFMTU"); | ||
142 | close(s); | ||
143 | return -1; | ||
144 | } | ||
145 | close(s); | ||
146 | |||
147 | return 0; | ||
148 | } | ||
149 | |||
150 | static int get_address(char *dev, int *htype) | ||
151 | { | ||
152 | struct ifreq ifr; | ||
153 | struct sockaddr_ll me; | ||
154 | socklen_t alen; | ||
155 | int s; | ||
156 | |||
157 | s = socket(PF_PACKET, SOCK_DGRAM, 0); | ||
158 | if (s < 0) { | ||
159 | perror("socket(PF_PACKET)"); | ||
160 | return -1; | ||
161 | } | ||
162 | |||
163 | memset(&ifr, 0, sizeof(ifr)); | ||
164 | strncpy(ifr.ifr_name, dev, sizeof(ifr.ifr_name)); | ||
165 | if (ioctl(s, SIOCGIFINDEX, &ifr) < 0) { | ||
166 | perror("SIOCGIFINDEX"); | ||
167 | close(s); | ||
168 | return -1; | ||
169 | } | ||
170 | |||
171 | memset(&me, 0, sizeof(me)); | ||
172 | me.sll_family = AF_PACKET; | ||
173 | me.sll_ifindex = ifr.ifr_ifindex; | ||
174 | me.sll_protocol = htons(ETH_P_LOOP); | ||
175 | if (bind(s, (struct sockaddr*)&me, sizeof(me)) == -1) { | ||
176 | perror("bind"); | ||
177 | close(s); | ||
178 | return -1; | ||
179 | } | ||
180 | |||
181 | alen = sizeof(me); | ||
182 | if (getsockname(s, (struct sockaddr*)&me, &alen) == -1) { | ||
183 | perror("getsockname"); | ||
184 | close(s); | ||
185 | return -1; | ||
186 | } | ||
187 | close(s); | ||
188 | *htype = me.sll_hatype; | ||
189 | return me.sll_halen; | ||
190 | } | ||
191 | |||
192 | static int parse_address(char *dev, int hatype, int halen, char *lla, struct ifreq *ifr) | ||
193 | { | ||
194 | int alen; | ||
195 | |||
196 | memset(ifr, 0, sizeof(*ifr)); | ||
197 | strncpy(ifr->ifr_name, dev, sizeof(ifr->ifr_name)); | ||
198 | ifr->ifr_hwaddr.sa_family = hatype; | ||
199 | alen = ll_addr_a2n((unsigned char *)(ifr->ifr_hwaddr.sa_data), 14, lla); | ||
200 | if (alen < 0) | ||
201 | return -1; | ||
202 | if (alen != halen) { | ||
203 | bb_error_msg("wrong address (%s) length: expected %d bytes", lla, halen); | ||
204 | return -1; | ||
205 | } | ||
206 | return 0; | ||
207 | } | ||
208 | |||
209 | static int set_address(struct ifreq *ifr, int brd) | ||
210 | { | ||
211 | int s; | ||
212 | |||
213 | s = get_ctl_fd(); | ||
214 | if (s < 0) | ||
215 | return -1; | ||
216 | if (ioctl(s, brd?SIOCSIFHWBROADCAST:SIOCSIFHWADDR, ifr) < 0) { | ||
217 | perror(brd?"SIOCSIFHWBROADCAST":"SIOCSIFHWADDR"); | ||
218 | close(s); | ||
219 | return -1; | ||
220 | } | ||
221 | close(s); | ||
222 | return 0; | ||
223 | } | ||
224 | |||
225 | |||
226 | static int do_set(int argc, char **argv) | ||
227 | { | ||
228 | char *dev = NULL; | ||
229 | __u32 mask = 0; | ||
230 | __u32 flags = 0; | ||
231 | int qlen = -1; | ||
232 | int mtu = -1; | ||
233 | char *newaddr = NULL; | ||
234 | char *newbrd = NULL; | ||
235 | struct ifreq ifr0, ifr1; | ||
236 | char *newname = NULL; | ||
237 | int htype, halen; | ||
238 | |||
239 | while (argc > 0) { | ||
240 | if (strcmp(*argv, "up") == 0) { | ||
241 | mask |= IFF_UP; | ||
242 | flags |= IFF_UP; | ||
243 | } else if (strcmp(*argv, "down") == 0) { | ||
244 | mask |= IFF_UP; | ||
245 | flags &= ~IFF_UP; | ||
246 | } else if (strcmp(*argv, "name") == 0) { | ||
247 | NEXT_ARG(); | ||
248 | newname = *argv; | ||
249 | } else if (strcmp(*argv, "mtu") == 0) { | ||
250 | NEXT_ARG(); | ||
251 | if (mtu != -1) | ||
252 | duparg("mtu", *argv); | ||
253 | if (get_integer(&mtu, *argv, 0)) | ||
254 | invarg(*argv, "mtu"); | ||
255 | } else if (strcmp(*argv, "multicast") == 0) { | ||
256 | NEXT_ARG(); | ||
257 | mask |= IFF_MULTICAST; | ||
258 | if (strcmp(*argv, "on") == 0) { | ||
259 | flags |= IFF_MULTICAST; | ||
260 | } else if (strcmp(*argv, "off") == 0) { | ||
261 | flags &= ~IFF_MULTICAST; | ||
262 | } else | ||
263 | return on_off("multicast"); | ||
264 | } else if (strcmp(*argv, "arp") == 0) { | ||
265 | NEXT_ARG(); | ||
266 | mask |= IFF_NOARP; | ||
267 | if (strcmp(*argv, "on") == 0) { | ||
268 | flags &= ~IFF_NOARP; | ||
269 | } else if (strcmp(*argv, "off") == 0) { | ||
270 | flags |= IFF_NOARP; | ||
271 | } else | ||
272 | return on_off("noarp"); | ||
273 | } else if (strcmp(*argv, "addr") == 0) { | ||
274 | NEXT_ARG(); | ||
275 | newaddr = *argv; | ||
276 | } else { | ||
277 | if (strcmp(*argv, "dev") == 0) { | ||
278 | NEXT_ARG(); | ||
279 | } | ||
280 | if (dev) | ||
281 | duparg2("dev", *argv); | ||
282 | dev = *argv; | ||
283 | } | ||
284 | argc--; argv++; | ||
285 | } | ||
286 | |||
287 | if (!dev) { | ||
288 | bb_error_msg(bb_msg_requires_arg, "\"dev\""); | ||
289 | exit(-1); | ||
290 | } | ||
291 | |||
292 | if (newaddr || newbrd) { | ||
293 | halen = get_address(dev, &htype); | ||
294 | if (halen < 0) | ||
295 | return -1; | ||
296 | if (newaddr) { | ||
297 | if (parse_address(dev, htype, halen, newaddr, &ifr0) < 0) | ||
298 | return -1; | ||
299 | } | ||
300 | if (newbrd) { | ||
301 | if (parse_address(dev, htype, halen, newbrd, &ifr1) < 0) | ||
302 | return -1; | ||
303 | } | ||
304 | } | ||
305 | |||
306 | if (newname && strcmp(dev, newname)) { | ||
307 | if (do_changename(dev, newname) < 0) | ||
308 | return -1; | ||
309 | dev = newname; | ||
310 | } | ||
311 | if (qlen != -1) { | ||
312 | if (set_qlen(dev, qlen) < 0) | ||
313 | return -1; | ||
314 | } | ||
315 | if (mtu != -1) { | ||
316 | if (set_mtu(dev, mtu) < 0) | ||
317 | return -1; | ||
318 | } | ||
319 | if (newaddr || newbrd) { | ||
320 | if (newbrd) { | ||
321 | if (set_address(&ifr1, 1) < 0) | ||
322 | return -1; | ||
323 | } | ||
324 | if (newaddr) { | ||
325 | if (set_address(&ifr0, 0) < 0) | ||
326 | return -1; | ||
327 | } | ||
328 | } | ||
329 | if (mask) | ||
330 | return do_chflags(dev, flags, mask); | ||
331 | return 0; | ||
332 | } | ||
333 | |||
334 | static int ipaddr_list_link(int argc, char **argv) | ||
335 | { | ||
336 | preferred_family = AF_PACKET; | ||
337 | return ipaddr_list_or_flush(argc, argv, 0); | ||
338 | } | ||
339 | |||
340 | int do_iplink(int argc, char **argv) | ||
341 | { | ||
342 | if (argc > 0) { | ||
343 | if (matches(*argv, "set") == 0) | ||
344 | return do_set(argc-1, argv+1); | ||
345 | if (matches(*argv, "show") == 0 || | ||
346 | matches(*argv, "lst") == 0 || | ||
347 | matches(*argv, "list") == 0) | ||
348 | return ipaddr_list_link(argc-1, argv+1); | ||
349 | } else | ||
350 | return ipaddr_list_link(0, NULL); | ||
351 | |||
352 | bb_error_msg("command \"%s\" is unknown", *argv); | ||
353 | exit(-1); | ||
354 | } | ||
diff --git a/networking/libiproute/iproute.c b/networking/libiproute/iproute.c new file mode 100644 index 000000000..9c3b87040 --- /dev/null +++ b/networking/libiproute/iproute.c | |||
@@ -0,0 +1,858 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * iproute.c "ip route". | ||
4 | * | ||
5 | * Licensed under the GPL v2 or later, see the file LICENSE in this tarball. | ||
6 | * | ||
7 | * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> | ||
8 | * | ||
9 | * | ||
10 | * Changes: | ||
11 | * | ||
12 | * Rani Assaf <rani@magic.metawire.com> 980929: resolve addresses | ||
13 | * Kunihiro Ishiguro <kunihiro@zebra.org> 001102: rtnh_ifindex was not initialized | ||
14 | */ | ||
15 | |||
16 | #include "libbb.h" | ||
17 | |||
18 | #include <sys/socket.h> | ||
19 | |||
20 | #include <string.h> | ||
21 | #include <fcntl.h> | ||
22 | #include <unistd.h> | ||
23 | |||
24 | #include "rt_names.h" | ||
25 | #include "utils.h" | ||
26 | #include "ip_common.h" | ||
27 | |||
28 | #ifndef RTAX_RTTVAR | ||
29 | #define RTAX_RTTVAR RTAX_HOPS | ||
30 | #endif | ||
31 | |||
32 | |||
33 | static struct | ||
34 | { | ||
35 | int tb; | ||
36 | int flushed; | ||
37 | char *flushb; | ||
38 | int flushp; | ||
39 | int flushe; | ||
40 | struct rtnl_handle *rth; | ||
41 | int protocol, protocolmask; | ||
42 | int scope, scopemask; | ||
43 | int type, typemask; | ||
44 | int tos, tosmask; | ||
45 | int iif, iifmask; | ||
46 | int oif, oifmask; | ||
47 | int realm, realmmask; | ||
48 | inet_prefix rprefsrc; | ||
49 | inet_prefix rvia; | ||
50 | inet_prefix rdst; | ||
51 | inet_prefix mdst; | ||
52 | inet_prefix rsrc; | ||
53 | inet_prefix msrc; | ||
54 | } filter; | ||
55 | |||
56 | static int flush_update(void) | ||
57 | { | ||
58 | if (rtnl_send(filter.rth, filter.flushb, filter.flushp) < 0) { | ||
59 | perror("Failed to send flush request\n"); | ||
60 | return -1; | ||
61 | } | ||
62 | filter.flushp = 0; | ||
63 | return 0; | ||
64 | } | ||
65 | |||
66 | static int print_route(struct sockaddr_nl *who ATTRIBUTE_UNUSED, | ||
67 | struct nlmsghdr *n, void *arg) | ||
68 | { | ||
69 | FILE *fp = (FILE*)arg; | ||
70 | struct rtmsg *r = NLMSG_DATA(n); | ||
71 | int len = n->nlmsg_len; | ||
72 | struct rtattr * tb[RTA_MAX+1]; | ||
73 | char abuf[256]; | ||
74 | inet_prefix dst; | ||
75 | inet_prefix src; | ||
76 | int host_len = -1; | ||
77 | SPRINT_BUF(b1); | ||
78 | |||
79 | |||
80 | if (n->nlmsg_type != RTM_NEWROUTE && n->nlmsg_type != RTM_DELROUTE) { | ||
81 | fprintf(stderr, "Not a route: %08x %08x %08x\n", | ||
82 | n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags); | ||
83 | return 0; | ||
84 | } | ||
85 | if (filter.flushb && n->nlmsg_type != RTM_NEWROUTE) | ||
86 | return 0; | ||
87 | len -= NLMSG_LENGTH(sizeof(*r)); | ||
88 | if (len < 0) { | ||
89 | bb_error_msg("wrong nlmsg len %d", len); | ||
90 | return -1; | ||
91 | } | ||
92 | |||
93 | if (r->rtm_family == AF_INET6) | ||
94 | host_len = 128; | ||
95 | else if (r->rtm_family == AF_INET) | ||
96 | host_len = 32; | ||
97 | |||
98 | if (r->rtm_family == AF_INET6) { | ||
99 | if (filter.tb) { | ||
100 | if (filter.tb < 0) { | ||
101 | if (!(r->rtm_flags&RTM_F_CLONED)) { | ||
102 | return 0; | ||
103 | } | ||
104 | } else { | ||
105 | if (r->rtm_flags&RTM_F_CLONED) { | ||
106 | return 0; | ||
107 | } | ||
108 | if (filter.tb == RT_TABLE_LOCAL) { | ||
109 | if (r->rtm_type != RTN_LOCAL) { | ||
110 | return 0; | ||
111 | } | ||
112 | } else if (filter.tb == RT_TABLE_MAIN) { | ||
113 | if (r->rtm_type == RTN_LOCAL) { | ||
114 | return 0; | ||
115 | } | ||
116 | } else { | ||
117 | return 0; | ||
118 | } | ||
119 | } | ||
120 | } | ||
121 | } else { | ||
122 | if (filter.tb > 0 && filter.tb != r->rtm_table) { | ||
123 | return 0; | ||
124 | } | ||
125 | } | ||
126 | if (filter.rdst.family && | ||
127 | (r->rtm_family != filter.rdst.family || filter.rdst.bitlen > r->rtm_dst_len)) { | ||
128 | return 0; | ||
129 | } | ||
130 | if (filter.mdst.family && | ||
131 | (r->rtm_family != filter.mdst.family || | ||
132 | (filter.mdst.bitlen >= 0 && filter.mdst.bitlen < r->rtm_dst_len))) { | ||
133 | return 0; | ||
134 | } | ||
135 | if (filter.rsrc.family && | ||
136 | (r->rtm_family != filter.rsrc.family || filter.rsrc.bitlen > r->rtm_src_len)) { | ||
137 | return 0; | ||
138 | } | ||
139 | if (filter.msrc.family && | ||
140 | (r->rtm_family != filter.msrc.family || | ||
141 | (filter.msrc.bitlen >= 0 && filter.msrc.bitlen < r->rtm_src_len))) { | ||
142 | return 0; | ||
143 | } | ||
144 | |||
145 | memset(tb, 0, sizeof(tb)); | ||
146 | parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len); | ||
147 | |||
148 | if (filter.rdst.family && inet_addr_match(&dst, &filter.rdst, filter.rdst.bitlen)) | ||
149 | return 0; | ||
150 | if (filter.mdst.family && filter.mdst.bitlen >= 0 && | ||
151 | inet_addr_match(&dst, &filter.mdst, r->rtm_dst_len)) | ||
152 | return 0; | ||
153 | |||
154 | if (filter.rsrc.family && inet_addr_match(&src, &filter.rsrc, filter.rsrc.bitlen)) | ||
155 | return 0; | ||
156 | if (filter.msrc.family && filter.msrc.bitlen >= 0 && | ||
157 | inet_addr_match(&src, &filter.msrc, r->rtm_src_len)) | ||
158 | return 0; | ||
159 | |||
160 | if (filter.flushb && | ||
161 | r->rtm_family == AF_INET6 && | ||
162 | r->rtm_dst_len == 0 && | ||
163 | r->rtm_type == RTN_UNREACHABLE && | ||
164 | tb[RTA_PRIORITY] && | ||
165 | *(int*)RTA_DATA(tb[RTA_PRIORITY]) == -1) | ||
166 | return 0; | ||
167 | |||
168 | if (filter.flushb) { | ||
169 | struct nlmsghdr *fn; | ||
170 | if (NLMSG_ALIGN(filter.flushp) + n->nlmsg_len > filter.flushe) { | ||
171 | if (flush_update()) | ||
172 | return -1; | ||
173 | } | ||
174 | fn = (struct nlmsghdr*)(filter.flushb + NLMSG_ALIGN(filter.flushp)); | ||
175 | memcpy(fn, n, n->nlmsg_len); | ||
176 | fn->nlmsg_type = RTM_DELROUTE; | ||
177 | fn->nlmsg_flags = NLM_F_REQUEST; | ||
178 | fn->nlmsg_seq = ++filter.rth->seq; | ||
179 | filter.flushp = (((char*)fn) + n->nlmsg_len) - filter.flushb; | ||
180 | filter.flushed++; | ||
181 | return 0; | ||
182 | } | ||
183 | |||
184 | if (n->nlmsg_type == RTM_DELROUTE) { | ||
185 | fprintf(fp, "Deleted "); | ||
186 | } | ||
187 | if (r->rtm_type != RTN_UNICAST && !filter.type) { | ||
188 | fprintf(fp, "%s ", rtnl_rtntype_n2a(r->rtm_type, b1, sizeof(b1))); | ||
189 | } | ||
190 | |||
191 | if (tb[RTA_DST]) { | ||
192 | if (r->rtm_dst_len != host_len) { | ||
193 | fprintf(fp, "%s/%u ", rt_addr_n2a(r->rtm_family, | ||
194 | RTA_PAYLOAD(tb[RTA_DST]), | ||
195 | RTA_DATA(tb[RTA_DST]), | ||
196 | abuf, sizeof(abuf)), | ||
197 | r->rtm_dst_len | ||
198 | ); | ||
199 | } else { | ||
200 | fprintf(fp, "%s ", format_host(r->rtm_family, | ||
201 | RTA_PAYLOAD(tb[RTA_DST]), | ||
202 | RTA_DATA(tb[RTA_DST]), | ||
203 | abuf, sizeof(abuf)) | ||
204 | ); | ||
205 | } | ||
206 | } else if (r->rtm_dst_len) { | ||
207 | fprintf(fp, "0/%d ", r->rtm_dst_len); | ||
208 | } else { | ||
209 | fprintf(fp, "default "); | ||
210 | } | ||
211 | if (tb[RTA_SRC]) { | ||
212 | if (r->rtm_src_len != host_len) { | ||
213 | fprintf(fp, "from %s/%u ", rt_addr_n2a(r->rtm_family, | ||
214 | RTA_PAYLOAD(tb[RTA_SRC]), | ||
215 | RTA_DATA(tb[RTA_SRC]), | ||
216 | abuf, sizeof(abuf)), | ||
217 | r->rtm_src_len | ||
218 | ); | ||
219 | } else { | ||
220 | fprintf(fp, "from %s ", format_host(r->rtm_family, | ||
221 | RTA_PAYLOAD(tb[RTA_SRC]), | ||
222 | RTA_DATA(tb[RTA_SRC]), | ||
223 | abuf, sizeof(abuf)) | ||
224 | ); | ||
225 | } | ||
226 | } else if (r->rtm_src_len) { | ||
227 | fprintf(fp, "from 0/%u ", r->rtm_src_len); | ||
228 | } | ||
229 | if (tb[RTA_GATEWAY] && filter.rvia.bitlen != host_len) { | ||
230 | fprintf(fp, "via %s ", | ||
231 | format_host(r->rtm_family, | ||
232 | RTA_PAYLOAD(tb[RTA_GATEWAY]), | ||
233 | RTA_DATA(tb[RTA_GATEWAY]), | ||
234 | abuf, sizeof(abuf))); | ||
235 | } | ||
236 | if (tb[RTA_OIF] && filter.oifmask != -1) { | ||
237 | fprintf(fp, "dev %s ", ll_index_to_name(*(int*)RTA_DATA(tb[RTA_OIF]))); | ||
238 | } | ||
239 | |||
240 | if (tb[RTA_PREFSRC] && filter.rprefsrc.bitlen != host_len) { | ||
241 | /* Do not use format_host(). It is our local addr | ||
242 | and symbolic name will not be useful. | ||
243 | */ | ||
244 | fprintf(fp, " src %s ", | ||
245 | rt_addr_n2a(r->rtm_family, | ||
246 | RTA_PAYLOAD(tb[RTA_PREFSRC]), | ||
247 | RTA_DATA(tb[RTA_PREFSRC]), | ||
248 | abuf, sizeof(abuf))); | ||
249 | } | ||
250 | if (tb[RTA_PRIORITY]) { | ||
251 | fprintf(fp, " metric %d ", *(__u32*)RTA_DATA(tb[RTA_PRIORITY])); | ||
252 | } | ||
253 | if (r->rtm_family == AF_INET6) { | ||
254 | struct rta_cacheinfo *ci = NULL; | ||
255 | if (tb[RTA_CACHEINFO]) { | ||
256 | ci = RTA_DATA(tb[RTA_CACHEINFO]); | ||
257 | } | ||
258 | if ((r->rtm_flags & RTM_F_CLONED) || (ci && ci->rta_expires)) { | ||
259 | static int hz; | ||
260 | if (!hz) { | ||
261 | hz = get_hz(); | ||
262 | } | ||
263 | if (r->rtm_flags & RTM_F_CLONED) { | ||
264 | fprintf(fp, "%s cache ", _SL_); | ||
265 | } | ||
266 | if (ci->rta_expires) { | ||
267 | fprintf(fp, " expires %dsec", ci->rta_expires/hz); | ||
268 | } | ||
269 | if (ci->rta_error != 0) { | ||
270 | fprintf(fp, " error %d", ci->rta_error); | ||
271 | } | ||
272 | } else if (ci) { | ||
273 | if (ci->rta_error != 0) | ||
274 | fprintf(fp, " error %d", ci->rta_error); | ||
275 | } | ||
276 | } | ||
277 | if (tb[RTA_IIF] && filter.iifmask != -1) { | ||
278 | fprintf(fp, " iif %s", ll_index_to_name(*(int*)RTA_DATA(tb[RTA_IIF]))); | ||
279 | } | ||
280 | fprintf(fp, "\n"); | ||
281 | fflush(fp); | ||
282 | return 0; | ||
283 | } | ||
284 | |||
285 | static int iproute_modify(int cmd, unsigned flags, int argc, char **argv) | ||
286 | { | ||
287 | struct rtnl_handle rth; | ||
288 | struct { | ||
289 | struct nlmsghdr n; | ||
290 | struct rtmsg r; | ||
291 | char buf[1024]; | ||
292 | } req; | ||
293 | char mxbuf[256]; | ||
294 | struct rtattr * mxrta = (void*)mxbuf; | ||
295 | unsigned mxlock = 0; | ||
296 | char *d = NULL; | ||
297 | int gw_ok = 0; | ||
298 | int dst_ok = 0; | ||
299 | int proto_ok = 0; | ||
300 | int type_ok = 0; | ||
301 | |||
302 | memset(&req, 0, sizeof(req)); | ||
303 | |||
304 | req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); | ||
305 | req.n.nlmsg_flags = NLM_F_REQUEST|flags; | ||
306 | req.n.nlmsg_type = cmd; | ||
307 | req.r.rtm_family = preferred_family; | ||
308 | req.r.rtm_table = RT_TABLE_MAIN; | ||
309 | req.r.rtm_scope = RT_SCOPE_NOWHERE; | ||
310 | |||
311 | if (cmd != RTM_DELROUTE) { | ||
312 | req.r.rtm_protocol = RTPROT_BOOT; | ||
313 | req.r.rtm_scope = RT_SCOPE_UNIVERSE; | ||
314 | req.r.rtm_type = RTN_UNICAST; | ||
315 | } | ||
316 | |||
317 | mxrta->rta_type = RTA_METRICS; | ||
318 | mxrta->rta_len = RTA_LENGTH(0); | ||
319 | |||
320 | while (argc > 0) { | ||
321 | if (strcmp(*argv, "src") == 0) { | ||
322 | inet_prefix addr; | ||
323 | NEXT_ARG(); | ||
324 | get_addr(&addr, *argv, req.r.rtm_family); | ||
325 | if (req.r.rtm_family == AF_UNSPEC) { | ||
326 | req.r.rtm_family = addr.family; | ||
327 | } | ||
328 | addattr_l(&req.n, sizeof(req), RTA_PREFSRC, &addr.data, addr.bytelen); | ||
329 | } else if (strcmp(*argv, "via") == 0) { | ||
330 | inet_prefix addr; | ||
331 | gw_ok = 1; | ||
332 | NEXT_ARG(); | ||
333 | get_addr(&addr, *argv, req.r.rtm_family); | ||
334 | if (req.r.rtm_family == AF_UNSPEC) { | ||
335 | req.r.rtm_family = addr.family; | ||
336 | } | ||
337 | addattr_l(&req.n, sizeof(req), RTA_GATEWAY, &addr.data, addr.bytelen); | ||
338 | } else if (strcmp(*argv, "mtu") == 0) { | ||
339 | unsigned mtu; | ||
340 | NEXT_ARG(); | ||
341 | if (strcmp(*argv, "lock") == 0) { | ||
342 | mxlock |= (1<<RTAX_MTU); | ||
343 | NEXT_ARG(); | ||
344 | } | ||
345 | if (get_unsigned(&mtu, *argv, 0)) { | ||
346 | invarg(*argv, "mtu"); | ||
347 | } | ||
348 | rta_addattr32(mxrta, sizeof(mxbuf), RTAX_MTU, mtu); | ||
349 | } else if (matches(*argv, "protocol") == 0) { | ||
350 | uint32_t prot; | ||
351 | NEXT_ARG(); | ||
352 | if (rtnl_rtprot_a2n(&prot, *argv)) | ||
353 | invarg(*argv, "protocol"); | ||
354 | req.r.rtm_protocol = prot; | ||
355 | proto_ok =1; | ||
356 | } else if (strcmp(*argv, "dev") == 0 || | ||
357 | strcmp(*argv, "oif") == 0) { | ||
358 | NEXT_ARG(); | ||
359 | d = *argv; | ||
360 | } else { | ||
361 | int type; | ||
362 | inet_prefix dst; | ||
363 | |||
364 | if (strcmp(*argv, "to") == 0) { | ||
365 | NEXT_ARG(); | ||
366 | } | ||
367 | if ((**argv < '0' || **argv > '9') && | ||
368 | rtnl_rtntype_a2n(&type, *argv) == 0) { | ||
369 | NEXT_ARG(); | ||
370 | req.r.rtm_type = type; | ||
371 | type_ok = 1; | ||
372 | } | ||
373 | |||
374 | if (dst_ok) { | ||
375 | duparg2("to", *argv); | ||
376 | } | ||
377 | get_prefix(&dst, *argv, req.r.rtm_family); | ||
378 | if (req.r.rtm_family == AF_UNSPEC) { | ||
379 | req.r.rtm_family = dst.family; | ||
380 | } | ||
381 | req.r.rtm_dst_len = dst.bitlen; | ||
382 | dst_ok = 1; | ||
383 | if (dst.bytelen) { | ||
384 | addattr_l(&req.n, sizeof(req), RTA_DST, &dst.data, dst.bytelen); | ||
385 | } | ||
386 | } | ||
387 | argc--; argv++; | ||
388 | } | ||
389 | |||
390 | if (rtnl_open(&rth, 0) < 0) { | ||
391 | exit(1); | ||
392 | } | ||
393 | |||
394 | if (d) { | ||
395 | int idx; | ||
396 | |||
397 | ll_init_map(&rth); | ||
398 | |||
399 | if (d) { | ||
400 | if ((idx = ll_name_to_index(d)) == 0) { | ||
401 | bb_error_msg("cannot find device \"%s\"", d); | ||
402 | return -1; | ||
403 | } | ||
404 | addattr32(&req.n, sizeof(req), RTA_OIF, idx); | ||
405 | } | ||
406 | } | ||
407 | |||
408 | if (mxrta->rta_len > RTA_LENGTH(0)) { | ||
409 | if (mxlock) { | ||
410 | rta_addattr32(mxrta, sizeof(mxbuf), RTAX_LOCK, mxlock); | ||
411 | } | ||
412 | addattr_l(&req.n, sizeof(req), RTA_METRICS, RTA_DATA(mxrta), RTA_PAYLOAD(mxrta)); | ||
413 | } | ||
414 | |||
415 | if (req.r.rtm_family == AF_UNSPEC) { | ||
416 | req.r.rtm_family = AF_INET; | ||
417 | } | ||
418 | |||
419 | if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0) { | ||
420 | exit(2); | ||
421 | } | ||
422 | |||
423 | return 0; | ||
424 | } | ||
425 | |||
426 | static int rtnl_rtcache_request(struct rtnl_handle *rth, int family) | ||
427 | { | ||
428 | struct { | ||
429 | struct nlmsghdr nlh; | ||
430 | struct rtmsg rtm; | ||
431 | } req; | ||
432 | struct sockaddr_nl nladdr; | ||
433 | |||
434 | memset(&nladdr, 0, sizeof(nladdr)); | ||
435 | memset(&req, 0, sizeof(req)); | ||
436 | nladdr.nl_family = AF_NETLINK; | ||
437 | |||
438 | req.nlh.nlmsg_len = sizeof(req); | ||
439 | req.nlh.nlmsg_type = RTM_GETROUTE; | ||
440 | req.nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_REQUEST; | ||
441 | req.nlh.nlmsg_pid = 0; | ||
442 | req.nlh.nlmsg_seq = rth->dump = ++rth->seq; | ||
443 | req.rtm.rtm_family = family; | ||
444 | req.rtm.rtm_flags |= RTM_F_CLONED; | ||
445 | |||
446 | return sendto(rth->fd, (void*)&req, sizeof(req), 0, (struct sockaddr*)&nladdr, sizeof(nladdr)); | ||
447 | } | ||
448 | |||
449 | static int iproute_flush_cache(void) | ||
450 | { | ||
451 | #define ROUTE_FLUSH_PATH "/proc/sys/net/ipv4/route/flush" | ||
452 | |||
453 | int len; | ||
454 | int flush_fd = open (ROUTE_FLUSH_PATH, O_WRONLY); | ||
455 | char *buffer = "-1"; | ||
456 | |||
457 | if (flush_fd < 0) { | ||
458 | fprintf(stderr, "Cannot open \"%s\"\n", ROUTE_FLUSH_PATH); | ||
459 | return -1; | ||
460 | } | ||
461 | |||
462 | len = strlen (buffer); | ||
463 | |||
464 | if ((write (flush_fd, (void *)buffer, len)) < len) { | ||
465 | fprintf(stderr, "Cannot flush routing cache\n"); | ||
466 | return -1; | ||
467 | } | ||
468 | close(flush_fd); | ||
469 | return 0; | ||
470 | } | ||
471 | |||
472 | static void iproute_reset_filter(void) | ||
473 | { | ||
474 | memset(&filter, 0, sizeof(filter)); | ||
475 | filter.mdst.bitlen = -1; | ||
476 | filter.msrc.bitlen = -1; | ||
477 | } | ||
478 | |||
479 | static int iproute_list_or_flush(int argc, char **argv, int flush) | ||
480 | { | ||
481 | int do_ipv6 = preferred_family; | ||
482 | struct rtnl_handle rth; | ||
483 | char *id = NULL; | ||
484 | char *od = NULL; | ||
485 | |||
486 | iproute_reset_filter(); | ||
487 | filter.tb = RT_TABLE_MAIN; | ||
488 | |||
489 | if (flush && argc <= 0) { | ||
490 | bb_error_msg(bb_msg_requires_arg, "\"ip route flush\""); | ||
491 | return -1; | ||
492 | } | ||
493 | |||
494 | while (argc > 0) { | ||
495 | if (matches(*argv, "protocol") == 0) { | ||
496 | uint32_t prot = 0; | ||
497 | NEXT_ARG(); | ||
498 | filter.protocolmask = -1; | ||
499 | if (rtnl_rtprot_a2n(&prot, *argv)) { | ||
500 | if (strcmp(*argv, "all") != 0) { | ||
501 | invarg(*argv, "protocol"); | ||
502 | } | ||
503 | prot = 0; | ||
504 | filter.protocolmask = 0; | ||
505 | } | ||
506 | filter.protocol = prot; | ||
507 | } else if (strcmp(*argv, "dev") == 0 || | ||
508 | strcmp(*argv, "oif") == 0) { | ||
509 | NEXT_ARG(); | ||
510 | od = *argv; | ||
511 | } else if (strcmp(*argv, "iif") == 0) { | ||
512 | NEXT_ARG(); | ||
513 | id = *argv; | ||
514 | } else if (matches(*argv, "from") == 0) { | ||
515 | NEXT_ARG(); | ||
516 | if (matches(*argv, "root") == 0) { | ||
517 | NEXT_ARG(); | ||
518 | get_prefix(&filter.rsrc, *argv, do_ipv6); | ||
519 | } else if (matches(*argv, "match") == 0) { | ||
520 | NEXT_ARG(); | ||
521 | get_prefix(&filter.msrc, *argv, do_ipv6); | ||
522 | } else { | ||
523 | if (matches(*argv, "exact") == 0) { | ||
524 | NEXT_ARG(); | ||
525 | } | ||
526 | get_prefix(&filter.msrc, *argv, do_ipv6); | ||
527 | filter.rsrc = filter.msrc; | ||
528 | } | ||
529 | } else { | ||
530 | if (matches(*argv, "to") == 0) { | ||
531 | NEXT_ARG(); | ||
532 | } | ||
533 | if (matches(*argv, "root") == 0) { | ||
534 | NEXT_ARG(); | ||
535 | get_prefix(&filter.rdst, *argv, do_ipv6); | ||
536 | } else if (matches(*argv, "match") == 0) { | ||
537 | NEXT_ARG(); | ||
538 | get_prefix(&filter.mdst, *argv, do_ipv6); | ||
539 | } else if (matches(*argv, "table") == 0) { | ||
540 | NEXT_ARG(); | ||
541 | if (matches(*argv, "cache") == 0) { | ||
542 | filter.tb = -1; | ||
543 | } else if (matches(*argv, "main") != 0) { | ||
544 | invarg(*argv, "table"); | ||
545 | } | ||
546 | } else if (matches(*argv, "cache") == 0) { | ||
547 | filter.tb = -1; | ||
548 | } else { | ||
549 | if (matches(*argv, "exact") == 0) { | ||
550 | NEXT_ARG(); | ||
551 | } | ||
552 | get_prefix(&filter.mdst, *argv, do_ipv6); | ||
553 | filter.rdst = filter.mdst; | ||
554 | } | ||
555 | } | ||
556 | argc--; argv++; | ||
557 | } | ||
558 | |||
559 | if (do_ipv6 == AF_UNSPEC && filter.tb) { | ||
560 | do_ipv6 = AF_INET; | ||
561 | } | ||
562 | |||
563 | if (rtnl_open(&rth, 0) < 0) { | ||
564 | exit(1); | ||
565 | } | ||
566 | |||
567 | ll_init_map(&rth); | ||
568 | |||
569 | if (id || od) { | ||
570 | int idx; | ||
571 | |||
572 | if (id) { | ||
573 | if ((idx = ll_name_to_index(id)) == 0) { | ||
574 | bb_error_msg("cannot find device \"%s\"", id); | ||
575 | return -1; | ||
576 | } | ||
577 | filter.iif = idx; | ||
578 | filter.iifmask = -1; | ||
579 | } | ||
580 | if (od) { | ||
581 | if ((idx = ll_name_to_index(od)) == 0) { | ||
582 | bb_error_msg("cannot find device \"%s\"", od); | ||
583 | } | ||
584 | filter.oif = idx; | ||
585 | filter.oifmask = -1; | ||
586 | } | ||
587 | } | ||
588 | |||
589 | if (flush) { | ||
590 | char flushb[4096-512]; | ||
591 | |||
592 | if (filter.tb == -1) { | ||
593 | if (do_ipv6 != AF_INET6) | ||
594 | iproute_flush_cache(); | ||
595 | if (do_ipv6 == AF_INET) | ||
596 | return 0; | ||
597 | } | ||
598 | |||
599 | filter.flushb = flushb; | ||
600 | filter.flushp = 0; | ||
601 | filter.flushe = sizeof(flushb); | ||
602 | filter.rth = &rth; | ||
603 | |||
604 | for (;;) { | ||
605 | if (rtnl_wilddump_request(&rth, do_ipv6, RTM_GETROUTE) < 0) { | ||
606 | perror("Cannot send dump request"); | ||
607 | return -1; | ||
608 | } | ||
609 | filter.flushed = 0; | ||
610 | if (rtnl_dump_filter(&rth, print_route, stdout, NULL, NULL) < 0) { | ||
611 | bb_error_msg("flush terminated"); | ||
612 | return -1; | ||
613 | } | ||
614 | if (filter.flushed == 0) { | ||
615 | fflush(stdout); | ||
616 | return 0; | ||
617 | } | ||
618 | if (flush_update() < 0) | ||
619 | exit(1); | ||
620 | } | ||
621 | } | ||
622 | |||
623 | if (filter.tb != -1) { | ||
624 | if (rtnl_wilddump_request(&rth, do_ipv6, RTM_GETROUTE) < 0) { | ||
625 | bb_perror_msg_and_die("cannot send dump request"); | ||
626 | } | ||
627 | } else { | ||
628 | if (rtnl_rtcache_request(&rth, do_ipv6) < 0) { | ||
629 | bb_perror_msg_and_die("cannot send dump request"); | ||
630 | } | ||
631 | } | ||
632 | |||
633 | if (rtnl_dump_filter(&rth, print_route, stdout, NULL, NULL) < 0) { | ||
634 | bb_error_msg_and_die("dump terminated"); | ||
635 | } | ||
636 | |||
637 | exit(0); | ||
638 | } | ||
639 | |||
640 | |||
641 | static int iproute_get(int argc, char **argv) | ||
642 | { | ||
643 | struct rtnl_handle rth; | ||
644 | struct { | ||
645 | struct nlmsghdr n; | ||
646 | struct rtmsg r; | ||
647 | char buf[1024]; | ||
648 | } req; | ||
649 | char *idev = NULL; | ||
650 | char *odev = NULL; | ||
651 | int connected = 0; | ||
652 | int from_ok = 0; | ||
653 | static const char * const options[] = | ||
654 | { "from", "iif", "oif", "dev", "notify", "connected", "to", 0 }; | ||
655 | |||
656 | memset(&req, 0, sizeof(req)); | ||
657 | |||
658 | iproute_reset_filter(); | ||
659 | |||
660 | req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); | ||
661 | req.n.nlmsg_flags = NLM_F_REQUEST; | ||
662 | req.n.nlmsg_type = RTM_GETROUTE; | ||
663 | req.r.rtm_family = preferred_family; | ||
664 | req.r.rtm_table = 0; | ||
665 | req.r.rtm_protocol = 0; | ||
666 | req.r.rtm_scope = 0; | ||
667 | req.r.rtm_type = 0; | ||
668 | req.r.rtm_src_len = 0; | ||
669 | req.r.rtm_dst_len = 0; | ||
670 | req.r.rtm_tos = 0; | ||
671 | |||
672 | while (argc > 0) { | ||
673 | switch (index_in_str_array(options, *argv)) { | ||
674 | case 0: /* from */ | ||
675 | { | ||
676 | inet_prefix addr; | ||
677 | NEXT_ARG(); | ||
678 | from_ok = 1; | ||
679 | get_prefix(&addr, *argv, req.r.rtm_family); | ||
680 | if (req.r.rtm_family == AF_UNSPEC) { | ||
681 | req.r.rtm_family = addr.family; | ||
682 | } | ||
683 | if (addr.bytelen) { | ||
684 | addattr_l(&req.n, sizeof(req), RTA_SRC, &addr.data, addr.bytelen); | ||
685 | } | ||
686 | req.r.rtm_src_len = addr.bitlen; | ||
687 | break; | ||
688 | } | ||
689 | case 1: /* iif */ | ||
690 | NEXT_ARG(); | ||
691 | idev = *argv; | ||
692 | break; | ||
693 | case 2: /* oif */ | ||
694 | case 3: /* dev */ | ||
695 | NEXT_ARG(); | ||
696 | odev = *argv; | ||
697 | break; | ||
698 | case 4: /* notify */ | ||
699 | req.r.rtm_flags |= RTM_F_NOTIFY; | ||
700 | break; | ||
701 | case 5: /* connected */ | ||
702 | connected = 1; | ||
703 | break; | ||
704 | case 6: /* to */ | ||
705 | NEXT_ARG(); | ||
706 | default: | ||
707 | { | ||
708 | inet_prefix addr; | ||
709 | get_prefix(&addr, *argv, req.r.rtm_family); | ||
710 | if (req.r.rtm_family == AF_UNSPEC) { | ||
711 | req.r.rtm_family = addr.family; | ||
712 | } | ||
713 | if (addr.bytelen) { | ||
714 | addattr_l(&req.n, sizeof(req), RTA_DST, &addr.data, addr.bytelen); | ||
715 | } | ||
716 | req.r.rtm_dst_len = addr.bitlen; | ||
717 | } | ||
718 | argc--; argv++; | ||
719 | } | ||
720 | } | ||
721 | |||
722 | if (req.r.rtm_dst_len == 0) { | ||
723 | bb_error_msg_and_die("need at least destination address"); | ||
724 | } | ||
725 | |||
726 | if (rtnl_open(&rth, 0) < 0) | ||
727 | exit(1); | ||
728 | |||
729 | ll_init_map(&rth); | ||
730 | |||
731 | if (idev || odev) { | ||
732 | int idx; | ||
733 | |||
734 | if (idev) { | ||
735 | if ((idx = ll_name_to_index(idev)) == 0) { | ||
736 | bb_error_msg("cannot find device \"%s\"", idev); | ||
737 | return -1; | ||
738 | } | ||
739 | addattr32(&req.n, sizeof(req), RTA_IIF, idx); | ||
740 | } | ||
741 | if (odev) { | ||
742 | if ((idx = ll_name_to_index(odev)) == 0) { | ||
743 | bb_error_msg("cannot find device \"%s\"", odev); | ||
744 | return -1; | ||
745 | } | ||
746 | addattr32(&req.n, sizeof(req), RTA_OIF, idx); | ||
747 | } | ||
748 | } | ||
749 | |||
750 | if (req.r.rtm_family == AF_UNSPEC) { | ||
751 | req.r.rtm_family = AF_INET; | ||
752 | } | ||
753 | |||
754 | if (rtnl_talk(&rth, &req.n, 0, 0, &req.n, NULL, NULL) < 0) { | ||
755 | exit(2); | ||
756 | } | ||
757 | |||
758 | if (connected && !from_ok) { | ||
759 | struct rtmsg *r = NLMSG_DATA(&req.n); | ||
760 | int len = req.n.nlmsg_len; | ||
761 | struct rtattr * tb[RTA_MAX+1]; | ||
762 | |||
763 | if (print_route(NULL, &req.n, (void*)stdout) < 0) { | ||
764 | bb_error_msg_and_die("an error :-)"); | ||
765 | } | ||
766 | |||
767 | if (req.n.nlmsg_type != RTM_NEWROUTE) { | ||
768 | bb_error_msg("not a route?"); | ||
769 | return -1; | ||
770 | } | ||
771 | len -= NLMSG_LENGTH(sizeof(*r)); | ||
772 | if (len < 0) { | ||
773 | bb_error_msg("wrong len %d", len); | ||
774 | return -1; | ||
775 | } | ||
776 | |||
777 | memset(tb, 0, sizeof(tb)); | ||
778 | parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len); | ||
779 | |||
780 | if (tb[RTA_PREFSRC]) { | ||
781 | tb[RTA_PREFSRC]->rta_type = RTA_SRC; | ||
782 | r->rtm_src_len = 8*RTA_PAYLOAD(tb[RTA_PREFSRC]); | ||
783 | } else if (!tb[RTA_SRC]) { | ||
784 | bb_error_msg("failed to connect the route"); | ||
785 | return -1; | ||
786 | } | ||
787 | if (!odev && tb[RTA_OIF]) { | ||
788 | tb[RTA_OIF]->rta_type = 0; | ||
789 | } | ||
790 | if (tb[RTA_GATEWAY]) { | ||
791 | tb[RTA_GATEWAY]->rta_type = 0; | ||
792 | } | ||
793 | if (!idev && tb[RTA_IIF]) { | ||
794 | tb[RTA_IIF]->rta_type = 0; | ||
795 | } | ||
796 | req.n.nlmsg_flags = NLM_F_REQUEST; | ||
797 | req.n.nlmsg_type = RTM_GETROUTE; | ||
798 | |||
799 | if (rtnl_talk(&rth, &req.n, 0, 0, &req.n, NULL, NULL) < 0) { | ||
800 | exit(2); | ||
801 | } | ||
802 | } | ||
803 | |||
804 | if (print_route(NULL, &req.n, (void*)stdout) < 0) { | ||
805 | bb_error_msg_and_die("an error :-)"); | ||
806 | } | ||
807 | |||
808 | exit(0); | ||
809 | } | ||
810 | |||
811 | int do_iproute(int argc, char **argv) | ||
812 | { | ||
813 | static const char * const ip_route_commands[] = | ||
814 | { "add", "append", "change", "chg", "delete", "get", | ||
815 | "list", "show", "prepend", "replace", "test", "flush", 0 }; | ||
816 | int command_num = 6; | ||
817 | unsigned int flags = 0; | ||
818 | int cmd = RTM_NEWROUTE; | ||
819 | |||
820 | /* "Standard" 'ip r a' treats 'a' as 'add', not 'append' */ | ||
821 | /* It probably means that it is using "first match" rule */ | ||
822 | if (*argv) { | ||
823 | command_num = index_in_substr_array(ip_route_commands, *argv); | ||
824 | } | ||
825 | switch (command_num) { | ||
826 | case 0: /* add*/ | ||
827 | flags = NLM_F_CREATE|NLM_F_EXCL; | ||
828 | break; | ||
829 | case 1: /* append */ | ||
830 | flags = NLM_F_CREATE|NLM_F_APPEND; | ||
831 | break; | ||
832 | case 2: /* change */ | ||
833 | case 3: /* chg */ | ||
834 | flags = NLM_F_REPLACE; | ||
835 | break; | ||
836 | case 4: /* delete */ | ||
837 | case 5: /* del */ | ||
838 | cmd = RTM_DELROUTE; | ||
839 | break; | ||
840 | case 6: /* get */ | ||
841 | return iproute_get(argc-1, argv+1); | ||
842 | case 7: /* list */ | ||
843 | case 8: /* show */ | ||
844 | return iproute_list_or_flush(argc-1, argv+1, 0); | ||
845 | case 9: /* prepend */ | ||
846 | flags = NLM_F_CREATE; | ||
847 | case 10: /* replace */ | ||
848 | flags = NLM_F_CREATE|NLM_F_REPLACE; | ||
849 | case 11: /* test */ | ||
850 | flags = NLM_F_EXCL; | ||
851 | case 12: /* flush */ | ||
852 | return iproute_list_or_flush(argc-1, argv+1, 1); | ||
853 | default: | ||
854 | bb_error_msg_and_die("unknown command %s", *argv); | ||
855 | } | ||
856 | |||
857 | return iproute_modify(cmd, flags, argc-1, argv+1); | ||
858 | } | ||
diff --git a/networking/libiproute/iprule.c b/networking/libiproute/iprule.c new file mode 100644 index 000000000..19338770f --- /dev/null +++ b/networking/libiproute/iprule.c | |||
@@ -0,0 +1,331 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * iprule.c "ip rule". | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or | ||
6 | * modify it under the terms of the GNU General Public License | ||
7 | * as published by the Free Software Foundation; either version | ||
8 | * 2 of the License, or (at your option) any later version. | ||
9 | * | ||
10 | * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> | ||
11 | * | ||
12 | * | ||
13 | * Changes: | ||
14 | * | ||
15 | * Rani Assaf <rani@magic.metawire.com> 980929: resolve addresses | ||
16 | */ | ||
17 | |||
18 | #include "libbb.h" | ||
19 | #include <syslog.h> | ||
20 | #include <sys/socket.h> | ||
21 | #include <netinet/in.h> | ||
22 | #include <netinet/ip.h> | ||
23 | #include <arpa/inet.h> | ||
24 | |||
25 | #include "rt_names.h" | ||
26 | #include "utils.h" | ||
27 | /* | ||
28 | static void usage(void) __attribute__((noreturn)); | ||
29 | |||
30 | static void usage(void) | ||
31 | { | ||
32 | fprintf(stderr, "Usage: ip rule [ list | add | del ] SELECTOR ACTION\n"); | ||
33 | fprintf(stderr, "SELECTOR := [ from PREFIX ] [ to PREFIX ] [ tos TOS ] [ fwmark FWMARK ]\n"); | ||
34 | fprintf(stderr, " [ dev STRING ] [ pref NUMBER ]\n"); | ||
35 | fprintf(stderr, "ACTION := [ table TABLE_ID ] [ nat ADDRESS ]\n"); | ||
36 | fprintf(stderr, " [ prohibit | reject | unreachable ]\n"); | ||
37 | fprintf(stderr, " [ realms [SRCREALM/]DSTREALM ]\n"); | ||
38 | fprintf(stderr, "TABLE_ID := [ local | main | default | NUMBER ]\n"); | ||
39 | exit(-1); | ||
40 | } | ||
41 | */ | ||
42 | static int print_rule(struct sockaddr_nl *who ATTRIBUTE_UNUSED, | ||
43 | struct nlmsghdr *n, void *arg) | ||
44 | { | ||
45 | FILE *fp = (FILE*)arg; | ||
46 | struct rtmsg *r = NLMSG_DATA(n); | ||
47 | int len = n->nlmsg_len; | ||
48 | int host_len = -1; | ||
49 | struct rtattr * tb[RTA_MAX+1]; | ||
50 | char abuf[256]; | ||
51 | SPRINT_BUF(b1); | ||
52 | |||
53 | if (n->nlmsg_type != RTM_NEWRULE) | ||
54 | return 0; | ||
55 | |||
56 | len -= NLMSG_LENGTH(sizeof(*r)); | ||
57 | if (len < 0) | ||
58 | return -1; | ||
59 | |||
60 | memset(tb, 0, sizeof(tb)); | ||
61 | parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len); | ||
62 | |||
63 | if (r->rtm_family == AF_INET) | ||
64 | host_len = 32; | ||
65 | else if (r->rtm_family == AF_INET6) | ||
66 | host_len = 128; | ||
67 | /* else if (r->rtm_family == AF_DECnet) | ||
68 | host_len = 16; | ||
69 | else if (r->rtm_family == AF_IPX) | ||
70 | host_len = 80; | ||
71 | */ | ||
72 | if (tb[RTA_PRIORITY]) | ||
73 | fprintf(fp, "%u:\t", *(unsigned*)RTA_DATA(tb[RTA_PRIORITY])); | ||
74 | else | ||
75 | fprintf(fp, "0:\t"); | ||
76 | |||
77 | fprintf(fp, "from "); | ||
78 | if (tb[RTA_SRC]) { | ||
79 | if (r->rtm_src_len != host_len) { | ||
80 | fprintf(fp, "%s/%u", rt_addr_n2a(r->rtm_family, | ||
81 | RTA_PAYLOAD(tb[RTA_SRC]), | ||
82 | RTA_DATA(tb[RTA_SRC]), | ||
83 | abuf, sizeof(abuf)), | ||
84 | r->rtm_src_len | ||
85 | ); | ||
86 | } else { | ||
87 | fprintf(fp, "%s", format_host(r->rtm_family, | ||
88 | RTA_PAYLOAD(tb[RTA_SRC]), | ||
89 | RTA_DATA(tb[RTA_SRC]), | ||
90 | abuf, sizeof(abuf)) | ||
91 | ); | ||
92 | } | ||
93 | } else if (r->rtm_src_len) { | ||
94 | fprintf(fp, "0/%d", r->rtm_src_len); | ||
95 | } else { | ||
96 | fprintf(fp, "all"); | ||
97 | } | ||
98 | fprintf(fp, " "); | ||
99 | |||
100 | if (tb[RTA_DST]) { | ||
101 | if (r->rtm_dst_len != host_len) { | ||
102 | fprintf(fp, "to %s/%u ", rt_addr_n2a(r->rtm_family, | ||
103 | RTA_PAYLOAD(tb[RTA_DST]), | ||
104 | RTA_DATA(tb[RTA_DST]), | ||
105 | abuf, sizeof(abuf)), | ||
106 | r->rtm_dst_len | ||
107 | ); | ||
108 | } else { | ||
109 | fprintf(fp, "to %s ", format_host(r->rtm_family, | ||
110 | RTA_PAYLOAD(tb[RTA_DST]), | ||
111 | RTA_DATA(tb[RTA_DST]), | ||
112 | abuf, sizeof(abuf))); | ||
113 | } | ||
114 | } else if (r->rtm_dst_len) { | ||
115 | fprintf(fp, "to 0/%d ", r->rtm_dst_len); | ||
116 | } | ||
117 | |||
118 | if (r->rtm_tos) { | ||
119 | fprintf(fp, "tos %s ", rtnl_dsfield_n2a(r->rtm_tos, b1, sizeof(b1))); | ||
120 | } | ||
121 | if (tb[RTA_PROTOINFO]) { | ||
122 | fprintf(fp, "fwmark %#x ", *(__u32*)RTA_DATA(tb[RTA_PROTOINFO])); | ||
123 | } | ||
124 | |||
125 | if (tb[RTA_IIF]) { | ||
126 | fprintf(fp, "iif %s ", (char*)RTA_DATA(tb[RTA_IIF])); | ||
127 | } | ||
128 | |||
129 | if (r->rtm_table) | ||
130 | fprintf(fp, "lookup %s ", rtnl_rttable_n2a(r->rtm_table, b1, sizeof(b1))); | ||
131 | |||
132 | if (tb[RTA_FLOW]) { | ||
133 | __u32 to = *(__u32*)RTA_DATA(tb[RTA_FLOW]); | ||
134 | __u32 from = to>>16; | ||
135 | to &= 0xFFFF; | ||
136 | if (from) { | ||
137 | fprintf(fp, "realms %s/", | ||
138 | rtnl_rtrealm_n2a(from, b1, sizeof(b1))); | ||
139 | } | ||
140 | fprintf(fp, "%s ", | ||
141 | rtnl_rtrealm_n2a(to, b1, sizeof(b1))); | ||
142 | } | ||
143 | |||
144 | if (r->rtm_type == RTN_NAT) { | ||
145 | if (tb[RTA_GATEWAY]) { | ||
146 | fprintf(fp, "map-to %s ", | ||
147 | format_host(r->rtm_family, | ||
148 | RTA_PAYLOAD(tb[RTA_GATEWAY]), | ||
149 | RTA_DATA(tb[RTA_GATEWAY]), | ||
150 | abuf, sizeof(abuf))); | ||
151 | } else | ||
152 | fprintf(fp, "masquerade"); | ||
153 | } else if (r->rtm_type != RTN_UNICAST) | ||
154 | fprintf(fp, "%s", rtnl_rtntype_n2a(r->rtm_type, b1, sizeof(b1))); | ||
155 | |||
156 | fprintf(fp, "\n"); | ||
157 | fflush(fp); | ||
158 | return 0; | ||
159 | } | ||
160 | |||
161 | int iprule_list(int argc, char **argv) | ||
162 | { | ||
163 | struct rtnl_handle rth; | ||
164 | int af = preferred_family; | ||
165 | |||
166 | if (af == AF_UNSPEC) | ||
167 | af = AF_INET; | ||
168 | |||
169 | if (argc > 0) { | ||
170 | bb_error_msg("\"rule show\" needs no arguments"); | ||
171 | return -1; | ||
172 | } | ||
173 | |||
174 | if (rtnl_open(&rth, 0) < 0) | ||
175 | return 1; | ||
176 | |||
177 | if (rtnl_wilddump_request(&rth, af, RTM_GETRULE) < 0) { | ||
178 | bb_perror_msg("Cannot send dump request"); | ||
179 | return 1; | ||
180 | } | ||
181 | |||
182 | if (rtnl_dump_filter(&rth, print_rule, stdout, NULL, NULL) < 0) { | ||
183 | bb_error_msg("Dump terminated"); | ||
184 | return 1; | ||
185 | } | ||
186 | |||
187 | return 0; | ||
188 | } | ||
189 | |||
190 | |||
191 | int iprule_modify(int cmd, int argc, char **argv) | ||
192 | { | ||
193 | int table_ok = 0; | ||
194 | struct rtnl_handle rth; | ||
195 | struct { | ||
196 | struct nlmsghdr n; | ||
197 | struct rtmsg r; | ||
198 | char buf[1024]; | ||
199 | } req; | ||
200 | |||
201 | memset(&req, 0, sizeof(req)); | ||
202 | |||
203 | req.n.nlmsg_type = cmd; | ||
204 | req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); | ||
205 | req.n.nlmsg_flags = NLM_F_REQUEST; | ||
206 | req.r.rtm_family = preferred_family; | ||
207 | req.r.rtm_protocol = RTPROT_BOOT; | ||
208 | req.r.rtm_scope = RT_SCOPE_UNIVERSE; | ||
209 | req.r.rtm_table = 0; | ||
210 | req.r.rtm_type = RTN_UNSPEC; | ||
211 | |||
212 | if (cmd == RTM_NEWRULE) { | ||
213 | req.n.nlmsg_flags |= NLM_F_CREATE|NLM_F_EXCL; | ||
214 | req.r.rtm_type = RTN_UNICAST; | ||
215 | } | ||
216 | |||
217 | while (argc > 0) { | ||
218 | if (strcmp(*argv, "from") == 0) { | ||
219 | inet_prefix dst; | ||
220 | NEXT_ARG(); | ||
221 | get_prefix(&dst, *argv, req.r.rtm_family); | ||
222 | req.r.rtm_src_len = dst.bitlen; | ||
223 | addattr_l(&req.n, sizeof(req), RTA_SRC, &dst.data, dst.bytelen); | ||
224 | } else if (strcmp(*argv, "to") == 0) { | ||
225 | inet_prefix dst; | ||
226 | NEXT_ARG(); | ||
227 | get_prefix(&dst, *argv, req.r.rtm_family); | ||
228 | req.r.rtm_dst_len = dst.bitlen; | ||
229 | addattr_l(&req.n, sizeof(req), RTA_DST, &dst.data, dst.bytelen); | ||
230 | } else if (matches(*argv, "preference") == 0 || | ||
231 | matches(*argv, "order") == 0 || | ||
232 | matches(*argv, "priority") == 0) { | ||
233 | __u32 pref; | ||
234 | NEXT_ARG(); | ||
235 | if (get_u32(&pref, *argv, 0)) | ||
236 | invarg("preference value", *argv); | ||
237 | addattr32(&req.n, sizeof(req), RTA_PRIORITY, pref); | ||
238 | } else if (strcmp(*argv, "tos") == 0) { | ||
239 | __u32 tos; | ||
240 | NEXT_ARG(); | ||
241 | if (rtnl_dsfield_a2n(&tos, *argv)) | ||
242 | invarg("TOS value", *argv); | ||
243 | req.r.rtm_tos = tos; | ||
244 | } else if (strcmp(*argv, "fwmark") == 0) { | ||
245 | __u32 fwmark; | ||
246 | NEXT_ARG(); | ||
247 | if (get_u32(&fwmark, *argv, 0)) | ||
248 | invarg("fwmark value", *argv); | ||
249 | addattr32(&req.n, sizeof(req), RTA_PROTOINFO, fwmark); | ||
250 | } else if (matches(*argv, "realms") == 0) { | ||
251 | __u32 realm; | ||
252 | NEXT_ARG(); | ||
253 | if (get_rt_realms(&realm, *argv)) | ||
254 | invarg("realms", *argv); | ||
255 | addattr32(&req.n, sizeof(req), RTA_FLOW, realm); | ||
256 | } else if (matches(*argv, "table") == 0 || | ||
257 | strcmp(*argv, "lookup") == 0) { | ||
258 | int tid; | ||
259 | NEXT_ARG(); | ||
260 | if (rtnl_rttable_a2n(&tid, *argv)) | ||
261 | invarg("table ID", *argv); | ||
262 | req.r.rtm_table = tid; | ||
263 | table_ok = 1; | ||
264 | } else if (strcmp(*argv, "dev") == 0 || | ||
265 | strcmp(*argv, "iif") == 0) { | ||
266 | NEXT_ARG(); | ||
267 | addattr_l(&req.n, sizeof(req), RTA_IIF, *argv, strlen(*argv)+1); | ||
268 | } else if (strcmp(*argv, "nat") == 0 || | ||
269 | matches(*argv, "map-to") == 0) { | ||
270 | NEXT_ARG(); | ||
271 | addattr32(&req.n, sizeof(req), RTA_GATEWAY, get_addr32(*argv)); | ||
272 | req.r.rtm_type = RTN_NAT; | ||
273 | } else { | ||
274 | int type; | ||
275 | |||
276 | if (strcmp(*argv, "type") == 0) { | ||
277 | NEXT_ARG(); | ||
278 | } | ||
279 | if (matches(*argv, "help") == 0) | ||
280 | bb_show_usage(); | ||
281 | if (rtnl_rtntype_a2n(&type, *argv)) | ||
282 | invarg("Failed to parse rule type", *argv); | ||
283 | req.r.rtm_type = type; | ||
284 | } | ||
285 | argc--; | ||
286 | argv++; | ||
287 | } | ||
288 | |||
289 | if (req.r.rtm_family == AF_UNSPEC) | ||
290 | req.r.rtm_family = AF_INET; | ||
291 | |||
292 | if (!table_ok && cmd == RTM_NEWRULE) | ||
293 | req.r.rtm_table = RT_TABLE_MAIN; | ||
294 | |||
295 | if (rtnl_open(&rth, 0) < 0) | ||
296 | return 1; | ||
297 | |||
298 | if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0) | ||
299 | return 2; | ||
300 | |||
301 | return 0; | ||
302 | } | ||
303 | |||
304 | int do_iprule(int argc, char **argv) | ||
305 | { | ||
306 | static const char * const ip_rule_commands[] = | ||
307 | {"add", "delete", "list", "show", 0}; | ||
308 | int command_num = 2; | ||
309 | int cmd; | ||
310 | |||
311 | if (argc < 1) | ||
312 | return iprule_list(0, NULL); | ||
313 | if (*argv) | ||
314 | command_num = index_in_substr_array(ip_rule_commands, *argv); | ||
315 | switch (command_num) { | ||
316 | case 0: /* add */ | ||
317 | cmd = RTM_NEWRULE; | ||
318 | break; | ||
319 | case 1: /* delete */ | ||
320 | cmd = RTM_DELRULE; | ||
321 | break; | ||
322 | case 2: /* list */ | ||
323 | case 3: /* show */ | ||
324 | return iprule_list(argc-1, argv+1); | ||
325 | break; | ||
326 | default: | ||
327 | bb_error_msg_and_die("unknown command %s", *argv); | ||
328 | } | ||
329 | return iprule_modify(cmd, argc-1, argv+1); | ||
330 | } | ||
331 | |||
diff --git a/networking/libiproute/iptunnel.c b/networking/libiproute/iptunnel.c new file mode 100644 index 000000000..2080324ac --- /dev/null +++ b/networking/libiproute/iptunnel.c | |||
@@ -0,0 +1,546 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * iptunnel.c "ip tunnel" | ||
4 | * | ||
5 | * Licensed under the GPL v2 or later, see the file LICENSE in this tarball. | ||
6 | * | ||
7 | * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> | ||
8 | * | ||
9 | * | ||
10 | * Changes: | ||
11 | * | ||
12 | * Rani Assaf <rani@magic.metawire.com> 980929: resolve addresses | ||
13 | * Rani Assaf <rani@magic.metawire.com> 980930: do not allow key for ipip/sit | ||
14 | * Phil Karn <karn@ka9q.ampr.org> 990408: "pmtudisc" flag | ||
15 | */ | ||
16 | |||
17 | #include "libbb.h" | ||
18 | #include <sys/socket.h> | ||
19 | #include <sys/ioctl.h> | ||
20 | |||
21 | #include <string.h> | ||
22 | #include <unistd.h> | ||
23 | |||
24 | #include <netinet/ip.h> | ||
25 | |||
26 | #include <net/if.h> | ||
27 | #include <net/if_arp.h> | ||
28 | |||
29 | #include <asm/types.h> | ||
30 | #ifndef __constant_htons | ||
31 | #define __constant_htons htons | ||
32 | #endif | ||
33 | #include <linux/if_tunnel.h> | ||
34 | |||
35 | #include "rt_names.h" | ||
36 | #include "utils.h" | ||
37 | #include "ip_common.h" | ||
38 | |||
39 | |||
40 | static int do_ioctl_get_ifindex(char *dev) | ||
41 | { | ||
42 | struct ifreq ifr; | ||
43 | int fd; | ||
44 | |||
45 | strncpy(ifr.ifr_name, dev, sizeof(ifr.ifr_name)); | ||
46 | fd = xsocket(AF_INET, SOCK_DGRAM, 0); | ||
47 | if (ioctl(fd, SIOCGIFINDEX, &ifr)) { | ||
48 | bb_perror_msg("ioctl"); | ||
49 | return 0; | ||
50 | } | ||
51 | close(fd); | ||
52 | return ifr.ifr_ifindex; | ||
53 | } | ||
54 | |||
55 | static int do_ioctl_get_iftype(char *dev) | ||
56 | { | ||
57 | struct ifreq ifr; | ||
58 | int fd; | ||
59 | |||
60 | strncpy(ifr.ifr_name, dev, sizeof(ifr.ifr_name)); | ||
61 | fd = xsocket(AF_INET, SOCK_DGRAM, 0); | ||
62 | if (ioctl(fd, SIOCGIFHWADDR, &ifr)) { | ||
63 | bb_perror_msg("ioctl"); | ||
64 | return -1; | ||
65 | } | ||
66 | close(fd); | ||
67 | return ifr.ifr_addr.sa_family; | ||
68 | } | ||
69 | |||
70 | |||
71 | static char *do_ioctl_get_ifname(int idx) | ||
72 | { | ||
73 | static struct ifreq ifr; | ||
74 | int fd; | ||
75 | |||
76 | ifr.ifr_ifindex = idx; | ||
77 | fd = xsocket(AF_INET, SOCK_DGRAM, 0); | ||
78 | if (ioctl(fd, SIOCGIFNAME, &ifr)) { | ||
79 | bb_perror_msg("ioctl"); | ||
80 | return NULL; | ||
81 | } | ||
82 | close(fd); | ||
83 | return ifr.ifr_name; | ||
84 | } | ||
85 | |||
86 | |||
87 | |||
88 | static int do_get_ioctl(char *basedev, struct ip_tunnel_parm *p) | ||
89 | { | ||
90 | struct ifreq ifr; | ||
91 | int fd; | ||
92 | int err; | ||
93 | |||
94 | strncpy(ifr.ifr_name, basedev, sizeof(ifr.ifr_name)); | ||
95 | ifr.ifr_ifru.ifru_data = (void*)p; | ||
96 | fd = xsocket(AF_INET, SOCK_DGRAM, 0); | ||
97 | err = ioctl(fd, SIOCGETTUNNEL, &ifr); | ||
98 | if (err) { | ||
99 | bb_perror_msg("ioctl"); | ||
100 | } | ||
101 | close(fd); | ||
102 | return err; | ||
103 | } | ||
104 | |||
105 | static int do_add_ioctl(int cmd, char *basedev, struct ip_tunnel_parm *p) | ||
106 | { | ||
107 | struct ifreq ifr; | ||
108 | int fd; | ||
109 | int err; | ||
110 | |||
111 | if (cmd == SIOCCHGTUNNEL && p->name[0]) { | ||
112 | strncpy(ifr.ifr_name, p->name, sizeof(ifr.ifr_name)); | ||
113 | } else { | ||
114 | strncpy(ifr.ifr_name, basedev, sizeof(ifr.ifr_name)); | ||
115 | } | ||
116 | ifr.ifr_ifru.ifru_data = (void*)p; | ||
117 | fd = xsocket(AF_INET, SOCK_DGRAM, 0); | ||
118 | err = ioctl(fd, cmd, &ifr); | ||
119 | if (err) { | ||
120 | bb_perror_msg("ioctl"); | ||
121 | } | ||
122 | close(fd); | ||
123 | return err; | ||
124 | } | ||
125 | |||
126 | static int do_del_ioctl(char *basedev, struct ip_tunnel_parm *p) | ||
127 | { | ||
128 | struct ifreq ifr; | ||
129 | int fd; | ||
130 | int err; | ||
131 | |||
132 | if (p->name[0]) { | ||
133 | strncpy(ifr.ifr_name, p->name, sizeof(ifr.ifr_name)); | ||
134 | } else { | ||
135 | strncpy(ifr.ifr_name, basedev, sizeof(ifr.ifr_name)); | ||
136 | } | ||
137 | ifr.ifr_ifru.ifru_data = (void*)p; | ||
138 | fd = xsocket(AF_INET, SOCK_DGRAM, 0); | ||
139 | err = ioctl(fd, SIOCDELTUNNEL, &ifr); | ||
140 | if (err) { | ||
141 | bb_perror_msg("ioctl"); | ||
142 | } | ||
143 | close(fd); | ||
144 | return err; | ||
145 | } | ||
146 | |||
147 | static int parse_args(int argc, char **argv, int cmd, struct ip_tunnel_parm *p) | ||
148 | { | ||
149 | int count = 0; | ||
150 | char medium[IFNAMSIZ]; | ||
151 | memset(p, 0, sizeof(*p)); | ||
152 | memset(&medium, 0, sizeof(medium)); | ||
153 | |||
154 | p->iph.version = 4; | ||
155 | p->iph.ihl = 5; | ||
156 | #ifndef IP_DF | ||
157 | #define IP_DF 0x4000 /* Flag: "Don't Fragment" */ | ||
158 | #endif | ||
159 | p->iph.frag_off = htons(IP_DF); | ||
160 | |||
161 | while (argc > 0) { | ||
162 | if (strcmp(*argv, "mode") == 0) { | ||
163 | NEXT_ARG(); | ||
164 | if (strcmp(*argv, "ipip") == 0 || | ||
165 | strcmp(*argv, "ip/ip") == 0) { | ||
166 | if (p->iph.protocol && p->iph.protocol != IPPROTO_IPIP) { | ||
167 | bb_error_msg("you managed to ask for more than one tunnel mode"); | ||
168 | exit(-1); | ||
169 | } | ||
170 | p->iph.protocol = IPPROTO_IPIP; | ||
171 | } else if (strcmp(*argv, "gre") == 0 || | ||
172 | strcmp(*argv, "gre/ip") == 0) { | ||
173 | if (p->iph.protocol && p->iph.protocol != IPPROTO_GRE) { | ||
174 | bb_error_msg("you managed to ask for more than one tunnel mode"); | ||
175 | exit(-1); | ||
176 | } | ||
177 | p->iph.protocol = IPPROTO_GRE; | ||
178 | } else if (strcmp(*argv, "sit") == 0 || | ||
179 | strcmp(*argv, "ipv6/ip") == 0) { | ||
180 | if (p->iph.protocol && p->iph.protocol != IPPROTO_IPV6) { | ||
181 | bb_error_msg("you managed to ask for more than one tunnel mode"); | ||
182 | exit(-1); | ||
183 | } | ||
184 | p->iph.protocol = IPPROTO_IPV6; | ||
185 | } else { | ||
186 | bb_error_msg("cannot guess tunnel mode"); | ||
187 | exit(-1); | ||
188 | } | ||
189 | } else if (strcmp(*argv, "key") == 0) { | ||
190 | unsigned uval; | ||
191 | NEXT_ARG(); | ||
192 | p->i_flags |= GRE_KEY; | ||
193 | p->o_flags |= GRE_KEY; | ||
194 | if (strchr(*argv, '.')) | ||
195 | p->i_key = p->o_key = get_addr32(*argv); | ||
196 | else { | ||
197 | if (get_unsigned(&uval, *argv, 0)<0) { | ||
198 | bb_error_msg("invalid value of \"key\""); | ||
199 | exit(-1); | ||
200 | } | ||
201 | p->i_key = p->o_key = htonl(uval); | ||
202 | } | ||
203 | } else if (strcmp(*argv, "ikey") == 0) { | ||
204 | unsigned uval; | ||
205 | NEXT_ARG(); | ||
206 | p->i_flags |= GRE_KEY; | ||
207 | if (strchr(*argv, '.')) | ||
208 | p->o_key = get_addr32(*argv); | ||
209 | else { | ||
210 | if (get_unsigned(&uval, *argv, 0)<0) { | ||
211 | bb_error_msg("invalid value of \"ikey\""); | ||
212 | exit(-1); | ||
213 | } | ||
214 | p->i_key = htonl(uval); | ||
215 | } | ||
216 | } else if (strcmp(*argv, "okey") == 0) { | ||
217 | unsigned uval; | ||
218 | NEXT_ARG(); | ||
219 | p->o_flags |= GRE_KEY; | ||
220 | if (strchr(*argv, '.')) | ||
221 | p->o_key = get_addr32(*argv); | ||
222 | else { | ||
223 | if (get_unsigned(&uval, *argv, 0)<0) { | ||
224 | bb_error_msg("invalid value of \"okey\""); | ||
225 | exit(-1); | ||
226 | } | ||
227 | p->o_key = htonl(uval); | ||
228 | } | ||
229 | } else if (strcmp(*argv, "seq") == 0) { | ||
230 | p->i_flags |= GRE_SEQ; | ||
231 | p->o_flags |= GRE_SEQ; | ||
232 | } else if (strcmp(*argv, "iseq") == 0) { | ||
233 | p->i_flags |= GRE_SEQ; | ||
234 | } else if (strcmp(*argv, "oseq") == 0) { | ||
235 | p->o_flags |= GRE_SEQ; | ||
236 | } else if (strcmp(*argv, "csum") == 0) { | ||
237 | p->i_flags |= GRE_CSUM; | ||
238 | p->o_flags |= GRE_CSUM; | ||
239 | } else if (strcmp(*argv, "icsum") == 0) { | ||
240 | p->i_flags |= GRE_CSUM; | ||
241 | } else if (strcmp(*argv, "ocsum") == 0) { | ||
242 | p->o_flags |= GRE_CSUM; | ||
243 | } else if (strcmp(*argv, "nopmtudisc") == 0) { | ||
244 | p->iph.frag_off = 0; | ||
245 | } else if (strcmp(*argv, "pmtudisc") == 0) { | ||
246 | p->iph.frag_off = htons(IP_DF); | ||
247 | } else if (strcmp(*argv, "remote") == 0) { | ||
248 | NEXT_ARG(); | ||
249 | if (strcmp(*argv, "any")) | ||
250 | p->iph.daddr = get_addr32(*argv); | ||
251 | } else if (strcmp(*argv, "local") == 0) { | ||
252 | NEXT_ARG(); | ||
253 | if (strcmp(*argv, "any")) | ||
254 | p->iph.saddr = get_addr32(*argv); | ||
255 | } else if (strcmp(*argv, "dev") == 0) { | ||
256 | NEXT_ARG(); | ||
257 | strncpy(medium, *argv, IFNAMSIZ-1); | ||
258 | } else if (strcmp(*argv, "ttl") == 0) { | ||
259 | unsigned uval; | ||
260 | NEXT_ARG(); | ||
261 | if (strcmp(*argv, "inherit") != 0) { | ||
262 | if (get_unsigned(&uval, *argv, 0)) | ||
263 | invarg(*argv, "TTL"); | ||
264 | if (uval > 255) | ||
265 | invarg(*argv, "TTL must be <=255"); | ||
266 | p->iph.ttl = uval; | ||
267 | } | ||
268 | } else if (strcmp(*argv, "tos") == 0 || | ||
269 | matches(*argv, "dsfield") == 0) { | ||
270 | __u32 uval; | ||
271 | NEXT_ARG(); | ||
272 | if (strcmp(*argv, "inherit") != 0) { | ||
273 | if (rtnl_dsfield_a2n(&uval, *argv)) | ||
274 | invarg(*argv, "TOS"); | ||
275 | p->iph.tos = uval; | ||
276 | } else | ||
277 | p->iph.tos = 1; | ||
278 | } else { | ||
279 | if (strcmp(*argv, "name") == 0) { | ||
280 | NEXT_ARG(); | ||
281 | } | ||
282 | if (p->name[0]) | ||
283 | duparg2("name", *argv); | ||
284 | strncpy(p->name, *argv, IFNAMSIZ); | ||
285 | if (cmd == SIOCCHGTUNNEL && count == 0) { | ||
286 | struct ip_tunnel_parm old_p; | ||
287 | memset(&old_p, 0, sizeof(old_p)); | ||
288 | if (do_get_ioctl(*argv, &old_p)) | ||
289 | return -1; | ||
290 | *p = old_p; | ||
291 | } | ||
292 | } | ||
293 | count++; | ||
294 | argc--; argv++; | ||
295 | } | ||
296 | |||
297 | |||
298 | if (p->iph.protocol == 0) { | ||
299 | if (memcmp(p->name, "gre", 3) == 0) | ||
300 | p->iph.protocol = IPPROTO_GRE; | ||
301 | else if (memcmp(p->name, "ipip", 4) == 0) | ||
302 | p->iph.protocol = IPPROTO_IPIP; | ||
303 | else if (memcmp(p->name, "sit", 3) == 0) | ||
304 | p->iph.protocol = IPPROTO_IPV6; | ||
305 | } | ||
306 | |||
307 | if (p->iph.protocol == IPPROTO_IPIP || p->iph.protocol == IPPROTO_IPV6) { | ||
308 | if ((p->i_flags & GRE_KEY) || (p->o_flags & GRE_KEY)) { | ||
309 | bb_error_msg("keys are not allowed with ipip and sit"); | ||
310 | return -1; | ||
311 | } | ||
312 | } | ||
313 | |||
314 | if (medium[0]) { | ||
315 | p->link = do_ioctl_get_ifindex(medium); | ||
316 | if (p->link == 0) | ||
317 | return -1; | ||
318 | } | ||
319 | |||
320 | if (p->i_key == 0 && IN_MULTICAST(ntohl(p->iph.daddr))) { | ||
321 | p->i_key = p->iph.daddr; | ||
322 | p->i_flags |= GRE_KEY; | ||
323 | } | ||
324 | if (p->o_key == 0 && IN_MULTICAST(ntohl(p->iph.daddr))) { | ||
325 | p->o_key = p->iph.daddr; | ||
326 | p->o_flags |= GRE_KEY; | ||
327 | } | ||
328 | if (IN_MULTICAST(ntohl(p->iph.daddr)) && !p->iph.saddr) { | ||
329 | bb_error_msg("broadcast tunnel requires a source address"); | ||
330 | return -1; | ||
331 | } | ||
332 | return 0; | ||
333 | } | ||
334 | |||
335 | |||
336 | static int do_add(int cmd, int argc, char **argv) | ||
337 | { | ||
338 | struct ip_tunnel_parm p; | ||
339 | |||
340 | if (parse_args(argc, argv, cmd, &p) < 0) | ||
341 | return -1; | ||
342 | |||
343 | if (p.iph.ttl && p.iph.frag_off == 0) { | ||
344 | bb_error_msg("ttl != 0 and noptmudisc are incompatible"); | ||
345 | return -1; | ||
346 | } | ||
347 | |||
348 | switch (p.iph.protocol) { | ||
349 | case IPPROTO_IPIP: | ||
350 | return do_add_ioctl(cmd, "tunl0", &p); | ||
351 | case IPPROTO_GRE: | ||
352 | return do_add_ioctl(cmd, "gre0", &p); | ||
353 | case IPPROTO_IPV6: | ||
354 | return do_add_ioctl(cmd, "sit0", &p); | ||
355 | default: | ||
356 | bb_error_msg("cannot determine tunnel mode (ipip, gre or sit)"); | ||
357 | return -1; | ||
358 | } | ||
359 | return -1; | ||
360 | } | ||
361 | |||
362 | static int do_del(int argc, char **argv) | ||
363 | { | ||
364 | struct ip_tunnel_parm p; | ||
365 | |||
366 | if (parse_args(argc, argv, SIOCDELTUNNEL, &p) < 0) | ||
367 | return -1; | ||
368 | |||
369 | switch (p.iph.protocol) { | ||
370 | case IPPROTO_IPIP: | ||
371 | return do_del_ioctl("tunl0", &p); | ||
372 | case IPPROTO_GRE: | ||
373 | return do_del_ioctl("gre0", &p); | ||
374 | case IPPROTO_IPV6: | ||
375 | return do_del_ioctl("sit0", &p); | ||
376 | default: | ||
377 | return do_del_ioctl(p.name, &p); | ||
378 | } | ||
379 | return -1; | ||
380 | } | ||
381 | |||
382 | static void print_tunnel(struct ip_tunnel_parm *p) | ||
383 | { | ||
384 | char s1[256]; | ||
385 | char s2[256]; | ||
386 | char s3[64]; | ||
387 | char s4[64]; | ||
388 | |||
389 | format_host(AF_INET, 4, &p->iph.daddr, s1, sizeof(s1)); | ||
390 | format_host(AF_INET, 4, &p->iph.saddr, s2, sizeof(s2)); | ||
391 | inet_ntop(AF_INET, &p->i_key, s3, sizeof(s3)); | ||
392 | inet_ntop(AF_INET, &p->o_key, s4, sizeof(s4)); | ||
393 | |||
394 | printf("%s: %s/ip remote %s local %s ", | ||
395 | p->name, | ||
396 | p->iph.protocol == IPPROTO_IPIP ? "ip" : | ||
397 | (p->iph.protocol == IPPROTO_GRE ? "gre" : | ||
398 | (p->iph.protocol == IPPROTO_IPV6 ? "ipv6" : "unknown")), | ||
399 | p->iph.daddr ? s1 : "any", p->iph.saddr ? s2 : "any"); | ||
400 | if (p->link) { | ||
401 | char *n = do_ioctl_get_ifname(p->link); | ||
402 | if (n) | ||
403 | printf(" dev %s ", n); | ||
404 | } | ||
405 | if (p->iph.ttl) | ||
406 | printf(" ttl %d ", p->iph.ttl); | ||
407 | else | ||
408 | printf(" ttl inherit "); | ||
409 | if (p->iph.tos) { | ||
410 | SPRINT_BUF(b1); | ||
411 | printf(" tos"); | ||
412 | if (p->iph.tos&1) | ||
413 | printf(" inherit"); | ||
414 | if (p->iph.tos&~1) | ||
415 | printf("%c%s ", p->iph.tos&1 ? '/' : ' ', | ||
416 | rtnl_dsfield_n2a(p->iph.tos&~1, b1, sizeof(b1))); | ||
417 | } | ||
418 | if (!(p->iph.frag_off&htons(IP_DF))) | ||
419 | printf(" nopmtudisc"); | ||
420 | |||
421 | if ((p->i_flags&GRE_KEY) && (p->o_flags&GRE_KEY) && p->o_key == p->i_key) | ||
422 | printf(" key %s", s3); | ||
423 | else if ((p->i_flags|p->o_flags)&GRE_KEY) { | ||
424 | if (p->i_flags&GRE_KEY) | ||
425 | printf(" ikey %s ", s3); | ||
426 | if (p->o_flags&GRE_KEY) | ||
427 | printf(" okey %s ", s4); | ||
428 | } | ||
429 | |||
430 | if (p->i_flags&GRE_SEQ) | ||
431 | printf("%s Drop packets out of sequence.\n", _SL_); | ||
432 | if (p->i_flags&GRE_CSUM) | ||
433 | printf("%s Checksum in received packet is required.", _SL_); | ||
434 | if (p->o_flags&GRE_SEQ) | ||
435 | printf("%s Sequence packets on output.", _SL_); | ||
436 | if (p->o_flags&GRE_CSUM) | ||
437 | printf("%s Checksum output packets.", _SL_); | ||
438 | } | ||
439 | |||
440 | static int do_tunnels_list(struct ip_tunnel_parm *p) | ||
441 | { | ||
442 | char name[IFNAMSIZ]; | ||
443 | unsigned long rx_bytes, rx_packets, rx_errs, rx_drops, | ||
444 | rx_fifo, rx_frame, | ||
445 | tx_bytes, tx_packets, tx_errs, tx_drops, | ||
446 | tx_fifo, tx_colls, tx_carrier, rx_multi; | ||
447 | int type; | ||
448 | struct ip_tunnel_parm p1; | ||
449 | |||
450 | char buf[512]; | ||
451 | FILE *fp = fopen("/proc/net/dev", "r"); | ||
452 | if (fp == NULL) { | ||
453 | perror("fopen"); | ||
454 | return -1; | ||
455 | } | ||
456 | |||
457 | fgets(buf, sizeof(buf), fp); | ||
458 | fgets(buf, sizeof(buf), fp); | ||
459 | |||
460 | while (fgets(buf, sizeof(buf), fp) != NULL) { | ||
461 | char *ptr; | ||
462 | buf[sizeof(buf) - 1] = 0; | ||
463 | if ((ptr = strchr(buf, ':')) == NULL || | ||
464 | (*ptr++ = 0, sscanf(buf, "%s", name) != 1)) { | ||
465 | bb_error_msg("wrong format of /proc/net/dev. Sorry"); | ||
466 | return -1; | ||
467 | } | ||
468 | if (sscanf(ptr, "%lu%lu%lu%lu%lu%lu%lu%*d%lu%lu%lu%lu%lu%lu%lu", | ||
469 | &rx_bytes, &rx_packets, &rx_errs, &rx_drops, | ||
470 | &rx_fifo, &rx_frame, &rx_multi, | ||
471 | &tx_bytes, &tx_packets, &tx_errs, &tx_drops, | ||
472 | &tx_fifo, &tx_colls, &tx_carrier) != 14) | ||
473 | continue; | ||
474 | if (p->name[0] && strcmp(p->name, name)) | ||
475 | continue; | ||
476 | type = do_ioctl_get_iftype(name); | ||
477 | if (type == -1) { | ||
478 | bb_error_msg("failed to get type of [%s]", name); | ||
479 | continue; | ||
480 | } | ||
481 | if (type != ARPHRD_TUNNEL && type != ARPHRD_IPGRE && type != ARPHRD_SIT) | ||
482 | continue; | ||
483 | memset(&p1, 0, sizeof(p1)); | ||
484 | if (do_get_ioctl(name, &p1)) | ||
485 | continue; | ||
486 | if ((p->link && p1.link != p->link) || | ||
487 | (p->name[0] && strcmp(p1.name, p->name)) || | ||
488 | (p->iph.daddr && p1.iph.daddr != p->iph.daddr) || | ||
489 | (p->iph.saddr && p1.iph.saddr != p->iph.saddr) || | ||
490 | (p->i_key && p1.i_key != p->i_key)) | ||
491 | continue; | ||
492 | print_tunnel(&p1); | ||
493 | puts(""); | ||
494 | } | ||
495 | return 0; | ||
496 | } | ||
497 | |||
498 | static int do_show(int argc, char **argv) | ||
499 | { | ||
500 | int err; | ||
501 | struct ip_tunnel_parm p; | ||
502 | |||
503 | if (parse_args(argc, argv, SIOCGETTUNNEL, &p) < 0) | ||
504 | return -1; | ||
505 | |||
506 | switch (p.iph.protocol) { | ||
507 | case IPPROTO_IPIP: | ||
508 | err = do_get_ioctl(p.name[0] ? p.name : "tunl0", &p); | ||
509 | break; | ||
510 | case IPPROTO_GRE: | ||
511 | err = do_get_ioctl(p.name[0] ? p.name : "gre0", &p); | ||
512 | break; | ||
513 | case IPPROTO_IPV6: | ||
514 | err = do_get_ioctl(p.name[0] ? p.name : "sit0", &p); | ||
515 | break; | ||
516 | default: | ||
517 | do_tunnels_list(&p); | ||
518 | return 0; | ||
519 | } | ||
520 | if (err) | ||
521 | return -1; | ||
522 | |||
523 | print_tunnel(&p); | ||
524 | puts(""); | ||
525 | return 0; | ||
526 | } | ||
527 | |||
528 | int do_iptunnel(int argc, char **argv) | ||
529 | { | ||
530 | if (argc > 0) { | ||
531 | if (matches(*argv, "add") == 0) | ||
532 | return do_add(SIOCADDTUNNEL, argc-1, argv+1); | ||
533 | if (matches(*argv, "change") == 0) | ||
534 | return do_add(SIOCCHGTUNNEL, argc-1, argv+1); | ||
535 | if (matches(*argv, "del") == 0) | ||
536 | return do_del(argc-1, argv+1); | ||
537 | if (matches(*argv, "show") == 0 || | ||
538 | matches(*argv, "lst") == 0 || | ||
539 | matches(*argv, "list") == 0) | ||
540 | return do_show(argc-1, argv+1); | ||
541 | } else | ||
542 | return do_show(0, NULL); | ||
543 | |||
544 | bb_error_msg("command \"%s\" is unknown", *argv); | ||
545 | exit(-1); | ||
546 | } | ||
diff --git a/networking/libiproute/libnetlink.c b/networking/libiproute/libnetlink.c new file mode 100644 index 000000000..c7e17e1dd --- /dev/null +++ b/networking/libiproute/libnetlink.c | |||
@@ -0,0 +1,396 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * libnetlink.c RTnetlink service routines. | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or | ||
6 | * modify it under the terms of the GNU General Public License | ||
7 | * as published by the Free Software Foundation; either version | ||
8 | * 2 of the License, or (at your option) any later version. | ||
9 | * | ||
10 | * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> | ||
11 | * | ||
12 | */ | ||
13 | |||
14 | #include "libbb.h" | ||
15 | #include <sys/socket.h> | ||
16 | |||
17 | #include <errno.h> | ||
18 | #include <string.h> | ||
19 | #include <time.h> | ||
20 | #include <unistd.h> | ||
21 | |||
22 | #include <sys/uio.h> | ||
23 | |||
24 | #include "libnetlink.h" | ||
25 | |||
26 | void rtnl_close(struct rtnl_handle *rth) | ||
27 | { | ||
28 | close(rth->fd); | ||
29 | } | ||
30 | |||
31 | int rtnl_open(struct rtnl_handle *rth, unsigned subscriptions) | ||
32 | { | ||
33 | socklen_t addr_len; | ||
34 | |||
35 | memset(rth, 0, sizeof(rth)); | ||
36 | |||
37 | rth->fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); | ||
38 | if (rth->fd < 0) { | ||
39 | bb_perror_msg("cannot open netlink socket"); | ||
40 | return -1; | ||
41 | } | ||
42 | |||
43 | memset(&rth->local, 0, sizeof(rth->local)); | ||
44 | rth->local.nl_family = AF_NETLINK; | ||
45 | rth->local.nl_groups = subscriptions; | ||
46 | |||
47 | if (bind(rth->fd, (struct sockaddr*)&rth->local, sizeof(rth->local)) < 0) { | ||
48 | bb_perror_msg("cannot bind netlink socket"); | ||
49 | return -1; | ||
50 | } | ||
51 | addr_len = sizeof(rth->local); | ||
52 | if (getsockname(rth->fd, (struct sockaddr*)&rth->local, &addr_len) < 0) { | ||
53 | bb_perror_msg("cannot getsockname"); | ||
54 | return -1; | ||
55 | } | ||
56 | if (addr_len != sizeof(rth->local)) { | ||
57 | bb_error_msg("wrong address length %d", addr_len); | ||
58 | return -1; | ||
59 | } | ||
60 | if (rth->local.nl_family != AF_NETLINK) { | ||
61 | bb_error_msg("wrong address family %d", rth->local.nl_family); | ||
62 | return -1; | ||
63 | } | ||
64 | rth->seq = time(NULL); | ||
65 | return 0; | ||
66 | } | ||
67 | |||
68 | int rtnl_wilddump_request(struct rtnl_handle *rth, int family, int type) | ||
69 | { | ||
70 | struct { | ||
71 | struct nlmsghdr nlh; | ||
72 | struct rtgenmsg g; | ||
73 | } req; | ||
74 | struct sockaddr_nl nladdr; | ||
75 | |||
76 | memset(&nladdr, 0, sizeof(nladdr)); | ||
77 | nladdr.nl_family = AF_NETLINK; | ||
78 | |||
79 | req.nlh.nlmsg_len = sizeof(req); | ||
80 | req.nlh.nlmsg_type = type; | ||
81 | req.nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST; | ||
82 | req.nlh.nlmsg_pid = 0; | ||
83 | req.nlh.nlmsg_seq = rth->dump = ++rth->seq; | ||
84 | req.g.rtgen_family = family; | ||
85 | |||
86 | return sendto(rth->fd, (void*)&req, sizeof(req), 0, (struct sockaddr*)&nladdr, sizeof(nladdr)); | ||
87 | } | ||
88 | |||
89 | int rtnl_send(struct rtnl_handle *rth, char *buf, int len) | ||
90 | { | ||
91 | struct sockaddr_nl nladdr; | ||
92 | |||
93 | memset(&nladdr, 0, sizeof(nladdr)); | ||
94 | nladdr.nl_family = AF_NETLINK; | ||
95 | |||
96 | return sendto(rth->fd, buf, len, 0, (struct sockaddr*)&nladdr, sizeof(nladdr)); | ||
97 | } | ||
98 | |||
99 | int rtnl_dump_request(struct rtnl_handle *rth, int type, void *req, int len) | ||
100 | { | ||
101 | struct nlmsghdr nlh; | ||
102 | struct sockaddr_nl nladdr; | ||
103 | struct iovec iov[2] = { { &nlh, sizeof(nlh) }, { req, len } }; | ||
104 | struct msghdr msg = { | ||
105 | (void*)&nladdr, sizeof(nladdr), | ||
106 | iov, 2, | ||
107 | NULL, 0, | ||
108 | 0 | ||
109 | }; | ||
110 | |||
111 | memset(&nladdr, 0, sizeof(nladdr)); | ||
112 | nladdr.nl_family = AF_NETLINK; | ||
113 | |||
114 | nlh.nlmsg_len = NLMSG_LENGTH(len); | ||
115 | nlh.nlmsg_type = type; | ||
116 | nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST; | ||
117 | nlh.nlmsg_pid = 0; | ||
118 | nlh.nlmsg_seq = rth->dump = ++rth->seq; | ||
119 | |||
120 | return sendmsg(rth->fd, &msg, 0); | ||
121 | } | ||
122 | |||
123 | int rtnl_dump_filter(struct rtnl_handle *rth, | ||
124 | int (*filter)(struct sockaddr_nl *, struct nlmsghdr *n, void *), | ||
125 | void *arg1, | ||
126 | int (*junk)(struct sockaddr_nl *, struct nlmsghdr *n, void *), | ||
127 | void *arg2) | ||
128 | { | ||
129 | char buf[8192]; | ||
130 | struct sockaddr_nl nladdr; | ||
131 | struct iovec iov = { buf, sizeof(buf) }; | ||
132 | |||
133 | while (1) { | ||
134 | int status; | ||
135 | struct nlmsghdr *h; | ||
136 | |||
137 | struct msghdr msg = { | ||
138 | (void*)&nladdr, sizeof(nladdr), | ||
139 | &iov, 1, | ||
140 | NULL, 0, | ||
141 | 0 | ||
142 | }; | ||
143 | |||
144 | status = recvmsg(rth->fd, &msg, 0); | ||
145 | |||
146 | if (status < 0) { | ||
147 | if (errno == EINTR) | ||
148 | continue; | ||
149 | bb_perror_msg("OVERRUN"); | ||
150 | continue; | ||
151 | } | ||
152 | if (status == 0) { | ||
153 | bb_error_msg("EOF on netlink"); | ||
154 | return -1; | ||
155 | } | ||
156 | if (msg.msg_namelen != sizeof(nladdr)) { | ||
157 | bb_error_msg_and_die("sender address length == %d", msg.msg_namelen); | ||
158 | } | ||
159 | |||
160 | h = (struct nlmsghdr*)buf; | ||
161 | while (NLMSG_OK(h, status)) { | ||
162 | int err; | ||
163 | |||
164 | if (nladdr.nl_pid != 0 || | ||
165 | h->nlmsg_pid != rth->local.nl_pid || | ||
166 | h->nlmsg_seq != rth->dump) { | ||
167 | if (junk) { | ||
168 | err = junk(&nladdr, h, arg2); | ||
169 | if (err < 0) { | ||
170 | return err; | ||
171 | } | ||
172 | } | ||
173 | goto skip_it; | ||
174 | } | ||
175 | |||
176 | if (h->nlmsg_type == NLMSG_DONE) { | ||
177 | return 0; | ||
178 | } | ||
179 | if (h->nlmsg_type == NLMSG_ERROR) { | ||
180 | struct nlmsgerr *l_err = (struct nlmsgerr*)NLMSG_DATA(h); | ||
181 | if (h->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr))) { | ||
182 | bb_error_msg("ERROR truncated"); | ||
183 | } else { | ||
184 | errno = -l_err->error; | ||
185 | bb_perror_msg("RTNETLINK answers"); | ||
186 | } | ||
187 | return -1; | ||
188 | } | ||
189 | err = filter(&nladdr, h, arg1); | ||
190 | if (err < 0) { | ||
191 | return err; | ||
192 | } | ||
193 | |||
194 | skip_it: | ||
195 | h = NLMSG_NEXT(h, status); | ||
196 | } | ||
197 | if (msg.msg_flags & MSG_TRUNC) { | ||
198 | bb_error_msg("message truncated"); | ||
199 | continue; | ||
200 | } | ||
201 | if (status) { | ||
202 | bb_error_msg_and_die("remnant of size %d!", status); | ||
203 | } | ||
204 | } | ||
205 | } | ||
206 | |||
207 | int rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n, pid_t peer, | ||
208 | unsigned groups, struct nlmsghdr *answer, | ||
209 | int (*junk)(struct sockaddr_nl *,struct nlmsghdr *n, void *), | ||
210 | void *jarg) | ||
211 | { | ||
212 | int status; | ||
213 | unsigned seq; | ||
214 | struct nlmsghdr *h; | ||
215 | struct sockaddr_nl nladdr; | ||
216 | struct iovec iov = { (void*)n, n->nlmsg_len }; | ||
217 | char buf[8192]; | ||
218 | struct msghdr msg = { | ||
219 | (void*)&nladdr, sizeof(nladdr), | ||
220 | &iov, 1, | ||
221 | NULL, 0, | ||
222 | 0 | ||
223 | }; | ||
224 | |||
225 | memset(&nladdr, 0, sizeof(nladdr)); | ||
226 | nladdr.nl_family = AF_NETLINK; | ||
227 | nladdr.nl_pid = peer; | ||
228 | nladdr.nl_groups = groups; | ||
229 | |||
230 | n->nlmsg_seq = seq = ++rtnl->seq; | ||
231 | if (answer == NULL) { | ||
232 | n->nlmsg_flags |= NLM_F_ACK; | ||
233 | } | ||
234 | status = sendmsg(rtnl->fd, &msg, 0); | ||
235 | |||
236 | if (status < 0) { | ||
237 | bb_perror_msg("cannot talk to rtnetlink"); | ||
238 | return -1; | ||
239 | } | ||
240 | |||
241 | iov.iov_base = buf; | ||
242 | |||
243 | while (1) { | ||
244 | iov.iov_len = sizeof(buf); | ||
245 | status = recvmsg(rtnl->fd, &msg, 0); | ||
246 | |||
247 | if (status < 0) { | ||
248 | if (errno == EINTR) { | ||
249 | continue; | ||
250 | } | ||
251 | bb_perror_msg("OVERRUN"); | ||
252 | continue; | ||
253 | } | ||
254 | if (status == 0) { | ||
255 | bb_error_msg("EOF on netlink"); | ||
256 | return -1; | ||
257 | } | ||
258 | if (msg.msg_namelen != sizeof(nladdr)) { | ||
259 | bb_error_msg_and_die("sender address length == %d", msg.msg_namelen); | ||
260 | } | ||
261 | for (h = (struct nlmsghdr*)buf; status >= sizeof(*h); ) { | ||
262 | int l_err; | ||
263 | int len = h->nlmsg_len; | ||
264 | int l = len - sizeof(*h); | ||
265 | |||
266 | if (l<0 || len>status) { | ||
267 | if (msg.msg_flags & MSG_TRUNC) { | ||
268 | bb_error_msg("truncated message"); | ||
269 | return -1; | ||
270 | } | ||
271 | bb_error_msg_and_die("malformed message: len=%d!", len); | ||
272 | } | ||
273 | |||
274 | if (nladdr.nl_pid != peer || | ||
275 | h->nlmsg_pid != rtnl->local.nl_pid || | ||
276 | h->nlmsg_seq != seq) { | ||
277 | if (junk) { | ||
278 | l_err = junk(&nladdr, h, jarg); | ||
279 | if (l_err < 0) { | ||
280 | return l_err; | ||
281 | } | ||
282 | } | ||
283 | continue; | ||
284 | } | ||
285 | |||
286 | if (h->nlmsg_type == NLMSG_ERROR) { | ||
287 | struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(h); | ||
288 | if (l < sizeof(struct nlmsgerr)) { | ||
289 | bb_error_msg("ERROR truncated"); | ||
290 | } else { | ||
291 | errno = -err->error; | ||
292 | if (errno == 0) { | ||
293 | if (answer) { | ||
294 | memcpy(answer, h, h->nlmsg_len); | ||
295 | } | ||
296 | return 0; | ||
297 | } | ||
298 | bb_perror_msg("RTNETLINK answers"); | ||
299 | } | ||
300 | return -1; | ||
301 | } | ||
302 | if (answer) { | ||
303 | memcpy(answer, h, h->nlmsg_len); | ||
304 | return 0; | ||
305 | } | ||
306 | |||
307 | bb_error_msg("unexpected reply!"); | ||
308 | |||
309 | status -= NLMSG_ALIGN(len); | ||
310 | h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len)); | ||
311 | } | ||
312 | if (msg.msg_flags & MSG_TRUNC) { | ||
313 | bb_error_msg("message truncated"); | ||
314 | continue; | ||
315 | } | ||
316 | if (status) { | ||
317 | bb_error_msg_and_die("remnant of size %d!", status); | ||
318 | } | ||
319 | } | ||
320 | } | ||
321 | |||
322 | int addattr32(struct nlmsghdr *n, int maxlen, int type, __u32 data) | ||
323 | { | ||
324 | int len = RTA_LENGTH(4); | ||
325 | struct rtattr *rta; | ||
326 | if (NLMSG_ALIGN(n->nlmsg_len) + len > maxlen) | ||
327 | return -1; | ||
328 | rta = (struct rtattr*)(((char*)n) + NLMSG_ALIGN(n->nlmsg_len)); | ||
329 | rta->rta_type = type; | ||
330 | rta->rta_len = len; | ||
331 | memcpy(RTA_DATA(rta), &data, 4); | ||
332 | n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + len; | ||
333 | return 0; | ||
334 | } | ||
335 | |||
336 | int addattr_l(struct nlmsghdr *n, int maxlen, int type, void *data, int alen) | ||
337 | { | ||
338 | int len = RTA_LENGTH(alen); | ||
339 | struct rtattr *rta; | ||
340 | |||
341 | if (NLMSG_ALIGN(n->nlmsg_len) + len > maxlen) | ||
342 | return -1; | ||
343 | rta = (struct rtattr*)(((char*)n) + NLMSG_ALIGN(n->nlmsg_len)); | ||
344 | rta->rta_type = type; | ||
345 | rta->rta_len = len; | ||
346 | memcpy(RTA_DATA(rta), data, alen); | ||
347 | n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + len; | ||
348 | return 0; | ||
349 | } | ||
350 | |||
351 | int rta_addattr32(struct rtattr *rta, int maxlen, int type, __u32 data) | ||
352 | { | ||
353 | int len = RTA_LENGTH(4); | ||
354 | struct rtattr *subrta; | ||
355 | |||
356 | if (RTA_ALIGN(rta->rta_len) + len > maxlen) { | ||
357 | return -1; | ||
358 | } | ||
359 | subrta = (struct rtattr*)(((char*)rta) + RTA_ALIGN(rta->rta_len)); | ||
360 | subrta->rta_type = type; | ||
361 | subrta->rta_len = len; | ||
362 | memcpy(RTA_DATA(subrta), &data, 4); | ||
363 | rta->rta_len = NLMSG_ALIGN(rta->rta_len) + len; | ||
364 | return 0; | ||
365 | } | ||
366 | |||
367 | int rta_addattr_l(struct rtattr *rta, int maxlen, int type, void *data, int alen) | ||
368 | { | ||
369 | struct rtattr *subrta; | ||
370 | int len = RTA_LENGTH(alen); | ||
371 | |||
372 | if (RTA_ALIGN(rta->rta_len) + len > maxlen) { | ||
373 | return -1; | ||
374 | } | ||
375 | subrta = (struct rtattr*)(((char*)rta) + RTA_ALIGN(rta->rta_len)); | ||
376 | subrta->rta_type = type; | ||
377 | subrta->rta_len = len; | ||
378 | memcpy(RTA_DATA(subrta), data, alen); | ||
379 | rta->rta_len = NLMSG_ALIGN(rta->rta_len) + len; | ||
380 | return 0; | ||
381 | } | ||
382 | |||
383 | |||
384 | int parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len) | ||
385 | { | ||
386 | while (RTA_OK(rta, len)) { | ||
387 | if (rta->rta_type <= max) { | ||
388 | tb[rta->rta_type] = rta; | ||
389 | } | ||
390 | rta = RTA_NEXT(rta,len); | ||
391 | } | ||
392 | if (len) { | ||
393 | bb_error_msg("deficit %d, rta_len=%d!", len, rta->rta_len); | ||
394 | } | ||
395 | return 0; | ||
396 | } | ||
diff --git a/networking/libiproute/libnetlink.h b/networking/libiproute/libnetlink.h new file mode 100644 index 000000000..7cfcd2042 --- /dev/null +++ b/networking/libiproute/libnetlink.h | |||
@@ -0,0 +1,43 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | #ifndef __LIBNETLINK_H__ | ||
3 | #define __LIBNETLINK_H__ 1 | ||
4 | |||
5 | #include <asm/types.h> | ||
6 | #include <linux/netlink.h> | ||
7 | #include <linux/rtnetlink.h> | ||
8 | |||
9 | struct rtnl_handle | ||
10 | { | ||
11 | int fd; | ||
12 | struct sockaddr_nl local; | ||
13 | struct sockaddr_nl peer; | ||
14 | __u32 seq; | ||
15 | __u32 dump; | ||
16 | }; | ||
17 | |||
18 | extern int rtnl_open(struct rtnl_handle *rth, unsigned subscriptions); | ||
19 | extern void rtnl_close(struct rtnl_handle *rth); | ||
20 | extern int rtnl_wilddump_request(struct rtnl_handle *rth, int fam, int type); | ||
21 | extern int rtnl_dump_request(struct rtnl_handle *rth, int type, void *req, int len); | ||
22 | extern int rtnl_dump_filter(struct rtnl_handle *rth, | ||
23 | int (*filter)(struct sockaddr_nl*, struct nlmsghdr *n, void*), | ||
24 | void *arg1, | ||
25 | int (*junk)(struct sockaddr_nl *, struct nlmsghdr *n, void *), | ||
26 | void *arg2); | ||
27 | extern int rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n, pid_t peer, | ||
28 | unsigned groups, struct nlmsghdr *answer, | ||
29 | int (*junk)(struct sockaddr_nl *,struct nlmsghdr *n, void *), | ||
30 | void *jarg); | ||
31 | extern int rtnl_send(struct rtnl_handle *rth, char *buf, int); | ||
32 | |||
33 | |||
34 | extern int addattr32(struct nlmsghdr *n, int maxlen, int type, __u32 data); | ||
35 | extern int addattr_l(struct nlmsghdr *n, int maxlen, int type, void *data, int alen); | ||
36 | extern int rta_addattr32(struct rtattr *rta, int maxlen, int type, __u32 data); | ||
37 | extern int rta_addattr_l(struct rtattr *rta, int maxlen, int type, void *data, int alen); | ||
38 | |||
39 | extern int parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len); | ||
40 | |||
41 | |||
42 | #endif /* __LIBNETLINK_H__ */ | ||
43 | |||
diff --git a/networking/libiproute/linux/pkt_sched.h b/networking/libiproute/linux/pkt_sched.h new file mode 100644 index 000000000..d6cf1fc06 --- /dev/null +++ b/networking/libiproute/linux/pkt_sched.h | |||
@@ -0,0 +1,414 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | #ifndef __LINUX_PKT_SCHED_H | ||
3 | #define __LINUX_PKT_SCHED_H | ||
4 | |||
5 | /* Logical priority bands not depending on specific packet scheduler. | ||
6 | Every scheduler will map them to real traffic classes, if it has | ||
7 | no more precise mechanism to classify packets. | ||
8 | |||
9 | These numbers have no special meaning, though their coincidence | ||
10 | with obsolete IPv6 values is not occasional :-). New IPv6 drafts | ||
11 | preferred full anarchy inspired by diffserv group. | ||
12 | |||
13 | Note: TC_PRIO_BESTEFFORT does not mean that it is the most unhappy | ||
14 | class, actually, as rule it will be handled with more care than | ||
15 | filler or even bulk. | ||
16 | */ | ||
17 | |||
18 | #include <asm/types.h> | ||
19 | |||
20 | #define TC_PRIO_BESTEFFORT 0 | ||
21 | #define TC_PRIO_FILLER 1 | ||
22 | #define TC_PRIO_BULK 2 | ||
23 | #define TC_PRIO_INTERACTIVE_BULK 4 | ||
24 | #define TC_PRIO_INTERACTIVE 6 | ||
25 | #define TC_PRIO_CONTROL 7 | ||
26 | |||
27 | #define TC_PRIO_MAX 15 | ||
28 | |||
29 | /* Generic queue statistics, available for all the elements. | ||
30 | Particular schedulers may have also their private records. | ||
31 | */ | ||
32 | |||
33 | struct tc_stats | ||
34 | { | ||
35 | __u64 bytes; /* NUmber of enqueues bytes */ | ||
36 | __u32 packets; /* Number of enqueued packets */ | ||
37 | __u32 drops; /* Packets dropped because of lack of resources */ | ||
38 | __u32 overlimits; /* Number of throttle events when this | ||
39 | * flow goes out of allocated bandwidth */ | ||
40 | __u32 bps; /* Current flow byte rate */ | ||
41 | __u32 pps; /* Current flow packet rate */ | ||
42 | __u32 qlen; | ||
43 | __u32 backlog; | ||
44 | #ifdef __KERNEL__ | ||
45 | spinlock_t *lock; | ||
46 | #endif | ||
47 | }; | ||
48 | |||
49 | struct tc_estimator | ||
50 | { | ||
51 | char interval; | ||
52 | unsigned char ewma_log; | ||
53 | }; | ||
54 | |||
55 | /* "Handles" | ||
56 | --------- | ||
57 | |||
58 | All the traffic control objects have 32bit identifiers, or "handles". | ||
59 | |||
60 | They can be considered as opaque numbers from user API viewpoint, | ||
61 | but actually they always consist of two fields: major and | ||
62 | minor numbers, which are interpreted by kernel specially, | ||
63 | that may be used by applications, though not recommended. | ||
64 | |||
65 | F.e. qdisc handles always have minor number equal to zero, | ||
66 | classes (or flows) have major equal to parent qdisc major, and | ||
67 | minor uniquely identifying class inside qdisc. | ||
68 | |||
69 | Macros to manipulate handles: | ||
70 | */ | ||
71 | |||
72 | #define TC_H_MAJ_MASK (0xFFFF0000U) | ||
73 | #define TC_H_MIN_MASK (0x0000FFFFU) | ||
74 | #define TC_H_MAJ(h) ((h)&TC_H_MAJ_MASK) | ||
75 | #define TC_H_MIN(h) ((h)&TC_H_MIN_MASK) | ||
76 | #define TC_H_MAKE(maj,min) (((maj)&TC_H_MAJ_MASK)|((min)&TC_H_MIN_MASK)) | ||
77 | |||
78 | #define TC_H_UNSPEC (0U) | ||
79 | #define TC_H_ROOT (0xFFFFFFFFU) | ||
80 | #define TC_H_INGRESS (0xFFFFFFF1U) | ||
81 | |||
82 | struct tc_ratespec | ||
83 | { | ||
84 | unsigned char cell_log; | ||
85 | unsigned char __reserved; | ||
86 | unsigned short feature; | ||
87 | short addend; | ||
88 | unsigned short mpu; | ||
89 | __u32 rate; | ||
90 | }; | ||
91 | |||
92 | /* FIFO section */ | ||
93 | |||
94 | struct tc_fifo_qopt | ||
95 | { | ||
96 | __u32 limit; /* Queue length: bytes for bfifo, packets for pfifo */ | ||
97 | }; | ||
98 | |||
99 | /* PRIO section */ | ||
100 | |||
101 | #define TCQ_PRIO_BANDS 16 | ||
102 | |||
103 | struct tc_prio_qopt | ||
104 | { | ||
105 | int bands; /* Number of bands */ | ||
106 | __u8 priomap[TC_PRIO_MAX+1]; /* Map: logical priority -> PRIO band */ | ||
107 | }; | ||
108 | |||
109 | /* CSZ section */ | ||
110 | |||
111 | struct tc_csz_qopt | ||
112 | { | ||
113 | int flows; /* Maximal number of guaranteed flows */ | ||
114 | unsigned char R_log; /* Fixed point position for round number */ | ||
115 | unsigned char delta_log; /* Log of maximal managed time interval */ | ||
116 | __u8 priomap[TC_PRIO_MAX+1]; /* Map: logical priority -> CSZ band */ | ||
117 | }; | ||
118 | |||
119 | struct tc_csz_copt | ||
120 | { | ||
121 | struct tc_ratespec slice; | ||
122 | struct tc_ratespec rate; | ||
123 | struct tc_ratespec peakrate; | ||
124 | __u32 limit; | ||
125 | __u32 buffer; | ||
126 | __u32 mtu; | ||
127 | }; | ||
128 | |||
129 | enum | ||
130 | { | ||
131 | TCA_CSZ_UNSPEC, | ||
132 | TCA_CSZ_PARMS, | ||
133 | TCA_CSZ_RTAB, | ||
134 | TCA_CSZ_PTAB, | ||
135 | }; | ||
136 | |||
137 | /* TBF section */ | ||
138 | |||
139 | struct tc_tbf_qopt | ||
140 | { | ||
141 | struct tc_ratespec rate; | ||
142 | struct tc_ratespec peakrate; | ||
143 | __u32 limit; | ||
144 | __u32 buffer; | ||
145 | __u32 mtu; | ||
146 | }; | ||
147 | |||
148 | enum | ||
149 | { | ||
150 | TCA_TBF_UNSPEC, | ||
151 | TCA_TBF_PARMS, | ||
152 | TCA_TBF_RTAB, | ||
153 | TCA_TBF_PTAB, | ||
154 | }; | ||
155 | |||
156 | |||
157 | /* TEQL section */ | ||
158 | |||
159 | /* TEQL does not require any parameters */ | ||
160 | |||
161 | /* SFQ section */ | ||
162 | |||
163 | struct tc_sfq_qopt | ||
164 | { | ||
165 | unsigned quantum; /* Bytes per round allocated to flow */ | ||
166 | int perturb_period; /* Period of hash perturbation */ | ||
167 | __u32 limit; /* Maximal packets in queue */ | ||
168 | unsigned divisor; /* Hash divisor */ | ||
169 | unsigned flows; /* Maximal number of flows */ | ||
170 | }; | ||
171 | |||
172 | /* | ||
173 | * NOTE: limit, divisor and flows are hardwired to code at the moment. | ||
174 | * | ||
175 | * limit=flows=128, divisor=1024; | ||
176 | * | ||
177 | * The only reason for this is efficiency, it is possible | ||
178 | * to change these parameters in compile time. | ||
179 | */ | ||
180 | |||
181 | /* RED section */ | ||
182 | |||
183 | enum | ||
184 | { | ||
185 | TCA_RED_UNSPEC, | ||
186 | TCA_RED_PARMS, | ||
187 | TCA_RED_STAB, | ||
188 | }; | ||
189 | |||
190 | struct tc_red_qopt | ||
191 | { | ||
192 | __u32 limit; /* HARD maximal queue length (bytes) */ | ||
193 | __u32 qth_min; /* Min average length threshold (bytes) */ | ||
194 | __u32 qth_max; /* Max average length threshold (bytes) */ | ||
195 | unsigned char Wlog; /* log(W) */ | ||
196 | unsigned char Plog; /* log(P_max/(qth_max-qth_min)) */ | ||
197 | unsigned char Scell_log; /* cell size for idle damping */ | ||
198 | unsigned char flags; | ||
199 | #define TC_RED_ECN 1 | ||
200 | }; | ||
201 | |||
202 | struct tc_red_xstats | ||
203 | { | ||
204 | __u32 early; /* Early drops */ | ||
205 | __u32 pdrop; /* Drops due to queue limits */ | ||
206 | __u32 other; /* Drops due to drop() calls */ | ||
207 | __u32 marked; /* Marked packets */ | ||
208 | }; | ||
209 | |||
210 | /* GRED section */ | ||
211 | |||
212 | #define MAX_DPs 16 | ||
213 | |||
214 | enum | ||
215 | { | ||
216 | TCA_GRED_UNSPEC, | ||
217 | TCA_GRED_PARMS, | ||
218 | TCA_GRED_STAB, | ||
219 | TCA_GRED_DPS, | ||
220 | }; | ||
221 | |||
222 | #define TCA_SET_OFF TCA_GRED_PARMS | ||
223 | struct tc_gred_qopt | ||
224 | { | ||
225 | __u32 limit; /* HARD maximal queue length (bytes) | ||
226 | */ | ||
227 | __u32 qth_min; /* Min average length threshold (bytes) | ||
228 | */ | ||
229 | __u32 qth_max; /* Max average length threshold (bytes) | ||
230 | */ | ||
231 | __u32 DP; /* upto 2^32 DPs */ | ||
232 | __u32 backlog; | ||
233 | __u32 qave; | ||
234 | __u32 forced; | ||
235 | __u32 early; | ||
236 | __u32 other; | ||
237 | __u32 pdrop; | ||
238 | |||
239 | unsigned char Wlog; /* log(W) */ | ||
240 | unsigned char Plog; /* log(P_max/(qth_max-qth_min)) */ | ||
241 | unsigned char Scell_log; /* cell size for idle damping */ | ||
242 | __u8 prio; /* prio of this VQ */ | ||
243 | __u32 packets; | ||
244 | __u32 bytesin; | ||
245 | }; | ||
246 | /* gred setup */ | ||
247 | struct tc_gred_sopt | ||
248 | { | ||
249 | __u32 DPs; | ||
250 | __u32 def_DP; | ||
251 | __u8 grio; | ||
252 | }; | ||
253 | |||
254 | /* HTB section */ | ||
255 | #define TC_HTB_NUMPRIO 4 | ||
256 | #define TC_HTB_MAXDEPTH 4 | ||
257 | |||
258 | struct tc_htb_opt | ||
259 | { | ||
260 | struct tc_ratespec rate; | ||
261 | struct tc_ratespec ceil; | ||
262 | __u32 buffer; | ||
263 | __u32 cbuffer; | ||
264 | __u32 quantum; /* out only */ | ||
265 | __u32 level; /* out only */ | ||
266 | __u8 prio; | ||
267 | __u8 injectd; /* inject class distance */ | ||
268 | __u8 pad[2]; | ||
269 | }; | ||
270 | struct tc_htb_glob | ||
271 | { | ||
272 | __u32 rate2quantum; /* bps->quantum divisor */ | ||
273 | __u32 defcls; /* default class number */ | ||
274 | __u32 use_dcache; /* use dequeue cache ? */ | ||
275 | __u32 debug; /* debug flags */ | ||
276 | |||
277 | |||
278 | /* stats */ | ||
279 | __u32 deq_rate; /* dequeue rate */ | ||
280 | __u32 utilz; /* dequeue utilization */ | ||
281 | __u32 trials; /* deq_prio trials per dequeue */ | ||
282 | __u32 dcache_hits; | ||
283 | __u32 direct_pkts; /* count of non shapped packets */ | ||
284 | }; | ||
285 | enum | ||
286 | { | ||
287 | TCA_HTB_UNSPEC, | ||
288 | TCA_HTB_PARMS, | ||
289 | TCA_HTB_INIT, | ||
290 | TCA_HTB_CTAB, | ||
291 | TCA_HTB_RTAB, | ||
292 | }; | ||
293 | struct tc_htb_xstats | ||
294 | { | ||
295 | __u32 lends; | ||
296 | __u32 borrows; | ||
297 | __u32 giants; /* too big packets (rate will not be accurate) */ | ||
298 | __u32 injects; /* how many times leaf used injected bw */ | ||
299 | __u32 tokens; | ||
300 | __u32 ctokens; | ||
301 | }; | ||
302 | |||
303 | /* CBQ section */ | ||
304 | |||
305 | #define TC_CBQ_MAXPRIO 8 | ||
306 | #define TC_CBQ_MAXLEVEL 8 | ||
307 | #define TC_CBQ_DEF_EWMA 5 | ||
308 | |||
309 | struct tc_cbq_lssopt | ||
310 | { | ||
311 | unsigned char change; | ||
312 | unsigned char flags; | ||
313 | #define TCF_CBQ_LSS_BOUNDED 1 | ||
314 | #define TCF_CBQ_LSS_ISOLATED 2 | ||
315 | unsigned char ewma_log; | ||
316 | unsigned char level; | ||
317 | #define TCF_CBQ_LSS_FLAGS 1 | ||
318 | #define TCF_CBQ_LSS_EWMA 2 | ||
319 | #define TCF_CBQ_LSS_MAXIDLE 4 | ||
320 | #define TCF_CBQ_LSS_MINIDLE 8 | ||
321 | #define TCF_CBQ_LSS_OFFTIME 0x10 | ||
322 | #define TCF_CBQ_LSS_AVPKT 0x20 | ||
323 | __u32 maxidle; | ||
324 | __u32 minidle; | ||
325 | __u32 offtime; | ||
326 | __u32 avpkt; | ||
327 | }; | ||
328 | |||
329 | struct tc_cbq_wrropt | ||
330 | { | ||
331 | unsigned char flags; | ||
332 | unsigned char priority; | ||
333 | unsigned char cpriority; | ||
334 | unsigned char __reserved; | ||
335 | __u32 allot; | ||
336 | __u32 weight; | ||
337 | }; | ||
338 | |||
339 | struct tc_cbq_ovl | ||
340 | { | ||
341 | unsigned char strategy; | ||
342 | #define TC_CBQ_OVL_CLASSIC 0 | ||
343 | #define TC_CBQ_OVL_DELAY 1 | ||
344 | #define TC_CBQ_OVL_LOWPRIO 2 | ||
345 | #define TC_CBQ_OVL_DROP 3 | ||
346 | #define TC_CBQ_OVL_RCLASSIC 4 | ||
347 | unsigned char priority2; | ||
348 | __u32 penalty; | ||
349 | }; | ||
350 | |||
351 | struct tc_cbq_police | ||
352 | { | ||
353 | unsigned char police; | ||
354 | unsigned char __res1; | ||
355 | unsigned short __res2; | ||
356 | }; | ||
357 | |||
358 | struct tc_cbq_fopt | ||
359 | { | ||
360 | __u32 split; | ||
361 | __u32 defmap; | ||
362 | __u32 defchange; | ||
363 | }; | ||
364 | |||
365 | struct tc_cbq_xstats | ||
366 | { | ||
367 | __u32 borrows; | ||
368 | __u32 overactions; | ||
369 | __s32 avgidle; | ||
370 | __s32 undertime; | ||
371 | }; | ||
372 | |||
373 | enum | ||
374 | { | ||
375 | TCA_CBQ_UNSPEC, | ||
376 | TCA_CBQ_LSSOPT, | ||
377 | TCA_CBQ_WRROPT, | ||
378 | TCA_CBQ_FOPT, | ||
379 | TCA_CBQ_OVL_STRATEGY, | ||
380 | TCA_CBQ_RATE, | ||
381 | TCA_CBQ_RTAB, | ||
382 | TCA_CBQ_POLICE, | ||
383 | }; | ||
384 | |||
385 | #define TCA_CBQ_MAX TCA_CBQ_POLICE | ||
386 | |||
387 | /* dsmark section */ | ||
388 | |||
389 | enum { | ||
390 | TCA_DSMARK_UNSPEC, | ||
391 | TCA_DSMARK_INDICES, | ||
392 | TCA_DSMARK_DEFAULT_INDEX, | ||
393 | TCA_DSMARK_SET_TC_INDEX, | ||
394 | TCA_DSMARK_MASK, | ||
395 | TCA_DSMARK_VALUE | ||
396 | }; | ||
397 | |||
398 | #define TCA_DSMARK_MAX TCA_DSMARK_VALUE | ||
399 | |||
400 | /* ATM section */ | ||
401 | |||
402 | enum { | ||
403 | TCA_ATM_UNSPEC, | ||
404 | TCA_ATM_FD, /* file/socket descriptor */ | ||
405 | TCA_ATM_PTR, /* pointer to descriptor - later */ | ||
406 | TCA_ATM_HDR, /* LL header */ | ||
407 | TCA_ATM_EXCESS, /* excess traffic class (0 for CLP) */ | ||
408 | TCA_ATM_ADDR, /* PVC address (for output only) */ | ||
409 | TCA_ATM_STATE /* VC state (ATM_VS_*; for output only) */ | ||
410 | }; | ||
411 | |||
412 | #define TCA_ATM_MAX TCA_ATM_STATE | ||
413 | |||
414 | #endif | ||
diff --git a/networking/libiproute/ll_addr.c b/networking/libiproute/ll_addr.c new file mode 100644 index 000000000..ba0a65a18 --- /dev/null +++ b/networking/libiproute/ll_addr.c | |||
@@ -0,0 +1,85 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * ll_addr.c | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or | ||
6 | * modify it under the terms of the GNU General Public License | ||
7 | * as published by the Free Software Foundation; either version | ||
8 | * 2 of the License, or (at your option) any later version. | ||
9 | * | ||
10 | * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> | ||
11 | */ | ||
12 | |||
13 | #include "libbb.h" | ||
14 | |||
15 | #include <string.h> | ||
16 | #include <net/if_arp.h> | ||
17 | |||
18 | #include "rt_names.h" | ||
19 | #include "utils.h" | ||
20 | |||
21 | |||
22 | const char *ll_addr_n2a(unsigned char *addr, int alen, int type, char *buf, int blen) | ||
23 | { | ||
24 | int i; | ||
25 | int l; | ||
26 | |||
27 | if (alen == 4 && | ||
28 | (type == ARPHRD_TUNNEL || type == ARPHRD_SIT || type == ARPHRD_IPGRE)) { | ||
29 | return inet_ntop(AF_INET, addr, buf, blen); | ||
30 | } | ||
31 | l = 0; | ||
32 | for (i=0; i<alen; i++) { | ||
33 | if (i==0) { | ||
34 | snprintf(buf+l, blen, "%02x", addr[i]); | ||
35 | blen -= 2; | ||
36 | l += 2; | ||
37 | } else { | ||
38 | snprintf(buf+l, blen, ":%02x", addr[i]); | ||
39 | blen -= 3; | ||
40 | l += 3; | ||
41 | } | ||
42 | } | ||
43 | return buf; | ||
44 | } | ||
45 | |||
46 | int ll_addr_a2n(unsigned char *lladdr, int len, char *arg) | ||
47 | { | ||
48 | if (strchr(arg, '.')) { | ||
49 | inet_prefix pfx; | ||
50 | if (get_addr_1(&pfx, arg, AF_INET)) { | ||
51 | bb_error_msg("\"%s\" is invalid lladdr", arg); | ||
52 | return -1; | ||
53 | } | ||
54 | if (len < 4) { | ||
55 | return -1; | ||
56 | } | ||
57 | memcpy(lladdr, pfx.data, 4); | ||
58 | return 4; | ||
59 | } else { | ||
60 | int i; | ||
61 | |||
62 | for (i=0; i<len; i++) { | ||
63 | int temp; | ||
64 | char *cp = strchr(arg, ':'); | ||
65 | if (cp) { | ||
66 | *cp = 0; | ||
67 | cp++; | ||
68 | } | ||
69 | if (sscanf(arg, "%x", &temp) != 1) { | ||
70 | bb_error_msg("\"%s\" is invalid lladdr", arg); | ||
71 | return -1; | ||
72 | } | ||
73 | if (temp < 0 || temp > 255) { | ||
74 | bb_error_msg("\"%s\" is invalid lladdr", arg); | ||
75 | return -1; | ||
76 | } | ||
77 | lladdr[i] = temp; | ||
78 | if (!cp) { | ||
79 | break; | ||
80 | } | ||
81 | arg = cp; | ||
82 | } | ||
83 | return i+1; | ||
84 | } | ||
85 | } | ||
diff --git a/networking/libiproute/ll_map.c b/networking/libiproute/ll_map.c new file mode 100644 index 000000000..a14fa4e42 --- /dev/null +++ b/networking/libiproute/ll_map.c | |||
@@ -0,0 +1,186 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * ll_map.c | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or | ||
6 | * modify it under the terms of the GNU General Public License | ||
7 | * as published by the Free Software Foundation; either version | ||
8 | * 2 of the License, or (at your option) any later version. | ||
9 | * | ||
10 | * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> | ||
11 | * | ||
12 | */ | ||
13 | |||
14 | #include "libbb.h" | ||
15 | #include <string.h> | ||
16 | |||
17 | #include "libnetlink.h" | ||
18 | #include "ll_map.h" | ||
19 | |||
20 | #include <sys/socket.h> /* socket() */ | ||
21 | #include <net/if.h> /* struct ifreq and co. */ | ||
22 | #include <sys/ioctl.h> /* ioctl() & SIOCGIFINDEX */ | ||
23 | |||
24 | struct idxmap { | ||
25 | struct idxmap * next; | ||
26 | int index; | ||
27 | int type; | ||
28 | int alen; | ||
29 | unsigned flags; | ||
30 | unsigned char addr[8]; | ||
31 | char name[16]; | ||
32 | }; | ||
33 | |||
34 | static struct idxmap *idxmap[16]; | ||
35 | |||
36 | int ll_remember_index(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) | ||
37 | { | ||
38 | int h; | ||
39 | struct ifinfomsg *ifi = NLMSG_DATA(n); | ||
40 | struct idxmap *im, **imp; | ||
41 | struct rtattr *tb[IFLA_MAX+1]; | ||
42 | |||
43 | if (n->nlmsg_type != RTM_NEWLINK) | ||
44 | return 0; | ||
45 | |||
46 | if (n->nlmsg_len < NLMSG_LENGTH(sizeof(ifi))) | ||
47 | return -1; | ||
48 | |||
49 | |||
50 | memset(tb, 0, sizeof(tb)); | ||
51 | parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), IFLA_PAYLOAD(n)); | ||
52 | if (tb[IFLA_IFNAME] == NULL) | ||
53 | return 0; | ||
54 | |||
55 | h = ifi->ifi_index&0xF; | ||
56 | |||
57 | for (imp=&idxmap[h]; (im=*imp)!=NULL; imp = &im->next) | ||
58 | if (im->index == ifi->ifi_index) | ||
59 | break; | ||
60 | |||
61 | if (im == NULL) { | ||
62 | im = xmalloc(sizeof(*im)); | ||
63 | im->next = *imp; | ||
64 | im->index = ifi->ifi_index; | ||
65 | *imp = im; | ||
66 | } | ||
67 | |||
68 | im->type = ifi->ifi_type; | ||
69 | im->flags = ifi->ifi_flags; | ||
70 | if (tb[IFLA_ADDRESS]) { | ||
71 | int alen; | ||
72 | im->alen = alen = RTA_PAYLOAD(tb[IFLA_ADDRESS]); | ||
73 | if (alen > sizeof(im->addr)) | ||
74 | alen = sizeof(im->addr); | ||
75 | memcpy(im->addr, RTA_DATA(tb[IFLA_ADDRESS]), alen); | ||
76 | } else { | ||
77 | im->alen = 0; | ||
78 | memset(im->addr, 0, sizeof(im->addr)); | ||
79 | } | ||
80 | strcpy(im->name, RTA_DATA(tb[IFLA_IFNAME])); | ||
81 | return 0; | ||
82 | } | ||
83 | |||
84 | const char *ll_idx_n2a(int idx, char *buf) | ||
85 | { | ||
86 | struct idxmap *im; | ||
87 | |||
88 | if (idx == 0) | ||
89 | return "*"; | ||
90 | for (im = idxmap[idx&0xF]; im; im = im->next) | ||
91 | if (im->index == idx) | ||
92 | return im->name; | ||
93 | snprintf(buf, 16, "if%d", idx); | ||
94 | return buf; | ||
95 | } | ||
96 | |||
97 | |||
98 | const char *ll_index_to_name(int idx) | ||
99 | { | ||
100 | static char nbuf[16]; | ||
101 | |||
102 | return ll_idx_n2a(idx, nbuf); | ||
103 | } | ||
104 | |||
105 | int ll_index_to_type(int idx) | ||
106 | { | ||
107 | struct idxmap *im; | ||
108 | |||
109 | if (idx == 0) | ||
110 | return -1; | ||
111 | for (im = idxmap[idx&0xF]; im; im = im->next) | ||
112 | if (im->index == idx) | ||
113 | return im->type; | ||
114 | return -1; | ||
115 | } | ||
116 | |||
117 | unsigned ll_index_to_flags(int idx) | ||
118 | { | ||
119 | struct idxmap *im; | ||
120 | |||
121 | if (idx == 0) | ||
122 | return 0; | ||
123 | |||
124 | for (im = idxmap[idx&0xF]; im; im = im->next) | ||
125 | if (im->index == idx) | ||
126 | return im->flags; | ||
127 | return 0; | ||
128 | } | ||
129 | |||
130 | int ll_name_to_index(char *name) | ||
131 | { | ||
132 | static char ncache[16]; | ||
133 | static int icache; | ||
134 | struct idxmap *im; | ||
135 | int sock_fd; | ||
136 | int i; | ||
137 | |||
138 | if (name == NULL) | ||
139 | return 0; | ||
140 | if (icache && strcmp(name, ncache) == 0) | ||
141 | return icache; | ||
142 | for (i=0; i<16; i++) { | ||
143 | for (im = idxmap[i]; im; im = im->next) { | ||
144 | if (strcmp(im->name, name) == 0) { | ||
145 | icache = im->index; | ||
146 | strcpy(ncache, name); | ||
147 | return im->index; | ||
148 | } | ||
149 | } | ||
150 | } | ||
151 | /* We have not found the interface in our cache, but the kernel | ||
152 | * may still know about it. One reason is that we may be using | ||
153 | * module on-demand loading, which means that the kernel will | ||
154 | * load the module and make the interface exist only when | ||
155 | * we explicitely request it (check for dev_load() in net/core/dev.c). | ||
156 | * I can think of other similar scenario, but they are less common... | ||
157 | * Jean II */ | ||
158 | sock_fd = socket(AF_INET, SOCK_DGRAM, 0); | ||
159 | if (sock_fd) { | ||
160 | struct ifreq ifr; | ||
161 | int ret; | ||
162 | strncpy(ifr.ifr_name, name, IFNAMSIZ); | ||
163 | ifr.ifr_ifindex = -1; | ||
164 | ret = ioctl(sock_fd, SIOCGIFINDEX, &ifr); | ||
165 | close(sock_fd); | ||
166 | if (ret >= 0) | ||
167 | /* In theory, we should redump the interface list | ||
168 | * to update our cache, this is left as an exercise | ||
169 | * to the reader... Jean II */ | ||
170 | return ifr.ifr_ifindex; | ||
171 | } | ||
172 | |||
173 | return 0; | ||
174 | } | ||
175 | |||
176 | int ll_init_map(struct rtnl_handle *rth) | ||
177 | { | ||
178 | if (rtnl_wilddump_request(rth, AF_UNSPEC, RTM_GETLINK) < 0) { | ||
179 | bb_perror_msg_and_die("cannot send dump request"); | ||
180 | } | ||
181 | |||
182 | if (rtnl_dump_filter(rth, ll_remember_index, &idxmap, NULL, NULL) < 0) { | ||
183 | bb_error_msg_and_die("dump terminated"); | ||
184 | } | ||
185 | return 0; | ||
186 | } | ||
diff --git a/networking/libiproute/ll_map.h b/networking/libiproute/ll_map.h new file mode 100644 index 000000000..226d48fc2 --- /dev/null +++ b/networking/libiproute/ll_map.h | |||
@@ -0,0 +1,13 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | #ifndef __LL_MAP_H__ | ||
3 | #define __LL_MAP_H__ 1 | ||
4 | |||
5 | extern int ll_remember_index(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg); | ||
6 | extern int ll_init_map(struct rtnl_handle *rth); | ||
7 | extern int ll_name_to_index(char *name); | ||
8 | extern const char *ll_index_to_name(int idx); | ||
9 | extern const char *ll_idx_n2a(int idx, char *buf); | ||
10 | extern int ll_index_to_type(int idx); | ||
11 | extern unsigned ll_index_to_flags(int idx); | ||
12 | |||
13 | #endif /* __LL_MAP_H__ */ | ||
diff --git a/networking/libiproute/ll_proto.c b/networking/libiproute/ll_proto.c new file mode 100644 index 000000000..a3fe9d376 --- /dev/null +++ b/networking/libiproute/ll_proto.c | |||
@@ -0,0 +1,122 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * ll_proto.c | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or | ||
6 | * modify it under the terms of the GNU General Public License | ||
7 | * as published by the Free Software Foundation; either version | ||
8 | * 2 of the License, or (at your option) any later version. | ||
9 | * | ||
10 | * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> | ||
11 | */ | ||
12 | |||
13 | #include "libbb.h" | ||
14 | #include <string.h> | ||
15 | |||
16 | #include "rt_names.h" | ||
17 | #include "utils.h" | ||
18 | |||
19 | #if __GLIBC__ >=2 && __GLIBC_MINOR__ >= 1 | ||
20 | #include <net/ethernet.h> | ||
21 | #else | ||
22 | #include <linux/if_ether.h> | ||
23 | #endif | ||
24 | |||
25 | #define __PF(f,n) { ETH_P_##f, #n }, | ||
26 | static struct { | ||
27 | int id; | ||
28 | char *name; | ||
29 | } llproto_names[] = { | ||
30 | __PF(LOOP,loop) | ||
31 | __PF(PUP,pup) | ||
32 | #ifdef ETH_P_PUPAT | ||
33 | __PF(PUPAT,pupat) | ||
34 | #endif | ||
35 | __PF(IP,ip) | ||
36 | __PF(X25,x25) | ||
37 | __PF(ARP,arp) | ||
38 | __PF(BPQ,bpq) | ||
39 | #ifdef ETH_P_IEEEPUP | ||
40 | __PF(IEEEPUP,ieeepup) | ||
41 | #endif | ||
42 | #ifdef ETH_P_IEEEPUPAT | ||
43 | __PF(IEEEPUPAT,ieeepupat) | ||
44 | #endif | ||
45 | __PF(DEC,dec) | ||
46 | __PF(DNA_DL,dna_dl) | ||
47 | __PF(DNA_RC,dna_rc) | ||
48 | __PF(DNA_RT,dna_rt) | ||
49 | __PF(LAT,lat) | ||
50 | __PF(DIAG,diag) | ||
51 | __PF(CUST,cust) | ||
52 | __PF(SCA,sca) | ||
53 | __PF(RARP,rarp) | ||
54 | __PF(ATALK,atalk) | ||
55 | __PF(AARP,aarp) | ||
56 | __PF(IPX,ipx) | ||
57 | __PF(IPV6,ipv6) | ||
58 | #ifdef ETH_P_PPP_DISC | ||
59 | __PF(PPP_DISC,ppp_disc) | ||
60 | #endif | ||
61 | #ifdef ETH_P_PPP_SES | ||
62 | __PF(PPP_SES,ppp_ses) | ||
63 | #endif | ||
64 | #ifdef ETH_P_ATMMPOA | ||
65 | __PF(ATMMPOA,atmmpoa) | ||
66 | #endif | ||
67 | #ifdef ETH_P_ATMFATE | ||
68 | __PF(ATMFATE,atmfate) | ||
69 | #endif | ||
70 | |||
71 | __PF(802_3,802_3) | ||
72 | __PF(AX25,ax25) | ||
73 | __PF(ALL,all) | ||
74 | __PF(802_2,802_2) | ||
75 | __PF(SNAP,snap) | ||
76 | __PF(DDCMP,ddcmp) | ||
77 | __PF(WAN_PPP,wan_ppp) | ||
78 | __PF(PPP_MP,ppp_mp) | ||
79 | __PF(LOCALTALK,localtalk) | ||
80 | __PF(PPPTALK,ppptalk) | ||
81 | __PF(TR_802_2,tr_802_2) | ||
82 | __PF(MOBITEX,mobitex) | ||
83 | __PF(CONTROL,control) | ||
84 | __PF(IRDA,irda) | ||
85 | #ifdef ETH_P_ECONET | ||
86 | __PF(ECONET,econet) | ||
87 | #endif | ||
88 | |||
89 | { 0x8100, "802.1Q" }, | ||
90 | { ETH_P_IP, "ipv4" }, | ||
91 | }; | ||
92 | #undef __PF | ||
93 | |||
94 | |||
95 | const char * ll_proto_n2a(unsigned short id, char *buf, int len) | ||
96 | { | ||
97 | int i; | ||
98 | |||
99 | id = ntohs(id); | ||
100 | |||
101 | for (i=0; i<sizeof(llproto_names)/sizeof(llproto_names[0]); i++) { | ||
102 | if (llproto_names[i].id == id) | ||
103 | return llproto_names[i].name; | ||
104 | } | ||
105 | snprintf(buf, len, "[%d]", id); | ||
106 | return buf; | ||
107 | } | ||
108 | |||
109 | int ll_proto_a2n(unsigned short *id, char *buf) | ||
110 | { | ||
111 | int i; | ||
112 | for (i=0; i<sizeof(llproto_names)/sizeof(llproto_names[0]); i++) { | ||
113 | if (strcasecmp(llproto_names[i].name, buf) == 0) { | ||
114 | *id = htons(llproto_names[i].id); | ||
115 | return 0; | ||
116 | } | ||
117 | } | ||
118 | if (get_u16(id, buf, 0)) | ||
119 | return -1; | ||
120 | *id = htons(*id); | ||
121 | return 0; | ||
122 | } | ||
diff --git a/networking/libiproute/ll_types.c b/networking/libiproute/ll_types.c new file mode 100644 index 000000000..a53c89432 --- /dev/null +++ b/networking/libiproute/ll_types.c | |||
@@ -0,0 +1,118 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * ll_types.c | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or | ||
6 | * modify it under the terms of the GNU General Public License | ||
7 | * as published by the Free Software Foundation; either version | ||
8 | * 2 of the License, or (at your option) any later version. | ||
9 | * | ||
10 | * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> | ||
11 | */ | ||
12 | #include <stdio.h> | ||
13 | #include <arpa/inet.h> | ||
14 | |||
15 | #include <linux/if_arp.h> | ||
16 | |||
17 | #include "rt_names.h" | ||
18 | |||
19 | const char * ll_type_n2a(int type, char *buf, int len) | ||
20 | { | ||
21 | #define __PF(f,n) { ARPHRD_##f, #n }, | ||
22 | static struct { | ||
23 | int type; | ||
24 | char *name; | ||
25 | } arphrd_names[] = { | ||
26 | { 0, "generic" }, | ||
27 | __PF(ETHER,ether) | ||
28 | __PF(EETHER,eether) | ||
29 | __PF(AX25,ax25) | ||
30 | __PF(PRONET,pronet) | ||
31 | __PF(CHAOS,chaos) | ||
32 | #ifdef ARPHRD_IEEE802_TR | ||
33 | __PF(IEEE802,ieee802) | ||
34 | #else | ||
35 | __PF(IEEE802,tr) | ||
36 | #endif | ||
37 | __PF(ARCNET,arcnet) | ||
38 | __PF(APPLETLK,atalk) | ||
39 | __PF(DLCI,dlci) | ||
40 | #ifdef ARPHRD_ATM | ||
41 | __PF(ATM,atm) | ||
42 | #endif | ||
43 | __PF(METRICOM,metricom) | ||
44 | #ifdef ARPHRD_IEEE1394 | ||
45 | __PF(IEEE1394,ieee1394) | ||
46 | #endif | ||
47 | |||
48 | __PF(SLIP,slip) | ||
49 | __PF(CSLIP,cslip) | ||
50 | __PF(SLIP6,slip6) | ||
51 | __PF(CSLIP6,cslip6) | ||
52 | __PF(RSRVD,rsrvd) | ||
53 | __PF(ADAPT,adapt) | ||
54 | __PF(ROSE,rose) | ||
55 | __PF(X25,x25) | ||
56 | #ifdef ARPHRD_HWX25 | ||
57 | __PF(HWX25,hwx25) | ||
58 | #endif | ||
59 | __PF(PPP,ppp) | ||
60 | __PF(HDLC,hdlc) | ||
61 | __PF(LAPB,lapb) | ||
62 | #ifdef ARPHRD_DDCMP | ||
63 | __PF(DDCMP,ddcmp) | ||
64 | __PF(RAWHDLC,rawhdlc) | ||
65 | #endif | ||
66 | |||
67 | __PF(TUNNEL,ipip) | ||
68 | __PF(TUNNEL6,tunnel6) | ||
69 | __PF(FRAD,frad) | ||
70 | __PF(SKIP,skip) | ||
71 | __PF(LOOPBACK,loopback) | ||
72 | __PF(LOCALTLK,ltalk) | ||
73 | __PF(FDDI,fddi) | ||
74 | __PF(BIF,bif) | ||
75 | __PF(SIT,sit) | ||
76 | __PF(IPDDP,ip/ddp) | ||
77 | __PF(IPGRE,gre) | ||
78 | __PF(PIMREG,pimreg) | ||
79 | __PF(HIPPI,hippi) | ||
80 | __PF(ASH,ash) | ||
81 | __PF(ECONET,econet) | ||
82 | __PF(IRDA,irda) | ||
83 | __PF(FCPP,fcpp) | ||
84 | __PF(FCAL,fcal) | ||
85 | __PF(FCPL,fcpl) | ||
86 | __PF(FCFABRIC,fcfb0) | ||
87 | __PF(FCFABRIC+1,fcfb1) | ||
88 | __PF(FCFABRIC+2,fcfb2) | ||
89 | __PF(FCFABRIC+3,fcfb3) | ||
90 | __PF(FCFABRIC+4,fcfb4) | ||
91 | __PF(FCFABRIC+5,fcfb5) | ||
92 | __PF(FCFABRIC+6,fcfb6) | ||
93 | __PF(FCFABRIC+7,fcfb7) | ||
94 | __PF(FCFABRIC+8,fcfb8) | ||
95 | __PF(FCFABRIC+9,fcfb9) | ||
96 | __PF(FCFABRIC+10,fcfb10) | ||
97 | __PF(FCFABRIC+11,fcfb11) | ||
98 | __PF(FCFABRIC+12,fcfb12) | ||
99 | #ifdef ARPHRD_IEEE802_TR | ||
100 | __PF(IEEE802_TR,tr) | ||
101 | #endif | ||
102 | #ifdef ARPHRD_IEEE80211 | ||
103 | __PF(IEEE80211,ieee802.11) | ||
104 | #endif | ||
105 | #ifdef ARPHRD_VOID | ||
106 | __PF(VOID,void) | ||
107 | #endif | ||
108 | }; | ||
109 | #undef __PF | ||
110 | |||
111 | int i; | ||
112 | for (i=0; i<sizeof(arphrd_names)/sizeof(arphrd_names[0]); i++) { | ||
113 | if (arphrd_names[i].type == type) | ||
114 | return arphrd_names[i].name; | ||
115 | } | ||
116 | snprintf(buf, len, "[%d]", type); | ||
117 | return buf; | ||
118 | } | ||
diff --git a/networking/libiproute/rt_names.c b/networking/libiproute/rt_names.c new file mode 100644 index 000000000..ed21fbe26 --- /dev/null +++ b/networking/libiproute/rt_names.c | |||
@@ -0,0 +1,384 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * rt_names.c rtnetlink names DB. | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or | ||
6 | * modify it under the terms of the GNU General Public License | ||
7 | * as published by the Free Software Foundation; either version | ||
8 | * 2 of the License, or (at your option) any later version. | ||
9 | * | ||
10 | * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> | ||
11 | */ | ||
12 | #include <stdio.h> | ||
13 | #include <stdlib.h> | ||
14 | #include <string.h> | ||
15 | |||
16 | #include <stdint.h> | ||
17 | #include "rt_names.h" | ||
18 | |||
19 | static void rtnl_tab_initialize(char *file, const char **tab, int size) | ||
20 | { | ||
21 | char buf[512]; | ||
22 | FILE *fp; | ||
23 | |||
24 | fp = fopen(file, "r"); | ||
25 | if (!fp) | ||
26 | return; | ||
27 | while (fgets(buf, sizeof(buf), fp)) { | ||
28 | char *p = buf; | ||
29 | int id; | ||
30 | char namebuf[512]; | ||
31 | |||
32 | while (*p == ' ' || *p == '\t') | ||
33 | p++; | ||
34 | if (*p == '#' || *p == '\n' || *p == 0) | ||
35 | continue; | ||
36 | if (sscanf(p, "0x%x %s\n", &id, namebuf) != 2 && | ||
37 | sscanf(p, "0x%x %s #", &id, namebuf) != 2 && | ||
38 | sscanf(p, "%d %s\n", &id, namebuf) != 2 && | ||
39 | sscanf(p, "%d %s #", &id, namebuf) != 2) { | ||
40 | fprintf(stderr, "Database %s is corrupted at %s\n", | ||
41 | file, p); | ||
42 | return; | ||
43 | } | ||
44 | |||
45 | if (id<0 || id>size) | ||
46 | continue; | ||
47 | |||
48 | tab[id] = strdup(namebuf); | ||
49 | } | ||
50 | fclose(fp); | ||
51 | } | ||
52 | |||
53 | |||
54 | static const char * rtnl_rtprot_tab[256] = { | ||
55 | "none", | ||
56 | "redirect", | ||
57 | "kernel", | ||
58 | "boot", | ||
59 | "static", | ||
60 | NULL, | ||
61 | NULL, | ||
62 | NULL, | ||
63 | "gated", | ||
64 | "ra", | ||
65 | "mrt", | ||
66 | "zebra", | ||
67 | "bird", | ||
68 | }; | ||
69 | |||
70 | |||
71 | |||
72 | static int rtnl_rtprot_init; | ||
73 | |||
74 | static void rtnl_rtprot_initialize(void) | ||
75 | { | ||
76 | rtnl_rtprot_init = 1; | ||
77 | rtnl_tab_initialize("/etc/iproute2/rt_protos", | ||
78 | rtnl_rtprot_tab, 256); | ||
79 | } | ||
80 | |||
81 | const char * rtnl_rtprot_n2a(int id, char *buf, int len) | ||
82 | { | ||
83 | if (id<0 || id>=256) { | ||
84 | snprintf(buf, len, "%d", id); | ||
85 | return buf; | ||
86 | } | ||
87 | if (!rtnl_rtprot_tab[id]) { | ||
88 | if (!rtnl_rtprot_init) | ||
89 | rtnl_rtprot_initialize(); | ||
90 | } | ||
91 | if (rtnl_rtprot_tab[id]) | ||
92 | return rtnl_rtprot_tab[id]; | ||
93 | snprintf(buf, len, "%d", id); | ||
94 | return buf; | ||
95 | } | ||
96 | |||
97 | int rtnl_rtprot_a2n(uint32_t *id, char *arg) | ||
98 | { | ||
99 | static const char *cache = NULL; | ||
100 | static unsigned long res; | ||
101 | char *end; | ||
102 | int i; | ||
103 | |||
104 | if (cache && strcmp(cache, arg) == 0) { | ||
105 | *id = res; | ||
106 | return 0; | ||
107 | } | ||
108 | |||
109 | if (!rtnl_rtprot_init) | ||
110 | rtnl_rtprot_initialize(); | ||
111 | |||
112 | for (i=0; i<256; i++) { | ||
113 | if (rtnl_rtprot_tab[i] && | ||
114 | strcmp(rtnl_rtprot_tab[i], arg) == 0) { | ||
115 | cache = rtnl_rtprot_tab[i]; | ||
116 | res = i; | ||
117 | *id = res; | ||
118 | return 0; | ||
119 | } | ||
120 | } | ||
121 | |||
122 | res = strtoul(arg, &end, 0); | ||
123 | if (!end || end == arg || *end || res > 255) | ||
124 | return -1; | ||
125 | *id = res; | ||
126 | return 0; | ||
127 | } | ||
128 | |||
129 | |||
130 | |||
131 | static const char * rtnl_rtscope_tab[256] = { | ||
132 | "global", | ||
133 | }; | ||
134 | |||
135 | static int rtnl_rtscope_init; | ||
136 | |||
137 | static void rtnl_rtscope_initialize(void) | ||
138 | { | ||
139 | rtnl_rtscope_init = 1; | ||
140 | rtnl_rtscope_tab[255] = "nowhere"; | ||
141 | rtnl_rtscope_tab[254] = "host"; | ||
142 | rtnl_rtscope_tab[253] = "link"; | ||
143 | rtnl_rtscope_tab[200] = "site"; | ||
144 | rtnl_tab_initialize("/etc/iproute2/rt_scopes", | ||
145 | rtnl_rtscope_tab, 256); | ||
146 | } | ||
147 | |||
148 | const char * rtnl_rtscope_n2a(int id, char *buf, int len) | ||
149 | { | ||
150 | if (id<0 || id>=256) { | ||
151 | snprintf(buf, len, "%d", id); | ||
152 | return buf; | ||
153 | } | ||
154 | if (!rtnl_rtscope_tab[id]) { | ||
155 | if (!rtnl_rtscope_init) | ||
156 | rtnl_rtscope_initialize(); | ||
157 | } | ||
158 | if (rtnl_rtscope_tab[id]) | ||
159 | return rtnl_rtscope_tab[id]; | ||
160 | snprintf(buf, len, "%d", id); | ||
161 | return buf; | ||
162 | } | ||
163 | |||
164 | int rtnl_rtscope_a2n(uint32_t *id, char *arg) | ||
165 | { | ||
166 | static const char *cache = NULL; | ||
167 | static unsigned long res; | ||
168 | char *end; | ||
169 | int i; | ||
170 | |||
171 | if (cache && strcmp(cache, arg) == 0) { | ||
172 | *id = res; | ||
173 | return 0; | ||
174 | } | ||
175 | |||
176 | if (!rtnl_rtscope_init) | ||
177 | rtnl_rtscope_initialize(); | ||
178 | |||
179 | for (i=0; i<256; i++) { | ||
180 | if (rtnl_rtscope_tab[i] && | ||
181 | strcmp(rtnl_rtscope_tab[i], arg) == 0) { | ||
182 | cache = rtnl_rtscope_tab[i]; | ||
183 | res = i; | ||
184 | *id = res; | ||
185 | return 0; | ||
186 | } | ||
187 | } | ||
188 | |||
189 | res = strtoul(arg, &end, 0); | ||
190 | if (!end || end == arg || *end || res > 255) | ||
191 | return -1; | ||
192 | *id = res; | ||
193 | return 0; | ||
194 | } | ||
195 | |||
196 | |||
197 | |||
198 | static const char * rtnl_rtrealm_tab[256] = { | ||
199 | "unknown", | ||
200 | }; | ||
201 | |||
202 | static int rtnl_rtrealm_init; | ||
203 | |||
204 | static void rtnl_rtrealm_initialize(void) | ||
205 | { | ||
206 | rtnl_rtrealm_init = 1; | ||
207 | rtnl_tab_initialize("/etc/iproute2/rt_realms", | ||
208 | rtnl_rtrealm_tab, 256); | ||
209 | } | ||
210 | |||
211 | int rtnl_rtrealm_a2n(uint32_t *id, char *arg) | ||
212 | { | ||
213 | static const char *cache = NULL; | ||
214 | static unsigned long res; | ||
215 | char *end; | ||
216 | int i; | ||
217 | |||
218 | if (cache && strcmp(cache, arg) == 0) { | ||
219 | *id = res; | ||
220 | return 0; | ||
221 | } | ||
222 | |||
223 | if (!rtnl_rtrealm_init) | ||
224 | rtnl_rtrealm_initialize(); | ||
225 | |||
226 | for (i=0; i<256; i++) { | ||
227 | if (rtnl_rtrealm_tab[i] && | ||
228 | strcmp(rtnl_rtrealm_tab[i], arg) == 0) { | ||
229 | cache = rtnl_rtrealm_tab[i]; | ||
230 | res = i; | ||
231 | *id = res; | ||
232 | return 0; | ||
233 | } | ||
234 | } | ||
235 | |||
236 | res = strtoul(arg, &end, 0); | ||
237 | if (!end || end == arg || *end || res > 255) | ||
238 | return -1; | ||
239 | *id = res; | ||
240 | return 0; | ||
241 | } | ||
242 | |||
243 | #if ENABLE_FEATURE_IP_RULE | ||
244 | const char * rtnl_rtrealm_n2a(int id, char *buf, int len) | ||
245 | { | ||
246 | if (id<0 || id>=256) { | ||
247 | snprintf(buf, len, "%d", id); | ||
248 | return buf; | ||
249 | } | ||
250 | if (!rtnl_rtrealm_tab[id]) { | ||
251 | if (!rtnl_rtrealm_init) | ||
252 | rtnl_rtrealm_initialize(); | ||
253 | } | ||
254 | if (rtnl_rtrealm_tab[id]) | ||
255 | return rtnl_rtrealm_tab[id]; | ||
256 | snprintf(buf, len, "%d", id); | ||
257 | return buf; | ||
258 | } | ||
259 | #endif | ||
260 | |||
261 | static const char * rtnl_rtdsfield_tab[256] = { | ||
262 | "0", | ||
263 | }; | ||
264 | |||
265 | static int rtnl_rtdsfield_init; | ||
266 | |||
267 | static void rtnl_rtdsfield_initialize(void) | ||
268 | { | ||
269 | rtnl_rtdsfield_init = 1; | ||
270 | rtnl_tab_initialize("/etc/iproute2/rt_dsfield", | ||
271 | rtnl_rtdsfield_tab, 256); | ||
272 | } | ||
273 | |||
274 | const char * rtnl_dsfield_n2a(int id, char *buf, int len) | ||
275 | { | ||
276 | if (id<0 || id>=256) { | ||
277 | snprintf(buf, len, "%d", id); | ||
278 | return buf; | ||
279 | } | ||
280 | if (!rtnl_rtdsfield_tab[id]) { | ||
281 | if (!rtnl_rtdsfield_init) | ||
282 | rtnl_rtdsfield_initialize(); | ||
283 | } | ||
284 | if (rtnl_rtdsfield_tab[id]) | ||
285 | return rtnl_rtdsfield_tab[id]; | ||
286 | snprintf(buf, len, "0x%02x", id); | ||
287 | return buf; | ||
288 | } | ||
289 | |||
290 | |||
291 | int rtnl_dsfield_a2n(uint32_t *id, char *arg) | ||
292 | { | ||
293 | static const char *cache = NULL; | ||
294 | static unsigned long res; | ||
295 | char *end; | ||
296 | int i; | ||
297 | |||
298 | if (cache && strcmp(cache, arg) == 0) { | ||
299 | *id = res; | ||
300 | return 0; | ||
301 | } | ||
302 | |||
303 | if (!rtnl_rtdsfield_init) | ||
304 | rtnl_rtdsfield_initialize(); | ||
305 | |||
306 | for (i=0; i<256; i++) { | ||
307 | if (rtnl_rtdsfield_tab[i] && | ||
308 | strcmp(rtnl_rtdsfield_tab[i], arg) == 0) { | ||
309 | cache = rtnl_rtdsfield_tab[i]; | ||
310 | res = i; | ||
311 | *id = res; | ||
312 | return 0; | ||
313 | } | ||
314 | } | ||
315 | |||
316 | res = strtoul(arg, &end, 16); | ||
317 | if (!end || end == arg || *end || res > 255) | ||
318 | return -1; | ||
319 | *id = res; | ||
320 | return 0; | ||
321 | } | ||
322 | |||
323 | #if ENABLE_FEATURE_IP_RULE | ||
324 | static int rtnl_rttable_init; | ||
325 | static const char * rtnl_rttable_tab[256] = { | ||
326 | "unspec", | ||
327 | }; | ||
328 | static void rtnl_rttable_initialize(void) | ||
329 | { | ||
330 | rtnl_rttable_init = 1; | ||
331 | rtnl_rttable_tab[255] = "local"; | ||
332 | rtnl_rttable_tab[254] = "main"; | ||
333 | rtnl_rttable_tab[253] = "default"; | ||
334 | rtnl_tab_initialize("/etc/iproute2/rt_tables", rtnl_rttable_tab, 256); | ||
335 | } | ||
336 | |||
337 | const char *rtnl_rttable_n2a(int id, char *buf, int len) | ||
338 | { | ||
339 | if (id < 0 || id >= 256) { | ||
340 | snprintf(buf, len, "%d", id); | ||
341 | return buf; | ||
342 | } | ||
343 | if (!rtnl_rttable_tab[id]) { | ||
344 | if (!rtnl_rttable_init) | ||
345 | rtnl_rttable_initialize(); | ||
346 | } | ||
347 | if (rtnl_rttable_tab[id]) | ||
348 | return rtnl_rttable_tab[id]; | ||
349 | snprintf(buf, len, "%d", id); | ||
350 | return buf; | ||
351 | } | ||
352 | |||
353 | int rtnl_rttable_a2n(uint32_t * id, char *arg) | ||
354 | { | ||
355 | static char *cache = NULL; | ||
356 | static unsigned long res; | ||
357 | char *end; | ||
358 | int i; | ||
359 | |||
360 | if (cache && strcmp(cache, arg) == 0) { | ||
361 | *id = res; | ||
362 | return 0; | ||
363 | } | ||
364 | |||
365 | if (!rtnl_rttable_init) | ||
366 | rtnl_rttable_initialize(); | ||
367 | |||
368 | for (i = 0; i < 256; i++) { | ||
369 | if (rtnl_rttable_tab[i] && strcmp(rtnl_rttable_tab[i], arg) == 0) { | ||
370 | cache = (char*)rtnl_rttable_tab[i]; | ||
371 | res = i; | ||
372 | *id = res; | ||
373 | return 0; | ||
374 | } | ||
375 | } | ||
376 | |||
377 | i = strtoul(arg, &end, 0); | ||
378 | if (!end || end == arg || *end || i > 255) | ||
379 | return -1; | ||
380 | *id = i; | ||
381 | return 0; | ||
382 | } | ||
383 | |||
384 | #endif | ||
diff --git a/networking/libiproute/rt_names.h b/networking/libiproute/rt_names.h new file mode 100644 index 000000000..92b807f3d --- /dev/null +++ b/networking/libiproute/rt_names.h | |||
@@ -0,0 +1,29 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | #ifndef RT_NAMES_H_ | ||
3 | #define RT_NAMES_H_ 1 | ||
4 | |||
5 | #include <stdint.h> | ||
6 | |||
7 | extern const char* rtnl_rtprot_n2a(int id, char *buf, int len); | ||
8 | extern const char* rtnl_rtscope_n2a(int id, char *buf, int len); | ||
9 | extern const char* rtnl_rtrealm_n2a(int id, char *buf, int len); | ||
10 | extern const char* rtnl_dsfield_n2a(int id, char *buf, int len); | ||
11 | extern const char* rtnl_rttable_n2a(int id, char *buf, int len); | ||
12 | extern int rtnl_rtprot_a2n(uint32_t *id, char *arg); | ||
13 | extern int rtnl_rtscope_a2n(uint32_t *id, char *arg); | ||
14 | extern int rtnl_rtrealm_a2n(uint32_t *id, char *arg); | ||
15 | extern int rtnl_dsfield_a2n(uint32_t *id, char *arg); | ||
16 | extern int rtnl_rttable_a2n(uint32_t *id, char *arg); | ||
17 | |||
18 | |||
19 | extern const char * ll_type_n2a(int type, char *buf, int len); | ||
20 | |||
21 | extern const char *ll_addr_n2a(unsigned char *addr, int alen, int type, | ||
22 | char *buf, int blen); | ||
23 | extern int ll_addr_a2n(unsigned char *lladdr, int len, char *arg); | ||
24 | |||
25 | extern const char * ll_proto_n2a(unsigned short id, char *buf, int len); | ||
26 | extern int ll_proto_a2n(unsigned short *id, char *buf); | ||
27 | |||
28 | |||
29 | #endif | ||
diff --git a/networking/libiproute/rtm_map.c b/networking/libiproute/rtm_map.c new file mode 100644 index 000000000..8aaee4bd7 --- /dev/null +++ b/networking/libiproute/rtm_map.c | |||
@@ -0,0 +1,111 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * rtm_map.c | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or | ||
6 | * modify it under the terms of the GNU General Public License | ||
7 | * as published by the Free Software Foundation; either version | ||
8 | * 2 of the License, or (at your option) any later version. | ||
9 | * | ||
10 | * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> | ||
11 | * | ||
12 | */ | ||
13 | |||
14 | #include <stdlib.h> | ||
15 | #include <string.h> | ||
16 | |||
17 | #include "rt_names.h" | ||
18 | #include "utils.h" | ||
19 | |||
20 | char *rtnl_rtntype_n2a(int id, char *buf, int len) | ||
21 | { | ||
22 | switch (id) { | ||
23 | case RTN_UNSPEC: | ||
24 | return "none"; | ||
25 | case RTN_UNICAST: | ||
26 | return "unicast"; | ||
27 | case RTN_LOCAL: | ||
28 | return "local"; | ||
29 | case RTN_BROADCAST: | ||
30 | return "broadcast"; | ||
31 | case RTN_ANYCAST: | ||
32 | return "anycast"; | ||
33 | case RTN_MULTICAST: | ||
34 | return "multicast"; | ||
35 | case RTN_BLACKHOLE: | ||
36 | return "blackhole"; | ||
37 | case RTN_UNREACHABLE: | ||
38 | return "unreachable"; | ||
39 | case RTN_PROHIBIT: | ||
40 | return "prohibit"; | ||
41 | case RTN_THROW: | ||
42 | return "throw"; | ||
43 | case RTN_NAT: | ||
44 | return "nat"; | ||
45 | case RTN_XRESOLVE: | ||
46 | return "xresolve"; | ||
47 | default: | ||
48 | snprintf(buf, len, "%d", id); | ||
49 | return buf; | ||
50 | } | ||
51 | } | ||
52 | |||
53 | |||
54 | int rtnl_rtntype_a2n(int *id, char *arg) | ||
55 | { | ||
56 | char *end; | ||
57 | unsigned long res; | ||
58 | |||
59 | if (strcmp(arg, "local") == 0) | ||
60 | res = RTN_LOCAL; | ||
61 | else if (strcmp(arg, "nat") == 0) | ||
62 | res = RTN_NAT; | ||
63 | else if (matches(arg, "broadcast") == 0 || | ||
64 | strcmp(arg, "brd") == 0) | ||
65 | res = RTN_BROADCAST; | ||
66 | else if (matches(arg, "anycast") == 0) | ||
67 | res = RTN_ANYCAST; | ||
68 | else if (matches(arg, "multicast") == 0) | ||
69 | res = RTN_MULTICAST; | ||
70 | else if (matches(arg, "prohibit") == 0) | ||
71 | res = RTN_PROHIBIT; | ||
72 | else if (matches(arg, "unreachable") == 0) | ||
73 | res = RTN_UNREACHABLE; | ||
74 | else if (matches(arg, "blackhole") == 0) | ||
75 | res = RTN_BLACKHOLE; | ||
76 | else if (matches(arg, "xresolve") == 0) | ||
77 | res = RTN_XRESOLVE; | ||
78 | else if (matches(arg, "unicast") == 0) | ||
79 | res = RTN_UNICAST; | ||
80 | else if (strcmp(arg, "throw") == 0) | ||
81 | res = RTN_THROW; | ||
82 | else { | ||
83 | res = strtoul(arg, &end, 0); | ||
84 | if (!end || end == arg || *end || res > 255) | ||
85 | return -1; | ||
86 | } | ||
87 | *id = res; | ||
88 | return 0; | ||
89 | } | ||
90 | |||
91 | int get_rt_realms(__u32 *realms, char *arg) | ||
92 | { | ||
93 | __u32 realm = 0; | ||
94 | char *p = strchr(arg, '/'); | ||
95 | |||
96 | *realms = 0; | ||
97 | if (p) { | ||
98 | *p = 0; | ||
99 | if (rtnl_rtrealm_a2n(realms, arg)) { | ||
100 | *p = '/'; | ||
101 | return -1; | ||
102 | } | ||
103 | *realms <<= 16; | ||
104 | *p = '/'; | ||
105 | arg = p+1; | ||
106 | } | ||
107 | if (*arg && rtnl_rtrealm_a2n(&realm, arg)) | ||
108 | return -1; | ||
109 | *realms |= realm; | ||
110 | return 0; | ||
111 | } | ||
diff --git a/networking/libiproute/rtm_map.h b/networking/libiproute/rtm_map.h new file mode 100644 index 000000000..1ffb940b9 --- /dev/null +++ b/networking/libiproute/rtm_map.h | |||
@@ -0,0 +1,11 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | #ifndef __RTM_MAP_H__ | ||
3 | #define __RTM_MAP_H__ 1 | ||
4 | |||
5 | char *rtnl_rtntype_n2a(int id, char *buf, int len); | ||
6 | int rtnl_rtntype_a2n(int *id, char *arg); | ||
7 | |||
8 | int get_rt_realms(__u32 *realms, char *arg); | ||
9 | |||
10 | |||
11 | #endif /* __RTM_MAP_H__ */ | ||
diff --git a/networking/libiproute/utils.c b/networking/libiproute/utils.c new file mode 100644 index 000000000..fc6aff1d1 --- /dev/null +++ b/networking/libiproute/utils.c | |||
@@ -0,0 +1,354 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * utils.c | ||
4 | * | ||
5 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | ||
6 | * | ||
7 | * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> | ||
8 | * | ||
9 | * Changes: | ||
10 | * | ||
11 | * Rani Assaf <rani@magic.metawire.com> 980929: resolve addresses | ||
12 | */ | ||
13 | |||
14 | #include "libbb.h" | ||
15 | |||
16 | #include <string.h> | ||
17 | #include <unistd.h> | ||
18 | |||
19 | #include "utils.h" | ||
20 | #include "inet_common.h" | ||
21 | |||
22 | int get_integer(int *val, char *arg, int base) | ||
23 | { | ||
24 | long res; | ||
25 | char *ptr; | ||
26 | |||
27 | if (!arg || !*arg) | ||
28 | return -1; | ||
29 | res = strtol(arg, &ptr, base); | ||
30 | if (!ptr || ptr == arg || *ptr || res > INT_MAX || res < INT_MIN) | ||
31 | return -1; | ||
32 | *val = res; | ||
33 | return 0; | ||
34 | } | ||
35 | |||
36 | int get_unsigned(unsigned *val, char *arg, int base) | ||
37 | { | ||
38 | unsigned long res; | ||
39 | char *ptr; | ||
40 | |||
41 | if (!arg || !*arg) | ||
42 | return -1; | ||
43 | res = strtoul(arg, &ptr, base); | ||
44 | if (!ptr || ptr == arg || *ptr || res > UINT_MAX) | ||
45 | return -1; | ||
46 | *val = res; | ||
47 | return 0; | ||
48 | } | ||
49 | |||
50 | int get_u32(__u32 * val, char *arg, int base) | ||
51 | { | ||
52 | unsigned long res; | ||
53 | char *ptr; | ||
54 | |||
55 | if (!arg || !*arg) | ||
56 | return -1; | ||
57 | res = strtoul(arg, &ptr, base); | ||
58 | if (!ptr || ptr == arg || *ptr || res > 0xFFFFFFFFUL) | ||
59 | return -1; | ||
60 | *val = res; | ||
61 | return 0; | ||
62 | } | ||
63 | |||
64 | int get_u16(__u16 * val, char *arg, int base) | ||
65 | { | ||
66 | unsigned long res; | ||
67 | char *ptr; | ||
68 | |||
69 | if (!arg || !*arg) | ||
70 | return -1; | ||
71 | res = strtoul(arg, &ptr, base); | ||
72 | if (!ptr || ptr == arg || *ptr || res > 0xFFFF) | ||
73 | return -1; | ||
74 | *val = res; | ||
75 | return 0; | ||
76 | } | ||
77 | |||
78 | int get_u8(__u8 * val, char *arg, int base) | ||
79 | { | ||
80 | unsigned long res; | ||
81 | char *ptr; | ||
82 | |||
83 | if (!arg || !*arg) | ||
84 | return -1; | ||
85 | res = strtoul(arg, &ptr, base); | ||
86 | if (!ptr || ptr == arg || *ptr || res > 0xFF) | ||
87 | return -1; | ||
88 | *val = res; | ||
89 | return 0; | ||
90 | } | ||
91 | |||
92 | int get_s16(__s16 * val, char *arg, int base) | ||
93 | { | ||
94 | long res; | ||
95 | char *ptr; | ||
96 | |||
97 | if (!arg || !*arg) | ||
98 | return -1; | ||
99 | res = strtol(arg, &ptr, base); | ||
100 | if (!ptr || ptr == arg || *ptr || res > 0x7FFF || res < -0x8000) | ||
101 | return -1; | ||
102 | *val = res; | ||
103 | return 0; | ||
104 | } | ||
105 | |||
106 | int get_s8(__s8 * val, char *arg, int base) | ||
107 | { | ||
108 | long res; | ||
109 | char *ptr; | ||
110 | |||
111 | if (!arg || !*arg) | ||
112 | return -1; | ||
113 | res = strtol(arg, &ptr, base); | ||
114 | if (!ptr || ptr == arg || *ptr || res > 0x7F || res < -0x80) | ||
115 | return -1; | ||
116 | *val = res; | ||
117 | return 0; | ||
118 | } | ||
119 | |||
120 | int get_addr_1(inet_prefix * addr, char *name, int family) | ||
121 | { | ||
122 | char *cp; | ||
123 | unsigned char *ap = (unsigned char *) addr->data; | ||
124 | int i; | ||
125 | |||
126 | memset(addr, 0, sizeof(*addr)); | ||
127 | |||
128 | if (strcmp(name, bb_str_default) == 0 || | ||
129 | strcmp(name, "all") == 0 || strcmp(name, "any") == 0) { | ||
130 | addr->family = family; | ||
131 | addr->bytelen = (family == AF_INET6 ? 16 : 4); | ||
132 | addr->bitlen = -1; | ||
133 | return 0; | ||
134 | } | ||
135 | |||
136 | if (strchr(name, ':')) { | ||
137 | addr->family = AF_INET6; | ||
138 | if (family != AF_UNSPEC && family != AF_INET6) | ||
139 | return -1; | ||
140 | if (inet_pton(AF_INET6, name, addr->data) <= 0) | ||
141 | return -1; | ||
142 | addr->bytelen = 16; | ||
143 | addr->bitlen = -1; | ||
144 | return 0; | ||
145 | } | ||
146 | |||
147 | addr->family = AF_INET; | ||
148 | if (family != AF_UNSPEC && family != AF_INET) | ||
149 | return -1; | ||
150 | addr->bytelen = 4; | ||
151 | addr->bitlen = -1; | ||
152 | for (cp = name, i = 0; *cp; cp++) { | ||
153 | if (*cp <= '9' && *cp >= '0') { | ||
154 | ap[i] = 10 * ap[i] + (*cp - '0'); | ||
155 | continue; | ||
156 | } | ||
157 | if (*cp == '.' && ++i <= 3) | ||
158 | continue; | ||
159 | return -1; | ||
160 | } | ||
161 | return 0; | ||
162 | } | ||
163 | |||
164 | int get_prefix_1(inet_prefix * dst, char *arg, int family) | ||
165 | { | ||
166 | int err; | ||
167 | int plen; | ||
168 | char *slash; | ||
169 | |||
170 | memset(dst, 0, sizeof(*dst)); | ||
171 | |||
172 | if (strcmp(arg, bb_str_default) == 0 || strcmp(arg, "any") == 0) { | ||
173 | dst->family = family; | ||
174 | dst->bytelen = 0; | ||
175 | dst->bitlen = 0; | ||
176 | return 0; | ||
177 | } | ||
178 | |||
179 | slash = strchr(arg, '/'); | ||
180 | if (slash) | ||
181 | *slash = 0; | ||
182 | err = get_addr_1(dst, arg, family); | ||
183 | if (err == 0) { | ||
184 | switch (dst->family) { | ||
185 | case AF_INET6: | ||
186 | dst->bitlen = 128; | ||
187 | break; | ||
188 | default: | ||
189 | case AF_INET: | ||
190 | dst->bitlen = 32; | ||
191 | } | ||
192 | if (slash) { | ||
193 | if (get_integer(&plen, slash + 1, 0) || plen > dst->bitlen) { | ||
194 | err = -1; | ||
195 | goto done; | ||
196 | } | ||
197 | dst->bitlen = plen; | ||
198 | } | ||
199 | } | ||
200 | done: | ||
201 | if (slash) | ||
202 | *slash = '/'; | ||
203 | return err; | ||
204 | } | ||
205 | |||
206 | int get_addr(inet_prefix * dst, char *arg, int family) | ||
207 | { | ||
208 | if (family == AF_PACKET) { | ||
209 | bb_error_msg_and_die("\"%s\" may be inet address, but it is not allowed in this context", arg); | ||
210 | } | ||
211 | if (get_addr_1(dst, arg, family)) { | ||
212 | bb_error_msg_and_die("an inet address is expected rather than \"%s\"", arg); | ||
213 | } | ||
214 | return 0; | ||
215 | } | ||
216 | |||
217 | int get_prefix(inet_prefix * dst, char *arg, int family) | ||
218 | { | ||
219 | if (family == AF_PACKET) { | ||
220 | bb_error_msg_and_die("\"%s\" may be inet address, but it is not allowed in this context", arg); | ||
221 | } | ||
222 | if (get_prefix_1(dst, arg, family)) { | ||
223 | bb_error_msg_and_die("an inet address is expected rather than \"%s\"", arg); | ||
224 | } | ||
225 | return 0; | ||
226 | } | ||
227 | |||
228 | __u32 get_addr32(char *name) | ||
229 | { | ||
230 | inet_prefix addr; | ||
231 | |||
232 | if (get_addr_1(&addr, name, AF_INET)) { | ||
233 | bb_error_msg_and_die("an IP address is expected rather than \"%s\"", name); | ||
234 | } | ||
235 | return addr.data[0]; | ||
236 | } | ||
237 | |||
238 | void incomplete_command(void) | ||
239 | { | ||
240 | bb_error_msg("command line is not complete, try option \"help\""); | ||
241 | exit(-1); | ||
242 | } | ||
243 | |||
244 | void invarg(const char * const arg, const char * const opt) | ||
245 | { | ||
246 | bb_error_msg(bb_msg_invalid_arg, arg, opt); | ||
247 | exit(-1); | ||
248 | } | ||
249 | |||
250 | void duparg(char *key, char *arg) | ||
251 | { | ||
252 | bb_error_msg("duplicate \"%s\": \"%s\" is the second value", key, arg); | ||
253 | exit(-1); | ||
254 | } | ||
255 | |||
256 | void duparg2(char *key, char *arg) | ||
257 | { | ||
258 | bb_error_msg("either \"%s\" is duplicate, or \"%s\" is garbage", key, arg); | ||
259 | exit(-1); | ||
260 | } | ||
261 | |||
262 | int matches(char *cmd, char *pattern) | ||
263 | { | ||
264 | int len = strlen(cmd); | ||
265 | |||
266 | return strncmp(pattern, cmd, len); | ||
267 | } | ||
268 | |||
269 | int inet_addr_match(inet_prefix * a, inet_prefix * b, int bits) | ||
270 | { | ||
271 | __u32 *a1 = a->data; | ||
272 | __u32 *a2 = b->data; | ||
273 | int words = bits >> 0x05; | ||
274 | |||
275 | bits &= 0x1f; | ||
276 | |||
277 | if (words) | ||
278 | if (memcmp(a1, a2, words << 2)) | ||
279 | return -1; | ||
280 | |||
281 | if (bits) { | ||
282 | __u32 w1, w2; | ||
283 | __u32 mask; | ||
284 | |||
285 | w1 = a1[words]; | ||
286 | w2 = a2[words]; | ||
287 | |||
288 | mask = htonl((0xffffffff) << (0x20 - bits)); | ||
289 | |||
290 | if ((w1 ^ w2) & mask) | ||
291 | return 1; | ||
292 | } | ||
293 | |||
294 | return 0; | ||
295 | } | ||
296 | |||
297 | int __iproute2_hz_internal; | ||
298 | |||
299 | int __get_hz(void) | ||
300 | { | ||
301 | int hz = 0; | ||
302 | FILE *fp = fopen("/proc/net/psched", "r"); | ||
303 | |||
304 | if (fp) { | ||
305 | unsigned nom, denom; | ||
306 | |||
307 | if (fscanf(fp, "%*08x%*08x%08x%08x", &nom, &denom) == 2) | ||
308 | if (nom == 1000000) | ||
309 | hz = denom; | ||
310 | fclose(fp); | ||
311 | } | ||
312 | if (hz) | ||
313 | return hz; | ||
314 | return sysconf(_SC_CLK_TCK); | ||
315 | } | ||
316 | |||
317 | const char *rt_addr_n2a(int af, int ATTRIBUTE_UNUSED len, | ||
318 | void *addr, char *buf, int buflen) | ||
319 | { | ||
320 | switch (af) { | ||
321 | case AF_INET: | ||
322 | case AF_INET6: | ||
323 | return inet_ntop(af, addr, buf, buflen); | ||
324 | default: | ||
325 | return "???"; | ||
326 | } | ||
327 | } | ||
328 | |||
329 | |||
330 | const char *format_host(int af, int len, void *addr, char *buf, int buflen) | ||
331 | { | ||
332 | #ifdef RESOLVE_HOSTNAMES | ||
333 | if (resolve_hosts) { | ||
334 | struct hostent *h_ent; | ||
335 | |||
336 | if (len <= 0) { | ||
337 | switch (af) { | ||
338 | case AF_INET: | ||
339 | len = 4; | ||
340 | break; | ||
341 | case AF_INET6: | ||
342 | len = 16; | ||
343 | break; | ||
344 | default:; | ||
345 | } | ||
346 | } | ||
347 | if (len > 0 && (h_ent = gethostbyaddr(addr, len, af)) != NULL) { | ||
348 | snprintf(buf, buflen - 1, "%s", h_ent->h_name); | ||
349 | return buf; | ||
350 | } | ||
351 | } | ||
352 | #endif | ||
353 | return rt_addr_n2a(af, len, addr, buf, buflen); | ||
354 | } | ||
diff --git a/networking/libiproute/utils.h b/networking/libiproute/utils.h new file mode 100644 index 000000000..0b0d7545a --- /dev/null +++ b/networking/libiproute/utils.h | |||
@@ -0,0 +1,103 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | #ifndef __UTILS_H__ | ||
3 | #define __UTILS_H__ 1 | ||
4 | |||
5 | #include "libbb.h" | ||
6 | #include <asm/types.h> | ||
7 | #include <resolv.h> | ||
8 | |||
9 | #include "libnetlink.h" | ||
10 | #include "ll_map.h" | ||
11 | #include "rtm_map.h" | ||
12 | |||
13 | extern int preferred_family; | ||
14 | extern int show_stats; | ||
15 | extern int show_details; | ||
16 | extern int show_raw; | ||
17 | extern int resolve_hosts; | ||
18 | extern int oneline; | ||
19 | extern char * _SL_; | ||
20 | |||
21 | #ifndef IPPROTO_ESP | ||
22 | #define IPPROTO_ESP 50 | ||
23 | #endif | ||
24 | #ifndef IPPROTO_AH | ||
25 | #define IPPROTO_AH 51 | ||
26 | #endif | ||
27 | |||
28 | #define SPRINT_BSIZE 64 | ||
29 | #define SPRINT_BUF(x) char x[SPRINT_BSIZE] | ||
30 | |||
31 | extern void incomplete_command(void) ATTRIBUTE_NORETURN; | ||
32 | |||
33 | #define NEXT_ARG() do { argv++; if (--argc <= 0) incomplete_command(); } while(0) | ||
34 | |||
35 | typedef struct | ||
36 | { | ||
37 | __u8 family; | ||
38 | __u8 bytelen; | ||
39 | __s16 bitlen; | ||
40 | __u32 data[4]; | ||
41 | } inet_prefix; | ||
42 | |||
43 | #define DN_MAXADDL 20 | ||
44 | #ifndef AF_DECnet | ||
45 | #define AF_DECnet 12 | ||
46 | #endif | ||
47 | |||
48 | struct dn_naddr | ||
49 | { | ||
50 | unsigned short a_len; | ||
51 | unsigned char a_addr[DN_MAXADDL]; | ||
52 | }; | ||
53 | |||
54 | #define IPX_NODE_LEN 6 | ||
55 | |||
56 | struct ipx_addr { | ||
57 | uint32_t ipx_net; | ||
58 | uint8_t ipx_node[IPX_NODE_LEN]; | ||
59 | }; | ||
60 | |||
61 | extern __u32 get_addr32(char *name); | ||
62 | extern int get_addr_1(inet_prefix *dst, char *arg, int family); | ||
63 | extern int get_prefix_1(inet_prefix *dst, char *arg, int family); | ||
64 | extern int get_addr(inet_prefix *dst, char *arg, int family); | ||
65 | extern int get_prefix(inet_prefix *dst, char *arg, int family); | ||
66 | |||
67 | extern int get_integer(int *val, char *arg, int base); | ||
68 | extern int get_unsigned(unsigned *val, char *arg, int base); | ||
69 | #define get_byte get_u8 | ||
70 | #define get_ushort get_u16 | ||
71 | #define get_short get_s16 | ||
72 | extern int get_u32(__u32 *val, char *arg, int base); | ||
73 | extern int get_u16(__u16 *val, char *arg, int base); | ||
74 | extern int get_s16(__s16 *val, char *arg, int base); | ||
75 | extern int get_u8(__u8 *val, char *arg, int base); | ||
76 | extern int get_s8(__s8 *val, char *arg, int base); | ||
77 | |||
78 | extern const char *format_host(int af, int len, void *addr, char *buf, int buflen); | ||
79 | extern const char *rt_addr_n2a(int af, int len, void *addr, char *buf, int buflen); | ||
80 | |||
81 | void invarg(const char * const, const char * const) ATTRIBUTE_NORETURN; | ||
82 | void duparg(char *, char *) ATTRIBUTE_NORETURN; | ||
83 | void duparg2(char *, char *) ATTRIBUTE_NORETURN; | ||
84 | int matches(char *arg, char *pattern); | ||
85 | extern int inet_addr_match(inet_prefix *a, inet_prefix *b, int bits); | ||
86 | |||
87 | const char *dnet_ntop(int af, const void *addr, char *str, size_t len); | ||
88 | int dnet_pton(int af, const char *src, void *addr); | ||
89 | |||
90 | const char *ipx_ntop(int af, const void *addr, char *str, size_t len); | ||
91 | int ipx_pton(int af, const char *src, void *addr); | ||
92 | |||
93 | extern int __iproute2_hz_internal; | ||
94 | extern int __get_hz(void); | ||
95 | |||
96 | static __inline__ int get_hz(void) | ||
97 | { | ||
98 | if (__iproute2_hz_internal == 0) | ||
99 | __iproute2_hz_internal = __get_hz(); | ||
100 | return __iproute2_hz_internal; | ||
101 | } | ||
102 | |||
103 | #endif /* __UTILS_H__ */ | ||
diff --git a/networking/nameif.c b/networking/nameif.c new file mode 100644 index 000000000..52aad2873 --- /dev/null +++ b/networking/nameif.c | |||
@@ -0,0 +1,170 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * nameif.c - Naming Interfaces based on MAC address for busybox. | ||
4 | * | ||
5 | * Written 2000 by Andi Kleen. | ||
6 | * Busybox port 2002 by Nick Fedchik <nick@fedchik.org.ua> | ||
7 | * Glenn McGrath <bug1@iinet.net.au> | ||
8 | * | ||
9 | * Licensed under the GPL v2 or later, see the file LICENSE in this tarball. | ||
10 | */ | ||
11 | |||
12 | #include "busybox.h" | ||
13 | #include <syslog.h> | ||
14 | #include <net/if.h> | ||
15 | #include <netinet/ether.h> | ||
16 | |||
17 | |||
18 | /* Older versions of net/if.h do not appear to define IF_NAMESIZE. */ | ||
19 | #ifndef IF_NAMESIZE | ||
20 | # ifdef IFNAMSIZ | ||
21 | # define IF_NAMESIZE IFNAMSIZ | ||
22 | # else | ||
23 | # define IF_NAMESIZE 16 | ||
24 | # endif | ||
25 | #endif | ||
26 | |||
27 | /* take from linux/sockios.h */ | ||
28 | #define SIOCSIFNAME 0x8923 /* set interface name */ | ||
29 | |||
30 | /* Octets in one Ethernet addr, from <linux/if_ether.h> */ | ||
31 | #define ETH_ALEN 6 | ||
32 | |||
33 | #ifndef ifr_newname | ||
34 | #define ifr_newname ifr_ifru.ifru_slave | ||
35 | #endif | ||
36 | |||
37 | typedef struct mactable_s { | ||
38 | struct mactable_s *next; | ||
39 | struct mactable_s *prev; | ||
40 | char *ifname; | ||
41 | struct ether_addr *mac; | ||
42 | } mactable_t; | ||
43 | |||
44 | /* Check ascii str_macaddr, convert and copy to *mac */ | ||
45 | static struct ether_addr *cc_macaddr(const char *str_macaddr) | ||
46 | { | ||
47 | struct ether_addr *lmac, *mac; | ||
48 | |||
49 | lmac = ether_aton(str_macaddr); | ||
50 | if (lmac == NULL) | ||
51 | bb_error_msg_and_die("cannot parse MAC %s", str_macaddr); | ||
52 | mac = xmalloc(ETH_ALEN); | ||
53 | memcpy(mac, lmac, ETH_ALEN); | ||
54 | |||
55 | return mac; | ||
56 | } | ||
57 | |||
58 | int nameif_main(int argc, char **argv) | ||
59 | { | ||
60 | mactable_t *clist = NULL; | ||
61 | FILE *ifh; | ||
62 | const char *fname = "/etc/mactab"; | ||
63 | char *line; | ||
64 | int ctl_sk; | ||
65 | int if_index = 1; | ||
66 | mactable_t *ch; | ||
67 | |||
68 | if (1 & getopt32(argc, argv, "sc:", &fname)) { | ||
69 | openlog(applet_name, 0, LOG_LOCAL0); | ||
70 | logmode = LOGMODE_SYSLOG; | ||
71 | } | ||
72 | |||
73 | if ((argc - optind) & 1) | ||
74 | bb_show_usage(); | ||
75 | |||
76 | if (optind < argc) { | ||
77 | char **a = argv + optind; | ||
78 | |||
79 | while (*a) { | ||
80 | if (strlen(*a) > IF_NAMESIZE) | ||
81 | bb_error_msg_and_die("interface name '%s' " | ||
82 | "too long", *a); | ||
83 | ch = xzalloc(sizeof(mactable_t)); | ||
84 | ch->ifname = xstrdup(*a++); | ||
85 | ch->mac = cc_macaddr(*a++); | ||
86 | if (clist) | ||
87 | clist->prev = ch; | ||
88 | ch->next = clist; | ||
89 | clist = ch; | ||
90 | } | ||
91 | } else { | ||
92 | ifh = xfopen(fname, "r"); | ||
93 | |||
94 | while ((line = xmalloc_fgets(ifh)) != NULL) { | ||
95 | char *line_ptr; | ||
96 | size_t name_length; | ||
97 | |||
98 | line_ptr = line + strspn(line, " \t"); | ||
99 | if ((line_ptr[0] == '#') || (line_ptr[0] == '\n')) { | ||
100 | free(line); | ||
101 | continue; | ||
102 | } | ||
103 | name_length = strcspn(line_ptr, " \t"); | ||
104 | ch = xzalloc(sizeof(mactable_t)); | ||
105 | ch->ifname = xstrndup(line_ptr, name_length); | ||
106 | if (name_length > IF_NAMESIZE) | ||
107 | bb_error_msg_and_die("interface name '%s' " | ||
108 | "too long", ch->ifname); | ||
109 | line_ptr += name_length; | ||
110 | line_ptr += strspn(line_ptr, " \t"); | ||
111 | name_length = strspn(line_ptr, "0123456789ABCDEFabcdef:"); | ||
112 | line_ptr[name_length] = '\0'; | ||
113 | ch->mac = cc_macaddr(line_ptr); | ||
114 | if (clist) | ||
115 | clist->prev = ch; | ||
116 | ch->next = clist; | ||
117 | clist = ch; | ||
118 | free(line); | ||
119 | } | ||
120 | fclose(ifh); | ||
121 | } | ||
122 | |||
123 | ctl_sk = xsocket(PF_INET, SOCK_DGRAM, 0); | ||
124 | |||
125 | while (clist) { | ||
126 | struct ifreq ifr; | ||
127 | |||
128 | memset(&ifr, 0, sizeof(struct ifreq)); | ||
129 | if_index++; | ||
130 | ifr.ifr_ifindex = if_index; | ||
131 | |||
132 | /* Get ifname by index or die */ | ||
133 | if (ioctl(ctl_sk, SIOCGIFNAME, &ifr)) | ||
134 | break; | ||
135 | |||
136 | /* Has this device hwaddr? */ | ||
137 | if (ioctl(ctl_sk, SIOCGIFHWADDR, &ifr)) | ||
138 | continue; | ||
139 | |||
140 | /* Search for mac like in ifr.ifr_hwaddr.sa_data */ | ||
141 | for (ch = clist; ch; ch = ch->next) | ||
142 | if (!memcmp(ch->mac, ifr.ifr_hwaddr.sa_data, ETH_ALEN)) | ||
143 | break; | ||
144 | |||
145 | /* Nothing found for current ifr.ifr_hwaddr.sa_data */ | ||
146 | if (ch == NULL) | ||
147 | continue; | ||
148 | |||
149 | strcpy(ifr.ifr_newname, ch->ifname); | ||
150 | if (ioctl(ctl_sk, SIOCSIFNAME, &ifr) < 0) | ||
151 | bb_perror_msg_and_die("cannot change ifname %s to %s", | ||
152 | ifr.ifr_name, ch->ifname); | ||
153 | |||
154 | /* Remove list entry of renamed interface */ | ||
155 | if (ch->prev != NULL) { | ||
156 | (ch->prev)->next = ch->next; | ||
157 | } else { | ||
158 | clist = ch->next; | ||
159 | } | ||
160 | if (ch->next != NULL) | ||
161 | (ch->next)->prev = ch->prev; | ||
162 | if (ENABLE_FEATURE_CLEAN_UP) { | ||
163 | free(ch->ifname); | ||
164 | free(ch->mac); | ||
165 | free(ch); | ||
166 | } | ||
167 | } | ||
168 | |||
169 | return 0; | ||
170 | } | ||
diff --git a/networking/nc.c b/networking/nc.c new file mode 100644 index 000000000..5fd9242cc --- /dev/null +++ b/networking/nc.c | |||
@@ -0,0 +1,201 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* nc: mini-netcat - built from the ground up for LRP | ||
3 | * | ||
4 | * Copyright (C) 1998, 1999 Charles P. Wright | ||
5 | * Copyright (C) 1998 Dave Cinege | ||
6 | * | ||
7 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | ||
8 | */ | ||
9 | |||
10 | #include "busybox.h" | ||
11 | |||
12 | static void timeout(int signum) | ||
13 | { | ||
14 | bb_error_msg_and_die("timed out"); | ||
15 | } | ||
16 | |||
17 | int nc_main(int argc, char **argv) | ||
18 | { | ||
19 | int sfd = 0; | ||
20 | int cfd = 0; | ||
21 | SKIP_NC_SERVER(const) unsigned do_listen = 0; | ||
22 | SKIP_NC_SERVER(const) unsigned lport = 0; | ||
23 | SKIP_NC_EXTRA (const) unsigned wsecs = 0; | ||
24 | SKIP_NC_EXTRA (const) unsigned delay = 0; | ||
25 | SKIP_NC_EXTRA (const int execparam = 0;) | ||
26 | USE_NC_EXTRA (char **execparam = NULL;) | ||
27 | struct sockaddr_in address; | ||
28 | fd_set readfds, testfds; | ||
29 | int opt; /* must be signed (getopt returns -1) */ | ||
30 | |||
31 | memset(&address, 0, sizeof(address)); | ||
32 | |||
33 | if (ENABLE_NC_SERVER || ENABLE_NC_EXTRA) { | ||
34 | /* getopt32 is _almost_ usable: | ||
35 | ** it cannot handle "... -e prog -prog-opt" */ | ||
36 | while ((opt = getopt(argc, argv, | ||
37 | "" USE_NC_SERVER("lp:") USE_NC_EXTRA("w:i:f:e:") )) > 0 | ||
38 | ) { | ||
39 | if (ENABLE_NC_SERVER && opt=='l') USE_NC_SERVER(do_listen++); | ||
40 | else if (ENABLE_NC_SERVER && opt=='p') USE_NC_SERVER(lport = bb_lookup_port(optarg, "tcp", 0)); | ||
41 | else if (ENABLE_NC_EXTRA && opt=='w') USE_NC_EXTRA( wsecs = xatou(optarg)); | ||
42 | else if (ENABLE_NC_EXTRA && opt=='i') USE_NC_EXTRA( delay = xatou(optarg)); | ||
43 | else if (ENABLE_NC_EXTRA && opt=='f') USE_NC_EXTRA( cfd = xopen(optarg, O_RDWR)); | ||
44 | else if (ENABLE_NC_EXTRA && opt=='e' && optind<=argc) { | ||
45 | /* We cannot just 'break'. We should let getopt finish. | ||
46 | ** Or else we won't be able to find where | ||
47 | ** 'host' and 'port' params are | ||
48 | ** (think "nc -w 60 host port -e prog"). */ | ||
49 | USE_NC_EXTRA( | ||
50 | char **p; | ||
51 | // +2: one for progname (optarg) and one for NULL | ||
52 | execparam = xzalloc(sizeof(char*) * (argc - optind + 2)); | ||
53 | p = execparam; | ||
54 | *p++ = optarg; | ||
55 | while (optind < argc) { | ||
56 | *p++ = argv[optind++]; | ||
57 | } | ||
58 | ) | ||
59 | /* optind points to argv[arvc] (NULL) now. | ||
60 | ** FIXME: we assume that getopt will not count options | ||
61 | ** possibly present on "-e prog args" and will not | ||
62 | ** include them into final value of optind | ||
63 | ** which is to be used ... */ | ||
64 | } else bb_show_usage(); | ||
65 | } | ||
66 | argv += optind; /* ... here! */ | ||
67 | argc -= optind; | ||
68 | // -l and -f don't mix | ||
69 | if (do_listen && cfd) bb_show_usage(); | ||
70 | // Listen or file modes need zero arguments, client mode needs 2 | ||
71 | opt = ((do_listen || cfd) ? 0 : 2); | ||
72 | if (argc != opt) | ||
73 | bb_show_usage(); | ||
74 | } else { | ||
75 | if (argc != 3) bb_show_usage(); | ||
76 | argc--; | ||
77 | argv++; | ||
78 | } | ||
79 | |||
80 | if (wsecs) { | ||
81 | signal(SIGALRM, timeout); | ||
82 | alarm(wsecs); | ||
83 | } | ||
84 | |||
85 | if (!cfd) { | ||
86 | sfd = xsocket(AF_INET, SOCK_STREAM, 0); | ||
87 | fcntl(sfd, F_SETFD, FD_CLOEXEC); | ||
88 | setsockopt_reuseaddr(sfd); | ||
89 | address.sin_family = AF_INET; | ||
90 | |||
91 | // Set local port. | ||
92 | |||
93 | if (lport != 0) { | ||
94 | address.sin_port = lport; | ||
95 | xbind(sfd, (struct sockaddr *) &address, sizeof(address)); | ||
96 | } | ||
97 | |||
98 | if (do_listen) { | ||
99 | socklen_t addrlen = sizeof(address); | ||
100 | |||
101 | xlisten(sfd, do_listen); | ||
102 | |||
103 | // If we didn't specify a port number, query and print it to stderr. | ||
104 | |||
105 | if (!lport) { | ||
106 | socklen_t len = sizeof(address); | ||
107 | getsockname(sfd, &address, &len); | ||
108 | fdprintf(2, "%d\n", SWAP_BE16(address.sin_port)); | ||
109 | } | ||
110 | repeatyness: | ||
111 | cfd = accept(sfd, (struct sockaddr *) &address, &addrlen); | ||
112 | if (cfd < 0) | ||
113 | bb_perror_msg_and_die("accept"); | ||
114 | |||
115 | if (!execparam) close(sfd); | ||
116 | } else { | ||
117 | struct hostent *hostinfo; | ||
118 | hostinfo = xgethostbyname(argv[0]); | ||
119 | |||
120 | address.sin_addr = *(struct in_addr *) *hostinfo->h_addr_list; | ||
121 | address.sin_port = bb_lookup_port(argv[1], "tcp", 0); | ||
122 | |||
123 | if (connect(sfd, (struct sockaddr *) &address, sizeof(address)) < 0) | ||
124 | bb_perror_msg_and_die("connect"); | ||
125 | cfd = sfd; | ||
126 | } | ||
127 | } | ||
128 | |||
129 | if (wsecs) { | ||
130 | alarm(0); | ||
131 | signal(SIGALRM, SIG_DFL); | ||
132 | } | ||
133 | |||
134 | /* -e given? */ | ||
135 | if (execparam) { | ||
136 | if (cfd) { | ||
137 | signal(SIGCHLD, SIG_IGN); | ||
138 | dup2(cfd, 0); | ||
139 | close(cfd); | ||
140 | } | ||
141 | dup2(0, 1); | ||
142 | dup2(0, 2); | ||
143 | |||
144 | // With more than one -l, repeatedly act as server. | ||
145 | |||
146 | if (do_listen > 1 && vfork()) { | ||
147 | // This is a bit weird as cleanup goes, since we wind up with no | ||
148 | // stdin/stdout/stderr. But it's small and shouldn't hurt anything. | ||
149 | // We check for cfd == 0 above. | ||
150 | logmode = LOGMODE_NONE; | ||
151 | close(0); | ||
152 | close(1); | ||
153 | close(2); | ||
154 | |||
155 | goto repeatyness; | ||
156 | } | ||
157 | USE_NC_EXTRA(execvp(execparam[0], execparam);) | ||
158 | /* Don't print stuff or it will go over the wire.... */ | ||
159 | _exit(127); | ||
160 | } | ||
161 | |||
162 | // Select loop copying stdin to cfd, and cfd to stdout. | ||
163 | |||
164 | FD_ZERO(&readfds); | ||
165 | FD_SET(cfd, &readfds); | ||
166 | FD_SET(STDIN_FILENO, &readfds); | ||
167 | |||
168 | for (;;) { | ||
169 | int fd; | ||
170 | int ofd; | ||
171 | int nread; | ||
172 | |||
173 | testfds = readfds; | ||
174 | |||
175 | if (select(FD_SETSIZE, &testfds, NULL, NULL, NULL) < 0) | ||
176 | bb_perror_msg_and_die("select"); | ||
177 | |||
178 | for (fd = 0; fd < FD_SETSIZE; fd++) { | ||
179 | if (FD_ISSET(fd, &testfds)) { | ||
180 | nread = safe_read(fd, bb_common_bufsiz1, | ||
181 | sizeof(bb_common_bufsiz1)); | ||
182 | |||
183 | if (fd == cfd) { | ||
184 | if (nread<1) exit(0); | ||
185 | ofd = STDOUT_FILENO; | ||
186 | } else { | ||
187 | if (nread<1) { | ||
188 | // Close outgoing half-connection so they get EOF, but | ||
189 | // leave incoming alone so we can see response. | ||
190 | shutdown(cfd, 1); | ||
191 | FD_CLR(STDIN_FILENO, &readfds); | ||
192 | } | ||
193 | ofd = cfd; | ||
194 | } | ||
195 | |||
196 | xwrite(ofd, bb_common_bufsiz1, nread); | ||
197 | if (delay > 0) sleep(delay); | ||
198 | } | ||
199 | } | ||
200 | } | ||
201 | } | ||
diff --git a/networking/netstat.c b/networking/netstat.c new file mode 100644 index 000000000..3dad57a40 --- /dev/null +++ b/networking/netstat.c | |||
@@ -0,0 +1,613 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * Mini netstat implementation(s) for busybox | ||
4 | * based in part on the netstat implementation from net-tools. | ||
5 | * | ||
6 | * Copyright (C) 2002 by Bart Visscher <magick@linux-fan.com> | ||
7 | * | ||
8 | * 2002-04-20 | ||
9 | * IPV6 support added by Bart Visscher <magick@linux-fan.com> | ||
10 | * | ||
11 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | ||
12 | */ | ||
13 | |||
14 | #include "busybox.h" | ||
15 | #include "inet_common.h" | ||
16 | |||
17 | #ifdef CONFIG_ROUTE | ||
18 | extern void displayroutes(int noresolve, int netstatfmt); | ||
19 | #endif | ||
20 | |||
21 | #define NETSTAT_CONNECTED 0x01 | ||
22 | #define NETSTAT_LISTENING 0x02 | ||
23 | #define NETSTAT_NUMERIC 0x04 | ||
24 | /* Must match getopt32 option string */ | ||
25 | #define NETSTAT_TCP 0x10 | ||
26 | #define NETSTAT_UDP 0x20 | ||
27 | #define NETSTAT_RAW 0x40 | ||
28 | #define NETSTAT_UNIX 0x80 | ||
29 | #define NETSTAT_ALLPROTO (NETSTAT_TCP|NETSTAT_UDP|NETSTAT_RAW|NETSTAT_UNIX) | ||
30 | |||
31 | static int flags = NETSTAT_CONNECTED | NETSTAT_ALLPROTO; | ||
32 | |||
33 | #define PROGNAME_WIDTHs PROGNAME_WIDTH1(PROGNAME_WIDTH) | ||
34 | #define PROGNAME_WIDTH1(s) PROGNAME_WIDTH2(s) | ||
35 | #define PROGNAME_WIDTH2(s) #s | ||
36 | |||
37 | #define PRG_HASH_SIZE 211 | ||
38 | |||
39 | enum { | ||
40 | TCP_ESTABLISHED = 1, | ||
41 | TCP_SYN_SENT, | ||
42 | TCP_SYN_RECV, | ||
43 | TCP_FIN_WAIT1, | ||
44 | TCP_FIN_WAIT2, | ||
45 | TCP_TIME_WAIT, | ||
46 | TCP_CLOSE, | ||
47 | TCP_CLOSE_WAIT, | ||
48 | TCP_LAST_ACK, | ||
49 | TCP_LISTEN, | ||
50 | TCP_CLOSING /* now a valid state */ | ||
51 | }; | ||
52 | |||
53 | static const char * const tcp_state[] = | ||
54 | { | ||
55 | "", | ||
56 | "ESTABLISHED", | ||
57 | "SYN_SENT", | ||
58 | "SYN_RECV", | ||
59 | "FIN_WAIT1", | ||
60 | "FIN_WAIT2", | ||
61 | "TIME_WAIT", | ||
62 | "CLOSE", | ||
63 | "CLOSE_WAIT", | ||
64 | "LAST_ACK", | ||
65 | "LISTEN", | ||
66 | "CLOSING" | ||
67 | }; | ||
68 | |||
69 | typedef enum { | ||
70 | SS_FREE = 0, /* not allocated */ | ||
71 | SS_UNCONNECTED, /* unconnected to any socket */ | ||
72 | SS_CONNECTING, /* in process of connecting */ | ||
73 | SS_CONNECTED, /* connected to socket */ | ||
74 | SS_DISCONNECTING /* in process of disconnecting */ | ||
75 | } socket_state; | ||
76 | |||
77 | #define SO_ACCEPTCON (1<<16) /* performed a listen */ | ||
78 | #define SO_WAITDATA (1<<17) /* wait data to read */ | ||
79 | #define SO_NOSPACE (1<<18) /* no space to write */ | ||
80 | |||
81 | static char *get_sname(int port, const char *proto, int num) | ||
82 | { | ||
83 | char *str=itoa(ntohs(port)); | ||
84 | if (num) { | ||
85 | } else { | ||
86 | struct servent *se=getservbyport(port,proto); | ||
87 | if (se) | ||
88 | str=se->s_name; | ||
89 | } | ||
90 | if (!port) { | ||
91 | str="*"; | ||
92 | } | ||
93 | return str; | ||
94 | } | ||
95 | |||
96 | static void snprint_ip_port(char *ip_port, int size, struct sockaddr *addr, int port, char *proto, int numeric) | ||
97 | { | ||
98 | char *port_name; | ||
99 | |||
100 | #ifdef CONFIG_FEATURE_IPV6 | ||
101 | if (addr->sa_family == AF_INET6) { | ||
102 | INET6_rresolve(ip_port, size, (struct sockaddr_in6 *)addr, | ||
103 | (numeric&NETSTAT_NUMERIC) ? 0x0fff : 0); | ||
104 | } else | ||
105 | #endif | ||
106 | { | ||
107 | INET_rresolve(ip_port, size, (struct sockaddr_in *)addr, | ||
108 | 0x4000 | ((numeric&NETSTAT_NUMERIC) ? 0x0fff : 0), | ||
109 | 0xffffffff); | ||
110 | } | ||
111 | port_name=get_sname(htons(port), proto, numeric); | ||
112 | if ((strlen(ip_port) + strlen(port_name)) > 22) | ||
113 | ip_port[22 - strlen(port_name)] = '\0'; | ||
114 | ip_port+=strlen(ip_port); | ||
115 | strcat(ip_port, ":"); | ||
116 | strcat(ip_port, port_name); | ||
117 | } | ||
118 | |||
119 | static void tcp_do_one(int lnr, const char *line) | ||
120 | { | ||
121 | char local_addr[64], rem_addr[64]; | ||
122 | const char *state_str; | ||
123 | char more[512]; | ||
124 | int num, local_port, rem_port, d, state, timer_run, uid, timeout; | ||
125 | #ifdef CONFIG_FEATURE_IPV6 | ||
126 | struct sockaddr_in6 localaddr, remaddr; | ||
127 | char addr6[INET6_ADDRSTRLEN]; | ||
128 | struct in6_addr in6; | ||
129 | #else | ||
130 | struct sockaddr_in localaddr, remaddr; | ||
131 | #endif | ||
132 | unsigned long rxq, txq, time_len, retr, inode; | ||
133 | |||
134 | if (lnr == 0) | ||
135 | return; | ||
136 | |||
137 | more[0] = '\0'; | ||
138 | num = sscanf(line, | ||
139 | "%d: %64[0-9A-Fa-f]:%X %64[0-9A-Fa-f]:%X %X %lX:%lX %X:%lX %lX %d %d %ld %512s\n", | ||
140 | &d, local_addr, &local_port, | ||
141 | rem_addr, &rem_port, &state, | ||
142 | &txq, &rxq, &timer_run, &time_len, &retr, &uid, &timeout, &inode, more); | ||
143 | |||
144 | if (strlen(local_addr) > 8) { | ||
145 | #ifdef CONFIG_FEATURE_IPV6 | ||
146 | sscanf(local_addr, "%08X%08X%08X%08X", | ||
147 | &in6.s6_addr32[0], &in6.s6_addr32[1], | ||
148 | &in6.s6_addr32[2], &in6.s6_addr32[3]); | ||
149 | inet_ntop(AF_INET6, &in6, addr6, sizeof(addr6)); | ||
150 | inet_pton(AF_INET6, addr6, (struct sockaddr *) &localaddr.sin6_addr); | ||
151 | sscanf(rem_addr, "%08X%08X%08X%08X", | ||
152 | &in6.s6_addr32[0], &in6.s6_addr32[1], | ||
153 | &in6.s6_addr32[2], &in6.s6_addr32[3]); | ||
154 | inet_ntop(AF_INET6, &in6, addr6, sizeof(addr6)); | ||
155 | inet_pton(AF_INET6, addr6, (struct sockaddr *) &remaddr.sin6_addr); | ||
156 | localaddr.sin6_family = AF_INET6; | ||
157 | remaddr.sin6_family = AF_INET6; | ||
158 | #endif | ||
159 | } else { | ||
160 | sscanf(local_addr, "%X", | ||
161 | &((struct sockaddr_in *) &localaddr)->sin_addr.s_addr); | ||
162 | sscanf(rem_addr, "%X", | ||
163 | &((struct sockaddr_in *) &remaddr)->sin_addr.s_addr); | ||
164 | ((struct sockaddr *) &localaddr)->sa_family = AF_INET; | ||
165 | ((struct sockaddr *) &remaddr)->sa_family = AF_INET; | ||
166 | } | ||
167 | |||
168 | if (num < 10) { | ||
169 | bb_error_msg("warning, got bogus tcp line"); | ||
170 | return; | ||
171 | } | ||
172 | state_str = tcp_state[state]; | ||
173 | if ((rem_port && (flags&NETSTAT_CONNECTED)) || | ||
174 | (!rem_port && (flags&NETSTAT_LISTENING))) | ||
175 | { | ||
176 | snprint_ip_port(local_addr, sizeof(local_addr), | ||
177 | (struct sockaddr *) &localaddr, local_port, | ||
178 | "tcp", flags&NETSTAT_NUMERIC); | ||
179 | |||
180 | snprint_ip_port(rem_addr, sizeof(rem_addr), | ||
181 | (struct sockaddr *) &remaddr, rem_port, | ||
182 | "tcp", flags&NETSTAT_NUMERIC); | ||
183 | |||
184 | printf("tcp %6ld %6ld %-23s %-23s %-12s\n", | ||
185 | rxq, txq, local_addr, rem_addr, state_str); | ||
186 | |||
187 | } | ||
188 | } | ||
189 | |||
190 | static void udp_do_one(int lnr, const char *line) | ||
191 | { | ||
192 | char local_addr[64], rem_addr[64]; | ||
193 | char *state_str, more[512]; | ||
194 | int num, local_port, rem_port, d, state, timer_run, uid, timeout; | ||
195 | #ifdef CONFIG_FEATURE_IPV6 | ||
196 | struct sockaddr_in6 localaddr, remaddr; | ||
197 | char addr6[INET6_ADDRSTRLEN]; | ||
198 | struct in6_addr in6; | ||
199 | #else | ||
200 | struct sockaddr_in localaddr, remaddr; | ||
201 | #endif | ||
202 | unsigned long rxq, txq, time_len, retr, inode; | ||
203 | |||
204 | if (lnr == 0) | ||
205 | return; | ||
206 | |||
207 | more[0] = '\0'; | ||
208 | num = sscanf(line, | ||
209 | "%d: %64[0-9A-Fa-f]:%X %64[0-9A-Fa-f]:%X %X %lX:%lX %X:%lX %lX %d %d %ld %512s\n", | ||
210 | &d, local_addr, &local_port, | ||
211 | rem_addr, &rem_port, &state, | ||
212 | &txq, &rxq, &timer_run, &time_len, &retr, &uid, &timeout, &inode, more); | ||
213 | |||
214 | if (strlen(local_addr) > 8) { | ||
215 | #ifdef CONFIG_FEATURE_IPV6 | ||
216 | /* Demangle what the kernel gives us */ | ||
217 | sscanf(local_addr, "%08X%08X%08X%08X", | ||
218 | &in6.s6_addr32[0], &in6.s6_addr32[1], | ||
219 | &in6.s6_addr32[2], &in6.s6_addr32[3]); | ||
220 | inet_ntop(AF_INET6, &in6, addr6, sizeof(addr6)); | ||
221 | inet_pton(AF_INET6, addr6, (struct sockaddr *) &localaddr.sin6_addr); | ||
222 | sscanf(rem_addr, "%08X%08X%08X%08X", | ||
223 | &in6.s6_addr32[0], &in6.s6_addr32[1], | ||
224 | &in6.s6_addr32[2], &in6.s6_addr32[3]); | ||
225 | inet_ntop(AF_INET6, &in6, addr6, sizeof(addr6)); | ||
226 | inet_pton(AF_INET6, addr6, (struct sockaddr *) &remaddr.sin6_addr); | ||
227 | localaddr.sin6_family = AF_INET6; | ||
228 | remaddr.sin6_family = AF_INET6; | ||
229 | #endif | ||
230 | } else { | ||
231 | sscanf(local_addr, "%X", | ||
232 | &((struct sockaddr_in *) &localaddr)->sin_addr.s_addr); | ||
233 | sscanf(rem_addr, "%X", | ||
234 | &((struct sockaddr_in *) &remaddr)->sin_addr.s_addr); | ||
235 | ((struct sockaddr *) &localaddr)->sa_family = AF_INET; | ||
236 | ((struct sockaddr *) &remaddr)->sa_family = AF_INET; | ||
237 | } | ||
238 | |||
239 | if (num < 10) { | ||
240 | bb_error_msg("warning, got bogus udp line"); | ||
241 | return; | ||
242 | } | ||
243 | switch (state) { | ||
244 | case TCP_ESTABLISHED: | ||
245 | state_str = "ESTABLISHED"; | ||
246 | break; | ||
247 | |||
248 | case TCP_CLOSE: | ||
249 | state_str = ""; | ||
250 | break; | ||
251 | |||
252 | default: | ||
253 | state_str = "UNKNOWN"; | ||
254 | break; | ||
255 | } | ||
256 | |||
257 | #ifdef CONFIG_FEATURE_IPV6 | ||
258 | # define notnull(A) (((A.sin6_family == AF_INET6) && \ | ||
259 | ((A.sin6_addr.s6_addr32[0]) || \ | ||
260 | (A.sin6_addr.s6_addr32[1]) || \ | ||
261 | (A.sin6_addr.s6_addr32[2]) || \ | ||
262 | (A.sin6_addr.s6_addr32[3]))) || \ | ||
263 | ((A.sin6_family == AF_INET) && \ | ||
264 | ((struct sockaddr_in *) &A)->sin_addr.s_addr)) | ||
265 | #else | ||
266 | # define notnull(A) (A.sin_addr.s_addr) | ||
267 | #endif | ||
268 | if ((notnull(remaddr) && (flags&NETSTAT_CONNECTED)) || | ||
269 | (!notnull(remaddr) && (flags&NETSTAT_LISTENING))) | ||
270 | { | ||
271 | snprint_ip_port(local_addr, sizeof(local_addr), | ||
272 | (struct sockaddr *) &localaddr, local_port, | ||
273 | "udp", flags&NETSTAT_NUMERIC); | ||
274 | |||
275 | snprint_ip_port(rem_addr, sizeof(rem_addr), | ||
276 | (struct sockaddr *) &remaddr, rem_port, | ||
277 | "udp", flags&NETSTAT_NUMERIC); | ||
278 | |||
279 | printf("udp %6ld %6ld %-23s %-23s %-12s\n", | ||
280 | rxq, txq, local_addr, rem_addr, state_str); | ||
281 | |||
282 | } | ||
283 | } | ||
284 | |||
285 | static void raw_do_one(int lnr, const char *line) | ||
286 | { | ||
287 | char local_addr[64], rem_addr[64]; | ||
288 | char *state_str, more[512]; | ||
289 | int num, local_port, rem_port, d, state, timer_run, uid, timeout; | ||
290 | #ifdef CONFIG_FEATURE_IPV6 | ||
291 | struct sockaddr_in6 localaddr, remaddr; | ||
292 | char addr6[INET6_ADDRSTRLEN]; | ||
293 | struct in6_addr in6; | ||
294 | #else | ||
295 | struct sockaddr_in localaddr, remaddr; | ||
296 | #endif | ||
297 | unsigned long rxq, txq, time_len, retr, inode; | ||
298 | |||
299 | if (lnr == 0) | ||
300 | return; | ||
301 | |||
302 | more[0] = '\0'; | ||
303 | num = sscanf(line, | ||
304 | "%d: %64[0-9A-Fa-f]:%X %64[0-9A-Fa-f]:%X %X %lX:%lX %X:%lX %lX %d %d %ld %512s\n", | ||
305 | &d, local_addr, &local_port, | ||
306 | rem_addr, &rem_port, &state, | ||
307 | &txq, &rxq, &timer_run, &time_len, &retr, &uid, &timeout, &inode, more); | ||
308 | |||
309 | if (strlen(local_addr) > 8) { | ||
310 | #ifdef CONFIG_FEATURE_IPV6 | ||
311 | sscanf(local_addr, "%08X%08X%08X%08X", | ||
312 | &in6.s6_addr32[0], &in6.s6_addr32[1], | ||
313 | &in6.s6_addr32[2], &in6.s6_addr32[3]); | ||
314 | inet_ntop(AF_INET6, &in6, addr6, sizeof(addr6)); | ||
315 | inet_pton(AF_INET6, addr6, (struct sockaddr *) &localaddr.sin6_addr); | ||
316 | sscanf(rem_addr, "%08X%08X%08X%08X", | ||
317 | &in6.s6_addr32[0], &in6.s6_addr32[1], | ||
318 | &in6.s6_addr32[2], &in6.s6_addr32[3]); | ||
319 | inet_ntop(AF_INET6, &in6, addr6, sizeof(addr6)); | ||
320 | inet_pton(AF_INET6, addr6, (struct sockaddr *) &remaddr.sin6_addr); | ||
321 | localaddr.sin6_family = AF_INET6; | ||
322 | remaddr.sin6_family = AF_INET6; | ||
323 | #endif | ||
324 | } else { | ||
325 | sscanf(local_addr, "%X", | ||
326 | &((struct sockaddr_in *) &localaddr)->sin_addr.s_addr); | ||
327 | sscanf(rem_addr, "%X", | ||
328 | &((struct sockaddr_in *) &remaddr)->sin_addr.s_addr); | ||
329 | ((struct sockaddr *) &localaddr)->sa_family = AF_INET; | ||
330 | ((struct sockaddr *) &remaddr)->sa_family = AF_INET; | ||
331 | } | ||
332 | |||
333 | if (num < 10) { | ||
334 | bb_error_msg("warning, got bogus raw line"); | ||
335 | return; | ||
336 | } | ||
337 | state_str=itoa(state); | ||
338 | |||
339 | #ifdef CONFIG_FEATURE_IPV6 | ||
340 | # define notnull(A) (((A.sin6_family == AF_INET6) && \ | ||
341 | ((A.sin6_addr.s6_addr32[0]) || \ | ||
342 | (A.sin6_addr.s6_addr32[1]) || \ | ||
343 | (A.sin6_addr.s6_addr32[2]) || \ | ||
344 | (A.sin6_addr.s6_addr32[3]))) || \ | ||
345 | ((A.sin6_family == AF_INET) && \ | ||
346 | ((struct sockaddr_in *) &A)->sin_addr.s_addr)) | ||
347 | #else | ||
348 | # define notnull(A) (A.sin_addr.s_addr) | ||
349 | #endif | ||
350 | if ((notnull(remaddr) && (flags&NETSTAT_CONNECTED)) || | ||
351 | (!notnull(remaddr) && (flags&NETSTAT_LISTENING))) | ||
352 | { | ||
353 | snprint_ip_port(local_addr, sizeof(local_addr), | ||
354 | (struct sockaddr *) &localaddr, local_port, | ||
355 | "raw", flags&NETSTAT_NUMERIC); | ||
356 | |||
357 | snprint_ip_port(rem_addr, sizeof(rem_addr), | ||
358 | (struct sockaddr *) &remaddr, rem_port, | ||
359 | "raw", flags&NETSTAT_NUMERIC); | ||
360 | |||
361 | printf("raw %6ld %6ld %-23s %-23s %-12s\n", | ||
362 | rxq, txq, local_addr, rem_addr, state_str); | ||
363 | |||
364 | } | ||
365 | } | ||
366 | |||
367 | #define HAS_INODE 1 | ||
368 | |||
369 | static void unix_do_one(int nr, const char *line) | ||
370 | { | ||
371 | static int has = 0; | ||
372 | char path[PATH_MAX], ss_flags[32]; | ||
373 | char *ss_proto, *ss_state, *ss_type; | ||
374 | int num, state, type, inode; | ||
375 | void *d; | ||
376 | unsigned long refcnt, proto, unix_flags; | ||
377 | |||
378 | if (nr == 0) { | ||
379 | if (strstr(line, "Inode")) | ||
380 | has |= HAS_INODE; | ||
381 | return; | ||
382 | } | ||
383 | path[0] = '\0'; | ||
384 | num = sscanf(line, "%p: %lX %lX %lX %X %X %d %s", | ||
385 | &d, &refcnt, &proto, &unix_flags, &type, &state, &inode, path); | ||
386 | if (num < 6) { | ||
387 | bb_error_msg("warning, got bogus unix line"); | ||
388 | return; | ||
389 | } | ||
390 | if (!(has & HAS_INODE)) | ||
391 | snprintf(path,sizeof(path),"%d",inode); | ||
392 | |||
393 | if ((flags&(NETSTAT_LISTENING|NETSTAT_CONNECTED))!=(NETSTAT_LISTENING|NETSTAT_CONNECTED)) { | ||
394 | if ((state == SS_UNCONNECTED) && (unix_flags & SO_ACCEPTCON)) { | ||
395 | if (!(flags&NETSTAT_LISTENING)) | ||
396 | return; | ||
397 | } else { | ||
398 | if (!(flags&NETSTAT_CONNECTED)) | ||
399 | return; | ||
400 | } | ||
401 | } | ||
402 | |||
403 | switch (proto) { | ||
404 | case 0: | ||
405 | ss_proto = "unix"; | ||
406 | break; | ||
407 | |||
408 | default: | ||
409 | ss_proto = "??"; | ||
410 | } | ||
411 | |||
412 | switch (type) { | ||
413 | case SOCK_STREAM: | ||
414 | ss_type = "STREAM"; | ||
415 | break; | ||
416 | |||
417 | case SOCK_DGRAM: | ||
418 | ss_type = "DGRAM"; | ||
419 | break; | ||
420 | |||
421 | case SOCK_RAW: | ||
422 | ss_type = "RAW"; | ||
423 | break; | ||
424 | |||
425 | case SOCK_RDM: | ||
426 | ss_type = "RDM"; | ||
427 | break; | ||
428 | |||
429 | case SOCK_SEQPACKET: | ||
430 | ss_type = "SEQPACKET"; | ||
431 | break; | ||
432 | |||
433 | default: | ||
434 | ss_type = "UNKNOWN"; | ||
435 | } | ||
436 | |||
437 | switch (state) { | ||
438 | case SS_FREE: | ||
439 | ss_state = "FREE"; | ||
440 | break; | ||
441 | |||
442 | case SS_UNCONNECTED: | ||
443 | /* | ||
444 | * Unconnected sockets may be listening | ||
445 | * for something. | ||
446 | */ | ||
447 | if (unix_flags & SO_ACCEPTCON) { | ||
448 | ss_state = "LISTENING"; | ||
449 | } else { | ||
450 | ss_state = ""; | ||
451 | } | ||
452 | break; | ||
453 | |||
454 | case SS_CONNECTING: | ||
455 | ss_state = "CONNECTING"; | ||
456 | break; | ||
457 | |||
458 | case SS_CONNECTED: | ||
459 | ss_state = "CONNECTED"; | ||
460 | break; | ||
461 | |||
462 | case SS_DISCONNECTING: | ||
463 | ss_state = "DISCONNECTING"; | ||
464 | break; | ||
465 | |||
466 | default: | ||
467 | ss_state = "UNKNOWN"; | ||
468 | } | ||
469 | |||
470 | strcpy(ss_flags, "[ "); | ||
471 | if (unix_flags & SO_ACCEPTCON) | ||
472 | strcat(ss_flags, "ACC "); | ||
473 | if (unix_flags & SO_WAITDATA) | ||
474 | strcat(ss_flags, "W "); | ||
475 | if (unix_flags & SO_NOSPACE) | ||
476 | strcat(ss_flags, "N "); | ||
477 | |||
478 | strcat(ss_flags, "]"); | ||
479 | |||
480 | printf("%-5s %-6ld %-11s %-10s %-13s ", | ||
481 | ss_proto, refcnt, ss_flags, ss_type, ss_state); | ||
482 | if (has & HAS_INODE) | ||
483 | printf("%-6d ",inode); | ||
484 | else | ||
485 | printf("- "); | ||
486 | puts(path); | ||
487 | } | ||
488 | |||
489 | #define _PATH_PROCNET_UDP "/proc/net/udp" | ||
490 | #define _PATH_PROCNET_UDP6 "/proc/net/udp6" | ||
491 | #define _PATH_PROCNET_TCP "/proc/net/tcp" | ||
492 | #define _PATH_PROCNET_TCP6 "/proc/net/tcp6" | ||
493 | #define _PATH_PROCNET_RAW "/proc/net/raw" | ||
494 | #define _PATH_PROCNET_RAW6 "/proc/net/raw6" | ||
495 | #define _PATH_PROCNET_UNIX "/proc/net/unix" | ||
496 | |||
497 | static void do_info(const char *file, const char *name, void (*proc)(int, const char *)) | ||
498 | { | ||
499 | int lnr = 0; | ||
500 | FILE *procinfo; | ||
501 | |||
502 | procinfo = fopen(file, "r"); | ||
503 | if (procinfo == NULL) { | ||
504 | if (errno != ENOENT) { | ||
505 | perror(file); | ||
506 | } else { | ||
507 | bb_error_msg("no support for '%s' on this system", name); | ||
508 | } | ||
509 | } else { | ||
510 | do { | ||
511 | char *buffer = xmalloc_fgets(procinfo); | ||
512 | if (buffer) { | ||
513 | (proc)(lnr++, buffer); | ||
514 | free(buffer); | ||
515 | } | ||
516 | } while (!feof(procinfo)); | ||
517 | fclose(procinfo); | ||
518 | } | ||
519 | } | ||
520 | |||
521 | /* | ||
522 | * Our main function. | ||
523 | */ | ||
524 | |||
525 | int netstat_main(int argc, char **argv) | ||
526 | { | ||
527 | enum { | ||
528 | OPT_extended = 0x4, | ||
529 | OPT_showroute = 0x100, | ||
530 | }; | ||
531 | unsigned opt; | ||
532 | #ifdef CONFIG_FEATURE_IPV6 | ||
533 | int inet = 1; | ||
534 | int inet6 = 1; | ||
535 | #else | ||
536 | # define inet 1 | ||
537 | # define inet6 0 | ||
538 | #endif | ||
539 | |||
540 | /* Option string must match NETSTAT_xxx constants */ | ||
541 | opt = getopt32(argc, argv, "laentuwxr"); | ||
542 | if (opt & 0x1) { // -l | ||
543 | flags &= ~NETSTAT_CONNECTED; | ||
544 | flags |= NETSTAT_LISTENING; | ||
545 | } | ||
546 | if (opt & 0x2) flags |= NETSTAT_LISTENING | NETSTAT_CONNECTED; // -a | ||
547 | //if (opt & 0x4) // -e | ||
548 | if (opt & 0x8) flags |= NETSTAT_NUMERIC; // -n | ||
549 | //if (opt & 0x10) // -t: NETSTAT_TCP | ||
550 | //if (opt & 0x20) // -u: NETSTAT_UDP | ||
551 | //if (opt & 0x40) // -w: NETSTAT_RAW | ||
552 | //if (opt & 0x80) // -x: NETSTAT_UNIX | ||
553 | if (opt & OPT_showroute) { // -r | ||
554 | #ifdef CONFIG_ROUTE | ||
555 | displayroutes(flags & NETSTAT_NUMERIC, !(opt & OPT_extended)); | ||
556 | return 0; | ||
557 | #else | ||
558 | bb_error_msg_and_die("-r (display routing table) is not compiled in"); | ||
559 | #endif | ||
560 | } | ||
561 | |||
562 | opt &= NETSTAT_ALLPROTO; | ||
563 | if (opt) { | ||
564 | flags &= ~NETSTAT_ALLPROTO; | ||
565 | flags |= opt; | ||
566 | } | ||
567 | if (flags & (NETSTAT_TCP|NETSTAT_UDP|NETSTAT_RAW)) { | ||
568 | printf("Active Internet connections "); /* xxx */ | ||
569 | |||
570 | if ((flags&(NETSTAT_LISTENING|NETSTAT_CONNECTED))==(NETSTAT_LISTENING|NETSTAT_CONNECTED)) | ||
571 | printf("(servers and established)"); | ||
572 | else { | ||
573 | if (flags & NETSTAT_LISTENING) | ||
574 | printf("(only servers)"); | ||
575 | else | ||
576 | printf("(w/o servers)"); | ||
577 | } | ||
578 | printf("\nProto Recv-Q Send-Q Local Address Foreign Address State\n"); | ||
579 | } | ||
580 | if (inet && flags&NETSTAT_TCP) | ||
581 | do_info(_PATH_PROCNET_TCP,"AF INET (tcp)",tcp_do_one); | ||
582 | #ifdef CONFIG_FEATURE_IPV6 | ||
583 | if (inet6 && flags&NETSTAT_TCP) | ||
584 | do_info(_PATH_PROCNET_TCP6,"AF INET6 (tcp)",tcp_do_one); | ||
585 | #endif | ||
586 | if (inet && flags&NETSTAT_UDP) | ||
587 | do_info(_PATH_PROCNET_UDP,"AF INET (udp)",udp_do_one); | ||
588 | #ifdef CONFIG_FEATURE_IPV6 | ||
589 | if (inet6 && flags&NETSTAT_UDP) | ||
590 | do_info(_PATH_PROCNET_UDP6,"AF INET6 (udp)",udp_do_one); | ||
591 | #endif | ||
592 | if (inet && flags&NETSTAT_RAW) | ||
593 | do_info(_PATH_PROCNET_RAW,"AF INET (raw)",raw_do_one); | ||
594 | #ifdef CONFIG_FEATURE_IPV6 | ||
595 | if (inet6 && flags&NETSTAT_RAW) | ||
596 | do_info(_PATH_PROCNET_RAW6,"AF INET6 (raw)",raw_do_one); | ||
597 | #endif | ||
598 | if (flags&NETSTAT_UNIX) { | ||
599 | printf("Active UNIX domain sockets "); | ||
600 | if ((flags&(NETSTAT_LISTENING|NETSTAT_CONNECTED))==(NETSTAT_LISTENING|NETSTAT_CONNECTED)) | ||
601 | printf("(servers and established)"); | ||
602 | else { | ||
603 | if (flags&NETSTAT_LISTENING) | ||
604 | printf("(only servers)"); | ||
605 | else | ||
606 | printf("(w/o servers)"); | ||
607 | } | ||
608 | |||
609 | printf("\nProto RefCnt Flags Type State I-Node Path\n"); | ||
610 | do_info(_PATH_PROCNET_UNIX,"AF UNIX",unix_do_one); | ||
611 | } | ||
612 | return 0; | ||
613 | } | ||
diff --git a/networking/nslookup.c b/networking/nslookup.c new file mode 100644 index 000000000..cc5ff95d6 --- /dev/null +++ b/networking/nslookup.c | |||
@@ -0,0 +1,149 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * Mini nslookup implementation for busybox | ||
4 | * | ||
5 | * Copyright (C) 1999,2000 by Lineo, inc. and John Beppu | ||
6 | * Copyright (C) 1999,2000,2001 by John Beppu <beppu@codepoet.org> | ||
7 | * | ||
8 | * Correct default name server display and explicit name server option | ||
9 | * added by Ben Zeckel <bzeckel@hmc.edu> June 2001 | ||
10 | * | ||
11 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | ||
12 | */ | ||
13 | |||
14 | #include <resolv.h> | ||
15 | #include "busybox.h" | ||
16 | |||
17 | /* | ||
18 | * I'm only implementing non-interactive mode; | ||
19 | * I totally forgot nslookup even had an interactive mode. | ||
20 | */ | ||
21 | |||
22 | /* Examples of 'standard' nslookup output | ||
23 | * $ nslookup yahoo.com | ||
24 | * Server: 128.193.0.10 | ||
25 | * Address: 128.193.0.10#53 | ||
26 | * | ||
27 | * Non-authoritative answer: | ||
28 | * Name: yahoo.com | ||
29 | * Address: 216.109.112.135 | ||
30 | * Name: yahoo.com | ||
31 | * Address: 66.94.234.13 | ||
32 | * | ||
33 | * $ nslookup 204.152.191.37 | ||
34 | * Server: 128.193.4.20 | ||
35 | * Address: 128.193.4.20#53 | ||
36 | * | ||
37 | * Non-authoritative answer: | ||
38 | * 37.191.152.204.in-addr.arpa canonical name = 37.32-27.191.152.204.in-addr.arpa. | ||
39 | * 37.32-27.191.152.204.in-addr.arpa name = zeus-pub2.kernel.org. | ||
40 | * | ||
41 | * Authoritative answers can be found from: | ||
42 | * 32-27.191.152.204.in-addr.arpa nameserver = ns1.kernel.org. | ||
43 | * 32-27.191.152.204.in-addr.arpa nameserver = ns2.kernel.org. | ||
44 | * 32-27.191.152.204.in-addr.arpa nameserver = ns3.kernel.org. | ||
45 | * ns1.kernel.org internet address = 140.211.167.34 | ||
46 | * ns2.kernel.org internet address = 204.152.191.4 | ||
47 | * ns3.kernel.org internet address = 204.152.191.36 | ||
48 | */ | ||
49 | |||
50 | static int sockaddr_to_dotted(struct sockaddr *saddr, char *buf, int buflen) | ||
51 | { | ||
52 | if (buflen <= 0) return -1; | ||
53 | buf[0] = '\0'; | ||
54 | if (saddr->sa_family == AF_INET) { | ||
55 | inet_ntop(AF_INET, &((struct sockaddr_in*)saddr)->sin_addr, buf, buflen); | ||
56 | return 0; | ||
57 | } | ||
58 | if (saddr->sa_family == AF_INET6) { | ||
59 | inet_ntop(AF_INET6, &((struct sockaddr_in6*)saddr)->sin6_addr, buf, buflen); | ||
60 | return 0; | ||
61 | } | ||
62 | return -1; | ||
63 | } | ||
64 | |||
65 | static int print_host(const char *hostname, const char *header) | ||
66 | { | ||
67 | char str[128]; /* IPv6 address will fit, hostnames hopefully too */ | ||
68 | struct addrinfo *result = NULL; | ||
69 | int rc; | ||
70 | struct addrinfo hint; | ||
71 | |||
72 | memset(&hint, 0 , sizeof(hint)); | ||
73 | /* hint.ai_family = AF_UNSPEC; - zero anyway */ | ||
74 | /* Needed. Or else we will get each address thrice (or more) | ||
75 | * for each possible socket type (tcp,udp,raw...): */ | ||
76 | hint.ai_socktype = SOCK_STREAM; | ||
77 | // hint.ai_flags = AI_CANONNAME; | ||
78 | rc = getaddrinfo(hostname, NULL /*service*/, &hint, &result); | ||
79 | if (!rc) { | ||
80 | struct addrinfo *cur = result; | ||
81 | // printf("%s\n", cur->ai_canonname); ? | ||
82 | while (cur) { | ||
83 | sockaddr_to_dotted(cur->ai_addr, str, sizeof(str)); | ||
84 | printf("%s %s\nAddress: %s", header, hostname, str); | ||
85 | str[0] = ' '; | ||
86 | if (getnameinfo(cur->ai_addr, cur->ai_addrlen, str+1, sizeof(str)-1, NULL, 0, NI_NAMEREQD)) | ||
87 | str[0] = '\0'; | ||
88 | puts(str); | ||
89 | cur = cur->ai_next; | ||
90 | } | ||
91 | } else { | ||
92 | bb_error_msg("getaddrinfo('%s') failed: %s", hostname, gai_strerror(rc)); | ||
93 | } | ||
94 | freeaddrinfo(result); | ||
95 | return (rc != 0); | ||
96 | } | ||
97 | |||
98 | |||
99 | /* alter the global _res nameserver structure to use | ||
100 | an explicit dns server instead of what is in /etc/resolv.h */ | ||
101 | static void set_default_dns(char *server) | ||
102 | { | ||
103 | struct in_addr server_in_addr; | ||
104 | |||
105 | if (inet_pton(AF_INET, server, &server_in_addr) > 0) { | ||
106 | _res.nscount = 1; | ||
107 | _res.nsaddr_list[0].sin_addr = server_in_addr; | ||
108 | } | ||
109 | } | ||
110 | |||
111 | |||
112 | /* lookup the default nameserver and display it */ | ||
113 | static void server_print(void) | ||
114 | { | ||
115 | char str[INET6_ADDRSTRLEN]; | ||
116 | |||
117 | sockaddr_to_dotted((struct sockaddr*)&_res.nsaddr_list[0], str, sizeof(str)); | ||
118 | print_host(str, "Server:"); | ||
119 | puts(""); | ||
120 | } | ||
121 | |||
122 | |||
123 | int nslookup_main(int argc, char **argv) | ||
124 | { | ||
125 | /* | ||
126 | * initialize DNS structure _res used in printing the default | ||
127 | * name server and in the explicit name server option feature. | ||
128 | */ | ||
129 | |||
130 | res_init(); | ||
131 | |||
132 | /* | ||
133 | * We allow 1 or 2 arguments. | ||
134 | * The first is the name to be looked up and the second is an | ||
135 | * optional DNS server with which to do the lookup. | ||
136 | * More than 3 arguments is an error to follow the pattern of the | ||
137 | * standard nslookup | ||
138 | */ | ||
139 | |||
140 | if (argc < 2 || *argv[1] == '-' || argc > 3) | ||
141 | bb_show_usage(); | ||
142 | else if(argc == 3) | ||
143 | set_default_dns(argv[2]); | ||
144 | |||
145 | server_print(); | ||
146 | return print_host(argv[1], "Name: "); | ||
147 | } | ||
148 | |||
149 | /* $Id: nslookup.c,v 1.33 2004/10/13 07:25:01 andersen Exp $ */ | ||
diff --git a/networking/ping.c b/networking/ping.c new file mode 100644 index 000000000..658c01518 --- /dev/null +++ b/networking/ping.c | |||
@@ -0,0 +1,440 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * $Id: ping.c,v 1.56 2004/03/15 08:28:48 andersen Exp $ | ||
4 | * Mini ping implementation for busybox | ||
5 | * | ||
6 | * Copyright (C) 1999 by Randolph Chung <tausq@debian.org> | ||
7 | * | ||
8 | * Adapted from the ping in netkit-base 0.10: | ||
9 | * Copyright (c) 1989 The Regents of the University of California. | ||
10 | * Derived from software contributed to Berkeley by Mike Muuss. | ||
11 | * | ||
12 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | ||
13 | */ | ||
14 | |||
15 | #include <sys/param.h> | ||
16 | #include <sys/socket.h> | ||
17 | #include <sys/file.h> | ||
18 | #include <sys/times.h> | ||
19 | #include <signal.h> | ||
20 | |||
21 | #include <netinet/in.h> | ||
22 | #include <netinet/ip.h> | ||
23 | #include <netinet/ip_icmp.h> | ||
24 | #include <arpa/inet.h> | ||
25 | #include <netdb.h> | ||
26 | #include <stdio.h> | ||
27 | #include <stdlib.h> | ||
28 | #include <errno.h> | ||
29 | #include <unistd.h> | ||
30 | #include <string.h> | ||
31 | #include <stdlib.h> | ||
32 | #include "busybox.h" | ||
33 | |||
34 | enum { | ||
35 | DEFDATALEN = 56, | ||
36 | MAXIPLEN = 60, | ||
37 | MAXICMPLEN = 76, | ||
38 | MAXPACKET = 65468, | ||
39 | MAX_DUP_CHK = (8 * 128), | ||
40 | MAXWAIT = 10, | ||
41 | PINGINTERVAL = 1 /* second */ | ||
42 | }; | ||
43 | |||
44 | static void ping(const char *host); | ||
45 | |||
46 | /* common routines */ | ||
47 | |||
48 | static int in_cksum(unsigned short *buf, int sz) | ||
49 | { | ||
50 | int nleft = sz; | ||
51 | int sum = 0; | ||
52 | unsigned short *w = buf; | ||
53 | unsigned short ans = 0; | ||
54 | |||
55 | while (nleft > 1) { | ||
56 | sum += *w++; | ||
57 | nleft -= 2; | ||
58 | } | ||
59 | |||
60 | if (nleft == 1) { | ||
61 | *(unsigned char *) (&ans) = *(unsigned char *) w; | ||
62 | sum += ans; | ||
63 | } | ||
64 | |||
65 | sum = (sum >> 16) + (sum & 0xFFFF); | ||
66 | sum += (sum >> 16); | ||
67 | ans = ~sum; | ||
68 | return ans; | ||
69 | } | ||
70 | |||
71 | #ifndef CONFIG_FEATURE_FANCY_PING | ||
72 | |||
73 | /* simple version */ | ||
74 | |||
75 | static char *hostname; | ||
76 | |||
77 | static void noresp(int ign) | ||
78 | { | ||
79 | printf("No response from %s\n", hostname); | ||
80 | exit(EXIT_FAILURE); | ||
81 | } | ||
82 | |||
83 | static void ping(const char *host) | ||
84 | { | ||
85 | struct hostent *h; | ||
86 | struct sockaddr_in pingaddr; | ||
87 | struct icmp *pkt; | ||
88 | int pingsock, c; | ||
89 | char packet[DEFDATALEN + MAXIPLEN + MAXICMPLEN]; | ||
90 | |||
91 | pingsock = create_icmp_socket(); | ||
92 | |||
93 | memset(&pingaddr, 0, sizeof(struct sockaddr_in)); | ||
94 | |||
95 | pingaddr.sin_family = AF_INET; | ||
96 | h = xgethostbyname(host); | ||
97 | memcpy(&pingaddr.sin_addr, h->h_addr, sizeof(pingaddr.sin_addr)); | ||
98 | hostname = h->h_name; | ||
99 | |||
100 | pkt = (struct icmp *) packet; | ||
101 | memset(pkt, 0, sizeof(packet)); | ||
102 | pkt->icmp_type = ICMP_ECHO; | ||
103 | pkt->icmp_cksum = in_cksum((unsigned short *) pkt, sizeof(packet)); | ||
104 | |||
105 | c = sendto(pingsock, packet, DEFDATALEN + ICMP_MINLEN, 0, | ||
106 | (struct sockaddr *) &pingaddr, sizeof(struct sockaddr_in)); | ||
107 | |||
108 | if (c < 0) { | ||
109 | if (ENABLE_FEATURE_CLEAN_UP) close(pingsock); | ||
110 | bb_perror_msg_and_die("sendto"); | ||
111 | } | ||
112 | |||
113 | signal(SIGALRM, noresp); | ||
114 | alarm(5); /* give the host 5000ms to respond */ | ||
115 | /* listen for replies */ | ||
116 | while (1) { | ||
117 | struct sockaddr_in from; | ||
118 | socklen_t fromlen = sizeof(from); | ||
119 | |||
120 | if ((c = recvfrom(pingsock, packet, sizeof(packet), 0, | ||
121 | (struct sockaddr *) &from, &fromlen)) < 0) { | ||
122 | if (errno == EINTR) | ||
123 | continue; | ||
124 | bb_perror_msg("recvfrom"); | ||
125 | continue; | ||
126 | } | ||
127 | if (c >= 76) { /* ip + icmp */ | ||
128 | struct iphdr *iphdr = (struct iphdr *) packet; | ||
129 | |||
130 | pkt = (struct icmp *) (packet + (iphdr->ihl << 2)); /* skip ip hdr */ | ||
131 | if (pkt->icmp_type == ICMP_ECHOREPLY) | ||
132 | break; | ||
133 | } | ||
134 | } | ||
135 | if (ENABLE_FEATURE_CLEAN_UP) close(pingsock); | ||
136 | printf("%s is alive!\n", hostname); | ||
137 | return; | ||
138 | } | ||
139 | |||
140 | int ping_main(int argc, char **argv) | ||
141 | { | ||
142 | argc--; | ||
143 | argv++; | ||
144 | if (argc < 1) | ||
145 | bb_show_usage(); | ||
146 | ping(*argv); | ||
147 | return EXIT_SUCCESS; | ||
148 | } | ||
149 | |||
150 | #else /* ! CONFIG_FEATURE_FANCY_PING */ | ||
151 | |||
152 | /* full(er) version */ | ||
153 | |||
154 | #define OPT_STRING "qc:s:I:" | ||
155 | enum { | ||
156 | OPT_QUIET = 1 << 0, | ||
157 | }; | ||
158 | |||
159 | static struct sockaddr_in pingaddr; | ||
160 | static struct sockaddr_in sourceaddr; | ||
161 | static int pingsock = -1; | ||
162 | static unsigned datalen; /* intentionally uninitialized to work around gcc bug */ | ||
163 | |||
164 | static unsigned long ntransmitted, nreceived, nrepeats, pingcount; | ||
165 | static int myid; | ||
166 | static unsigned long tmin = ULONG_MAX, tmax, tsum; | ||
167 | static char rcvd_tbl[MAX_DUP_CHK / 8]; | ||
168 | |||
169 | static struct hostent *hostent; | ||
170 | |||
171 | static void sendping(int); | ||
172 | static void pingstats(int); | ||
173 | static void unpack(char *, int, struct sockaddr_in *); | ||
174 | |||
175 | #define A(bit) rcvd_tbl[(bit)>>3] /* identify byte in array */ | ||
176 | #define B(bit) (1 << ((bit) & 0x07)) /* identify bit in byte */ | ||
177 | #define SET(bit) (A(bit) |= B(bit)) | ||
178 | #define CLR(bit) (A(bit) &= (~B(bit))) | ||
179 | #define TST(bit) (A(bit) & B(bit)) | ||
180 | |||
181 | /**************************************************************************/ | ||
182 | |||
183 | static void pingstats(int junk) | ||
184 | { | ||
185 | int status; | ||
186 | |||
187 | signal(SIGINT, SIG_IGN); | ||
188 | |||
189 | printf("\n--- %s ping statistics ---\n", hostent->h_name); | ||
190 | printf("%lu packets transmitted, ", ntransmitted); | ||
191 | printf("%lu packets received, ", nreceived); | ||
192 | if (nrepeats) | ||
193 | printf("%lu duplicates, ", nrepeats); | ||
194 | if (ntransmitted) | ||
195 | printf("%lu%% packet loss\n", | ||
196 | (ntransmitted - nreceived) * 100 / ntransmitted); | ||
197 | if (nreceived) | ||
198 | printf("round-trip min/avg/max = %lu.%lu/%lu.%lu/%lu.%lu ms\n", | ||
199 | tmin / 10, tmin % 10, | ||
200 | (tsum / (nreceived + nrepeats)) / 10, | ||
201 | (tsum / (nreceived + nrepeats)) % 10, tmax / 10, tmax % 10); | ||
202 | if (nreceived != 0) | ||
203 | status = EXIT_SUCCESS; | ||
204 | else | ||
205 | status = EXIT_FAILURE; | ||
206 | exit(status); | ||
207 | } | ||
208 | |||
209 | static void sendping(int junk) | ||
210 | { | ||
211 | struct icmp *pkt; | ||
212 | int i; | ||
213 | char packet[datalen + ICMP_MINLEN]; | ||
214 | |||
215 | pkt = (struct icmp *) packet; | ||
216 | |||
217 | pkt->icmp_type = ICMP_ECHO; | ||
218 | pkt->icmp_code = 0; | ||
219 | pkt->icmp_cksum = 0; | ||
220 | pkt->icmp_seq = htons(ntransmitted++); | ||
221 | pkt->icmp_id = myid; | ||
222 | CLR(ntohs(pkt->icmp_seq) % MAX_DUP_CHK); | ||
223 | |||
224 | gettimeofday((struct timeval *) &pkt->icmp_dun, NULL); | ||
225 | pkt->icmp_cksum = in_cksum((unsigned short *) pkt, sizeof(packet)); | ||
226 | |||
227 | i = sendto(pingsock, packet, sizeof(packet), 0, | ||
228 | (struct sockaddr *) &pingaddr, sizeof(struct sockaddr_in)); | ||
229 | |||
230 | if (i < 0) | ||
231 | bb_perror_msg_and_die("sendto"); | ||
232 | else if ((size_t)i != sizeof(packet)) | ||
233 | bb_error_msg_and_die("ping wrote %d chars; %d expected", i, | ||
234 | (int)sizeof(packet)); | ||
235 | |||
236 | signal(SIGALRM, sendping); | ||
237 | if (pingcount == 0 || ntransmitted < pingcount) { /* schedule next in 1s */ | ||
238 | alarm(PINGINTERVAL); | ||
239 | } else { /* done, wait for the last ping to come back */ | ||
240 | /* todo, don't necessarily need to wait so long... */ | ||
241 | signal(SIGALRM, pingstats); | ||
242 | alarm(MAXWAIT); | ||
243 | } | ||
244 | } | ||
245 | |||
246 | static char *icmp_type_name(int id) | ||
247 | { | ||
248 | switch (id) { | ||
249 | case ICMP_ECHOREPLY: return "Echo Reply"; | ||
250 | case ICMP_DEST_UNREACH: return "Destination Unreachable"; | ||
251 | case ICMP_SOURCE_QUENCH: return "Source Quench"; | ||
252 | case ICMP_REDIRECT: return "Redirect (change route)"; | ||
253 | case ICMP_ECHO: return "Echo Request"; | ||
254 | case ICMP_TIME_EXCEEDED: return "Time Exceeded"; | ||
255 | case ICMP_PARAMETERPROB: return "Parameter Problem"; | ||
256 | case ICMP_TIMESTAMP: return "Timestamp Request"; | ||
257 | case ICMP_TIMESTAMPREPLY: return "Timestamp Reply"; | ||
258 | case ICMP_INFO_REQUEST: return "Information Request"; | ||
259 | case ICMP_INFO_REPLY: return "Information Reply"; | ||
260 | case ICMP_ADDRESS: return "Address Mask Request"; | ||
261 | case ICMP_ADDRESSREPLY: return "Address Mask Reply"; | ||
262 | default: return "unknown ICMP type"; | ||
263 | } | ||
264 | } | ||
265 | |||
266 | static void unpack(char *buf, int sz, struct sockaddr_in *from) | ||
267 | { | ||
268 | struct icmp *icmppkt; | ||
269 | struct iphdr *iphdr; | ||
270 | struct timeval tv, *tp; | ||
271 | int hlen, dupflag; | ||
272 | unsigned long triptime; | ||
273 | |||
274 | gettimeofday(&tv, NULL); | ||
275 | |||
276 | /* discard if too short */ | ||
277 | if (sz < (datalen + ICMP_MINLEN)) | ||
278 | return; | ||
279 | |||
280 | /* check IP header */ | ||
281 | iphdr = (struct iphdr *) buf; | ||
282 | hlen = iphdr->ihl << 2; | ||
283 | sz -= hlen; | ||
284 | icmppkt = (struct icmp *) (buf + hlen); | ||
285 | if (icmppkt->icmp_id != myid) | ||
286 | return; /* not our ping */ | ||
287 | |||
288 | if (icmppkt->icmp_type == ICMP_ECHOREPLY) { | ||
289 | u_int16_t recv_seq = ntohs(icmppkt->icmp_seq); | ||
290 | ++nreceived; | ||
291 | tp = (struct timeval *) icmppkt->icmp_data; | ||
292 | |||
293 | if ((tv.tv_usec -= tp->tv_usec) < 0) { | ||
294 | --tv.tv_sec; | ||
295 | tv.tv_usec += 1000000; | ||
296 | } | ||
297 | tv.tv_sec -= tp->tv_sec; | ||
298 | |||
299 | triptime = tv.tv_sec * 10000 + (tv.tv_usec / 100); | ||
300 | tsum += triptime; | ||
301 | if (triptime < tmin) | ||
302 | tmin = triptime; | ||
303 | if (triptime > tmax) | ||
304 | tmax = triptime; | ||
305 | |||
306 | if (TST(recv_seq % MAX_DUP_CHK)) { | ||
307 | ++nrepeats; | ||
308 | --nreceived; | ||
309 | dupflag = 1; | ||
310 | } else { | ||
311 | SET(recv_seq % MAX_DUP_CHK); | ||
312 | dupflag = 0; | ||
313 | } | ||
314 | |||
315 | if (option_mask32 & OPT_QUIET) | ||
316 | return; | ||
317 | |||
318 | printf("%d bytes from %s: icmp_seq=%u", sz, | ||
319 | inet_ntoa(*(struct in_addr *) &from->sin_addr.s_addr), | ||
320 | recv_seq); | ||
321 | printf(" ttl=%d", iphdr->ttl); | ||
322 | printf(" time=%lu.%lu ms", triptime / 10, triptime % 10); | ||
323 | if (dupflag) | ||
324 | printf(" (DUP!)"); | ||
325 | puts(""); | ||
326 | } else | ||
327 | if (icmppkt->icmp_type != ICMP_ECHO) | ||
328 | bb_error_msg("warning: got ICMP %d (%s)", | ||
329 | icmppkt->icmp_type, icmp_type_name(icmppkt->icmp_type)); | ||
330 | fflush(stdout); | ||
331 | } | ||
332 | |||
333 | static void ping(const char *host) | ||
334 | { | ||
335 | char packet[datalen + MAXIPLEN + MAXICMPLEN]; | ||
336 | int sockopt; | ||
337 | |||
338 | pingsock = create_icmp_socket(); | ||
339 | |||
340 | if (sourceaddr.sin_addr.s_addr) { | ||
341 | xbind(pingsock, (struct sockaddr*)&sourceaddr, sizeof(sourceaddr)); | ||
342 | } | ||
343 | |||
344 | memset(&pingaddr, 0, sizeof(struct sockaddr_in)); | ||
345 | |||
346 | pingaddr.sin_family = AF_INET; | ||
347 | hostent = xgethostbyname(host); | ||
348 | if (hostent->h_addrtype != AF_INET) | ||
349 | bb_error_msg_and_die("unknown address type; only AF_INET is currently supported"); | ||
350 | |||
351 | memcpy(&pingaddr.sin_addr, hostent->h_addr, sizeof(pingaddr.sin_addr)); | ||
352 | |||
353 | /* enable broadcast pings */ | ||
354 | setsockopt_broadcast(pingsock); | ||
355 | |||
356 | /* set recv buf for broadcast pings */ | ||
357 | sockopt = 48 * 1024; | ||
358 | setsockopt(pingsock, SOL_SOCKET, SO_RCVBUF, (char *) &sockopt, | ||
359 | sizeof(sockopt)); | ||
360 | |||
361 | printf("PING %s (%s)", | ||
362 | hostent->h_name, | ||
363 | inet_ntoa(*(struct in_addr *) &pingaddr.sin_addr.s_addr)); | ||
364 | if (sourceaddr.sin_addr.s_addr) { | ||
365 | printf(" from %s", | ||
366 | inet_ntoa(*(struct in_addr *) &sourceaddr.sin_addr.s_addr)); | ||
367 | } | ||
368 | printf(": %d data bytes\n", datalen); | ||
369 | |||
370 | signal(SIGINT, pingstats); | ||
371 | |||
372 | /* start the ping's going ... */ | ||
373 | sendping(0); | ||
374 | |||
375 | /* listen for replies */ | ||
376 | while (1) { | ||
377 | struct sockaddr_in from; | ||
378 | socklen_t fromlen = (socklen_t) sizeof(from); | ||
379 | int c; | ||
380 | |||
381 | if ((c = recvfrom(pingsock, packet, sizeof(packet), 0, | ||
382 | (struct sockaddr *) &from, &fromlen)) < 0) { | ||
383 | if (errno == EINTR) | ||
384 | continue; | ||
385 | bb_perror_msg("recvfrom"); | ||
386 | continue; | ||
387 | } | ||
388 | unpack(packet, c, &from); | ||
389 | if (pingcount > 0 && nreceived >= pingcount) | ||
390 | break; | ||
391 | } | ||
392 | pingstats(0); | ||
393 | } | ||
394 | |||
395 | /* TODO: consolidate ether-wake.c, dnsd.c, ifupdown.c, nslookup.c | ||
396 | * versions of below thing. BTW we have far too many "%u.%u.%u.%u" too... | ||
397 | */ | ||
398 | static int parse_nipquad(const char *str, struct sockaddr_in* addr) | ||
399 | { | ||
400 | char dummy; | ||
401 | unsigned i1, i2, i3, i4; | ||
402 | if (sscanf(str, "%u.%u.%u.%u%c", | ||
403 | &i1, &i2, &i3, &i4, &dummy) == 4 | ||
404 | && ( (i1|i2|i3|i4) <= 0xff ) | ||
405 | ) { | ||
406 | uint8_t* ptr = (uint8_t*)&addr->sin_addr; | ||
407 | ptr[0] = i1; | ||
408 | ptr[1] = i2; | ||
409 | ptr[2] = i3; | ||
410 | ptr[3] = i4; | ||
411 | return 0; | ||
412 | } | ||
413 | return 1; /* error */ | ||
414 | } | ||
415 | |||
416 | int ping_main(int argc, char **argv) | ||
417 | { | ||
418 | char *opt_c, *opt_s, *opt_I; | ||
419 | |||
420 | datalen = DEFDATALEN; /* initialized here rather than in global scope to work around gcc bug */ | ||
421 | |||
422 | /* exactly one argument needed */ | ||
423 | opt_complementary = "=1"; | ||
424 | getopt32(argc, argv, OPT_STRING, &opt_c, &opt_s, &opt_I); | ||
425 | if (option_mask32 & 2) pingcount = xatoul(opt_c); // -c | ||
426 | if (option_mask32 & 4) datalen = xatou16(opt_s); // -s | ||
427 | if (option_mask32 & 8) { // -I | ||
428 | /* TODO: ping6 accepts iface too: | ||
429 | if_index = if_nametoindex(*argv); | ||
430 | if (!if_index) ... | ||
431 | make it true for ping. */ | ||
432 | if (parse_nipquad(opt_I, &sourceaddr)) | ||
433 | bb_show_usage(); | ||
434 | } | ||
435 | |||
436 | myid = (int16_t) getpid(); | ||
437 | ping(argv[optind]); | ||
438 | return EXIT_SUCCESS; | ||
439 | } | ||
440 | #endif /* ! CONFIG_FEATURE_FANCY_PING */ | ||
diff --git a/networking/ping6.c b/networking/ping6.c new file mode 100644 index 000000000..9f0509e66 --- /dev/null +++ b/networking/ping6.c | |||
@@ -0,0 +1,480 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * $Id: ping6.c,v 1.6 2004/03/15 08:28:48 andersen Exp $ | ||
4 | * Mini ping implementation for busybox | ||
5 | * | ||
6 | * Copyright (C) 1999 by Randolph Chung <tausq@debian.org> | ||
7 | * | ||
8 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | ||
9 | * | ||
10 | * This version of ping is adapted from the ping in netkit-base 0.10, | ||
11 | * which is: | ||
12 | * | ||
13 | * Copyright (c) 1989 The Regents of the University of California. | ||
14 | * All rights reserved. | ||
15 | * | ||
16 | * This code is derived from software contributed to Berkeley by | ||
17 | * Mike Muuss. | ||
18 | * | ||
19 | * Original copyright notice is retained at the end of this file. | ||
20 | * | ||
21 | * This version is an adaptation of ping.c from busybox. | ||
22 | * The code was modified by Bart Visscher <magick@linux-fan.com> | ||
23 | */ | ||
24 | |||
25 | #include <sys/param.h> | ||
26 | #include <sys/socket.h> | ||
27 | #include <sys/file.h> | ||
28 | #include <sys/times.h> | ||
29 | #include <signal.h> | ||
30 | |||
31 | #include <netinet/in.h> | ||
32 | #include <netinet/ip6.h> | ||
33 | #include <netinet/icmp6.h> | ||
34 | #include <arpa/inet.h> | ||
35 | #include <net/if.h> | ||
36 | #include <netdb.h> | ||
37 | #include <stdio.h> | ||
38 | #include <stdlib.h> | ||
39 | #include <errno.h> | ||
40 | #include <unistd.h> | ||
41 | #include <string.h> | ||
42 | #include <stdlib.h> | ||
43 | #include <stddef.h> /* offsetof */ | ||
44 | #include "busybox.h" | ||
45 | |||
46 | enum { | ||
47 | DEFDATALEN = 56, | ||
48 | MAXIPLEN = 60, | ||
49 | MAXICMPLEN = 76, | ||
50 | MAXPACKET = 65468, | ||
51 | MAX_DUP_CHK = (8 * 128), | ||
52 | MAXWAIT = 10, | ||
53 | PINGINTERVAL = 1 /* second */ | ||
54 | }; | ||
55 | |||
56 | static void ping(const char *host); | ||
57 | |||
58 | #ifndef CONFIG_FEATURE_FANCY_PING6 | ||
59 | |||
60 | /* simple version */ | ||
61 | |||
62 | static struct hostent *h; | ||
63 | |||
64 | static void noresp(int ign) | ||
65 | { | ||
66 | printf("No response from %s\n", h->h_name); | ||
67 | exit(EXIT_FAILURE); | ||
68 | } | ||
69 | |||
70 | static void ping(const char *host) | ||
71 | { | ||
72 | struct sockaddr_in6 pingaddr; | ||
73 | struct icmp6_hdr *pkt; | ||
74 | int pingsock, c; | ||
75 | int sockopt; | ||
76 | char packet[DEFDATALEN + MAXIPLEN + MAXICMPLEN]; | ||
77 | |||
78 | pingsock = create_icmp6_socket(); | ||
79 | |||
80 | memset(&pingaddr, 0, sizeof(struct sockaddr_in)); | ||
81 | |||
82 | pingaddr.sin6_family = AF_INET6; | ||
83 | h = xgethostbyname2(host, AF_INET6); | ||
84 | memcpy(&pingaddr.sin6_addr, h->h_addr, sizeof(pingaddr.sin6_addr)); | ||
85 | |||
86 | pkt = (struct icmp6_hdr *) packet; | ||
87 | memset(pkt, 0, sizeof(packet)); | ||
88 | pkt->icmp6_type = ICMP6_ECHO_REQUEST; | ||
89 | |||
90 | sockopt = offsetof(struct icmp6_hdr, icmp6_cksum); | ||
91 | setsockopt(pingsock, SOL_RAW, IPV6_CHECKSUM, (char *) &sockopt, | ||
92 | sizeof(sockopt)); | ||
93 | |||
94 | c = sendto(pingsock, packet, DEFDATALEN + sizeof (struct icmp6_hdr), 0, | ||
95 | (struct sockaddr *) &pingaddr, sizeof(struct sockaddr_in6)); | ||
96 | |||
97 | if (c < 0 || c != sizeof(packet)) { | ||
98 | if (ENABLE_FEATURE_CLEAN_UP) close(pingsock); | ||
99 | bb_perror_msg_and_die("sendto"); | ||
100 | } | ||
101 | |||
102 | signal(SIGALRM, noresp); | ||
103 | alarm(5); /* give the host 5000ms to respond */ | ||
104 | /* listen for replies */ | ||
105 | while (1) { | ||
106 | struct sockaddr_in6 from; | ||
107 | size_t fromlen = sizeof(from); | ||
108 | |||
109 | if ((c = recvfrom(pingsock, packet, sizeof(packet), 0, | ||
110 | (struct sockaddr *) &from, &fromlen)) < 0) { | ||
111 | if (errno == EINTR) | ||
112 | continue; | ||
113 | bb_perror_msg("recvfrom"); | ||
114 | continue; | ||
115 | } | ||
116 | if (c >= 8) { /* icmp6_hdr */ | ||
117 | pkt = (struct icmp6_hdr *) packet; | ||
118 | if (pkt->icmp6_type == ICMP6_ECHO_REPLY) | ||
119 | break; | ||
120 | } | ||
121 | } | ||
122 | if (ENABLE_FEATURE_CLEAN_UP) close(pingsock); | ||
123 | printf("%s is alive!\n", h->h_name); | ||
124 | return; | ||
125 | } | ||
126 | |||
127 | int ping6_main(int argc, char **argv) | ||
128 | { | ||
129 | argc--; | ||
130 | argv++; | ||
131 | if (argc < 1) | ||
132 | bb_show_usage(); | ||
133 | ping(*argv); | ||
134 | return EXIT_SUCCESS; | ||
135 | } | ||
136 | |||
137 | #else /* ! CONFIG_FEATURE_FANCY_PING6 */ | ||
138 | |||
139 | /* full(er) version */ | ||
140 | |||
141 | #define OPT_STRING "qvc:s:I:" | ||
142 | enum { | ||
143 | OPT_QUIET = 1 << 0, | ||
144 | OPT_VERBOSE = 1 << 1, | ||
145 | }; | ||
146 | |||
147 | static struct sockaddr_in6 pingaddr; | ||
148 | static int pingsock = -1; | ||
149 | static unsigned datalen; /* intentionally uninitialized to work around gcc bug */ | ||
150 | static int if_index; | ||
151 | |||
152 | static unsigned long ntransmitted, nreceived, nrepeats, pingcount; | ||
153 | static int myid; | ||
154 | static unsigned long tmin = ULONG_MAX, tmax, tsum; | ||
155 | static char rcvd_tbl[MAX_DUP_CHK / 8]; | ||
156 | |||
157 | static struct hostent *hostent; | ||
158 | |||
159 | static void sendping(int); | ||
160 | static void pingstats(int); | ||
161 | static void unpack(char *, int, struct sockaddr_in6 *, int); | ||
162 | |||
163 | #define A(bit) rcvd_tbl[(bit)>>3] /* identify byte in array */ | ||
164 | #define B(bit) (1 << ((bit) & 0x07)) /* identify bit in byte */ | ||
165 | #define SET(bit) (A(bit) |= B(bit)) | ||
166 | #define CLR(bit) (A(bit) &= (~B(bit))) | ||
167 | #define TST(bit) (A(bit) & B(bit)) | ||
168 | |||
169 | /**************************************************************************/ | ||
170 | |||
171 | static void pingstats(int junk) | ||
172 | { | ||
173 | int status; | ||
174 | |||
175 | signal(SIGINT, SIG_IGN); | ||
176 | |||
177 | printf("\n--- %s ping statistics ---\n", hostent->h_name); | ||
178 | printf("%lu packets transmitted, ", ntransmitted); | ||
179 | printf("%lu packets received, ", nreceived); | ||
180 | if (nrepeats) | ||
181 | printf("%lu duplicates, ", nrepeats); | ||
182 | if (ntransmitted) | ||
183 | printf("%lu%% packet loss\n", | ||
184 | (ntransmitted - nreceived) * 100 / ntransmitted); | ||
185 | if (nreceived) | ||
186 | printf("round-trip min/avg/max = %lu.%lu/%lu.%lu/%lu.%lu ms\n", | ||
187 | tmin / 10, tmin % 10, | ||
188 | (tsum / (nreceived + nrepeats)) / 10, | ||
189 | (tsum / (nreceived + nrepeats)) % 10, tmax / 10, tmax % 10); | ||
190 | if (nreceived != 0) | ||
191 | status = EXIT_SUCCESS; | ||
192 | else | ||
193 | status = EXIT_FAILURE; | ||
194 | exit(status); | ||
195 | } | ||
196 | |||
197 | static void sendping(int junk) | ||
198 | { | ||
199 | struct icmp6_hdr *pkt; | ||
200 | int i; | ||
201 | char packet[datalen + sizeof (struct icmp6_hdr)]; | ||
202 | |||
203 | pkt = (struct icmp6_hdr *) packet; | ||
204 | |||
205 | pkt->icmp6_type = ICMP6_ECHO_REQUEST; | ||
206 | pkt->icmp6_code = 0; | ||
207 | pkt->icmp6_cksum = 0; | ||
208 | pkt->icmp6_seq = htons(ntransmitted++); | ||
209 | pkt->icmp6_id = myid; | ||
210 | CLR(pkt->icmp6_seq % MAX_DUP_CHK); | ||
211 | |||
212 | gettimeofday((struct timeval *) &pkt->icmp6_data8[4], NULL); | ||
213 | |||
214 | i = sendto(pingsock, packet, sizeof(packet), 0, | ||
215 | (struct sockaddr *) &pingaddr, sizeof(struct sockaddr_in6)); | ||
216 | |||
217 | if (i < 0) | ||
218 | bb_perror_msg_and_die("sendto"); | ||
219 | else if ((size_t)i != sizeof(packet)) | ||
220 | bb_error_msg_and_die("ping wrote %d chars; %d expected", i, | ||
221 | (int)sizeof(packet)); | ||
222 | |||
223 | signal(SIGALRM, sendping); | ||
224 | if (pingcount == 0 || ntransmitted < pingcount) { /* schedule next in 1s */ | ||
225 | alarm(PINGINTERVAL); | ||
226 | } else { /* done, wait for the last ping to come back */ | ||
227 | /* todo, don't necessarily need to wait so long... */ | ||
228 | signal(SIGALRM, pingstats); | ||
229 | alarm(MAXWAIT); | ||
230 | } | ||
231 | } | ||
232 | |||
233 | /* RFC3542 changed some definitions from RFC2292 for no good reason, whee ! | ||
234 | * the newer 3542 uses a MLD_ prefix where as 2292 uses ICMP6_ prefix */ | ||
235 | #ifndef MLD_LISTENER_QUERY | ||
236 | # define MLD_LISTENER_QUERY ICMP6_MEMBERSHIP_QUERY | ||
237 | #endif | ||
238 | #ifndef MLD_LISTENER_REPORT | ||
239 | # define MLD_LISTENER_REPORT ICMP6_MEMBERSHIP_REPORT | ||
240 | #endif | ||
241 | #ifndef MLD_LISTENER_REDUCTION | ||
242 | # define MLD_LISTENER_REDUCTION ICMP6_MEMBERSHIP_REDUCTION | ||
243 | #endif | ||
244 | static char *icmp6_type_name(int id) | ||
245 | { | ||
246 | switch (id) { | ||
247 | case ICMP6_DST_UNREACH: return "Destination Unreachable"; | ||
248 | case ICMP6_PACKET_TOO_BIG: return "Packet too big"; | ||
249 | case ICMP6_TIME_EXCEEDED: return "Time Exceeded"; | ||
250 | case ICMP6_PARAM_PROB: return "Parameter Problem"; | ||
251 | case ICMP6_ECHO_REPLY: return "Echo Reply"; | ||
252 | case ICMP6_ECHO_REQUEST: return "Echo Request"; | ||
253 | case MLD_LISTENER_QUERY: return "Listener Query"; | ||
254 | case MLD_LISTENER_REPORT: return "Listener Report"; | ||
255 | case MLD_LISTENER_REDUCTION: return "Listener Reduction"; | ||
256 | default: return "unknown ICMP type"; | ||
257 | } | ||
258 | } | ||
259 | |||
260 | static void unpack(char *packet, int sz, struct sockaddr_in6 *from, int hoplimit) | ||
261 | { | ||
262 | struct icmp6_hdr *icmppkt; | ||
263 | struct timeval tv, *tp; | ||
264 | int dupflag; | ||
265 | unsigned long triptime; | ||
266 | char buf[INET6_ADDRSTRLEN]; | ||
267 | |||
268 | gettimeofday(&tv, NULL); | ||
269 | |||
270 | /* discard if too short */ | ||
271 | if (sz < (datalen + sizeof(struct icmp6_hdr))) | ||
272 | return; | ||
273 | |||
274 | icmppkt = (struct icmp6_hdr *) packet; | ||
275 | if (icmppkt->icmp6_id != myid) | ||
276 | return; /* not our ping */ | ||
277 | |||
278 | if (icmppkt->icmp6_type == ICMP6_ECHO_REPLY) { | ||
279 | ++nreceived; | ||
280 | tp = (struct timeval *) &icmppkt->icmp6_data8[4]; | ||
281 | |||
282 | if ((tv.tv_usec -= tp->tv_usec) < 0) { | ||
283 | --tv.tv_sec; | ||
284 | tv.tv_usec += 1000000; | ||
285 | } | ||
286 | tv.tv_sec -= tp->tv_sec; | ||
287 | |||
288 | triptime = tv.tv_sec * 10000 + (tv.tv_usec / 100); | ||
289 | tsum += triptime; | ||
290 | if (triptime < tmin) | ||
291 | tmin = triptime; | ||
292 | if (triptime > tmax) | ||
293 | tmax = triptime; | ||
294 | |||
295 | if (TST(icmppkt->icmp6_seq % MAX_DUP_CHK)) { | ||
296 | ++nrepeats; | ||
297 | --nreceived; | ||
298 | dupflag = 1; | ||
299 | } else { | ||
300 | SET(icmppkt->icmp6_seq % MAX_DUP_CHK); | ||
301 | dupflag = 0; | ||
302 | } | ||
303 | |||
304 | if (option_mask32 & OPT_QUIET) | ||
305 | return; | ||
306 | |||
307 | printf("%d bytes from %s: icmp6_seq=%u", sz, | ||
308 | inet_ntop(AF_INET6, &pingaddr.sin6_addr, | ||
309 | buf, sizeof(buf)), | ||
310 | icmppkt->icmp6_seq); | ||
311 | printf(" ttl=%d time=%lu.%lu ms", hoplimit, | ||
312 | triptime / 10, triptime % 10); | ||
313 | if (dupflag) | ||
314 | printf(" (DUP!)"); | ||
315 | puts(""); | ||
316 | } else | ||
317 | if (icmppkt->icmp6_type != ICMP6_ECHO_REQUEST) | ||
318 | bb_error_msg("warning: got ICMP %d (%s)", | ||
319 | icmppkt->icmp6_type, icmp6_type_name(icmppkt->icmp6_type)); | ||
320 | } | ||
321 | |||
322 | static void ping(const char *host) | ||
323 | { | ||
324 | char packet[datalen + MAXIPLEN + MAXICMPLEN]; | ||
325 | char buf[INET6_ADDRSTRLEN]; | ||
326 | int sockopt; | ||
327 | struct msghdr msg; | ||
328 | struct sockaddr_in6 from; | ||
329 | struct iovec iov; | ||
330 | char control_buf[CMSG_SPACE(36)]; | ||
331 | |||
332 | pingsock = create_icmp6_socket(); | ||
333 | |||
334 | memset(&pingaddr, 0, sizeof(struct sockaddr_in)); | ||
335 | |||
336 | pingaddr.sin6_family = AF_INET6; | ||
337 | hostent = xgethostbyname2(host, AF_INET6); | ||
338 | if (hostent->h_addrtype != AF_INET6) | ||
339 | bb_error_msg_and_die("unknown address type; only AF_INET6 is currently supported"); | ||
340 | |||
341 | memcpy(&pingaddr.sin6_addr, hostent->h_addr, sizeof(pingaddr.sin6_addr)); | ||
342 | |||
343 | #ifdef ICMP6_FILTER | ||
344 | { | ||
345 | struct icmp6_filter filt; | ||
346 | if (!(option_mask32 & OPT_VERBOSE)) { | ||
347 | ICMP6_FILTER_SETBLOCKALL(&filt); | ||
348 | ICMP6_FILTER_SETPASS(ICMP6_ECHO_REPLY, &filt); | ||
349 | } else { | ||
350 | ICMP6_FILTER_SETPASSALL(&filt); | ||
351 | } | ||
352 | if (setsockopt(pingsock, IPPROTO_ICMPV6, ICMP6_FILTER, &filt, | ||
353 | sizeof(filt)) < 0) | ||
354 | bb_error_msg_and_die("setsockopt(ICMP6_FILTER)"); | ||
355 | } | ||
356 | #endif /*ICMP6_FILTER*/ | ||
357 | |||
358 | /* enable broadcast pings */ | ||
359 | setsockopt_broadcast(pingsock); | ||
360 | |||
361 | /* set recv buf for broadcast pings */ | ||
362 | sockopt = 48 * 1024; | ||
363 | setsockopt(pingsock, SOL_SOCKET, SO_RCVBUF, (char *) &sockopt, | ||
364 | sizeof(sockopt)); | ||
365 | |||
366 | sockopt = offsetof(struct icmp6_hdr, icmp6_cksum); | ||
367 | setsockopt(pingsock, SOL_RAW, IPV6_CHECKSUM, (char *) &sockopt, | ||
368 | sizeof(sockopt)); | ||
369 | |||
370 | sockopt = 1; | ||
371 | setsockopt(pingsock, SOL_IPV6, IPV6_HOPLIMIT, (char *) &sockopt, | ||
372 | sizeof(sockopt)); | ||
373 | |||
374 | if (if_index) | ||
375 | pingaddr.sin6_scope_id = if_index; | ||
376 | |||
377 | printf("PING %s (%s): %d data bytes\n", | ||
378 | hostent->h_name, | ||
379 | inet_ntop(AF_INET6, &pingaddr.sin6_addr, | ||
380 | buf, sizeof(buf)), | ||
381 | datalen); | ||
382 | |||
383 | signal(SIGINT, pingstats); | ||
384 | |||
385 | /* start the ping's going ... */ | ||
386 | sendping(0); | ||
387 | |||
388 | /* listen for replies */ | ||
389 | msg.msg_name = &from; | ||
390 | msg.msg_namelen = sizeof(from); | ||
391 | msg.msg_iov = &iov; | ||
392 | msg.msg_iovlen = 1; | ||
393 | msg.msg_control = control_buf; | ||
394 | iov.iov_base = packet; | ||
395 | iov.iov_len = sizeof(packet); | ||
396 | while (1) { | ||
397 | int c; | ||
398 | struct cmsghdr *cmsgptr = NULL; | ||
399 | int hoplimit = -1; | ||
400 | msg.msg_controllen = sizeof(control_buf); | ||
401 | |||
402 | if ((c = recvmsg(pingsock, &msg, 0)) < 0) { | ||
403 | if (errno == EINTR) | ||
404 | continue; | ||
405 | bb_perror_msg("recvfrom"); | ||
406 | continue; | ||
407 | } | ||
408 | for (cmsgptr = CMSG_FIRSTHDR(&msg); cmsgptr != NULL; | ||
409 | cmsgptr = CMSG_NXTHDR(&msg, cmsgptr)) { | ||
410 | if (cmsgptr->cmsg_level == SOL_IPV6 && | ||
411 | cmsgptr->cmsg_type == IPV6_HOPLIMIT ) { | ||
412 | hoplimit = *(int*)CMSG_DATA(cmsgptr); | ||
413 | } | ||
414 | } | ||
415 | unpack(packet, c, &from, hoplimit); | ||
416 | if (pingcount > 0 && nreceived >= pingcount) | ||
417 | break; | ||
418 | } | ||
419 | pingstats(0); | ||
420 | } | ||
421 | |||
422 | int ping6_main(int argc, char **argv) | ||
423 | { | ||
424 | char *opt_c, *opt_s, *opt_I; | ||
425 | |||
426 | datalen = DEFDATALEN; /* initialized here rather than in global scope to work around gcc bug */ | ||
427 | |||
428 | /* exactly one argument needed, -v and -q don't mix */ | ||
429 | opt_complementary = "=1:q--v:v--q"; | ||
430 | getopt32(argc, argv, OPT_STRING, &opt_c, &opt_s, &opt_I); | ||
431 | if (option_mask32 & 4) pingcount = xatoul(opt_c); // -c | ||
432 | if (option_mask32 & 8) datalen = xatou16(opt_s); // -s | ||
433 | if (option_mask32 & 0x10) { // -I | ||
434 | if_index = if_nametoindex(opt_I); | ||
435 | if (!if_index) | ||
436 | bb_error_msg_and_die( | ||
437 | "%s: invalid interface name", opt_I); | ||
438 | } | ||
439 | |||
440 | myid = (int16_t)getpid(); | ||
441 | ping(argv[optind]); | ||
442 | return EXIT_SUCCESS; | ||
443 | } | ||
444 | #endif /* ! CONFIG_FEATURE_FANCY_PING6 */ | ||
445 | |||
446 | /* | ||
447 | * Copyright (c) 1989 The Regents of the University of California. | ||
448 | * All rights reserved. | ||
449 | * | ||
450 | * This code is derived from software contributed to Berkeley by | ||
451 | * Mike Muuss. | ||
452 | * | ||
453 | * Redistribution and use in source and binary forms, with or without | ||
454 | * modification, are permitted provided that the following conditions | ||
455 | * are met: | ||
456 | * 1. Redistributions of source code must retain the above copyright | ||
457 | * notice, this list of conditions and the following disclaimer. | ||
458 | * 2. Redistributions in binary form must reproduce the above copyright | ||
459 | * notice, this list of conditions and the following disclaimer in the | ||
460 | * documentation and/or other materials provided with the distribution. | ||
461 | * | ||
462 | * 3. <BSD Advertising Clause omitted per the July 22, 1999 licensing change | ||
463 | * ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change> | ||
464 | * | ||
465 | * 4. Neither the name of the University nor the names of its contributors | ||
466 | * may be used to endorse or promote products derived from this software | ||
467 | * without specific prior written permission. | ||
468 | * | ||
469 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | ||
470 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||
471 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | ||
472 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | ||
473 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||
474 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | ||
475 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | ||
476 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | ||
477 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | ||
478 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | ||
479 | * SUCH DAMAGE. | ||
480 | */ | ||
diff --git a/networking/route.c b/networking/route.c new file mode 100644 index 000000000..a8926f44e --- /dev/null +++ b/networking/route.c | |||
@@ -0,0 +1,703 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* route | ||
3 | * | ||
4 | * Similar to the standard Unix route, but with only the necessary | ||
5 | * parts for AF_INET and AF_INET6 | ||
6 | * | ||
7 | * Bjorn Wesen, Axis Communications AB | ||
8 | * | ||
9 | * Author of the original route: | ||
10 | * Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org> | ||
11 | * (derived from FvK's 'route.c 1.70 01/04/94') | ||
12 | * | ||
13 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | ||
14 | * | ||
15 | * $Id: route.c,v 1.26 2004/03/19 23:27:08 mjn3 Exp $ | ||
16 | * | ||
17 | * displayroute() code added by Vladimir N. Oleynik <dzo@simtreas.ru> | ||
18 | * adjustments by Larry Doolittle <LRDoolittle@lbl.gov> | ||
19 | * | ||
20 | * IPV6 support added by Bart Visscher <magick@linux-fan.com> | ||
21 | */ | ||
22 | |||
23 | /* 2004/03/09 Manuel Novoa III <mjn3@codepoet.org> | ||
24 | * | ||
25 | * Rewritten to fix several bugs, add additional error checking, and | ||
26 | * remove ridiculous amounts of bloat. | ||
27 | */ | ||
28 | |||
29 | #include "busybox.h" | ||
30 | #include "inet_common.h" | ||
31 | #include <getopt.h> | ||
32 | #include <net/route.h> | ||
33 | #include <net/if.h> | ||
34 | |||
35 | |||
36 | #ifndef RTF_UP | ||
37 | /* Keep this in sync with /usr/src/linux/include/linux/route.h */ | ||
38 | #define RTF_UP 0x0001 /* route usable */ | ||
39 | #define RTF_GATEWAY 0x0002 /* destination is a gateway */ | ||
40 | #define RTF_HOST 0x0004 /* host entry (net otherwise) */ | ||
41 | #define RTF_REINSTATE 0x0008 /* reinstate route after tmout */ | ||
42 | #define RTF_DYNAMIC 0x0010 /* created dyn. (by redirect) */ | ||
43 | #define RTF_MODIFIED 0x0020 /* modified dyn. (by redirect) */ | ||
44 | #define RTF_MTU 0x0040 /* specific MTU for this route */ | ||
45 | #ifndef RTF_MSS | ||
46 | #define RTF_MSS RTF_MTU /* Compatibility :-( */ | ||
47 | #endif | ||
48 | #define RTF_WINDOW 0x0080 /* per route window clamping */ | ||
49 | #define RTF_IRTT 0x0100 /* Initial round trip time */ | ||
50 | #define RTF_REJECT 0x0200 /* Reject route */ | ||
51 | #endif | ||
52 | |||
53 | #if defined (SIOCADDRTOLD) || defined (RTF_IRTT) /* route */ | ||
54 | #define HAVE_NEW_ADDRT 1 | ||
55 | #endif | ||
56 | |||
57 | #if HAVE_NEW_ADDRT | ||
58 | #define mask_in_addr(x) (((struct sockaddr_in *)&((x).rt_genmask))->sin_addr.s_addr) | ||
59 | #define full_mask(x) (x) | ||
60 | #else | ||
61 | #define mask_in_addr(x) ((x).rt_genmask) | ||
62 | #define full_mask(x) (((struct sockaddr_in *)&(x))->sin_addr.s_addr) | ||
63 | #endif | ||
64 | |||
65 | /* The RTACTION entries must agree with tbl_verb[] below! */ | ||
66 | #define RTACTION_ADD 1 | ||
67 | #define RTACTION_DEL 2 | ||
68 | |||
69 | /* For the various tbl_*[] arrays, the 1st byte is the offset to | ||
70 | * the next entry and the 2nd byte is return value. */ | ||
71 | |||
72 | #define NET_FLAG 1 | ||
73 | #define HOST_FLAG 2 | ||
74 | |||
75 | /* We remap '-' to '#' to avoid problems with getopt. */ | ||
76 | static const char tbl_hash_net_host[] = | ||
77 | "\007\001#net\0" | ||
78 | /* "\010\002#host\0" */ | ||
79 | "\007\002#host" /* Since last, we can save a byte. */ | ||
80 | ; | ||
81 | |||
82 | #define KW_TAKES_ARG 020 | ||
83 | #define KW_SETS_FLAG 040 | ||
84 | |||
85 | #define KW_IPVx_METRIC 020 | ||
86 | #define KW_IPVx_NETMASK 021 | ||
87 | #define KW_IPVx_GATEWAY 022 | ||
88 | #define KW_IPVx_MSS 023 | ||
89 | #define KW_IPVx_WINDOW 024 | ||
90 | #define KW_IPVx_IRTT 025 | ||
91 | #define KW_IPVx_DEVICE 026 | ||
92 | |||
93 | #define KW_IPVx_FLAG_ONLY 040 | ||
94 | #define KW_IPVx_REJECT 040 | ||
95 | #define KW_IPVx_MOD 041 | ||
96 | #define KW_IPVx_DYN 042 | ||
97 | #define KW_IPVx_REINSTATE 043 | ||
98 | |||
99 | static const char tbl_ipvx[] = | ||
100 | /* 020 is the "takes an arg" bit */ | ||
101 | #if HAVE_NEW_ADDRT | ||
102 | "\011\020metric\0" | ||
103 | #endif | ||
104 | "\012\021netmask\0" | ||
105 | "\005\022gw\0" | ||
106 | "\012\022gateway\0" | ||
107 | "\006\023mss\0" | ||
108 | "\011\024window\0" | ||
109 | #ifdef RTF_IRTT | ||
110 | "\007\025irtt\0" | ||
111 | #endif | ||
112 | "\006\026dev\0" | ||
113 | "\011\026device\0" | ||
114 | /* 040 is the "sets a flag" bit - MUST match flags_ipvx[] values below. */ | ||
115 | #ifdef RTF_REJECT | ||
116 | "\011\040reject\0" | ||
117 | #endif | ||
118 | "\006\041mod\0" | ||
119 | "\006\042dyn\0" | ||
120 | /* "\014\043reinstate\0" */ | ||
121 | "\013\043reinstate" /* Since last, we can save a byte. */ | ||
122 | ; | ||
123 | |||
124 | static const int flags_ipvx[] = { /* MUST match tbl_ipvx[] values above. */ | ||
125 | #ifdef RTF_REJECT | ||
126 | RTF_REJECT, | ||
127 | #endif | ||
128 | RTF_MODIFIED, | ||
129 | RTF_DYNAMIC, | ||
130 | RTF_REINSTATE | ||
131 | }; | ||
132 | |||
133 | static int kw_lookup(const char *kwtbl, char ***pargs) | ||
134 | { | ||
135 | if (**pargs) { | ||
136 | do { | ||
137 | if (strcmp(kwtbl+2, **pargs) == 0) { /* Found a match. */ | ||
138 | *pargs += 1; | ||
139 | if (kwtbl[1] & KW_TAKES_ARG) { | ||
140 | if (!**pargs) { /* No more args! */ | ||
141 | bb_show_usage(); | ||
142 | } | ||
143 | *pargs += 1; /* Calling routine will use args[-1]. */ | ||
144 | } | ||
145 | return kwtbl[1]; | ||
146 | } | ||
147 | kwtbl += *kwtbl; | ||
148 | } while (*kwtbl); | ||
149 | } | ||
150 | return 0; | ||
151 | } | ||
152 | |||
153 | /* Add or delete a route, depending on action. */ | ||
154 | |||
155 | static void INET_setroute(int action, char **args) | ||
156 | { | ||
157 | struct rtentry rt; | ||
158 | const char *netmask = NULL; | ||
159 | int skfd, isnet, xflag; | ||
160 | |||
161 | /* Grab the -net or -host options. Remember they were transformed. */ | ||
162 | xflag = kw_lookup(tbl_hash_net_host, &args); | ||
163 | |||
164 | /* If we did grab -net or -host, make sure we still have an arg left. */ | ||
165 | if (*args == NULL) { | ||
166 | bb_show_usage(); | ||
167 | } | ||
168 | |||
169 | /* Clean out the RTREQ structure. */ | ||
170 | memset((char *) &rt, 0, sizeof(struct rtentry)); | ||
171 | |||
172 | { | ||
173 | const char *target = *args++; | ||
174 | char *prefix; | ||
175 | |||
176 | /* recognize x.x.x.x/mask format. */ | ||
177 | prefix = strchr(target, '/'); | ||
178 | if(prefix) { | ||
179 | int prefix_len; | ||
180 | |||
181 | prefix_len = xatoul_range(prefix+1, 0, 32); | ||
182 | mask_in_addr(rt) = htonl( ~ (0xffffffffUL >> prefix_len)); | ||
183 | *prefix = '\0'; | ||
184 | #if HAVE_NEW_ADDRT | ||
185 | rt.rt_genmask.sa_family = AF_INET; | ||
186 | #endif | ||
187 | } else { | ||
188 | /* Default netmask. */ | ||
189 | netmask = bb_str_default; | ||
190 | } | ||
191 | /* Prefer hostname lookup is -host flag (xflag==1) was given. */ | ||
192 | isnet = INET_resolve(target, (struct sockaddr_in *) &rt.rt_dst, | ||
193 | (xflag & HOST_FLAG)); | ||
194 | if (isnet < 0) { | ||
195 | bb_error_msg_and_die("resolving %s", target); | ||
196 | } | ||
197 | if(prefix) { | ||
198 | /* do not destroy prefix for process args */ | ||
199 | *prefix = '/'; | ||
200 | } | ||
201 | } | ||
202 | |||
203 | if (xflag) { /* Reinit isnet if -net or -host was specified. */ | ||
204 | isnet = (xflag & NET_FLAG); | ||
205 | } | ||
206 | |||
207 | /* Fill in the other fields. */ | ||
208 | rt.rt_flags = ((isnet) ? RTF_UP : (RTF_UP | RTF_HOST)); | ||
209 | |||
210 | while (*args) { | ||
211 | int k = kw_lookup(tbl_ipvx, &args); | ||
212 | const char *args_m1 = args[-1]; | ||
213 | |||
214 | if (k & KW_IPVx_FLAG_ONLY) { | ||
215 | rt.rt_flags |= flags_ipvx[k & 3]; | ||
216 | continue; | ||
217 | } | ||
218 | |||
219 | #if HAVE_NEW_ADDRT | ||
220 | if (k == KW_IPVx_METRIC) { | ||
221 | rt.rt_metric = xatoul(args_m1) + 1; | ||
222 | continue; | ||
223 | } | ||
224 | #endif | ||
225 | |||
226 | if (k == KW_IPVx_NETMASK) { | ||
227 | struct sockaddr mask; | ||
228 | |||
229 | if (mask_in_addr(rt)) { | ||
230 | bb_show_usage(); | ||
231 | } | ||
232 | |||
233 | netmask = args_m1; | ||
234 | isnet = INET_resolve(netmask, (struct sockaddr_in *) &mask, 0); | ||
235 | if (isnet < 0) { | ||
236 | bb_error_msg_and_die("resolving %s", netmask); | ||
237 | } | ||
238 | rt.rt_genmask = full_mask(mask); | ||
239 | continue; | ||
240 | } | ||
241 | |||
242 | if (k == KW_IPVx_GATEWAY) { | ||
243 | if (rt.rt_flags & RTF_GATEWAY) { | ||
244 | bb_show_usage(); | ||
245 | } | ||
246 | |||
247 | isnet = INET_resolve(args_m1, | ||
248 | (struct sockaddr_in *) &rt.rt_gateway, 1); | ||
249 | rt.rt_flags |= RTF_GATEWAY; | ||
250 | |||
251 | if (isnet) { | ||
252 | if (isnet < 0) { | ||
253 | bb_error_msg_and_die("resolving %s", args_m1); | ||
254 | } | ||
255 | bb_error_msg_and_die("gateway %s is a NETWORK", args_m1); | ||
256 | } | ||
257 | continue; | ||
258 | } | ||
259 | |||
260 | if (k == KW_IPVx_MSS) { /* Check valid MSS bounds. */ | ||
261 | rt.rt_flags |= RTF_MSS; | ||
262 | rt.rt_mss = xatoul_range(args_m1, 64, 32768); | ||
263 | continue; | ||
264 | } | ||
265 | |||
266 | if (k == KW_IPVx_WINDOW) { /* Check valid window bounds. */ | ||
267 | rt.rt_flags |= RTF_WINDOW; | ||
268 | rt.rt_window = xatoul_range(args_m1, 128, INT_MAX); | ||
269 | continue; | ||
270 | } | ||
271 | |||
272 | #ifdef RTF_IRTT | ||
273 | if (k == KW_IPVx_IRTT) { | ||
274 | rt.rt_flags |= RTF_IRTT; | ||
275 | rt.rt_irtt = xatoul(args_m1); | ||
276 | rt.rt_irtt *= (sysconf(_SC_CLK_TCK) / 100); /* FIXME */ | ||
277 | #if 0 /* FIXME: do we need to check anything of this? */ | ||
278 | if (rt.rt_irtt < 1 || rt.rt_irtt > (120 * HZ)) { | ||
279 | bb_error_msg_and_die("bad irtt"); | ||
280 | } | ||
281 | #endif | ||
282 | continue; | ||
283 | } | ||
284 | #endif | ||
285 | |||
286 | /* Device is special in that it can be the last arg specified | ||
287 | * and doesn't requre the dev/device keyword in that case. */ | ||
288 | if (!rt.rt_dev && ((k == KW_IPVx_DEVICE) || (!k && !*++args))) { | ||
289 | /* Don't use args_m1 here since args may have changed! */ | ||
290 | rt.rt_dev = args[-1]; | ||
291 | continue; | ||
292 | } | ||
293 | |||
294 | /* Nothing matched. */ | ||
295 | bb_show_usage(); | ||
296 | } | ||
297 | |||
298 | #ifdef RTF_REJECT | ||
299 | if ((rt.rt_flags & RTF_REJECT) && !rt.rt_dev) { | ||
300 | rt.rt_dev = "lo"; | ||
301 | } | ||
302 | #endif | ||
303 | |||
304 | /* sanity checks.. */ | ||
305 | if (mask_in_addr(rt)) { | ||
306 | unsigned long mask = mask_in_addr(rt); | ||
307 | |||
308 | mask = ~ntohl(mask); | ||
309 | if ((rt.rt_flags & RTF_HOST) && mask != 0xffffffff) { | ||
310 | bb_error_msg_and_die("netmask %.8x and host route conflict", | ||
311 | (unsigned int) mask); | ||
312 | } | ||
313 | if (mask & (mask + 1)) { | ||
314 | bb_error_msg_and_die("bogus netmask %s", netmask); | ||
315 | } | ||
316 | mask = ((struct sockaddr_in *) &rt.rt_dst)->sin_addr.s_addr; | ||
317 | if (mask & ~mask_in_addr(rt)) { | ||
318 | bb_error_msg_and_die("netmask and route address conflict"); | ||
319 | } | ||
320 | } | ||
321 | |||
322 | /* Fill out netmask if still unset */ | ||
323 | if ((action == RTACTION_ADD) && (rt.rt_flags & RTF_HOST)) { | ||
324 | mask_in_addr(rt) = 0xffffffff; | ||
325 | } | ||
326 | |||
327 | /* Create a socket to the INET kernel. */ | ||
328 | skfd = xsocket(AF_INET, SOCK_DGRAM, 0); | ||
329 | |||
330 | if (ioctl(skfd, ((action==RTACTION_ADD) ? SIOCADDRT : SIOCDELRT), &rt)<0) { | ||
331 | bb_perror_msg_and_die("SIOC[ADD|DEL]RT"); | ||
332 | } | ||
333 | |||
334 | if (ENABLE_FEATURE_CLEAN_UP) close(skfd); | ||
335 | } | ||
336 | |||
337 | #ifdef CONFIG_FEATURE_IPV6 | ||
338 | |||
339 | static void INET6_setroute(int action, char **args) | ||
340 | { | ||
341 | struct sockaddr_in6 sa6; | ||
342 | struct in6_rtmsg rt; | ||
343 | int prefix_len, skfd; | ||
344 | const char *devname; | ||
345 | |||
346 | /* We know args isn't NULL from the check in route_main. */ | ||
347 | const char *target = *args++; | ||
348 | |||
349 | if (strcmp(target, bb_str_default) == 0) { | ||
350 | prefix_len = 0; | ||
351 | memset(&sa6, 0, sizeof(sa6)); | ||
352 | } else { | ||
353 | char *cp; | ||
354 | if ((cp = strchr(target, '/'))) { /* Yes... const to non is ok. */ | ||
355 | *cp = 0; | ||
356 | prefix_len = xatoul_range(cp+1, 0, 128); | ||
357 | } else { | ||
358 | prefix_len = 128; | ||
359 | } | ||
360 | if (INET6_resolve(target, (struct sockaddr_in6 *) &sa6) < 0) { | ||
361 | bb_error_msg_and_die("resolving %s", target); | ||
362 | } | ||
363 | } | ||
364 | |||
365 | /* Clean out the RTREQ structure. */ | ||
366 | memset((char *) &rt, 0, sizeof(struct in6_rtmsg)); | ||
367 | |||
368 | memcpy(&rt.rtmsg_dst, sa6.sin6_addr.s6_addr, sizeof(struct in6_addr)); | ||
369 | |||
370 | /* Fill in the other fields. */ | ||
371 | rt.rtmsg_dst_len = prefix_len; | ||
372 | rt.rtmsg_flags = ((prefix_len == 128) ? (RTF_UP|RTF_HOST) : RTF_UP); | ||
373 | rt.rtmsg_metric = 1; | ||
374 | |||
375 | devname = NULL; | ||
376 | |||
377 | while (*args) { | ||
378 | int k = kw_lookup(tbl_ipvx, &args); | ||
379 | const char *args_m1 = args[-1]; | ||
380 | |||
381 | if ((k == KW_IPVx_MOD) || (k == KW_IPVx_DYN)) { | ||
382 | rt.rtmsg_flags |= flags_ipvx[k & 3]; | ||
383 | continue; | ||
384 | } | ||
385 | |||
386 | if (k == KW_IPVx_METRIC) { | ||
387 | rt.rtmsg_metric = xatoul(args_m1); | ||
388 | continue; | ||
389 | } | ||
390 | |||
391 | if (k == KW_IPVx_GATEWAY) { | ||
392 | if (rt.rtmsg_flags & RTF_GATEWAY) { | ||
393 | bb_show_usage(); | ||
394 | } | ||
395 | |||
396 | if (INET6_resolve(args_m1, (struct sockaddr_in6 *) &sa6) < 0) { | ||
397 | bb_error_msg_and_die("resolving %s", args_m1); | ||
398 | } | ||
399 | memcpy(&rt.rtmsg_gateway, sa6.sin6_addr.s6_addr, | ||
400 | sizeof(struct in6_addr)); | ||
401 | rt.rtmsg_flags |= RTF_GATEWAY; | ||
402 | continue; | ||
403 | } | ||
404 | |||
405 | /* Device is special in that it can be the last arg specified | ||
406 | * and doesn't requre the dev/device keyword in that case. */ | ||
407 | if (!devname && ((k == KW_IPVx_DEVICE) || (!k && !*++args))) { | ||
408 | /* Don't use args_m1 here since args may have changed! */ | ||
409 | devname = args[-1]; | ||
410 | continue; | ||
411 | } | ||
412 | |||
413 | /* Nothing matched. */ | ||
414 | bb_show_usage(); | ||
415 | } | ||
416 | |||
417 | /* Create a socket to the INET6 kernel. */ | ||
418 | skfd = xsocket(AF_INET6, SOCK_DGRAM, 0); | ||
419 | |||
420 | rt.rtmsg_ifindex = 0; | ||
421 | |||
422 | if (devname) { | ||
423 | struct ifreq ifr; | ||
424 | memset(&ifr, 0, sizeof(ifr)); | ||
425 | strncpy(ifr.ifr_name, devname, sizeof(ifr.ifr_name)); | ||
426 | |||
427 | if (ioctl(skfd, SIOGIFINDEX, &ifr) < 0) { | ||
428 | bb_perror_msg_and_die("SIOGIFINDEX"); | ||
429 | } | ||
430 | rt.rtmsg_ifindex = ifr.ifr_ifindex; | ||
431 | } | ||
432 | |||
433 | /* Tell the kernel to accept this route. */ | ||
434 | if (ioctl(skfd, ((action==RTACTION_ADD) ? SIOCADDRT : SIOCDELRT), &rt)<0) { | ||
435 | bb_perror_msg_and_die("SIOC[ADD|DEL]RT"); | ||
436 | } | ||
437 | |||
438 | if (ENABLE_FEATURE_CLEAN_UP) close(skfd); | ||
439 | } | ||
440 | #endif | ||
441 | |||
442 | static const unsigned int flagvals[] = { /* Must agree with flagchars[]. */ | ||
443 | RTF_GATEWAY, | ||
444 | RTF_HOST, | ||
445 | RTF_REINSTATE, | ||
446 | RTF_DYNAMIC, | ||
447 | RTF_MODIFIED, | ||
448 | #ifdef CONFIG_FEATURE_IPV6 | ||
449 | RTF_DEFAULT, | ||
450 | RTF_ADDRCONF, | ||
451 | RTF_CACHE | ||
452 | #endif | ||
453 | }; | ||
454 | |||
455 | #define IPV4_MASK (RTF_GATEWAY|RTF_HOST|RTF_REINSTATE|RTF_DYNAMIC|RTF_MODIFIED) | ||
456 | #define IPV6_MASK (RTF_GATEWAY|RTF_HOST|RTF_DEFAULT|RTF_ADDRCONF|RTF_CACHE) | ||
457 | |||
458 | static const char flagchars[] = /* Must agree with flagvals[]. */ | ||
459 | "GHRDM" | ||
460 | #ifdef CONFIG_FEATURE_IPV6 | ||
461 | "DAC" | ||
462 | #endif | ||
463 | ; | ||
464 | |||
465 | static | ||
466 | #ifndef CONFIG_FEATURE_IPV6 | ||
467 | __inline | ||
468 | #endif | ||
469 | void set_flags(char *flagstr, int flags) | ||
470 | { | ||
471 | int i; | ||
472 | |||
473 | *flagstr++ = 'U'; | ||
474 | |||
475 | for (i=0 ; (*flagstr = flagchars[i]) != 0 ; i++) { | ||
476 | if (flags & flagvals[i]) { | ||
477 | ++flagstr; | ||
478 | } | ||
479 | } | ||
480 | } | ||
481 | |||
482 | /* also used in netstat */ | ||
483 | void displayroutes(int noresolve, int netstatfmt); | ||
484 | void displayroutes(int noresolve, int netstatfmt) | ||
485 | { | ||
486 | char devname[64], flags[16], sdest[16], sgw[16]; | ||
487 | unsigned long int d, g, m; | ||
488 | int flgs, ref, use, metric, mtu, win, ir; | ||
489 | struct sockaddr_in s_addr; | ||
490 | struct in_addr mask; | ||
491 | |||
492 | FILE *fp = xfopen("/proc/net/route", "r"); | ||
493 | |||
494 | printf("Kernel IP routing table\n" | ||
495 | "Destination Gateway Genmask" | ||
496 | " Flags %s Iface\n", | ||
497 | netstatfmt ? " MSS Window irtt" : "Metric Ref Use"); | ||
498 | |||
499 | if (fscanf(fp, "%*[^\n]\n") < 0) { /* Skip the first line. */ | ||
500 | goto ERROR; /* Empty or missing line, or read error. */ | ||
501 | } | ||
502 | while (1) { | ||
503 | int r; | ||
504 | r = fscanf(fp, "%63s%lx%lx%X%d%d%d%lx%d%d%d\n", | ||
505 | devname, &d, &g, &flgs, &ref, &use, &metric, &m, | ||
506 | &mtu, &win, &ir); | ||
507 | if (r != 11) { | ||
508 | if ((r < 0) && feof(fp)) { /* EOF with no (nonspace) chars read. */ | ||
509 | break; | ||
510 | } | ||
511 | ERROR: | ||
512 | bb_error_msg_and_die("fscanf"); | ||
513 | } | ||
514 | |||
515 | if (!(flgs & RTF_UP)) { /* Skip interfaces that are down. */ | ||
516 | continue; | ||
517 | } | ||
518 | |||
519 | set_flags(flags, (flgs & IPV4_MASK)); | ||
520 | #ifdef RTF_REJECT | ||
521 | if (flgs & RTF_REJECT) { | ||
522 | flags[0] = '!'; | ||
523 | } | ||
524 | #endif | ||
525 | |||
526 | memset(&s_addr, 0, sizeof(struct sockaddr_in)); | ||
527 | s_addr.sin_family = AF_INET; | ||
528 | s_addr.sin_addr.s_addr = d; | ||
529 | INET_rresolve(sdest, sizeof(sdest), &s_addr, | ||
530 | (noresolve | 0x8000), m); /* Default instead of *. */ | ||
531 | |||
532 | s_addr.sin_addr.s_addr = g; | ||
533 | INET_rresolve(sgw, sizeof(sgw), &s_addr, | ||
534 | (noresolve | 0x4000), m); /* Host instead of net. */ | ||
535 | |||
536 | mask.s_addr = m; | ||
537 | printf("%-16s%-16s%-16s%-6s", sdest, sgw, inet_ntoa(mask), flags); | ||
538 | if (netstatfmt) { | ||
539 | printf("%5d %-5d %6d %s\n", mtu, win, ir, devname); | ||
540 | } else { | ||
541 | printf("%-6d %-2d %7d %s\n", metric, ref, use, devname); | ||
542 | } | ||
543 | } | ||
544 | } | ||
545 | |||
546 | #ifdef CONFIG_FEATURE_IPV6 | ||
547 | |||
548 | static void INET6_displayroutes(int noresolve) | ||
549 | { | ||
550 | char addr6[128], naddr6[128]; | ||
551 | /* In addr6x, we store both 40-byte ':'-delimited ipv6 addresses. | ||
552 | * We read the non-delimited strings into the tail of the buffer | ||
553 | * using fscanf and then modify the buffer by shifting forward | ||
554 | * while inserting ':'s and the nul terminator for the first string. | ||
555 | * Hence the strings are at addr6x and addr6x+40. This generates | ||
556 | * _much_ less code than the previous (upstream) approach. */ | ||
557 | char addr6x[80]; | ||
558 | char iface[16], flags[16]; | ||
559 | int iflags, metric, refcnt, use, prefix_len, slen; | ||
560 | struct sockaddr_in6 snaddr6; | ||
561 | |||
562 | FILE *fp = xfopen("/proc/net/ipv6_route", "r"); | ||
563 | |||
564 | printf("Kernel IPv6 routing table\n%-44s%-40s" | ||
565 | "Flags Metric Ref Use Iface\n", | ||
566 | "Destination", "Next Hop"); | ||
567 | |||
568 | while (1) { | ||
569 | int r; | ||
570 | r = fscanf(fp, "%32s%x%*s%x%32s%x%x%x%x%s\n", | ||
571 | addr6x+14, &prefix_len, &slen, addr6x+40+7, | ||
572 | &metric, &use, &refcnt, &iflags, iface); | ||
573 | if (r != 9) { | ||
574 | if ((r < 0) && feof(fp)) { /* EOF with no (nonspace) chars read. */ | ||
575 | break; | ||
576 | } | ||
577 | ERROR: | ||
578 | bb_error_msg_and_die("fscanf"); | ||
579 | } | ||
580 | |||
581 | /* Do the addr6x shift-and-insert changes to ':'-delimit addresses. | ||
582 | * For now, always do this to validate the proc route format, even | ||
583 | * if the interface is down. */ | ||
584 | { | ||
585 | int i = 0; | ||
586 | char *p = addr6x+14; | ||
587 | |||
588 | do { | ||
589 | if (!*p) { | ||
590 | if (i==40) { /* nul terminator for 1st address? */ | ||
591 | addr6x[39] = 0; /* Fixup... need 0 instead of ':'. */ | ||
592 | ++p; /* Skip and continue. */ | ||
593 | continue; | ||
594 | } | ||
595 | goto ERROR; | ||
596 | } | ||
597 | addr6x[i++] = *p++; | ||
598 | if (!((i+1)%5)) { | ||
599 | addr6x[i++] = ':'; | ||
600 | } | ||
601 | } while (i < 40+28+7); | ||
602 | } | ||
603 | |||
604 | if (!(iflags & RTF_UP)) { /* Skip interfaces that are down. */ | ||
605 | continue; | ||
606 | } | ||
607 | |||
608 | set_flags(flags, (iflags & IPV6_MASK)); | ||
609 | |||
610 | r = 0; | ||
611 | do { | ||
612 | inet_pton(AF_INET6, addr6x + r, | ||
613 | (struct sockaddr *) &snaddr6.sin6_addr); | ||
614 | snaddr6.sin6_family = AF_INET6; | ||
615 | INET6_rresolve(naddr6, sizeof(naddr6), | ||
616 | (struct sockaddr_in6 *) &snaddr6, | ||
617 | 0x0fff /* Apparently, upstream never resolves. */ | ||
618 | ); | ||
619 | |||
620 | if (!r) { /* 1st pass */ | ||
621 | snprintf(addr6, sizeof(addr6), "%s/%d", naddr6, prefix_len); | ||
622 | r += 40; | ||
623 | } else { /* 2nd pass */ | ||
624 | /* Print the info. */ | ||
625 | printf("%-43s %-39s %-5s %-6d %-2d %7d %-8s\n", | ||
626 | addr6, naddr6, flags, metric, refcnt, use, iface); | ||
627 | break; | ||
628 | } | ||
629 | } while (1); | ||
630 | } | ||
631 | } | ||
632 | |||
633 | #endif | ||
634 | |||
635 | #define ROUTE_OPT_A 0x01 | ||
636 | #define ROUTE_OPT_n 0x02 | ||
637 | #define ROUTE_OPT_e 0x04 | ||
638 | #define ROUTE_OPT_INET6 0x08 /* Not an actual option. See below. */ | ||
639 | |||
640 | /* 1st byte is offset to next entry offset. 2nd byte is return value. */ | ||
641 | static const char tbl_verb[] = /* 2nd byte matches RTACTION_* code */ | ||
642 | "\006\001add\0" | ||
643 | "\006\002del\0" | ||
644 | /* "\011\002delete\0" */ | ||
645 | "\010\002delete" /* Since last, we can save a byte. */ | ||
646 | ; | ||
647 | |||
648 | int route_main(int argc, char **argv) | ||
649 | { | ||
650 | unsigned opt; | ||
651 | int what; | ||
652 | char *family; | ||
653 | char **p; | ||
654 | |||
655 | /* First, remap '-net' and '-host' to avoid getopt problems. */ | ||
656 | p = argv; | ||
657 | while (*++p) { | ||
658 | if ((strcmp(*p, "-net") == 0) || (strcmp(*p, "-host") == 0)) { | ||
659 | p[0][0] = '#'; | ||
660 | } | ||
661 | } | ||
662 | |||
663 | opt = getopt32(argc, argv, "A:ne", &family); | ||
664 | |||
665 | if ((opt & ROUTE_OPT_A) && strcmp(family, "inet")) { | ||
666 | #ifdef CONFIG_FEATURE_IPV6 | ||
667 | if (strcmp(family, "inet6") == 0) { | ||
668 | opt |= ROUTE_OPT_INET6; /* Set flag for ipv6. */ | ||
669 | } else | ||
670 | #endif | ||
671 | bb_show_usage(); | ||
672 | } | ||
673 | |||
674 | argv += optind; | ||
675 | |||
676 | /* No more args means display the routing table. */ | ||
677 | if (!*argv) { | ||
678 | int noresolve = (opt & ROUTE_OPT_n) ? 0x0fff : 0; | ||
679 | #ifdef CONFIG_FEATURE_IPV6 | ||
680 | if (opt & ROUTE_OPT_INET6) | ||
681 | INET6_displayroutes(noresolve); | ||
682 | else | ||
683 | #endif | ||
684 | displayroutes(noresolve, opt & ROUTE_OPT_e); | ||
685 | |||
686 | fflush_stdout_and_exit(EXIT_SUCCESS); | ||
687 | } | ||
688 | |||
689 | /* Check verb. At the moment, must be add, del, or delete. */ | ||
690 | what = kw_lookup(tbl_verb, &argv); | ||
691 | if (!what || !*argv) { /* Unknown verb or no more args. */ | ||
692 | bb_show_usage(); | ||
693 | } | ||
694 | |||
695 | #ifdef CONFIG_FEATURE_IPV6 | ||
696 | if (opt & ROUTE_OPT_INET6) | ||
697 | INET6_setroute(what, argv); | ||
698 | else | ||
699 | #endif | ||
700 | INET_setroute(what, argv); | ||
701 | |||
702 | return EXIT_SUCCESS; | ||
703 | } | ||
diff --git a/networking/telnet.c b/networking/telnet.c new file mode 100644 index 000000000..6085d885a --- /dev/null +++ b/networking/telnet.c | |||
@@ -0,0 +1,714 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * telnet implementation for busybox | ||
4 | * | ||
5 | * Author: Tomi Ollila <too@iki.fi> | ||
6 | * Copyright (C) 1994-2000 by Tomi Ollila | ||
7 | * | ||
8 | * Created: Thu Apr 7 13:29:41 1994 too | ||
9 | * Last modified: Fri Jun 9 14:34:24 2000 too | ||
10 | * | ||
11 | * Licensed under the GPL v2 or later, see the file LICENSE in this tarball. | ||
12 | * | ||
13 | * HISTORY | ||
14 | * Revision 3.1 1994/04/17 11:31:54 too | ||
15 | * initial revision | ||
16 | * Modified 2000/06/13 for inclusion into BusyBox by Erik Andersen <andersen@codepoet.org> | ||
17 | * Modified 2001/05/07 to add ability to pass TTYPE to remote host by Jim McQuillan | ||
18 | * <jam@ltsp.org> | ||
19 | * Modified 2004/02/11 to add ability to pass the USER variable to remote host | ||
20 | * by Fernando Silveira <swrh@gmx.net> | ||
21 | * | ||
22 | */ | ||
23 | |||
24 | #include <termios.h> | ||
25 | #include <unistd.h> | ||
26 | #include <errno.h> | ||
27 | #include <stdlib.h> | ||
28 | #include <stdarg.h> | ||
29 | #include <string.h> | ||
30 | #include <signal.h> | ||
31 | #include <arpa/telnet.h> | ||
32 | #include <sys/types.h> | ||
33 | #include <sys/socket.h> | ||
34 | #include <netinet/in.h> | ||
35 | #include "busybox.h" | ||
36 | |||
37 | #ifdef DOTRACE | ||
38 | #include <arpa/inet.h> /* for inet_ntoa()... */ | ||
39 | #define TRACE(x, y) do { if (x) printf y; } while (0) | ||
40 | #else | ||
41 | #define TRACE(x, y) | ||
42 | #endif | ||
43 | |||
44 | #define DATABUFSIZE 128 | ||
45 | #define IACBUFSIZE 128 | ||
46 | |||
47 | enum { | ||
48 | CHM_TRY = 0, | ||
49 | CHM_ON = 1, | ||
50 | CHM_OFF = 2, | ||
51 | |||
52 | UF_ECHO = 0x01, | ||
53 | UF_SGA = 0x02, | ||
54 | |||
55 | TS_0 = 1, | ||
56 | TS_IAC = 2, | ||
57 | TS_OPT = 3, | ||
58 | TS_SUB1 = 4, | ||
59 | TS_SUB2 = 5, | ||
60 | }; | ||
61 | |||
62 | #define WriteCS(fd, str) write(fd, str, sizeof str -1) | ||
63 | |||
64 | typedef unsigned char byte; | ||
65 | |||
66 | /* use globals to reduce size ??? */ /* test this hypothesis later */ | ||
67 | static struct Globalvars { | ||
68 | int netfd; /* console fd:s are 0 and 1 (and 2) */ | ||
69 | /* same buffer used both for network and console read/write */ | ||
70 | char buf[DATABUFSIZE]; /* allocating so static size is smaller */ | ||
71 | byte telstate; /* telnet negotiation state from network input */ | ||
72 | byte telwish; /* DO, DONT, WILL, WONT */ | ||
73 | byte charmode; | ||
74 | byte telflags; | ||
75 | byte gotsig; | ||
76 | byte do_termios; | ||
77 | /* buffer to handle telnet negotiations */ | ||
78 | char iacbuf[IACBUFSIZE]; | ||
79 | short iaclen; /* could even use byte */ | ||
80 | struct termios termios_def; | ||
81 | struct termios termios_raw; | ||
82 | } G; | ||
83 | |||
84 | #define xUSE_GLOBALVAR_PTR /* xUSE... -> don't use :D (makes smaller code) */ | ||
85 | |||
86 | #ifdef USE_GLOBALVAR_PTR | ||
87 | struct Globalvars * Gptr; | ||
88 | #define G (*Gptr) | ||
89 | #endif | ||
90 | |||
91 | static void iacflush(void) | ||
92 | { | ||
93 | write(G.netfd, G.iacbuf, G.iaclen); | ||
94 | G.iaclen = 0; | ||
95 | } | ||
96 | |||
97 | /* Function prototypes */ | ||
98 | static void rawmode(void); | ||
99 | static void cookmode(void); | ||
100 | static void do_linemode(void); | ||
101 | static void will_charmode(void); | ||
102 | static void telopt(byte c); | ||
103 | static int subneg(byte c); | ||
104 | |||
105 | /* Some globals */ | ||
106 | static const int one = 1; | ||
107 | |||
108 | #ifdef CONFIG_FEATURE_TELNET_TTYPE | ||
109 | static char *ttype; | ||
110 | #endif | ||
111 | |||
112 | #ifdef CONFIG_FEATURE_TELNET_AUTOLOGIN | ||
113 | static const char *autologin; | ||
114 | #endif | ||
115 | |||
116 | #ifdef CONFIG_FEATURE_AUTOWIDTH | ||
117 | static int win_width, win_height; | ||
118 | #endif | ||
119 | |||
120 | static void doexit(int ev) | ||
121 | { | ||
122 | cookmode(); | ||
123 | exit(ev); | ||
124 | } | ||
125 | |||
126 | static void conescape(void) | ||
127 | { | ||
128 | char b; | ||
129 | |||
130 | if (G.gotsig) /* came from line mode... go raw */ | ||
131 | rawmode(); | ||
132 | |||
133 | WriteCS(1, "\r\nConsole escape. Commands are:\r\n\n" | ||
134 | " l go to line mode\r\n" | ||
135 | " c go to character mode\r\n" | ||
136 | " z suspend telnet\r\n" | ||
137 | " e exit telnet\r\n"); | ||
138 | |||
139 | if (read(0, &b, 1) <= 0) | ||
140 | doexit(1); | ||
141 | |||
142 | switch (b) | ||
143 | { | ||
144 | case 'l': | ||
145 | if (!G.gotsig) | ||
146 | { | ||
147 | do_linemode(); | ||
148 | goto rrturn; | ||
149 | } | ||
150 | break; | ||
151 | case 'c': | ||
152 | if (G.gotsig) | ||
153 | { | ||
154 | will_charmode(); | ||
155 | goto rrturn; | ||
156 | } | ||
157 | break; | ||
158 | case 'z': | ||
159 | cookmode(); | ||
160 | kill(0, SIGTSTP); | ||
161 | rawmode(); | ||
162 | break; | ||
163 | case 'e': | ||
164 | doexit(0); | ||
165 | } | ||
166 | |||
167 | WriteCS(1, "continuing...\r\n"); | ||
168 | |||
169 | if (G.gotsig) | ||
170 | cookmode(); | ||
171 | |||
172 | rrturn: | ||
173 | G.gotsig = 0; | ||
174 | |||
175 | } | ||
176 | static void handlenetoutput(int len) | ||
177 | { | ||
178 | /* here we could do smart tricks how to handle 0xFF:s in output | ||
179 | * stream like writing twice every sequence of FF:s (thus doing | ||
180 | * many write()s. But I think interactive telnet application does | ||
181 | * not need to be 100% 8-bit clean, so changing every 0xff:s to | ||
182 | * 0x7f:s | ||
183 | * | ||
184 | * 2002-mar-21, Przemyslaw Czerpak (druzus@polbox.com) | ||
185 | * I don't agree. | ||
186 | * first - I cannot use programs like sz/rz | ||
187 | * second - the 0x0D is sent as one character and if the next | ||
188 | * char is 0x0A then it's eaten by a server side. | ||
189 | * third - whay doy you have to make 'many write()s'? | ||
190 | * I don't understand. | ||
191 | * So I implemented it. It's realy useful for me. I hope that | ||
192 | * others people will find it interesting to. | ||
193 | */ | ||
194 | |||
195 | int i, j; | ||
196 | byte * p = (byte*)G.buf; | ||
197 | byte outbuf[4*DATABUFSIZE]; | ||
198 | |||
199 | for (i = len, j = 0; i > 0; i--, p++) | ||
200 | { | ||
201 | if (*p == 0x1d) | ||
202 | { | ||
203 | conescape(); | ||
204 | return; | ||
205 | } | ||
206 | outbuf[j++] = *p; | ||
207 | if (*p == 0xff) | ||
208 | outbuf[j++] = 0xff; | ||
209 | else if (*p == 0x0d) | ||
210 | outbuf[j++] = 0x00; | ||
211 | } | ||
212 | if (j > 0 ) | ||
213 | write(G.netfd, outbuf, j); | ||
214 | } | ||
215 | |||
216 | |||
217 | static void handlenetinput(int len) | ||
218 | { | ||
219 | int i; | ||
220 | int cstart = 0; | ||
221 | |||
222 | for (i = 0; i < len; i++) | ||
223 | { | ||
224 | byte c = G.buf[i]; | ||
225 | |||
226 | if (G.telstate == 0) /* most of the time state == 0 */ | ||
227 | { | ||
228 | if (c == IAC) | ||
229 | { | ||
230 | cstart = i; | ||
231 | G.telstate = TS_IAC; | ||
232 | } | ||
233 | } | ||
234 | else | ||
235 | switch (G.telstate) | ||
236 | { | ||
237 | case TS_0: | ||
238 | if (c == IAC) | ||
239 | G.telstate = TS_IAC; | ||
240 | else | ||
241 | G.buf[cstart++] = c; | ||
242 | break; | ||
243 | |||
244 | case TS_IAC: | ||
245 | if (c == IAC) /* IAC IAC -> 0xFF */ | ||
246 | { | ||
247 | G.buf[cstart++] = c; | ||
248 | G.telstate = TS_0; | ||
249 | break; | ||
250 | } | ||
251 | /* else */ | ||
252 | switch (c) | ||
253 | { | ||
254 | case SB: | ||
255 | G.telstate = TS_SUB1; | ||
256 | break; | ||
257 | case DO: | ||
258 | case DONT: | ||
259 | case WILL: | ||
260 | case WONT: | ||
261 | G.telwish = c; | ||
262 | G.telstate = TS_OPT; | ||
263 | break; | ||
264 | default: | ||
265 | G.telstate = TS_0; /* DATA MARK must be added later */ | ||
266 | } | ||
267 | break; | ||
268 | case TS_OPT: /* WILL, WONT, DO, DONT */ | ||
269 | telopt(c); | ||
270 | G.telstate = TS_0; | ||
271 | break; | ||
272 | case TS_SUB1: /* Subnegotiation */ | ||
273 | case TS_SUB2: /* Subnegotiation */ | ||
274 | if (subneg(c)) | ||
275 | G.telstate = TS_0; | ||
276 | break; | ||
277 | } | ||
278 | } | ||
279 | if (G.telstate) | ||
280 | { | ||
281 | if (G.iaclen) iacflush(); | ||
282 | if (G.telstate == TS_0) G.telstate = 0; | ||
283 | |||
284 | len = cstart; | ||
285 | } | ||
286 | |||
287 | if (len) | ||
288 | write(1, G.buf, len); | ||
289 | } | ||
290 | |||
291 | |||
292 | /* ******************************* */ | ||
293 | |||
294 | static void putiac(int c) | ||
295 | { | ||
296 | G.iacbuf[G.iaclen++] = c; | ||
297 | } | ||
298 | |||
299 | |||
300 | static void putiac2(byte wwdd, byte c) | ||
301 | { | ||
302 | if (G.iaclen + 3 > IACBUFSIZE) | ||
303 | iacflush(); | ||
304 | |||
305 | putiac(IAC); | ||
306 | putiac(wwdd); | ||
307 | putiac(c); | ||
308 | } | ||
309 | |||
310 | #ifdef CONFIG_FEATURE_TELNET_TTYPE | ||
311 | static void putiac_subopt(byte c, char *str) | ||
312 | { | ||
313 | int len = strlen(str) + 6; // ( 2 + 1 + 1 + strlen + 2 ) | ||
314 | |||
315 | if (G.iaclen + len > IACBUFSIZE) | ||
316 | iacflush(); | ||
317 | |||
318 | putiac(IAC); | ||
319 | putiac(SB); | ||
320 | putiac(c); | ||
321 | putiac(0); | ||
322 | |||
323 | while(*str) | ||
324 | putiac(*str++); | ||
325 | |||
326 | putiac(IAC); | ||
327 | putiac(SE); | ||
328 | } | ||
329 | #endif | ||
330 | |||
331 | #ifdef CONFIG_FEATURE_TELNET_AUTOLOGIN | ||
332 | static void putiac_subopt_autologin(void) | ||
333 | { | ||
334 | int len = strlen(autologin) + 6; // (2 + 1 + 1 + strlen + 2) | ||
335 | char *user = "USER"; | ||
336 | |||
337 | if (G.iaclen + len > IACBUFSIZE) | ||
338 | iacflush(); | ||
339 | |||
340 | putiac(IAC); | ||
341 | putiac(SB); | ||
342 | putiac(TELOPT_NEW_ENVIRON); | ||
343 | putiac(TELQUAL_IS); | ||
344 | putiac(NEW_ENV_VAR); | ||
345 | |||
346 | while(*user) | ||
347 | putiac(*user++); | ||
348 | |||
349 | putiac(NEW_ENV_VALUE); | ||
350 | |||
351 | while(*autologin) | ||
352 | putiac(*autologin++); | ||
353 | |||
354 | putiac(IAC); | ||
355 | putiac(SE); | ||
356 | } | ||
357 | #endif | ||
358 | |||
359 | #ifdef CONFIG_FEATURE_AUTOWIDTH | ||
360 | static void putiac_naws(byte c, int x, int y) | ||
361 | { | ||
362 | if (G.iaclen + 9 > IACBUFSIZE) | ||
363 | iacflush(); | ||
364 | |||
365 | putiac(IAC); | ||
366 | putiac(SB); | ||
367 | putiac(c); | ||
368 | |||
369 | putiac((x >> 8) & 0xff); | ||
370 | putiac(x & 0xff); | ||
371 | putiac((y >> 8) & 0xff); | ||
372 | putiac(y & 0xff); | ||
373 | |||
374 | putiac(IAC); | ||
375 | putiac(SE); | ||
376 | } | ||
377 | #endif | ||
378 | |||
379 | /* void putiacstring (subneg strings) */ | ||
380 | |||
381 | /* ******************************* */ | ||
382 | |||
383 | static char const escapecharis[] = "\r\nEscape character is "; | ||
384 | |||
385 | static void setConMode(void) | ||
386 | { | ||
387 | if (G.telflags & UF_ECHO) | ||
388 | { | ||
389 | if (G.charmode == CHM_TRY) { | ||
390 | G.charmode = CHM_ON; | ||
391 | printf("\r\nEntering character mode%s'^]'.\r\n", escapecharis); | ||
392 | rawmode(); | ||
393 | } | ||
394 | } | ||
395 | else | ||
396 | { | ||
397 | if (G.charmode != CHM_OFF) { | ||
398 | G.charmode = CHM_OFF; | ||
399 | printf("\r\nEntering line mode%s'^C'.\r\n", escapecharis); | ||
400 | cookmode(); | ||
401 | } | ||
402 | } | ||
403 | } | ||
404 | |||
405 | /* ******************************* */ | ||
406 | |||
407 | static void will_charmode(void) | ||
408 | { | ||
409 | G.charmode = CHM_TRY; | ||
410 | G.telflags |= (UF_ECHO | UF_SGA); | ||
411 | setConMode(); | ||
412 | |||
413 | putiac2(DO, TELOPT_ECHO); | ||
414 | putiac2(DO, TELOPT_SGA); | ||
415 | iacflush(); | ||
416 | } | ||
417 | |||
418 | static void do_linemode(void) | ||
419 | { | ||
420 | G.charmode = CHM_TRY; | ||
421 | G.telflags &= ~(UF_ECHO | UF_SGA); | ||
422 | setConMode(); | ||
423 | |||
424 | putiac2(DONT, TELOPT_ECHO); | ||
425 | putiac2(DONT, TELOPT_SGA); | ||
426 | iacflush(); | ||
427 | } | ||
428 | |||
429 | /* ******************************* */ | ||
430 | |||
431 | static void to_notsup(char c) | ||
432 | { | ||
433 | if (G.telwish == WILL) putiac2(DONT, c); | ||
434 | else if (G.telwish == DO) putiac2(WONT, c); | ||
435 | } | ||
436 | |||
437 | static void to_echo(void) | ||
438 | { | ||
439 | /* if server requests ECHO, don't agree */ | ||
440 | if (G.telwish == DO) { putiac2(WONT, TELOPT_ECHO); return; } | ||
441 | else if (G.telwish == DONT) return; | ||
442 | |||
443 | if (G.telflags & UF_ECHO) | ||
444 | { | ||
445 | if (G.telwish == WILL) | ||
446 | return; | ||
447 | } | ||
448 | else | ||
449 | if (G.telwish == WONT) | ||
450 | return; | ||
451 | |||
452 | if (G.charmode != CHM_OFF) | ||
453 | G.telflags ^= UF_ECHO; | ||
454 | |||
455 | if (G.telflags & UF_ECHO) | ||
456 | putiac2(DO, TELOPT_ECHO); | ||
457 | else | ||
458 | putiac2(DONT, TELOPT_ECHO); | ||
459 | |||
460 | setConMode(); | ||
461 | WriteCS(1, "\r\n"); /* sudden modec */ | ||
462 | } | ||
463 | |||
464 | static void to_sga(void) | ||
465 | { | ||
466 | /* daemon always sends will/wont, client do/dont */ | ||
467 | |||
468 | if (G.telflags & UF_SGA) | ||
469 | { | ||
470 | if (G.telwish == WILL) | ||
471 | return; | ||
472 | } | ||
473 | else | ||
474 | if (G.telwish == WONT) | ||
475 | return; | ||
476 | |||
477 | if ((G.telflags ^= UF_SGA) & UF_SGA) /* toggle */ | ||
478 | putiac2(DO, TELOPT_SGA); | ||
479 | else | ||
480 | putiac2(DONT, TELOPT_SGA); | ||
481 | |||
482 | return; | ||
483 | } | ||
484 | |||
485 | #ifdef CONFIG_FEATURE_TELNET_TTYPE | ||
486 | static void to_ttype(void) | ||
487 | { | ||
488 | /* Tell server we will (or won't) do TTYPE */ | ||
489 | |||
490 | if(ttype) | ||
491 | putiac2(WILL, TELOPT_TTYPE); | ||
492 | else | ||
493 | putiac2(WONT, TELOPT_TTYPE); | ||
494 | |||
495 | return; | ||
496 | } | ||
497 | #endif | ||
498 | |||
499 | #ifdef CONFIG_FEATURE_TELNET_AUTOLOGIN | ||
500 | static void to_new_environ(void) | ||
501 | { | ||
502 | /* Tell server we will (or will not) do AUTOLOGIN */ | ||
503 | |||
504 | if (autologin) | ||
505 | putiac2(WILL, TELOPT_NEW_ENVIRON); | ||
506 | else | ||
507 | putiac2(WONT, TELOPT_NEW_ENVIRON); | ||
508 | |||
509 | return; | ||
510 | } | ||
511 | #endif | ||
512 | |||
513 | #ifdef CONFIG_FEATURE_AUTOWIDTH | ||
514 | static void to_naws(void) | ||
515 | { | ||
516 | /* Tell server we will do NAWS */ | ||
517 | putiac2(WILL, TELOPT_NAWS); | ||
518 | return; | ||
519 | } | ||
520 | #endif | ||
521 | |||
522 | static void telopt(byte c) | ||
523 | { | ||
524 | switch (c) | ||
525 | { | ||
526 | case TELOPT_ECHO: to_echo(); break; | ||
527 | case TELOPT_SGA: to_sga(); break; | ||
528 | #ifdef CONFIG_FEATURE_TELNET_TTYPE | ||
529 | case TELOPT_TTYPE: to_ttype();break; | ||
530 | #endif | ||
531 | #ifdef CONFIG_FEATURE_TELNET_AUTOLOGIN | ||
532 | case TELOPT_NEW_ENVIRON: to_new_environ(); break; | ||
533 | #endif | ||
534 | #ifdef CONFIG_FEATURE_AUTOWIDTH | ||
535 | case TELOPT_NAWS: to_naws(); | ||
536 | putiac_naws(c, win_width, win_height); | ||
537 | break; | ||
538 | #endif | ||
539 | default: to_notsup(c); | ||
540 | break; | ||
541 | } | ||
542 | } | ||
543 | |||
544 | |||
545 | /* ******************************* */ | ||
546 | |||
547 | /* subnegotiation -- ignore all (except TTYPE,NAWS) */ | ||
548 | |||
549 | static int subneg(byte c) | ||
550 | { | ||
551 | switch (G.telstate) | ||
552 | { | ||
553 | case TS_SUB1: | ||
554 | if (c == IAC) | ||
555 | G.telstate = TS_SUB2; | ||
556 | #ifdef CONFIG_FEATURE_TELNET_TTYPE | ||
557 | else | ||
558 | if (c == TELOPT_TTYPE) | ||
559 | putiac_subopt(TELOPT_TTYPE,ttype); | ||
560 | #endif | ||
561 | #ifdef CONFIG_FEATURE_TELNET_AUTOLOGIN | ||
562 | else | ||
563 | if (c == TELOPT_NEW_ENVIRON) | ||
564 | putiac_subopt_autologin(); | ||
565 | #endif | ||
566 | break; | ||
567 | case TS_SUB2: | ||
568 | if (c == SE) | ||
569 | return TRUE; | ||
570 | G.telstate = TS_SUB1; | ||
571 | /* break; */ | ||
572 | } | ||
573 | return FALSE; | ||
574 | } | ||
575 | |||
576 | /* ******************************* */ | ||
577 | |||
578 | static void fgotsig(int sig) | ||
579 | { | ||
580 | G.gotsig = sig; | ||
581 | } | ||
582 | |||
583 | |||
584 | static void rawmode(void) | ||
585 | { | ||
586 | if (G.do_termios) tcsetattr(0, TCSADRAIN, &G.termios_raw); | ||
587 | } | ||
588 | |||
589 | static void cookmode(void) | ||
590 | { | ||
591 | if (G.do_termios) tcsetattr(0, TCSADRAIN, &G.termios_def); | ||
592 | } | ||
593 | |||
594 | int telnet_main(int argc, char** argv) | ||
595 | { | ||
596 | int len; | ||
597 | struct sockaddr_in s_in; | ||
598 | #ifdef USE_POLL | ||
599 | struct pollfd ufds[2]; | ||
600 | #else | ||
601 | fd_set readfds; | ||
602 | int maxfd; | ||
603 | #endif | ||
604 | |||
605 | #ifdef CONFIG_FEATURE_AUTOWIDTH | ||
606 | get_terminal_width_height(0, &win_width, &win_height); | ||
607 | #endif | ||
608 | |||
609 | #ifdef CONFIG_FEATURE_TELNET_TTYPE | ||
610 | ttype = getenv("TERM"); | ||
611 | #endif | ||
612 | |||
613 | memset(&G, 0, sizeof G); | ||
614 | |||
615 | if (tcgetattr(0, &G.termios_def) >= 0) { | ||
616 | G.do_termios = 1; | ||
617 | |||
618 | G.termios_raw = G.termios_def; | ||
619 | cfmakeraw(&G.termios_raw); | ||
620 | } | ||
621 | |||
622 | if (argc < 2) | ||
623 | bb_show_usage(); | ||
624 | |||
625 | #ifdef CONFIG_FEATURE_TELNET_AUTOLOGIN | ||
626 | if (1 & getopt32(argc, argv, "al:", &autologin)) | ||
627 | autologin = getenv("USER"); | ||
628 | |||
629 | if (optind < argc) { | ||
630 | bb_lookup_host(&s_in, argv[optind++]); | ||
631 | s_in.sin_port = bb_lookup_port((optind < argc) ? argv[optind++] : | ||
632 | "telnet", "tcp", 23); | ||
633 | if (optind < argc) | ||
634 | bb_show_usage(); | ||
635 | } else | ||
636 | bb_show_usage(); | ||
637 | #else | ||
638 | bb_lookup_host(&s_in, argv[1]); | ||
639 | s_in.sin_port = bb_lookup_port((argc == 3) ? argv[2] : "telnet", "tcp", 23); | ||
640 | #endif | ||
641 | |||
642 | G.netfd = xconnect_tcp_v4(&s_in); | ||
643 | |||
644 | setsockopt(G.netfd, SOL_SOCKET, SO_KEEPALIVE, &one, sizeof one); | ||
645 | |||
646 | signal(SIGINT, fgotsig); | ||
647 | |||
648 | #ifdef USE_POLL | ||
649 | ufds[0].fd = 0; ufds[1].fd = G.netfd; | ||
650 | ufds[0].events = ufds[1].events = POLLIN; | ||
651 | #else | ||
652 | FD_ZERO(&readfds); | ||
653 | FD_SET(0, &readfds); | ||
654 | FD_SET(G.netfd, &readfds); | ||
655 | maxfd = G.netfd + 1; | ||
656 | #endif | ||
657 | |||
658 | while (1) | ||
659 | { | ||
660 | #ifndef USE_POLL | ||
661 | fd_set rfds = readfds; | ||
662 | |||
663 | switch (select(maxfd, &rfds, NULL, NULL, NULL)) | ||
664 | #else | ||
665 | switch (poll(ufds, 2, -1)) | ||
666 | #endif | ||
667 | { | ||
668 | case 0: | ||
669 | /* timeout */ | ||
670 | case -1: | ||
671 | /* error, ignore and/or log something, bay go to loop */ | ||
672 | if (G.gotsig) | ||
673 | conescape(); | ||
674 | else | ||
675 | sleep(1); | ||
676 | break; | ||
677 | default: | ||
678 | |||
679 | #ifdef USE_POLL | ||
680 | if (ufds[0].revents) /* well, should check POLLIN, but ... */ | ||
681 | #else | ||
682 | if (FD_ISSET(0, &rfds)) | ||
683 | #endif | ||
684 | { | ||
685 | len = read(0, G.buf, DATABUFSIZE); | ||
686 | |||
687 | if (len <= 0) | ||
688 | doexit(0); | ||
689 | |||
690 | TRACE(0, ("Read con: %d\n", len)); | ||
691 | |||
692 | handlenetoutput(len); | ||
693 | } | ||
694 | |||
695 | #ifdef USE_POLL | ||
696 | if (ufds[1].revents) /* well, should check POLLIN, but ... */ | ||
697 | #else | ||
698 | if (FD_ISSET(G.netfd, &rfds)) | ||
699 | #endif | ||
700 | { | ||
701 | len = read(G.netfd, G.buf, DATABUFSIZE); | ||
702 | |||
703 | if (len <= 0) | ||
704 | { | ||
705 | WriteCS(1, "Connection closed by foreign host.\r\n"); | ||
706 | doexit(1); | ||
707 | } | ||
708 | TRACE(0, ("Read netfd (%d): %d\n", G.netfd, len)); | ||
709 | |||
710 | handlenetinput(len); | ||
711 | } | ||
712 | } | ||
713 | } | ||
714 | } | ||
diff --git a/networking/telnetd.c b/networking/telnetd.c new file mode 100644 index 000000000..604f65c91 --- /dev/null +++ b/networking/telnetd.c | |||
@@ -0,0 +1,578 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * Simple telnet server | ||
4 | * Bjorn Wesen, Axis Communications AB (bjornw@axis.com) | ||
5 | * | ||
6 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | ||
7 | * | ||
8 | * --------------------------------------------------------------------------- | ||
9 | * (C) Copyright 2000, Axis Communications AB, LUND, SWEDEN | ||
10 | **************************************************************************** | ||
11 | * | ||
12 | * The telnetd manpage says it all: | ||
13 | * | ||
14 | * Telnetd operates by allocating a pseudo-terminal device (see pty(4)) for | ||
15 | * a client, then creating a login process which has the slave side of the | ||
16 | * pseudo-terminal as stdin, stdout, and stderr. Telnetd manipulates the | ||
17 | * master side of the pseudo-terminal, implementing the telnet protocol and | ||
18 | * passing characters between the remote client and the login process. | ||
19 | * | ||
20 | * Vladimir Oleynik <dzo@simtreas.ru> 2001 | ||
21 | * Set process group corrections, initial busybox port | ||
22 | */ | ||
23 | |||
24 | /*#define DEBUG 1 */ | ||
25 | #define DEBUG 0 | ||
26 | |||
27 | #include "busybox.h" | ||
28 | |||
29 | #if DEBUG | ||
30 | #define TELCMDS | ||
31 | #define TELOPTS | ||
32 | #endif | ||
33 | #include <arpa/telnet.h> | ||
34 | #include <sys/syslog.h> | ||
35 | |||
36 | |||
37 | #define BUFSIZE 4000 | ||
38 | |||
39 | #if ENABLE_FEATURE_IPV6 | ||
40 | typedef struct sockaddr_in6 sockaddr_type; | ||
41 | #else | ||
42 | typedef struct sockaddr_in sockaddr_type; | ||
43 | #endif | ||
44 | |||
45 | #if ENABLE_LOGIN | ||
46 | static const char *loginpath = "/bin/login"; | ||
47 | #else | ||
48 | static const char *loginpath = DEFAULT_SHELL; | ||
49 | #endif | ||
50 | |||
51 | static const char *issuefile = "/etc/issue.net"; | ||
52 | |||
53 | /* shell name and arguments */ | ||
54 | |||
55 | static const char *argv_init[2]; | ||
56 | |||
57 | /* structure that describes a session */ | ||
58 | |||
59 | struct tsession { | ||
60 | struct tsession *next; | ||
61 | int sockfd_read, sockfd_write, ptyfd; | ||
62 | int shell_pid; | ||
63 | /* two circular buffers */ | ||
64 | char *buf1, *buf2; | ||
65 | int rdidx1, wridx1, size1; | ||
66 | int rdidx2, wridx2, size2; | ||
67 | }; | ||
68 | |||
69 | /* | ||
70 | This is how the buffers are used. The arrows indicate the movement | ||
71 | of data. | ||
72 | |||
73 | +-------+ wridx1++ +------+ rdidx1++ +----------+ | ||
74 | | | <-------------- | buf1 | <-------------- | | | ||
75 | | | size1-- +------+ size1++ | | | ||
76 | | pty | | socket | | ||
77 | | | rdidx2++ +------+ wridx2++ | | | ||
78 | | | --------------> | buf2 | --------------> | | | ||
79 | +-------+ size2++ +------+ size2-- +----------+ | ||
80 | |||
81 | Each session has got two buffers. | ||
82 | */ | ||
83 | |||
84 | static int maxfd; | ||
85 | |||
86 | static struct tsession *sessions; | ||
87 | |||
88 | |||
89 | /* | ||
90 | Remove all IAC's from the buffer pointed to by bf (received IACs are ignored | ||
91 | and must be removed so as to not be interpreted by the terminal). Make an | ||
92 | uninterrupted string of characters fit for the terminal. Do this by packing | ||
93 | all characters meant for the terminal sequentially towards the end of bf. | ||
94 | |||
95 | Return a pointer to the beginning of the characters meant for the terminal. | ||
96 | and make *num_totty the number of characters that should be sent to | ||
97 | the terminal. | ||
98 | |||
99 | Note - If an IAC (3 byte quantity) starts before (bf + len) but extends | ||
100 | past (bf + len) then that IAC will be left unprocessed and *processed will be | ||
101 | less than len. | ||
102 | |||
103 | FIXME - if we mean to send 0xFF to the terminal then it will be escaped, | ||
104 | what is the escape character? We aren't handling that situation here. | ||
105 | |||
106 | CR-LF ->'s CR mapping is also done here, for convenience | ||
107 | */ | ||
108 | static char * | ||
109 | remove_iacs(struct tsession *ts, int *pnum_totty) | ||
110 | { | ||
111 | unsigned char *ptr0 = (unsigned char *)ts->buf1 + ts->wridx1; | ||
112 | unsigned char *ptr = ptr0; | ||
113 | unsigned char *totty = ptr; | ||
114 | unsigned char *end = ptr + MIN(BUFSIZE - ts->wridx1, ts->size1); | ||
115 | int processed; | ||
116 | int num_totty; | ||
117 | |||
118 | while (ptr < end) { | ||
119 | if (*ptr != IAC) { | ||
120 | int c = *ptr; | ||
121 | *totty++ = *ptr++; | ||
122 | /* We now map \r\n ==> \r for pragmatic reasons. | ||
123 | * Many client implementations send \r\n when | ||
124 | * the user hits the CarriageReturn key. | ||
125 | */ | ||
126 | if (c == '\r' && (*ptr == '\n' || *ptr == 0) && ptr < end) | ||
127 | ptr++; | ||
128 | } else { | ||
129 | /* | ||
130 | * TELOPT_NAWS support! | ||
131 | */ | ||
132 | if ((ptr+2) >= end) { | ||
133 | /* only the beginning of the IAC is in the | ||
134 | buffer we were asked to process, we can't | ||
135 | process this char. */ | ||
136 | break; | ||
137 | } | ||
138 | |||
139 | /* | ||
140 | * IAC -> SB -> TELOPT_NAWS -> 4-byte -> IAC -> SE | ||
141 | */ | ||
142 | else if (ptr[1] == SB && ptr[2] == TELOPT_NAWS) { | ||
143 | struct winsize ws; | ||
144 | if ((ptr+8) >= end) | ||
145 | break; /* incomplete, can't process */ | ||
146 | ws.ws_col = (ptr[3] << 8) | ptr[4]; | ||
147 | ws.ws_row = (ptr[5] << 8) | ptr[6]; | ||
148 | ioctl(ts->ptyfd, TIOCSWINSZ, (char *)&ws); | ||
149 | ptr += 9; | ||
150 | } else { | ||
151 | /* skip 3-byte IAC non-SB cmd */ | ||
152 | #if DEBUG | ||
153 | fprintf(stderr, "Ignoring IAC %s,%s\n", | ||
154 | TELCMD(ptr[1]), TELOPT(ptr[2])); | ||
155 | #endif | ||
156 | ptr += 3; | ||
157 | } | ||
158 | } | ||
159 | } | ||
160 | |||
161 | processed = ptr - ptr0; | ||
162 | num_totty = totty - ptr0; | ||
163 | /* the difference between processed and num_to tty | ||
164 | is all the iacs we removed from the stream. | ||
165 | Adjust buf1 accordingly. */ | ||
166 | ts->wridx1 += processed - num_totty; | ||
167 | ts->size1 -= processed - num_totty; | ||
168 | *pnum_totty = num_totty; | ||
169 | /* move the chars meant for the terminal towards the end of the | ||
170 | buffer. */ | ||
171 | return memmove(ptr - num_totty, ptr0, num_totty); | ||
172 | } | ||
173 | |||
174 | |||
175 | static int | ||
176 | getpty(char *line, int size) | ||
177 | { | ||
178 | int p; | ||
179 | #if ENABLE_FEATURE_DEVPTS | ||
180 | p = open("/dev/ptmx", O_RDWR); | ||
181 | if (p > 0) { | ||
182 | const char *name; | ||
183 | grantpt(p); | ||
184 | unlockpt(p); | ||
185 | name = ptsname(p); | ||
186 | if (!name) { | ||
187 | bb_perror_msg("ptsname error (is /dev/pts mounted?)"); | ||
188 | return -1; | ||
189 | } | ||
190 | safe_strncpy(line, name, size); | ||
191 | return p; | ||
192 | } | ||
193 | #else | ||
194 | struct stat stb; | ||
195 | int i; | ||
196 | int j; | ||
197 | |||
198 | strcpy(line, "/dev/ptyXX"); | ||
199 | |||
200 | for (i = 0; i < 16; i++) { | ||
201 | line[8] = "pqrstuvwxyzabcde"[i]; | ||
202 | line[9] = '0'; | ||
203 | if (stat(line, &stb) < 0) { | ||
204 | continue; | ||
205 | } | ||
206 | for (j = 0; j < 16; j++) { | ||
207 | line[9] = j < 10 ? j + '0' : j - 10 + 'a'; | ||
208 | if (DEBUG) | ||
209 | fprintf(stderr, "Trying to open device: %s\n", line); | ||
210 | p = open(line, O_RDWR | O_NOCTTY); | ||
211 | if (p >= 0) { | ||
212 | line[5] = 't'; | ||
213 | return p; | ||
214 | } | ||
215 | } | ||
216 | } | ||
217 | #endif /* FEATURE_DEVPTS */ | ||
218 | return -1; | ||
219 | } | ||
220 | |||
221 | |||
222 | static void | ||
223 | send_iac(struct tsession *ts, unsigned char command, int option) | ||
224 | { | ||
225 | /* We rely on that there is space in the buffer for now. */ | ||
226 | char *b = ts->buf2 + ts->rdidx2; | ||
227 | *b++ = IAC; | ||
228 | *b++ = command; | ||
229 | *b++ = option; | ||
230 | ts->rdidx2 += 3; | ||
231 | ts->size2 += 3; | ||
232 | } | ||
233 | |||
234 | |||
235 | static struct tsession * | ||
236 | make_new_session( | ||
237 | USE_FEATURE_TELNETD_STANDALONE(int sock_r, int sock_w) | ||
238 | SKIP_FEATURE_TELNETD_STANDALONE(void) | ||
239 | ) { | ||
240 | struct termios termbuf; | ||
241 | int fd, pid; | ||
242 | char tty_name[32]; | ||
243 | struct tsession *ts = xzalloc(sizeof(struct tsession) + BUFSIZE * 2); | ||
244 | |||
245 | ts->buf1 = (char *)(&ts[1]); | ||
246 | ts->buf2 = ts->buf1 + BUFSIZE; | ||
247 | |||
248 | /* Got a new connection, set up a tty. */ | ||
249 | fd = getpty(tty_name, 32); | ||
250 | if (fd < 0) { | ||
251 | bb_error_msg("all terminals in use"); | ||
252 | return NULL; | ||
253 | } | ||
254 | if (fd > maxfd) maxfd = fd; | ||
255 | ndelay_on(ts->ptyfd = fd); | ||
256 | #if ENABLE_FEATURE_TELNETD_STANDALONE | ||
257 | if (sock_w > maxfd) maxfd = sock_w; | ||
258 | if (sock_r > maxfd) maxfd = sock_r; | ||
259 | ndelay_on(ts->sockfd_write = sock_w); | ||
260 | ndelay_on(ts->sockfd_read = sock_r); | ||
261 | #else | ||
262 | ts->sockfd_write = 1; | ||
263 | /* xzalloc: ts->sockfd_read = 0; */ | ||
264 | ndelay_on(0); | ||
265 | ndelay_on(1); | ||
266 | #endif | ||
267 | /* Make the telnet client understand we will echo characters so it | ||
268 | * should not do it locally. We don't tell the client to run linemode, | ||
269 | * because we want to handle line editing and tab completion and other | ||
270 | * stuff that requires char-by-char support. */ | ||
271 | send_iac(ts, DO, TELOPT_ECHO); | ||
272 | send_iac(ts, DO, TELOPT_NAWS); | ||
273 | send_iac(ts, DO, TELOPT_LFLOW); | ||
274 | send_iac(ts, WILL, TELOPT_ECHO); | ||
275 | send_iac(ts, WILL, TELOPT_SGA); | ||
276 | |||
277 | pid = fork(); | ||
278 | if (pid < 0) { | ||
279 | free(ts); | ||
280 | close(fd); | ||
281 | bb_perror_msg("fork"); | ||
282 | return NULL; | ||
283 | } | ||
284 | if (pid > 0) { | ||
285 | /* parent */ | ||
286 | ts->shell_pid = pid; | ||
287 | return ts; | ||
288 | } | ||
289 | |||
290 | /* child */ | ||
291 | |||
292 | /* open the child's side of the tty. */ | ||
293 | fd = xopen(tty_name, O_RDWR /*| O_NOCTTY*/); | ||
294 | dup2(fd, 0); | ||
295 | dup2(fd, 1); | ||
296 | dup2(fd, 2); | ||
297 | while (fd > 2) close(fd--); | ||
298 | /* make new process group */ | ||
299 | setsid(); | ||
300 | tcsetpgrp(0, getpid()); | ||
301 | |||
302 | /* The pseudo-terminal allocated to the client is configured to operate in | ||
303 | * cooked mode, and with XTABS CRMOD enabled (see tty(4)). */ | ||
304 | tcgetattr(0, &termbuf); | ||
305 | termbuf.c_lflag |= ECHO; /* if we use readline we dont want this */ | ||
306 | termbuf.c_oflag |= ONLCR|XTABS; | ||
307 | termbuf.c_iflag |= ICRNL; | ||
308 | termbuf.c_iflag &= ~IXOFF; | ||
309 | /*termbuf.c_lflag &= ~ICANON;*/ | ||
310 | tcsetattr(0, TCSANOW, &termbuf); | ||
311 | |||
312 | print_login_issue(issuefile, NULL); | ||
313 | |||
314 | /* exec shell, with correct argv and env */ | ||
315 | execv(loginpath, (char *const *)argv_init); | ||
316 | bb_perror_msg_and_die("execv"); | ||
317 | } | ||
318 | |||
319 | #if ENABLE_FEATURE_TELNETD_STANDALONE | ||
320 | |||
321 | static void | ||
322 | free_session(struct tsession *ts) | ||
323 | { | ||
324 | struct tsession *t = sessions; | ||
325 | |||
326 | /* unlink this telnet session from the session list */ | ||
327 | if (t == ts) | ||
328 | sessions = ts->next; | ||
329 | else { | ||
330 | while (t->next != ts) | ||
331 | t = t->next; | ||
332 | t->next = ts->next; | ||
333 | } | ||
334 | |||
335 | kill(ts->shell_pid, SIGKILL); | ||
336 | wait4(ts->shell_pid, NULL, 0, NULL); | ||
337 | close(ts->ptyfd); | ||
338 | close(ts->sockfd_read); | ||
339 | /* error if ts->sockfd_read == ts->sockfd_write. So what? ;) */ | ||
340 | close(ts->sockfd_write); | ||
341 | free(ts); | ||
342 | |||
343 | /* scan all sessions and find new maxfd */ | ||
344 | ts = sessions; | ||
345 | maxfd = 0; | ||
346 | while (ts) { | ||
347 | if (maxfd < ts->ptyfd) | ||
348 | maxfd = ts->ptyfd; | ||
349 | if (maxfd < ts->sockfd_read) | ||
350 | maxfd = ts->sockfd_read; | ||
351 | if (maxfd < ts->sockfd_write) | ||
352 | maxfd = ts->sockfd_write; | ||
353 | ts = ts->next; | ||
354 | } | ||
355 | } | ||
356 | |||
357 | #else /* !FEATURE_TELNETD_STANDALONE */ | ||
358 | |||
359 | /* Never actually called */ | ||
360 | void free_session(struct tsession *ts); | ||
361 | |||
362 | #endif | ||
363 | |||
364 | |||
365 | int | ||
366 | telnetd_main(int argc, char **argv) | ||
367 | { | ||
368 | fd_set rdfdset, wrfdset; | ||
369 | unsigned opt; | ||
370 | int selret, maxlen, w, r; | ||
371 | struct tsession *ts; | ||
372 | #if ENABLE_FEATURE_TELNETD_STANDALONE | ||
373 | #define IS_INETD (opt & OPT_INETD) | ||
374 | int master_fd = -1; /* be happy, gcc */ | ||
375 | unsigned portnbr = 23; | ||
376 | char *opt_bindaddr = NULL; | ||
377 | char *opt_portnbr; | ||
378 | #else | ||
379 | enum { | ||
380 | IS_INETD = 1, | ||
381 | master_fd = -1, | ||
382 | portnbr = 23, | ||
383 | }; | ||
384 | #endif | ||
385 | enum { | ||
386 | OPT_PORT = 4 * ENABLE_FEATURE_TELNETD_STANDALONE, | ||
387 | OPT_FOREGROUND = 0x10 * ENABLE_FEATURE_TELNETD_STANDALONE, | ||
388 | OPT_INETD = 0x20 * ENABLE_FEATURE_TELNETD_STANDALONE, | ||
389 | }; | ||
390 | |||
391 | opt = getopt32(argc, argv, "f:l:" USE_FEATURE_TELNETD_STANDALONE("p:b:Fi"), | ||
392 | &issuefile, &loginpath | ||
393 | USE_FEATURE_TELNETD_STANDALONE(, &opt_portnbr, &opt_bindaddr)); | ||
394 | /* Redirect log to syslog early, if needed */ | ||
395 | if (IS_INETD || !(opt & OPT_FOREGROUND)) { | ||
396 | openlog(applet_name, 0, LOG_USER); | ||
397 | logmode = LOGMODE_SYSLOG; | ||
398 | } | ||
399 | //if (opt & 1) // -f | ||
400 | //if (opt & 2) // -l | ||
401 | USE_FEATURE_TELNETD_STANDALONE( | ||
402 | if (opt & OPT_PORT) // -p | ||
403 | portnbr = xatou16(opt_portnbr); | ||
404 | //if (opt & 8) // -b | ||
405 | //if (opt & 0x10) // -F | ||
406 | //if (opt & 0x20) // -i | ||
407 | ); | ||
408 | |||
409 | /* Used to check access(loginpath, X_OK) here. Pointless. | ||
410 | * exec will do this for us for free later. */ | ||
411 | argv_init[0] = loginpath; | ||
412 | |||
413 | #if ENABLE_FEATURE_TELNETD_STANDALONE | ||
414 | if (IS_INETD) { | ||
415 | sessions = make_new_session(0, 1); | ||
416 | } else { | ||
417 | master_fd = create_and_bind_socket_ip4or6(opt_bindaddr, portnbr); | ||
418 | xlisten(master_fd, 1); | ||
419 | if (!(opt & OPT_FOREGROUND)) | ||
420 | xdaemon(0, 0); | ||
421 | } | ||
422 | #else | ||
423 | sessions = make_new_session(); | ||
424 | #endif | ||
425 | |||
426 | /* We don't want to die if just one session is broken */ | ||
427 | signal(SIGPIPE, SIG_IGN); | ||
428 | |||
429 | again: | ||
430 | FD_ZERO(&rdfdset); | ||
431 | FD_ZERO(&wrfdset); | ||
432 | if (!IS_INETD) { | ||
433 | FD_SET(master_fd, &rdfdset); | ||
434 | /* This is needed because free_session() does not | ||
435 | * take into account master_fd when it finds new | ||
436 | * maxfd among remaining fd's: */ | ||
437 | if (master_fd > maxfd) | ||
438 | maxfd = master_fd; | ||
439 | } | ||
440 | |||
441 | /* select on the master socket, all telnet sockets and their | ||
442 | * ptys if there is room in their session buffers. */ | ||
443 | ts = sessions; | ||
444 | while (ts) { | ||
445 | /* buf1 is used from socket to pty | ||
446 | * buf2 is used from pty to socket */ | ||
447 | if (ts->size1 > 0) /* can write to pty */ | ||
448 | FD_SET(ts->ptyfd, &wrfdset); | ||
449 | if (ts->size1 < BUFSIZE) /* can read from socket */ | ||
450 | FD_SET(ts->sockfd_read, &rdfdset); | ||
451 | if (ts->size2 > 0) /* can write to socket */ | ||
452 | FD_SET(ts->sockfd_write, &wrfdset); | ||
453 | if (ts->size2 < BUFSIZE) /* can read from pty */ | ||
454 | FD_SET(ts->ptyfd, &rdfdset); | ||
455 | ts = ts->next; | ||
456 | } | ||
457 | |||
458 | selret = select(maxfd + 1, &rdfdset, &wrfdset, 0, 0); | ||
459 | if (!selret) | ||
460 | return 0; | ||
461 | |||
462 | #if ENABLE_FEATURE_TELNETD_STANDALONE | ||
463 | /* First check for and accept new sessions. */ | ||
464 | if (!IS_INETD && FD_ISSET(master_fd, &rdfdset)) { | ||
465 | sockaddr_type sa; | ||
466 | int fd; | ||
467 | socklen_t salen; | ||
468 | struct tsession *new_ts; | ||
469 | |||
470 | salen = sizeof(sa); | ||
471 | fd = accept(master_fd, (struct sockaddr *)&sa, &salen); | ||
472 | if (fd < 0) | ||
473 | goto again; | ||
474 | /* Create a new session and link it into our active list */ | ||
475 | new_ts = make_new_session(fd, fd); | ||
476 | if (new_ts) { | ||
477 | new_ts->next = sessions; | ||
478 | sessions = new_ts; | ||
479 | } else { | ||
480 | close(fd); | ||
481 | } | ||
482 | } | ||
483 | #endif | ||
484 | |||
485 | /* Then check for data tunneling. */ | ||
486 | ts = sessions; | ||
487 | while (ts) { /* For all sessions... */ | ||
488 | struct tsession *next = ts->next; /* in case we free ts. */ | ||
489 | |||
490 | if (ts->size1 && FD_ISSET(ts->ptyfd, &wrfdset)) { | ||
491 | int num_totty; | ||
492 | char *ptr; | ||
493 | /* Write to pty from buffer 1. */ | ||
494 | ptr = remove_iacs(ts, &num_totty); | ||
495 | w = safe_write(ts->ptyfd, ptr, num_totty); | ||
496 | /* needed? if (w < 0 && errno == EAGAIN) continue; */ | ||
497 | if (w < 0) { | ||
498 | if (IS_INETD) | ||
499 | return 0; | ||
500 | free_session(ts); | ||
501 | ts = next; | ||
502 | continue; | ||
503 | } | ||
504 | ts->wridx1 += w; | ||
505 | ts->size1 -= w; | ||
506 | if (ts->wridx1 == BUFSIZE) | ||
507 | ts->wridx1 = 0; | ||
508 | } | ||
509 | |||
510 | if (ts->size2 && FD_ISSET(ts->sockfd_write, &wrfdset)) { | ||
511 | /* Write to socket from buffer 2. */ | ||
512 | maxlen = MIN(BUFSIZE - ts->wridx2, ts->size2); | ||
513 | w = safe_write(ts->sockfd_write, ts->buf2 + ts->wridx2, maxlen); | ||
514 | /* needed? if (w < 0 && errno == EAGAIN) continue; */ | ||
515 | if (w < 0) { | ||
516 | if (IS_INETD) | ||
517 | return 0; | ||
518 | free_session(ts); | ||
519 | ts = next; | ||
520 | continue; | ||
521 | } | ||
522 | ts->wridx2 += w; | ||
523 | ts->size2 -= w; | ||
524 | if (ts->wridx2 == BUFSIZE) | ||
525 | ts->wridx2 = 0; | ||
526 | } | ||
527 | |||
528 | if (ts->size1 < BUFSIZE && FD_ISSET(ts->sockfd_read, &rdfdset)) { | ||
529 | /* Read from socket to buffer 1. */ | ||
530 | maxlen = MIN(BUFSIZE - ts->rdidx1, BUFSIZE - ts->size1); | ||
531 | r = safe_read(ts->sockfd_read, ts->buf1 + ts->rdidx1, maxlen); | ||
532 | if (r < 0 && errno == EAGAIN) continue; | ||
533 | if (r <= 0) { | ||
534 | if (IS_INETD) | ||
535 | return 0; | ||
536 | free_session(ts); | ||
537 | ts = next; | ||
538 | continue; | ||
539 | } | ||
540 | if (!ts->buf1[ts->rdidx1 + r - 1]) | ||
541 | if (!--r) | ||
542 | continue; | ||
543 | ts->rdidx1 += r; | ||
544 | ts->size1 += r; | ||
545 | if (ts->rdidx1 == BUFSIZE) | ||
546 | ts->rdidx1 = 0; | ||
547 | } | ||
548 | |||
549 | if (ts->size2 < BUFSIZE && FD_ISSET(ts->ptyfd, &rdfdset)) { | ||
550 | /* Read from pty to buffer 2. */ | ||
551 | maxlen = MIN(BUFSIZE - ts->rdidx2, BUFSIZE - ts->size2); | ||
552 | r = safe_read(ts->ptyfd, ts->buf2 + ts->rdidx2, maxlen); | ||
553 | if (r < 0 && errno == EAGAIN) continue; | ||
554 | if (r <= 0) { | ||
555 | if (IS_INETD) | ||
556 | return 0; | ||
557 | free_session(ts); | ||
558 | ts = next; | ||
559 | continue; | ||
560 | } | ||
561 | ts->rdidx2 += r; | ||
562 | ts->size2 += r; | ||
563 | if (ts->rdidx2 == BUFSIZE) | ||
564 | ts->rdidx2 = 0; | ||
565 | } | ||
566 | |||
567 | if (ts->size1 == 0) { | ||
568 | ts->rdidx1 = 0; | ||
569 | ts->wridx1 = 0; | ||
570 | } | ||
571 | if (ts->size2 == 0) { | ||
572 | ts->rdidx2 = 0; | ||
573 | ts->wridx2 = 0; | ||
574 | } | ||
575 | ts = next; | ||
576 | } | ||
577 | goto again; | ||
578 | } | ||
diff --git a/networking/tftp.c b/networking/tftp.c new file mode 100644 index 000000000..64d376fa7 --- /dev/null +++ b/networking/tftp.c | |||
@@ -0,0 +1,561 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* ------------------------------------------------------------------------- | ||
3 | * tftp.c | ||
4 | * | ||
5 | * A simple tftp client for busybox. | ||
6 | * Tries to follow RFC1350. | ||
7 | * Only "octet" mode supported. | ||
8 | * Optional blocksize negotiation (RFC2347 + RFC2348) | ||
9 | * | ||
10 | * Copyright (C) 2001 Magnus Damm <damm@opensource.se> | ||
11 | * | ||
12 | * Parts of the code based on: | ||
13 | * | ||
14 | * atftp: Copyright (C) 2000 Jean-Pierre Lefebvre <helix@step.polymtl.ca> | ||
15 | * and Remi Lefebvre <remi@debian.org> | ||
16 | * | ||
17 | * utftp: Copyright (C) 1999 Uwe Ohse <uwe@ohse.de> | ||
18 | * | ||
19 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | ||
20 | * ------------------------------------------------------------------------- */ | ||
21 | |||
22 | #include "busybox.h" | ||
23 | |||
24 | |||
25 | #define TFTP_BLOCKSIZE_DEFAULT 512 /* according to RFC 1350, don't change */ | ||
26 | #define TFTP_TIMEOUT 5 /* seconds */ | ||
27 | #define TFTP_NUM_RETRIES 5 /* number of retries */ | ||
28 | |||
29 | static const char * const MODE_OCTET = "octet"; | ||
30 | #define MODE_OCTET_LEN 6 /* sizeof(MODE_OCTET)*/ | ||
31 | |||
32 | static const char * const OPTION_BLOCKSIZE = "blksize"; | ||
33 | #define OPTION_BLOCKSIZE_LEN 8 /* sizeof(OPTION_BLOCKSIZE) */ | ||
34 | |||
35 | /* opcodes we support */ | ||
36 | #define TFTP_RRQ 1 | ||
37 | #define TFTP_WRQ 2 | ||
38 | #define TFTP_DATA 3 | ||
39 | #define TFTP_ACK 4 | ||
40 | #define TFTP_ERROR 5 | ||
41 | #define TFTP_OACK 6 | ||
42 | |||
43 | static const char *const tftp_bb_error_msg[] = { | ||
44 | "Undefined error", | ||
45 | "File not found", | ||
46 | "Access violation", | ||
47 | "Disk full or allocation error", | ||
48 | "Illegal TFTP operation", | ||
49 | "Unknown transfer ID", | ||
50 | "File already exists", | ||
51 | "No such user" | ||
52 | }; | ||
53 | |||
54 | #define tftp_cmd_get ENABLE_FEATURE_TFTP_GET | ||
55 | |||
56 | #if ENABLE_FEATURE_TFTP_PUT | ||
57 | # define tftp_cmd_put (tftp_cmd_get+ENABLE_FEATURE_TFTP_PUT) | ||
58 | #else | ||
59 | # define tftp_cmd_put 0 | ||
60 | #endif | ||
61 | |||
62 | |||
63 | #if ENABLE_FEATURE_TFTP_BLOCKSIZE | ||
64 | |||
65 | static int tftp_blocksize_check(int blocksize, int bufsize) | ||
66 | { | ||
67 | /* Check if the blocksize is valid: | ||
68 | * RFC2348 says between 8 and 65464, | ||
69 | * but our implementation makes it impossible | ||
70 | * to use blocksizes smaller than 22 octets. | ||
71 | */ | ||
72 | |||
73 | if ((bufsize && (blocksize > bufsize)) || | ||
74 | (blocksize < 8) || (blocksize > 65564)) { | ||
75 | bb_error_msg("bad blocksize"); | ||
76 | return 0; | ||
77 | } | ||
78 | |||
79 | return blocksize; | ||
80 | } | ||
81 | |||
82 | static char *tftp_option_get(char *buf, int len, const char * const option) | ||
83 | { | ||
84 | int opt_val = 0; | ||
85 | int opt_found = 0; | ||
86 | int k; | ||
87 | |||
88 | while (len > 0) { | ||
89 | |||
90 | /* Make sure the options are terminated correctly */ | ||
91 | |||
92 | for (k = 0; k < len; k++) { | ||
93 | if (buf[k] == '\0') { | ||
94 | break; | ||
95 | } | ||
96 | } | ||
97 | |||
98 | if (k >= len) { | ||
99 | break; | ||
100 | } | ||
101 | |||
102 | if (opt_val == 0) { | ||
103 | if (strcasecmp(buf, option) == 0) { | ||
104 | opt_found = 1; | ||
105 | } | ||
106 | } else { | ||
107 | if (opt_found) { | ||
108 | return buf; | ||
109 | } | ||
110 | } | ||
111 | |||
112 | k++; | ||
113 | |||
114 | buf += k; | ||
115 | len -= k; | ||
116 | |||
117 | opt_val ^= 1; | ||
118 | } | ||
119 | |||
120 | return NULL; | ||
121 | } | ||
122 | |||
123 | #endif | ||
124 | |||
125 | static int tftp(const int cmd, const struct hostent *host, | ||
126 | const char *remotefile, const int localfd, | ||
127 | const unsigned short port, int tftp_bufsize) | ||
128 | { | ||
129 | struct sockaddr_in sa; | ||
130 | struct sockaddr_in from; | ||
131 | struct timeval tv; | ||
132 | socklen_t fromlen; | ||
133 | fd_set rfds; | ||
134 | int socketfd; | ||
135 | int len; | ||
136 | int opcode = 0; | ||
137 | int finished = 0; | ||
138 | int timeout = TFTP_NUM_RETRIES; | ||
139 | unsigned short block_nr = 1; | ||
140 | unsigned short tmp; | ||
141 | char *cp; | ||
142 | |||
143 | USE_FEATURE_TFTP_BLOCKSIZE(int want_option_ack = 0;) | ||
144 | |||
145 | /* Can't use RESERVE_CONFIG_BUFFER here since the allocation | ||
146 | * size varies meaning BUFFERS_GO_ON_STACK would fail */ | ||
147 | char *buf=xmalloc(tftp_bufsize += 4); | ||
148 | |||
149 | if ((socketfd = socket(PF_INET, SOCK_DGRAM, 0)) < 0) { | ||
150 | /* need to unlink the localfile, so don't use xsocket here. */ | ||
151 | bb_perror_msg("socket"); | ||
152 | return EXIT_FAILURE; | ||
153 | } | ||
154 | |||
155 | len = sizeof(sa); | ||
156 | |||
157 | memset(&sa, 0, len); | ||
158 | xbind(socketfd, (struct sockaddr *)&sa, len); | ||
159 | |||
160 | sa.sin_family = host->h_addrtype; | ||
161 | sa.sin_port = port; | ||
162 | memcpy(&sa.sin_addr, (struct in_addr *) host->h_addr, | ||
163 | sizeof(sa.sin_addr)); | ||
164 | |||
165 | /* build opcode */ | ||
166 | if (cmd & tftp_cmd_get) { | ||
167 | opcode = TFTP_RRQ; | ||
168 | } | ||
169 | if (cmd & tftp_cmd_put) { | ||
170 | opcode = TFTP_WRQ; | ||
171 | } | ||
172 | |||
173 | while (1) { | ||
174 | |||
175 | cp = buf; | ||
176 | |||
177 | /* first create the opcode part */ | ||
178 | *((unsigned short *) cp) = htons(opcode); | ||
179 | cp += 2; | ||
180 | |||
181 | /* add filename and mode */ | ||
182 | if (((cmd & tftp_cmd_get) && (opcode == TFTP_RRQ)) || | ||
183 | ((cmd & tftp_cmd_put) && (opcode == TFTP_WRQ))) | ||
184 | { | ||
185 | int too_long = 0; | ||
186 | |||
187 | /* see if the filename fits into buf | ||
188 | * and fill in packet. */ | ||
189 | len = strlen(remotefile) + 1; | ||
190 | |||
191 | if ((cp + len) >= &buf[tftp_bufsize - 1]) { | ||
192 | too_long = 1; | ||
193 | } else { | ||
194 | safe_strncpy(cp, remotefile, len); | ||
195 | cp += len; | ||
196 | } | ||
197 | |||
198 | if (too_long || ((&buf[tftp_bufsize - 1] - cp) < MODE_OCTET_LEN)) { | ||
199 | bb_error_msg("remote filename too long"); | ||
200 | break; | ||
201 | } | ||
202 | |||
203 | /* add "mode" part of the package */ | ||
204 | memcpy(cp, MODE_OCTET, MODE_OCTET_LEN); | ||
205 | cp += MODE_OCTET_LEN; | ||
206 | |||
207 | #if ENABLE_FEATURE_TFTP_BLOCKSIZE | ||
208 | |||
209 | len = tftp_bufsize - 4; /* data block size */ | ||
210 | |||
211 | if (len != TFTP_BLOCKSIZE_DEFAULT) { | ||
212 | |||
213 | if ((&buf[tftp_bufsize - 1] - cp) < 15) { | ||
214 | bb_error_msg("remote filename too long"); | ||
215 | break; | ||
216 | } | ||
217 | |||
218 | /* add "blksize" + number of blocks */ | ||
219 | memcpy(cp, OPTION_BLOCKSIZE, OPTION_BLOCKSIZE_LEN); | ||
220 | cp += OPTION_BLOCKSIZE_LEN; | ||
221 | cp += snprintf(cp, 6, "%d", len) + 1; | ||
222 | |||
223 | want_option_ack = 1; | ||
224 | } | ||
225 | #endif | ||
226 | } | ||
227 | |||
228 | /* add ack and data */ | ||
229 | |||
230 | if (((cmd & tftp_cmd_get) && (opcode == TFTP_ACK)) || | ||
231 | ((cmd & tftp_cmd_put) && (opcode == TFTP_DATA))) { | ||
232 | |||
233 | *((unsigned short *) cp) = htons(block_nr); | ||
234 | |||
235 | cp += 2; | ||
236 | |||
237 | block_nr++; | ||
238 | |||
239 | if ((cmd & tftp_cmd_put) && (opcode == TFTP_DATA)) { | ||
240 | len = full_read(localfd, cp, tftp_bufsize - 4); | ||
241 | |||
242 | if (len < 0) { | ||
243 | bb_perror_msg(bb_msg_read_error); | ||
244 | break; | ||
245 | } | ||
246 | |||
247 | if (len != (tftp_bufsize - 4)) { | ||
248 | finished++; | ||
249 | } | ||
250 | |||
251 | cp += len; | ||
252 | } | ||
253 | } | ||
254 | |||
255 | |||
256 | /* send packet */ | ||
257 | |||
258 | |||
259 | timeout = TFTP_NUM_RETRIES; /* re-initialize */ | ||
260 | do { | ||
261 | |||
262 | len = cp - buf; | ||
263 | |||
264 | #if ENABLE_DEBUG_TFTP | ||
265 | fprintf(stderr, "sending %u bytes\n", len); | ||
266 | for (cp = buf; cp < &buf[len]; cp++) | ||
267 | fprintf(stderr, "%02x ", (unsigned char) *cp); | ||
268 | fprintf(stderr, "\n"); | ||
269 | #endif | ||
270 | if (sendto(socketfd, buf, len, 0, | ||
271 | (struct sockaddr *) &sa, sizeof(sa)) < 0) { | ||
272 | bb_perror_msg("send"); | ||
273 | len = -1; | ||
274 | break; | ||
275 | } | ||
276 | |||
277 | |||
278 | if (finished && (opcode == TFTP_ACK)) { | ||
279 | break; | ||
280 | } | ||
281 | |||
282 | /* receive packet */ | ||
283 | |||
284 | memset(&from, 0, sizeof(from)); | ||
285 | fromlen = sizeof(from); | ||
286 | |||
287 | tv.tv_sec = TFTP_TIMEOUT; | ||
288 | tv.tv_usec = 0; | ||
289 | |||
290 | FD_ZERO(&rfds); | ||
291 | FD_SET(socketfd, &rfds); | ||
292 | |||
293 | switch (select(socketfd + 1, &rfds, NULL, NULL, &tv)) { | ||
294 | case 1: | ||
295 | len = recvfrom(socketfd, buf, tftp_bufsize, 0, | ||
296 | (struct sockaddr *) &from, &fromlen); | ||
297 | |||
298 | if (len < 0) { | ||
299 | bb_perror_msg("recvfrom"); | ||
300 | break; | ||
301 | } | ||
302 | |||
303 | timeout = 0; | ||
304 | |||
305 | if (sa.sin_port == port) { | ||
306 | sa.sin_port = from.sin_port; | ||
307 | } | ||
308 | if (sa.sin_port == from.sin_port) { | ||
309 | break; | ||
310 | } | ||
311 | |||
312 | /* fall-through for bad packets! */ | ||
313 | /* discard the packet - treat as timeout */ | ||
314 | timeout = TFTP_NUM_RETRIES; | ||
315 | case 0: | ||
316 | bb_error_msg("timeout"); | ||
317 | |||
318 | timeout--; | ||
319 | if (timeout == 0) { | ||
320 | len = -1; | ||
321 | bb_error_msg("last timeout"); | ||
322 | } | ||
323 | break; | ||
324 | default: | ||
325 | bb_perror_msg("select"); | ||
326 | len = -1; | ||
327 | } | ||
328 | |||
329 | } while (timeout && (len >= 0)); | ||
330 | |||
331 | if ((finished) || (len < 0)) { | ||
332 | break; | ||
333 | } | ||
334 | |||
335 | /* process received packet */ | ||
336 | |||
337 | opcode = ntohs(*((unsigned short *) buf)); | ||
338 | tmp = ntohs(*((unsigned short *) &buf[2])); | ||
339 | |||
340 | #if ENABLE_DEBUG_TFTP | ||
341 | fprintf(stderr, "received %d bytes: %04x %04x\n", len, opcode, tmp); | ||
342 | #endif | ||
343 | |||
344 | if (opcode == TFTP_ERROR) { | ||
345 | const char *msg = NULL; | ||
346 | |||
347 | if (buf[4] != '\0') { | ||
348 | msg = &buf[4]; | ||
349 | buf[tftp_bufsize - 1] = '\0'; | ||
350 | } else if (tmp < (sizeof(tftp_bb_error_msg) | ||
351 | / sizeof(char *))) { | ||
352 | |||
353 | msg = tftp_bb_error_msg[tmp]; | ||
354 | } | ||
355 | |||
356 | if (msg) { | ||
357 | bb_error_msg("server says: %s", msg); | ||
358 | } | ||
359 | |||
360 | break; | ||
361 | } | ||
362 | #if ENABLE_FEATURE_TFTP_BLOCKSIZE | ||
363 | if (want_option_ack) { | ||
364 | |||
365 | want_option_ack = 0; | ||
366 | |||
367 | if (opcode == TFTP_OACK) { | ||
368 | |||
369 | /* server seems to support options */ | ||
370 | |||
371 | char *res; | ||
372 | |||
373 | res = tftp_option_get(&buf[2], len - 2, OPTION_BLOCKSIZE); | ||
374 | |||
375 | if (res) { | ||
376 | int blksize = xatoi_u(res); | ||
377 | |||
378 | if (tftp_blocksize_check(blksize, tftp_bufsize - 4)) { | ||
379 | |||
380 | if (cmd & tftp_cmd_put) { | ||
381 | opcode = TFTP_DATA; | ||
382 | } else { | ||
383 | opcode = TFTP_ACK; | ||
384 | } | ||
385 | #if ENABLE_DEBUG_TFTP | ||
386 | fprintf(stderr, "using %s %u\n", OPTION_BLOCKSIZE, | ||
387 | blksize); | ||
388 | #endif | ||
389 | tftp_bufsize = blksize + 4; | ||
390 | block_nr = 0; | ||
391 | continue; | ||
392 | } | ||
393 | } | ||
394 | /* FIXME: | ||
395 | * we should send ERROR 8 */ | ||
396 | bb_error_msg("bad server option"); | ||
397 | break; | ||
398 | } | ||
399 | |||
400 | bb_error_msg("warning: blksize not supported by server" | ||
401 | " - reverting to 512"); | ||
402 | |||
403 | tftp_bufsize = TFTP_BLOCKSIZE_DEFAULT + 4; | ||
404 | } | ||
405 | #endif | ||
406 | |||
407 | if ((cmd & tftp_cmd_get) && (opcode == TFTP_DATA)) { | ||
408 | |||
409 | if (tmp == block_nr) { | ||
410 | |||
411 | len = full_write(localfd, &buf[4], len - 4); | ||
412 | |||
413 | if (len < 0) { | ||
414 | bb_perror_msg(bb_msg_write_error); | ||
415 | break; | ||
416 | } | ||
417 | |||
418 | if (len != (tftp_bufsize - 4)) { | ||
419 | finished++; | ||
420 | } | ||
421 | |||
422 | opcode = TFTP_ACK; | ||
423 | continue; | ||
424 | } | ||
425 | /* in case the last ack disappeared into the ether */ | ||
426 | if (tmp == (block_nr - 1)) { | ||
427 | --block_nr; | ||
428 | opcode = TFTP_ACK; | ||
429 | continue; | ||
430 | } else if (tmp + 1 == block_nr) { | ||
431 | /* Server lost our TFTP_ACK. Resend it */ | ||
432 | block_nr = tmp; | ||
433 | opcode = TFTP_ACK; | ||
434 | continue; | ||
435 | } | ||
436 | } | ||
437 | |||
438 | if ((cmd & tftp_cmd_put) && (opcode == TFTP_ACK)) { | ||
439 | |||
440 | if (tmp == (unsigned short) (block_nr - 1)) { | ||
441 | if (finished) { | ||
442 | break; | ||
443 | } | ||
444 | |||
445 | opcode = TFTP_DATA; | ||
446 | continue; | ||
447 | } | ||
448 | } | ||
449 | } | ||
450 | |||
451 | #if ENABLE_FEATURE_CLEAN_UP | ||
452 | close(socketfd); | ||
453 | free(buf); | ||
454 | #endif | ||
455 | |||
456 | return finished ? EXIT_SUCCESS : EXIT_FAILURE; | ||
457 | } | ||
458 | |||
459 | int tftp_main(int argc, char **argv) | ||
460 | { | ||
461 | struct hostent *host = NULL; | ||
462 | const char *localfile = NULL; | ||
463 | const char *remotefile = NULL; | ||
464 | int port; | ||
465 | int cmd = 0; | ||
466 | int fd = -1; | ||
467 | int flags = 0; | ||
468 | int result; | ||
469 | int blocksize = TFTP_BLOCKSIZE_DEFAULT; | ||
470 | |||
471 | /* figure out what to pass to getopt */ | ||
472 | |||
473 | #if ENABLE_FEATURE_TFTP_BLOCKSIZE | ||
474 | char *sblocksize = NULL; | ||
475 | |||
476 | #define BS "b:" | ||
477 | #define BS_ARG , &sblocksize | ||
478 | #else | ||
479 | #define BS | ||
480 | #define BS_ARG | ||
481 | #endif | ||
482 | |||
483 | #if ENABLE_FEATURE_TFTP_GET | ||
484 | #define GET "g" | ||
485 | #define GET_COMPL ":g" | ||
486 | #else | ||
487 | #define GET | ||
488 | #define GET_COMPL | ||
489 | #endif | ||
490 | |||
491 | #if ENABLE_FEATURE_TFTP_PUT | ||
492 | #define PUT "p" | ||
493 | #define PUT_COMPL ":p" | ||
494 | #else | ||
495 | #define PUT | ||
496 | #define PUT_COMPL | ||
497 | #endif | ||
498 | |||
499 | #if defined(CONFIG_FEATURE_TFTP_GET) && defined(CONFIG_FEATURE_TFTP_PUT) | ||
500 | opt_complementary = GET_COMPL PUT_COMPL ":?g--p:p--g"; | ||
501 | #elif defined(CONFIG_FEATURE_TFTP_GET) || defined(CONFIG_FEATURE_TFTP_PUT) | ||
502 | opt_complementary = GET_COMPL PUT_COMPL; | ||
503 | #endif | ||
504 | |||
505 | cmd = getopt32(argc, argv, GET PUT "l:r:" BS, &localfile, &remotefile BS_ARG); | ||
506 | |||
507 | cmd &= (tftp_cmd_get | tftp_cmd_put); | ||
508 | #if ENABLE_FEATURE_TFTP_GET | ||
509 | if (cmd == tftp_cmd_get) | ||
510 | flags = O_WRONLY | O_CREAT | O_TRUNC; | ||
511 | #endif | ||
512 | #if ENABLE_FEATURE_TFTP_PUT | ||
513 | if (cmd == tftp_cmd_put) | ||
514 | flags = O_RDONLY; | ||
515 | #endif | ||
516 | |||
517 | #if ENABLE_FEATURE_TFTP_BLOCKSIZE | ||
518 | if (sblocksize) { | ||
519 | blocksize = xatoi_u(sblocksize); | ||
520 | if (!tftp_blocksize_check(blocksize, 0)) { | ||
521 | return EXIT_FAILURE; | ||
522 | } | ||
523 | } | ||
524 | #endif | ||
525 | |||
526 | if (localfile == NULL) | ||
527 | localfile = remotefile; | ||
528 | if (remotefile == NULL) | ||
529 | remotefile = localfile; | ||
530 | if ((localfile == NULL && remotefile == NULL) || (argv[optind] == NULL)) | ||
531 | bb_show_usage(); | ||
532 | |||
533 | if (localfile == NULL || strcmp(localfile, "-") == 0) { | ||
534 | fd = (cmd == tftp_cmd_get) ? STDOUT_FILENO : STDIN_FILENO; | ||
535 | } else { | ||
536 | fd = open(localfile, flags, 0644); /* fail below */ | ||
537 | } | ||
538 | if (fd < 0) { | ||
539 | bb_perror_msg_and_die("local file"); | ||
540 | } | ||
541 | |||
542 | host = xgethostbyname(argv[optind]); | ||
543 | port = bb_lookup_port(argv[optind + 1], "udp", 69); | ||
544 | |||
545 | #if ENABLE_DEBUG_TFTP | ||
546 | fprintf(stderr, "using server \"%s\", remotefile \"%s\", " | ||
547 | "localfile \"%s\".\n", | ||
548 | inet_ntoa(*((struct in_addr *) host->h_addr)), | ||
549 | remotefile, localfile); | ||
550 | #endif | ||
551 | |||
552 | result = tftp(cmd, host, remotefile, fd, port, blocksize); | ||
553 | |||
554 | if (!(fd == STDOUT_FILENO || fd == STDIN_FILENO)) { | ||
555 | if (ENABLE_FEATURE_CLEAN_UP) | ||
556 | close(fd); | ||
557 | if (cmd == tftp_cmd_get && result != EXIT_SUCCESS) | ||
558 | unlink(localfile); | ||
559 | } | ||
560 | return result; | ||
561 | } | ||
diff --git a/networking/traceroute.c b/networking/traceroute.c new file mode 100644 index 000000000..490076543 --- /dev/null +++ b/networking/traceroute.c | |||
@@ -0,0 +1,1350 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * Copyright (c) 1988, 1989, 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000 | ||
4 | * The Regents of the University of California. All rights reserved. | ||
5 | * | ||
6 | * Busybox port by Vladimir Oleynik (C) 2005 <dzo@simtreas.ru> | ||
7 | * | ||
8 | * Redistribution and use in source and binary forms, with or without | ||
9 | * modification, are permitted provided that: (1) source code distributions | ||
10 | * retain the above copyright notice and this paragraph in its entirety, (2) | ||
11 | * distributions including binary code include the above copyright notice and | ||
12 | * this paragraph in its entirety in the documentation or other materials | ||
13 | * provided with the distribution, and (3) all advertising materials mentioning | ||
14 | * features or use of this software display the following acknowledgement: | ||
15 | * ``This product includes software developed by the University of California, | ||
16 | * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of | ||
17 | * the University nor the names of its contributors may be used to endorse | ||
18 | * or promote products derived from this software without specific prior | ||
19 | * written permission. | ||
20 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED | ||
21 | * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF | ||
22 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. | ||
23 | */ | ||
24 | |||
25 | //#define version "1.4a12" | ||
26 | |||
27 | |||
28 | /* | ||
29 | * traceroute host - trace the route ip packets follow going to "host". | ||
30 | * | ||
31 | * Attempt to trace the route an ip packet would follow to some | ||
32 | * internet host. We find out intermediate hops by launching probe | ||
33 | * packets with a small ttl (time to live) then listening for an | ||
34 | * icmp "time exceeded" reply from a gateway. We start our probes | ||
35 | * with a ttl of one and increase by one until we get an icmp "port | ||
36 | * unreachable" (which means we got to "host") or hit a max (which | ||
37 | * defaults to 30 hops & can be changed with the -m flag). Three | ||
38 | * probes (change with -q flag) are sent at each ttl setting and a | ||
39 | * line is printed showing the ttl, address of the gateway and | ||
40 | * round trip time of each probe. If the probe answers come from | ||
41 | * different gateways, the address of each responding system will | ||
42 | * be printed. If there is no response within a 5 sec. timeout | ||
43 | * interval (changed with the -w flag), a "*" is printed for that | ||
44 | * probe. | ||
45 | * | ||
46 | * Probe packets are UDP format. We don't want the destination | ||
47 | * host to process them so the destination port is set to an | ||
48 | * unlikely value (if some clod on the destination is using that | ||
49 | * value, it can be changed with the -p flag). | ||
50 | * | ||
51 | * A sample use might be: | ||
52 | * | ||
53 | * [yak 71]% traceroute nis.nsf.net. | ||
54 | * traceroute to nis.nsf.net (35.1.1.48), 30 hops max, 56 byte packet | ||
55 | * 1 helios.ee.lbl.gov (128.3.112.1) 19 ms 19 ms 0 ms | ||
56 | * 2 lilac-dmc.Berkeley.EDU (128.32.216.1) 39 ms 39 ms 19 ms | ||
57 | * 3 lilac-dmc.Berkeley.EDU (128.32.216.1) 39 ms 39 ms 19 ms | ||
58 | * 4 ccngw-ner-cc.Berkeley.EDU (128.32.136.23) 39 ms 40 ms 39 ms | ||
59 | * 5 ccn-nerif22.Berkeley.EDU (128.32.168.22) 39 ms 39 ms 39 ms | ||
60 | * 6 128.32.197.4 (128.32.197.4) 40 ms 59 ms 59 ms | ||
61 | * 7 131.119.2.5 (131.119.2.5) 59 ms 59 ms 59 ms | ||
62 | * 8 129.140.70.13 (129.140.70.13) 99 ms 99 ms 80 ms | ||
63 | * 9 129.140.71.6 (129.140.71.6) 139 ms 239 ms 319 ms | ||
64 | * 10 129.140.81.7 (129.140.81.7) 220 ms 199 ms 199 ms | ||
65 | * 11 nic.merit.edu (35.1.1.48) 239 ms 239 ms 239 ms | ||
66 | * | ||
67 | * Note that lines 2 & 3 are the same. This is due to a buggy | ||
68 | * kernel on the 2nd hop system -- lbl-csam.arpa -- that forwards | ||
69 | * packets with a zero ttl. | ||
70 | * | ||
71 | * A more interesting example is: | ||
72 | * | ||
73 | * [yak 72]% traceroute allspice.lcs.mit.edu. | ||
74 | * traceroute to allspice.lcs.mit.edu (18.26.0.115), 30 hops max | ||
75 | * 1 helios.ee.lbl.gov (128.3.112.1) 0 ms 0 ms 0 ms | ||
76 | * 2 lilac-dmc.Berkeley.EDU (128.32.216.1) 19 ms 19 ms 19 ms | ||
77 | * 3 lilac-dmc.Berkeley.EDU (128.32.216.1) 39 ms 19 ms 19 ms | ||
78 | * 4 ccngw-ner-cc.Berkeley.EDU (128.32.136.23) 19 ms 39 ms 39 ms | ||
79 | * 5 ccn-nerif22.Berkeley.EDU (128.32.168.22) 20 ms 39 ms 39 ms | ||
80 | * 6 128.32.197.4 (128.32.197.4) 59 ms 119 ms 39 ms | ||
81 | * 7 131.119.2.5 (131.119.2.5) 59 ms 59 ms 39 ms | ||
82 | * 8 129.140.70.13 (129.140.70.13) 80 ms 79 ms 99 ms | ||
83 | * 9 129.140.71.6 (129.140.71.6) 139 ms 139 ms 159 ms | ||
84 | * 10 129.140.81.7 (129.140.81.7) 199 ms 180 ms 300 ms | ||
85 | * 11 129.140.72.17 (129.140.72.17) 300 ms 239 ms 239 ms | ||
86 | * 12 * * * | ||
87 | * 13 128.121.54.72 (128.121.54.72) 259 ms 499 ms 279 ms | ||
88 | * 14 * * * | ||
89 | * 15 * * * | ||
90 | * 16 * * * | ||
91 | * 17 * * * | ||
92 | * 18 ALLSPICE.LCS.MIT.EDU (18.26.0.115) 339 ms 279 ms 279 ms | ||
93 | * | ||
94 | * (I start to see why I'm having so much trouble with mail to | ||
95 | * MIT.) Note that the gateways 12, 14, 15, 16 & 17 hops away | ||
96 | * either don't send ICMP "time exceeded" messages or send them | ||
97 | * with a ttl too small to reach us. 14 - 17 are running the | ||
98 | * MIT C Gateway code that doesn't send "time exceeded"s. God | ||
99 | * only knows what's going on with 12. | ||
100 | * | ||
101 | * The silent gateway 12 in the above may be the result of a bug in | ||
102 | * the 4.[23]BSD network code (and its derivatives): 4.x (x <= 3) | ||
103 | * sends an unreachable message using whatever ttl remains in the | ||
104 | * original datagram. Since, for gateways, the remaining ttl is | ||
105 | * zero, the icmp "time exceeded" is guaranteed to not make it back | ||
106 | * to us. The behavior of this bug is slightly more interesting | ||
107 | * when it appears on the destination system: | ||
108 | * | ||
109 | * 1 helios.ee.lbl.gov (128.3.112.1) 0 ms 0 ms 0 ms | ||
110 | * 2 lilac-dmc.Berkeley.EDU (128.32.216.1) 39 ms 19 ms 39 ms | ||
111 | * 3 lilac-dmc.Berkeley.EDU (128.32.216.1) 19 ms 39 ms 19 ms | ||
112 | * 4 ccngw-ner-cc.Berkeley.EDU (128.32.136.23) 39 ms 40 ms 19 ms | ||
113 | * 5 ccn-nerif35.Berkeley.EDU (128.32.168.35) 39 ms 39 ms 39 ms | ||
114 | * 6 csgw.Berkeley.EDU (128.32.133.254) 39 ms 59 ms 39 ms | ||
115 | * 7 * * * | ||
116 | * 8 * * * | ||
117 | * 9 * * * | ||
118 | * 10 * * * | ||
119 | * 11 * * * | ||
120 | * 12 * * * | ||
121 | * 13 rip.Berkeley.EDU (128.32.131.22) 59 ms ! 39 ms ! 39 ms ! | ||
122 | * | ||
123 | * Notice that there are 12 "gateways" (13 is the final | ||
124 | * destination) and exactly the last half of them are "missing". | ||
125 | * What's really happening is that rip (a Sun-3 running Sun OS3.5) | ||
126 | * is using the ttl from our arriving datagram as the ttl in its | ||
127 | * icmp reply. So, the reply will time out on the return path | ||
128 | * (with no notice sent to anyone since icmp's aren't sent for | ||
129 | * icmp's) until we probe with a ttl that's at least twice the path | ||
130 | * length. I.e., rip is really only 7 hops away. A reply that | ||
131 | * returns with a ttl of 1 is a clue this problem exists. | ||
132 | * Traceroute prints a "!" after the time if the ttl is <= 1. | ||
133 | * Since vendors ship a lot of obsolete (DEC's Ultrix, Sun 3.x) or | ||
134 | * non-standard (HPUX) software, expect to see this problem | ||
135 | * frequently and/or take care picking the target host of your | ||
136 | * probes. | ||
137 | * | ||
138 | * Other possible annotations after the time are !H, !N, !P (got a host, | ||
139 | * network or protocol unreachable, respectively), !S or !F (source | ||
140 | * route failed or fragmentation needed -- neither of these should | ||
141 | * ever occur and the associated gateway is busted if you see one). If | ||
142 | * almost all the probes result in some kind of unreachable, traceroute | ||
143 | * will give up and exit. | ||
144 | * | ||
145 | * Notes | ||
146 | * ----- | ||
147 | * This program must be run by root or be setuid. (I suggest that | ||
148 | * you *don't* make it setuid -- casual use could result in a lot | ||
149 | * of unnecessary traffic on our poor, congested nets.) | ||
150 | * | ||
151 | * This program requires a kernel mod that does not appear in any | ||
152 | * system available from Berkeley: A raw ip socket using proto | ||
153 | * IPPROTO_RAW must interpret the data sent as an ip datagram (as | ||
154 | * opposed to data to be wrapped in a ip datagram). See the README | ||
155 | * file that came with the source to this program for a description | ||
156 | * of the mods I made to /sys/netinet/raw_ip.c. Your mileage may | ||
157 | * vary. But, again, ANY 4.x (x < 4) BSD KERNEL WILL HAVE TO BE | ||
158 | * MODIFIED TO RUN THIS PROGRAM. | ||
159 | * | ||
160 | * The udp port usage may appear bizarre (well, ok, it is bizarre). | ||
161 | * The problem is that an icmp message only contains 8 bytes of | ||
162 | * data from the original datagram. 8 bytes is the size of a udp | ||
163 | * header so, if we want to associate replies with the original | ||
164 | * datagram, the necessary information must be encoded into the | ||
165 | * udp header (the ip id could be used but there's no way to | ||
166 | * interlock with the kernel's assignment of ip id's and, anyway, | ||
167 | * it would have taken a lot more kernel hacking to allow this | ||
168 | * code to set the ip id). So, to allow two or more users to | ||
169 | * use traceroute simultaneously, we use this task's pid as the | ||
170 | * source port (the high bit is set to move the port number out | ||
171 | * of the "likely" range). To keep track of which probe is being | ||
172 | * replied to (so times and/or hop counts don't get confused by a | ||
173 | * reply that was delayed in transit), we increment the destination | ||
174 | * port number before each probe. | ||
175 | * | ||
176 | * Don't use this as a coding example. I was trying to find a | ||
177 | * routing problem and this code sort-of popped out after 48 hours | ||
178 | * without sleep. I was amazed it ever compiled, much less ran. | ||
179 | * | ||
180 | * I stole the idea for this program from Steve Deering. Since | ||
181 | * the first release, I've learned that had I attended the right | ||
182 | * IETF working group meetings, I also could have stolen it from Guy | ||
183 | * Almes or Matt Mathis. I don't know (or care) who came up with | ||
184 | * the idea first. I envy the originators' perspicacity and I'm | ||
185 | * glad they didn't keep the idea a secret. | ||
186 | * | ||
187 | * Tim Seaver, Ken Adelman and C. Philip Wood provided bug fixes and/or | ||
188 | * enhancements to the original distribution. | ||
189 | * | ||
190 | * I've hacked up a round-trip-route version of this that works by | ||
191 | * sending a loose-source-routed udp datagram through the destination | ||
192 | * back to yourself. Unfortunately, SO many gateways botch source | ||
193 | * routing, the thing is almost worthless. Maybe one day... | ||
194 | * | ||
195 | * -- Van Jacobson (van@ee.lbl.gov) | ||
196 | * Tue Dec 20 03:50:13 PST 1988 | ||
197 | */ | ||
198 | |||
199 | #define TRACEROUTE_SO_DEBUG 0 | ||
200 | |||
201 | /* TODO: undefs were uncommented - ??! we have config system for that! */ | ||
202 | /* probably ok to remove altogether */ | ||
203 | //#undef CONFIG_FEATURE_TRACEROUTE_VERBOSE | ||
204 | //#define CONFIG_FEATURE_TRACEROUTE_VERBOSE | ||
205 | //#undef CONFIG_FEATURE_TRACEROUTE_SOURCE_ROUTE | ||
206 | //#define CONFIG_FEATURE_TRACEROUTE_SOURCE_ROUTE | ||
207 | //#undef CONFIG_FEATURE_TRACEROUTE_USE_ICMP | ||
208 | //#define CONFIG_FEATURE_TRACEROUTE_USE_ICMP | ||
209 | |||
210 | #include "inet_common.h" | ||
211 | |||
212 | #include <net/if.h> | ||
213 | #include <arpa/inet.h> | ||
214 | #include <netinet/in.h> | ||
215 | #include <netinet/udp.h> | ||
216 | #include <netinet/ip.h> | ||
217 | #include <netinet/ip_icmp.h> | ||
218 | |||
219 | #include "busybox.h" | ||
220 | |||
221 | |||
222 | /* | ||
223 | * Definitions for internet protocol version 4. | ||
224 | * Per RFC 791, September 1981. | ||
225 | */ | ||
226 | #define IPVERSION 4 | ||
227 | |||
228 | #ifndef IPPROTO_ICMP | ||
229 | /* Grrrr.... */ | ||
230 | #define IPPROTO_ICMP 1 | ||
231 | #endif | ||
232 | #ifndef IPPROTO_IP | ||
233 | #define IPPROTO_IP 0 | ||
234 | #endif | ||
235 | |||
236 | /* | ||
237 | * Overlay for ip header used by other protocols (tcp, udp). | ||
238 | */ | ||
239 | struct ipovly { | ||
240 | unsigned char ih_x1[9]; /* (unused) */ | ||
241 | unsigned char ih_pr; /* protocol */ | ||
242 | short ih_len; /* protocol length */ | ||
243 | struct in_addr ih_src; /* source internet address */ | ||
244 | struct in_addr ih_dst; /* destination internet address */ | ||
245 | }; | ||
246 | |||
247 | /* | ||
248 | * UDP kernel structures and variables. | ||
249 | */ | ||
250 | struct udpiphdr { | ||
251 | struct ipovly ui_i; /* overlaid ip structure */ | ||
252 | struct udphdr ui_u; /* udp header */ | ||
253 | }; | ||
254 | #define ui_next ui_i.ih_next | ||
255 | #define ui_prev ui_i.ih_prev | ||
256 | #define ui_x1 ui_i.ih_x1 | ||
257 | #define ui_pr ui_i.ih_pr | ||
258 | #define ui_len ui_i.ih_len | ||
259 | #define ui_src ui_i.ih_src | ||
260 | #define ui_dst ui_i.ih_dst | ||
261 | #define ui_sport ui_u.uh_sport | ||
262 | #define ui_dport ui_u.uh_dport | ||
263 | #define ui_ulen ui_u.uh_ulen | ||
264 | #define ui_sum ui_u.uh_sum | ||
265 | |||
266 | |||
267 | /* Host name and address list */ | ||
268 | struct hostinfo { | ||
269 | char *name; | ||
270 | int n; | ||
271 | u_int32_t *addrs; | ||
272 | }; | ||
273 | |||
274 | /* Data section of the probe packet */ | ||
275 | struct outdata { | ||
276 | unsigned char seq; /* sequence number of this packet */ | ||
277 | unsigned char ttl; /* ttl packet left with */ | ||
278 | struct timeval tv ATTRIBUTE_PACKED; /* time packet left */ | ||
279 | }; | ||
280 | |||
281 | struct IFADDRLIST { | ||
282 | u_int32_t addr; | ||
283 | char device[sizeof(struct ifreq)]; | ||
284 | }; | ||
285 | |||
286 | |||
287 | static const char route[] = "/proc/net/route"; | ||
288 | |||
289 | /* last inbound (icmp) packet */ | ||
290 | static unsigned char packet[512] ATTRIBUTE_ALIGNED(32); | ||
291 | |||
292 | static struct ip *outip; /* last output (udp) packet */ | ||
293 | static struct udphdr *outudp; /* last output (udp) packet */ | ||
294 | static struct outdata *outdata; /* last output (udp) packet */ | ||
295 | |||
296 | #if ENABLE_FEATURE_TRACEROUTE_USE_ICMP | ||
297 | static struct icmp *outicmp; /* last output (icmp) packet */ | ||
298 | #endif | ||
299 | |||
300 | #if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE | ||
301 | /* Maximum number of gateways (include room for one noop) */ | ||
302 | #define NGATEWAYS ((int)((MAX_IPOPTLEN - IPOPT_MINOFF - 1) / sizeof(u_int32_t))) | ||
303 | /* loose source route gateway list (including room for final destination) */ | ||
304 | static u_int32_t gwlist[NGATEWAYS + 1]; | ||
305 | #endif | ||
306 | |||
307 | static int s; /* receive (icmp) socket file descriptor */ | ||
308 | static int sndsock; /* send (udp/icmp) socket file descriptor */ | ||
309 | |||
310 | static struct sockaddr_storage whereto; /* Who to try to reach */ | ||
311 | static struct sockaddr_storage wherefrom; /* Who we are */ | ||
312 | static int packlen; /* total length of packet */ | ||
313 | static int minpacket; /* min ip packet size */ | ||
314 | static int maxpacket = 32 * 1024; /* max ip packet size */ | ||
315 | static int pmtu; /* Path MTU Discovery (RFC1191) */ | ||
316 | |||
317 | static char *hostname; | ||
318 | |||
319 | static u_short ident; | ||
320 | static u_short port = 32768 + 666; /* start udp dest port # for probe packets */ | ||
321 | |||
322 | static int waittime = 5; /* time to wait for response (in seconds) */ | ||
323 | static int nflag; /* print addresses numerically */ | ||
324 | static int doipcksum = 1; /* calculate ip checksums by default */ | ||
325 | |||
326 | #if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE | ||
327 | static int optlen; /* length of ip options */ | ||
328 | #else | ||
329 | #define optlen 0 | ||
330 | #endif | ||
331 | |||
332 | #if ENABLE_FEATURE_TRACEROUTE_USE_ICMP | ||
333 | static int useicmp; /* use icmp echo instead of udp packets */ | ||
334 | #endif | ||
335 | #if ENABLE_FEATURE_TRACEROUTE_VERBOSE | ||
336 | static int verbose; | ||
337 | #endif | ||
338 | |||
339 | /* | ||
340 | * Return the interface list | ||
341 | */ | ||
342 | static int | ||
343 | ifaddrlist(struct IFADDRLIST **ipaddrp) | ||
344 | { | ||
345 | int fd, nipaddr; | ||
346 | #ifdef HAVE_SOCKADDR_SA_LEN | ||
347 | int n; | ||
348 | #endif | ||
349 | struct ifreq *ifrp, *ifend, *ifnext; | ||
350 | struct sockaddr_in *addr_sin; | ||
351 | struct IFADDRLIST *al; | ||
352 | struct ifconf ifc; | ||
353 | struct ifreq ibuf[(32 * 1024) / sizeof(struct ifreq)], ifr; | ||
354 | struct IFADDRLIST *st_ifaddrlist; | ||
355 | |||
356 | fd = xsocket(AF_INET, SOCK_DGRAM, 0); | ||
357 | |||
358 | ifc.ifc_len = sizeof(ibuf); | ||
359 | ifc.ifc_buf = (caddr_t)ibuf; | ||
360 | |||
361 | if (ioctl(fd, SIOCGIFCONF, (char *)&ifc) < 0 || | ||
362 | ifc.ifc_len < sizeof(struct ifreq)) { | ||
363 | if (errno == EINVAL) | ||
364 | bb_error_msg_and_die( | ||
365 | "SIOCGIFCONF: ifreq struct too small (%d bytes)", | ||
366 | (int)sizeof(ibuf)); | ||
367 | else | ||
368 | bb_perror_msg_and_die("SIOCGIFCONF"); | ||
369 | } | ||
370 | ifrp = ibuf; | ||
371 | ifend = (struct ifreq *)((char *)ibuf + ifc.ifc_len); | ||
372 | |||
373 | nipaddr = 1 + (ifc.ifc_len / sizeof(struct ifreq)); | ||
374 | st_ifaddrlist = xzalloc(nipaddr * sizeof(struct IFADDRLIST)); | ||
375 | al = st_ifaddrlist; | ||
376 | nipaddr = 0; | ||
377 | |||
378 | for (; ifrp < ifend; ifrp = ifnext) { | ||
379 | #ifdef HAVE_SOCKADDR_SA_LEN | ||
380 | n = ifrp->ifr_addr.sa_len + sizeof(ifrp->ifr_name); | ||
381 | if (n < sizeof(*ifrp)) | ||
382 | ifnext = ifrp + 1; | ||
383 | else | ||
384 | ifnext = (struct ifreq *)((char *)ifrp + n); | ||
385 | if (ifrp->ifr_addr.sa_family != AF_INET) | ||
386 | continue; | ||
387 | #else | ||
388 | ifnext = ifrp + 1; | ||
389 | #endif | ||
390 | /* | ||
391 | * Need a template to preserve address info that is | ||
392 | * used below to locate the next entry. (Otherwise, | ||
393 | * SIOCGIFFLAGS stomps over it because the requests | ||
394 | * are returned in a union.) | ||
395 | */ | ||
396 | strncpy(ifr.ifr_name, ifrp->ifr_name, sizeof(ifr.ifr_name)); | ||
397 | if (ioctl(fd, SIOCGIFFLAGS, (char *)&ifr) < 0) { | ||
398 | if (errno == ENXIO) | ||
399 | continue; | ||
400 | bb_perror_msg_and_die("SIOCGIFFLAGS: %.*s", | ||
401 | (int)sizeof(ifr.ifr_name), ifr.ifr_name); | ||
402 | } | ||
403 | |||
404 | /* Must be up */ | ||
405 | if ((ifr.ifr_flags & IFF_UP) == 0) | ||
406 | continue; | ||
407 | |||
408 | safe_strncpy(al->device, ifr.ifr_name, sizeof(ifr.ifr_name) + 1); | ||
409 | #ifdef sun | ||
410 | /* Ignore sun virtual interfaces */ | ||
411 | if (strchr(al->device, ':') != NULL) | ||
412 | continue; | ||
413 | #endif | ||
414 | if (ioctl(fd, SIOCGIFADDR, (char *)&ifr) < 0) | ||
415 | bb_perror_msg_and_die("SIOCGIFADDR: %s", al->device); | ||
416 | |||
417 | addr_sin = (struct sockaddr_in *)&ifr.ifr_addr; | ||
418 | al->addr = addr_sin->sin_addr.s_addr; | ||
419 | ++al; | ||
420 | ++nipaddr; | ||
421 | } | ||
422 | if (nipaddr == 0) | ||
423 | bb_error_msg_and_die ("can't find any network interfaces"); | ||
424 | (void)close(fd); | ||
425 | |||
426 | *ipaddrp = st_ifaddrlist; | ||
427 | return nipaddr; | ||
428 | } | ||
429 | |||
430 | |||
431 | static void | ||
432 | setsin(struct sockaddr_in *addr_sin, u_int32_t addr) | ||
433 | { | ||
434 | memset(addr_sin, 0, sizeof(*addr_sin)); | ||
435 | #ifdef HAVE_SOCKADDR_SA_LEN | ||
436 | addr_sin->sin_len = sizeof(*addr_sin); | ||
437 | #endif | ||
438 | addr_sin->sin_family = AF_INET; | ||
439 | addr_sin->sin_addr.s_addr = addr; | ||
440 | } | ||
441 | |||
442 | |||
443 | /* | ||
444 | * Return the source address for the given destination address | ||
445 | */ | ||
446 | static void | ||
447 | findsaddr(const struct sockaddr_in *to, struct sockaddr_in *from) | ||
448 | { | ||
449 | int i, n; | ||
450 | FILE *f; | ||
451 | u_int32_t mask; | ||
452 | u_int32_t dest, tmask; | ||
453 | struct IFADDRLIST *al; | ||
454 | char buf[256], tdevice[256], device[256]; | ||
455 | |||
456 | f = xfopen(route, "r"); | ||
457 | |||
458 | /* Find the appropriate interface */ | ||
459 | n = 0; | ||
460 | mask = 0; | ||
461 | device[0] = '\0'; | ||
462 | while (fgets(buf, sizeof(buf), f) != NULL) { | ||
463 | ++n; | ||
464 | if (n == 1 && strncmp(buf, "Iface", 5) == 0) | ||
465 | continue; | ||
466 | if ((i = sscanf(buf, "%255s %x %*s %*s %*s %*s %*s %x", | ||
467 | tdevice, &dest, &tmask)) != 3) | ||
468 | bb_error_msg_and_die ("junk in buffer"); | ||
469 | if ((to->sin_addr.s_addr & tmask) == dest && | ||
470 | (tmask > mask || mask == 0)) { | ||
471 | mask = tmask; | ||
472 | strcpy(device, tdevice); | ||
473 | } | ||
474 | } | ||
475 | fclose(f); | ||
476 | |||
477 | if (device[0] == '\0') | ||
478 | bb_error_msg_and_die ("can't find interface"); | ||
479 | |||
480 | /* Get the interface address list */ | ||
481 | n = ifaddrlist(&al); | ||
482 | |||
483 | /* Find our appropriate source address */ | ||
484 | for (i = n; i > 0; --i, ++al) | ||
485 | if (strcmp(device, al->device) == 0) | ||
486 | break; | ||
487 | if (i <= 0) | ||
488 | bb_error_msg_and_die("can't find interface %s", device); | ||
489 | |||
490 | setsin(from, al->addr); | ||
491 | } | ||
492 | |||
493 | /* | ||
494 | "Usage: %s [-dFIlnrvx] [-g gateway] [-i iface] [-f first_ttl]\n" | ||
495 | "\t[-m max_ttl] [ -p port] [-q nqueries] [-s src_addr] [-t tos]\n" | ||
496 | "\t[-w waittime] [-z pausemsecs] host [packetlen]" | ||
497 | |||
498 | */ | ||
499 | |||
500 | /* | ||
501 | * Subtract 2 timeval structs: out = out - in. | ||
502 | * Out is assumed to be >= in. | ||
503 | */ | ||
504 | static inline void | ||
505 | tvsub(struct timeval *out, struct timeval *in) | ||
506 | { | ||
507 | |||
508 | if ((out->tv_usec -= in->tv_usec) < 0) { | ||
509 | --out->tv_sec; | ||
510 | out->tv_usec += 1000000; | ||
511 | } | ||
512 | out->tv_sec -= in->tv_sec; | ||
513 | } | ||
514 | |||
515 | static int | ||
516 | wait_for_reply(int sock, struct sockaddr_in *fromp, const struct timeval *tp) | ||
517 | { | ||
518 | fd_set fds; | ||
519 | struct timeval now, tvwait; | ||
520 | struct timezone tz; | ||
521 | int cc = 0; | ||
522 | socklen_t fromlen = sizeof(*fromp); | ||
523 | |||
524 | FD_ZERO(&fds); | ||
525 | FD_SET(sock, &fds); | ||
526 | |||
527 | tvwait.tv_sec = tp->tv_sec + waittime; | ||
528 | tvwait.tv_usec = tp->tv_usec; | ||
529 | (void)gettimeofday(&now, &tz); | ||
530 | tvsub(&tvwait, &now); | ||
531 | |||
532 | if (select(sock + 1, &fds, NULL, NULL, &tvwait) > 0) | ||
533 | cc = recvfrom(sock, (char *)packet, sizeof(packet), 0, | ||
534 | (struct sockaddr *)fromp, &fromlen); | ||
535 | |||
536 | return cc; | ||
537 | } | ||
538 | |||
539 | /* | ||
540 | * Checksum routine for Internet Protocol family headers (C Version) | ||
541 | */ | ||
542 | static u_short | ||
543 | in_cksum(u_short *addr, int len) | ||
544 | { | ||
545 | int nleft = len; | ||
546 | u_short *w = addr; | ||
547 | u_short answer; | ||
548 | int sum = 0; | ||
549 | |||
550 | /* | ||
551 | * Our algorithm is simple, using a 32 bit accumulator (sum), | ||
552 | * we add sequential 16 bit words to it, and at the end, fold | ||
553 | * back all the carry bits from the top 16 bits into the lower | ||
554 | * 16 bits. | ||
555 | */ | ||
556 | while (nleft > 1) { | ||
557 | sum += *w++; | ||
558 | nleft -= 2; | ||
559 | } | ||
560 | |||
561 | /* mop up an odd byte, if necessary */ | ||
562 | if (nleft == 1) | ||
563 | sum += *(unsigned char *)w; | ||
564 | |||
565 | /* | ||
566 | * add back carry outs from top 16 bits to low 16 bits | ||
567 | */ | ||
568 | sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */ | ||
569 | sum += (sum >> 16); /* add carry */ | ||
570 | answer = ~sum; /* truncate to 16 bits */ | ||
571 | return answer; | ||
572 | } | ||
573 | |||
574 | |||
575 | static void | ||
576 | send_probe(int seq, int ttl, struct timeval *tp) | ||
577 | { | ||
578 | int cc; | ||
579 | struct udpiphdr *ui, *oui; | ||
580 | struct ip tip; | ||
581 | |||
582 | outip->ip_ttl = ttl; | ||
583 | outip->ip_id = htons(ident + seq); | ||
584 | |||
585 | /* | ||
586 | * In most cases, the kernel will recalculate the ip checksum. | ||
587 | * But we must do it anyway so that the udp checksum comes out | ||
588 | * right. | ||
589 | */ | ||
590 | if (doipcksum) { | ||
591 | outip->ip_sum = | ||
592 | in_cksum((u_short *)outip, sizeof(*outip) + optlen); | ||
593 | if (outip->ip_sum == 0) | ||
594 | outip->ip_sum = 0xffff; | ||
595 | } | ||
596 | |||
597 | /* Payload */ | ||
598 | outdata->seq = seq; | ||
599 | outdata->ttl = ttl; | ||
600 | memcpy(&outdata->tv, tp, sizeof(outdata->tv)); | ||
601 | |||
602 | #if ENABLE_FEATURE_TRACEROUTE_USE_ICMP | ||
603 | if (useicmp) | ||
604 | outicmp->icmp_seq = htons(seq); | ||
605 | else | ||
606 | #endif | ||
607 | outudp->dest = htons(port + seq); | ||
608 | |||
609 | #if ENABLE_FEATURE_TRACEROUTE_USE_ICMP | ||
610 | if (useicmp) { | ||
611 | /* Always calculate checksum for icmp packets */ | ||
612 | outicmp->icmp_cksum = 0; | ||
613 | outicmp->icmp_cksum = in_cksum((u_short *)outicmp, | ||
614 | packlen - (sizeof(*outip) + optlen)); | ||
615 | if (outicmp->icmp_cksum == 0) | ||
616 | outicmp->icmp_cksum = 0xffff; | ||
617 | } else | ||
618 | #endif | ||
619 | if (doipcksum) { | ||
620 | /* Checksum (we must save and restore ip header) */ | ||
621 | tip = *outip; | ||
622 | ui = (struct udpiphdr *)outip; | ||
623 | oui = (struct udpiphdr *)&tip; | ||
624 | /* Easier to zero and put back things that are ok */ | ||
625 | memset((char *)ui, 0, sizeof(ui->ui_i)); | ||
626 | ui->ui_src = oui->ui_src; | ||
627 | ui->ui_dst = oui->ui_dst; | ||
628 | ui->ui_pr = oui->ui_pr; | ||
629 | ui->ui_len = outudp->len; | ||
630 | outudp->check = 0; | ||
631 | outudp->check = in_cksum((u_short *)ui, packlen); | ||
632 | if (outudp->check == 0) | ||
633 | outudp->check = 0xffff; | ||
634 | *outip = tip; | ||
635 | } | ||
636 | |||
637 | #if ENABLE_FEATURE_TRACEROUTE_VERBOSE | ||
638 | /* XXX undocumented debugging hack */ | ||
639 | if (verbose > 1) { | ||
640 | const u_short *sp; | ||
641 | int nshorts, i; | ||
642 | |||
643 | sp = (u_short *)outip; | ||
644 | nshorts = (u_int)packlen / sizeof(u_short); | ||
645 | i = 0; | ||
646 | printf("[ %d bytes", packlen); | ||
647 | while (--nshorts >= 0) { | ||
648 | if ((i++ % 8) == 0) | ||
649 | printf("\n\t"); | ||
650 | printf(" %04x", ntohs(*sp)); | ||
651 | sp++; | ||
652 | } | ||
653 | if (packlen & 1) { | ||
654 | if ((i % 8) == 0) | ||
655 | printf("\n\t"); | ||
656 | printf(" %02x", *(unsigned char *)sp); | ||
657 | } | ||
658 | printf("]\n"); | ||
659 | } | ||
660 | #endif | ||
661 | |||
662 | #if !defined(IP_HDRINCL) && defined(IP_TTL) | ||
663 | if (setsockopt(sndsock, IPPROTO_IP, IP_TTL, | ||
664 | (char *)&ttl, sizeof(ttl)) < 0) { | ||
665 | bb_perror_msg_and_die("setsockopt ttl %d", ttl); | ||
666 | } | ||
667 | #endif | ||
668 | |||
669 | cc = sendto(sndsock, (char *)outip, | ||
670 | packlen, 0, (struct sockaddr *)&whereto, sizeof(whereto)); | ||
671 | if (cc < 0 || cc != packlen) { | ||
672 | if (cc < 0) | ||
673 | bb_perror_msg_and_die("sendto"); | ||
674 | printf("%s: wrote %s %d chars, ret=%d\n", | ||
675 | applet_name, hostname, packlen, cc); | ||
676 | (void)fflush(stdout); | ||
677 | } | ||
678 | } | ||
679 | |||
680 | static inline double | ||
681 | deltaT(struct timeval *t1p, struct timeval *t2p) | ||
682 | { | ||
683 | double dt; | ||
684 | |||
685 | dt = (double)(t2p->tv_sec - t1p->tv_sec) * 1000.0 + | ||
686 | (double)(t2p->tv_usec - t1p->tv_usec) / 1000.0; | ||
687 | return dt; | ||
688 | } | ||
689 | |||
690 | #if ENABLE_FEATURE_TRACEROUTE_VERBOSE | ||
691 | /* | ||
692 | * Convert an ICMP "type" field to a printable string. | ||
693 | */ | ||
694 | static inline const char * | ||
695 | pr_type(unsigned char t) | ||
696 | { | ||
697 | static const char * const ttab[] = { | ||
698 | "Echo Reply", "ICMP 1", "ICMP 2", "Dest Unreachable", | ||
699 | "Source Quench", "Redirect", "ICMP 6", "ICMP 7", | ||
700 | "Echo", "Router Advert", "Router Solicit", "Time Exceeded", | ||
701 | "Param Problem", "Timestamp", "Timestamp Reply", "Info Request", | ||
702 | "Info Reply", "Mask Request", "Mask Reply" | ||
703 | }; | ||
704 | |||
705 | if (t > 18) | ||
706 | return "OUT-OF-RANGE"; | ||
707 | |||
708 | return ttab[t]; | ||
709 | } | ||
710 | #endif | ||
711 | |||
712 | static int | ||
713 | packet_ok(unsigned char *buf, int cc, struct sockaddr_in *from, int seq) | ||
714 | { | ||
715 | struct icmp *icp; | ||
716 | unsigned char type, code; | ||
717 | int hlen; | ||
718 | struct ip *ip; | ||
719 | |||
720 | ip = (struct ip *) buf; | ||
721 | hlen = ip->ip_hl << 2; | ||
722 | if (cc < hlen + ICMP_MINLEN) { | ||
723 | #if ENABLE_FEATURE_TRACEROUTE_VERBOSE | ||
724 | if (verbose) | ||
725 | printf("packet too short (%d bytes) from %s\n", cc, | ||
726 | inet_ntoa(from->sin_addr)); | ||
727 | #endif | ||
728 | return 0; | ||
729 | } | ||
730 | cc -= hlen; | ||
731 | icp = (struct icmp *)(buf + hlen); | ||
732 | type = icp->icmp_type; | ||
733 | code = icp->icmp_code; | ||
734 | /* Path MTU Discovery (RFC1191) */ | ||
735 | if (code != ICMP_UNREACH_NEEDFRAG) | ||
736 | pmtu = 0; | ||
737 | else { | ||
738 | pmtu = ntohs(icp->icmp_nextmtu); | ||
739 | } | ||
740 | if ((type == ICMP_TIMXCEED && code == ICMP_TIMXCEED_INTRANS) || | ||
741 | type == ICMP_UNREACH || type == ICMP_ECHOREPLY) { | ||
742 | struct ip *hip; | ||
743 | struct udphdr *up; | ||
744 | |||
745 | hip = &icp->icmp_ip; | ||
746 | hlen = hip->ip_hl << 2; | ||
747 | #if ENABLE_FEATURE_TRACEROUTE_USE_ICMP | ||
748 | if (useicmp) { | ||
749 | struct icmp *hicmp; | ||
750 | |||
751 | /* XXX */ | ||
752 | if (type == ICMP_ECHOREPLY && | ||
753 | icp->icmp_id == htons(ident) && | ||
754 | icp->icmp_seq == htons(seq)) | ||
755 | return -2; | ||
756 | |||
757 | hicmp = (struct icmp *)((unsigned char *)hip + hlen); | ||
758 | /* XXX 8 is a magic number */ | ||
759 | if (hlen + 8 <= cc && | ||
760 | hip->ip_p == IPPROTO_ICMP && | ||
761 | hicmp->icmp_id == htons(ident) && | ||
762 | hicmp->icmp_seq == htons(seq)) | ||
763 | return (type == ICMP_TIMXCEED ? -1 : code + 1); | ||
764 | } else | ||
765 | #endif | ||
766 | { | ||
767 | up = (struct udphdr *)((unsigned char *)hip + hlen); | ||
768 | /* XXX 8 is a magic number */ | ||
769 | if (hlen + 12 <= cc && | ||
770 | hip->ip_p == IPPROTO_UDP && | ||
771 | up->source == htons(ident) && | ||
772 | up->dest == htons(port + seq)) | ||
773 | return (type == ICMP_TIMXCEED ? -1 : code + 1); | ||
774 | } | ||
775 | } | ||
776 | #if ENABLE_FEATURE_TRACEROUTE_VERBOSE | ||
777 | if (verbose) { | ||
778 | int i; | ||
779 | u_int32_t *lp = (u_int32_t *)&icp->icmp_ip; | ||
780 | |||
781 | printf("\n%d bytes from %s to " | ||
782 | "%s: icmp type %d (%s) code %d\n", | ||
783 | cc, inet_ntoa(from->sin_addr), | ||
784 | inet_ntoa(ip->ip_dst), type, pr_type(type), icp->icmp_code); | ||
785 | for (i = 4; i < cc ; i += sizeof(*lp)) | ||
786 | printf("%2d: x%8.8x\n", i, *lp++); | ||
787 | } | ||
788 | #endif | ||
789 | return 0; | ||
790 | } | ||
791 | |||
792 | |||
793 | /* | ||
794 | * Construct an Internet address representation. | ||
795 | * If the nflag has been supplied, give | ||
796 | * numeric value, otherwise try for symbolic name. | ||
797 | */ | ||
798 | static inline void | ||
799 | inetname(struct sockaddr_in *from) | ||
800 | { | ||
801 | const char *n = NULL; | ||
802 | const char *ina; | ||
803 | char name[257]; | ||
804 | |||
805 | if (!nflag && from->sin_addr.s_addr != INADDR_ANY) { | ||
806 | if (INET_rresolve(name, sizeof(name), from, 0x4000, 0xffffffff) >= 0) | ||
807 | n = name; | ||
808 | } | ||
809 | ina = inet_ntoa(from->sin_addr); | ||
810 | if (nflag) | ||
811 | printf(" %s", ina); | ||
812 | else | ||
813 | printf(" %s (%s)", (n ? n : ina), ina); | ||
814 | } | ||
815 | |||
816 | static inline void | ||
817 | print(unsigned char *buf, int cc, struct sockaddr_in *from) | ||
818 | { | ||
819 | struct ip *ip; | ||
820 | int hlen; | ||
821 | |||
822 | ip = (struct ip *) buf; | ||
823 | hlen = ip->ip_hl << 2; | ||
824 | cc -= hlen; | ||
825 | |||
826 | inetname(from); | ||
827 | #if ENABLE_FEATURE_TRACEROUTE_VERBOSE | ||
828 | if (verbose) | ||
829 | printf(" %d bytes to %s", cc, inet_ntoa (ip->ip_dst)); | ||
830 | #endif | ||
831 | } | ||
832 | |||
833 | |||
834 | static struct hostinfo * | ||
835 | gethostinfo(const char *host) | ||
836 | { | ||
837 | int n; | ||
838 | struct hostent *hp; | ||
839 | struct hostinfo *hi; | ||
840 | char **p; | ||
841 | u_int32_t addr, *ap; | ||
842 | |||
843 | hi = xzalloc(sizeof(*hi)); | ||
844 | addr = inet_addr(host); | ||
845 | if ((int32_t)addr != -1) { | ||
846 | hi->name = xstrdup(host); | ||
847 | hi->n = 1; | ||
848 | hi->addrs = xzalloc(sizeof(hi->addrs[0])); | ||
849 | hi->addrs[0] = addr; | ||
850 | return hi; | ||
851 | } | ||
852 | |||
853 | hp = xgethostbyname(host); | ||
854 | if (hp->h_addrtype != AF_INET || hp->h_length != 4) | ||
855 | bb_perror_msg_and_die("bad host %s", host); | ||
856 | hi->name = xstrdup(hp->h_name); | ||
857 | for (n = 0, p = hp->h_addr_list; *p != NULL; ++n, ++p) | ||
858 | continue; | ||
859 | hi->n = n; | ||
860 | hi->addrs = xzalloc(n * sizeof(hi->addrs[0])); | ||
861 | for (ap = hi->addrs, p = hp->h_addr_list; *p != NULL; ++ap, ++p) | ||
862 | memcpy(ap, *p, sizeof(*ap)); | ||
863 | return hi; | ||
864 | } | ||
865 | |||
866 | static void | ||
867 | freehostinfo(struct hostinfo *hi) | ||
868 | { | ||
869 | free(hi->name); | ||
870 | hi->name = NULL; | ||
871 | free((char *)hi->addrs); | ||
872 | free((char *)hi); | ||
873 | } | ||
874 | |||
875 | #if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE | ||
876 | static void | ||
877 | getaddr(u_int32_t *ap, const char *host) | ||
878 | { | ||
879 | struct hostinfo *hi; | ||
880 | |||
881 | hi = gethostinfo(host); | ||
882 | *ap = hi->addrs[0]; | ||
883 | freehostinfo(hi); | ||
884 | } | ||
885 | #endif | ||
886 | |||
887 | |||
888 | int | ||
889 | traceroute_main(int argc, char *argv[]) | ||
890 | { | ||
891 | static const int on = 1; | ||
892 | |||
893 | int code, n; | ||
894 | unsigned char *outp; | ||
895 | u_int32_t *ap; | ||
896 | struct sockaddr_in *from = (struct sockaddr_in *)&wherefrom; | ||
897 | struct sockaddr_in *to = (struct sockaddr_in *)&whereto; | ||
898 | struct hostinfo *hi; | ||
899 | int ttl, probe, i; | ||
900 | int seq = 0; | ||
901 | int tos = 0; | ||
902 | char *tos_str = NULL; | ||
903 | char *source = NULL; | ||
904 | unsigned long op; | ||
905 | |||
906 | #if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE | ||
907 | int lsrr = 0; | ||
908 | #endif | ||
909 | u_short off = 0; | ||
910 | struct IFADDRLIST *al; | ||
911 | char *device = NULL; | ||
912 | int max_ttl = 30; | ||
913 | char *max_ttl_str = NULL; | ||
914 | char *port_str = NULL; | ||
915 | int nprobes = 3; | ||
916 | char *nprobes_str = NULL; | ||
917 | char *waittime_str = NULL; | ||
918 | u_int pausemsecs = 0; | ||
919 | char *pausemsecs_str = NULL; | ||
920 | int first_ttl = 1; | ||
921 | char *first_ttl_str = NULL; | ||
922 | #if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE | ||
923 | llist_t *sourse_route_list = NULL; | ||
924 | #endif | ||
925 | |||
926 | opterr = 0; | ||
927 | #if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE | ||
928 | opt_complementary = "x-x:g::"; | ||
929 | #else | ||
930 | opt_complementary = "x-x"; | ||
931 | #endif | ||
932 | |||
933 | op = getopt32(argc, argv, "FIlnrdvxt:i:m:p:q:s:w:z:f:" | ||
934 | #define USAGE_OP_DONT_FRAGMNT (1<<0) /* F */ | ||
935 | #define USAGE_OP_USE_ICMP (1<<1) /* I */ | ||
936 | #define USAGE_OP_TTL_FLAG (1<<2) /* l */ | ||
937 | #define USAGE_OP_ADDR_NUM (1<<3) /* n */ | ||
938 | #define USAGE_OP_BYPASS_ROUTE (1<<4) /* r */ | ||
939 | #define USAGE_OP_DEBUG (1<<5) /* d */ | ||
940 | #define USAGE_OP_VERBOSE (1<<6) /* v */ | ||
941 | #define USAGE_OP_IP_CHKSUM (1<<7) /* x */ | ||
942 | |||
943 | #if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE | ||
944 | "g:" | ||
945 | #endif | ||
946 | , &tos_str, &device, &max_ttl_str, &port_str, &nprobes_str, | ||
947 | &source, &waittime_str, &pausemsecs_str, &first_ttl_str | ||
948 | #if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE | ||
949 | , &sourse_route_list | ||
950 | #endif | ||
951 | ); | ||
952 | |||
953 | if (op & USAGE_OP_DONT_FRAGMNT) | ||
954 | off = IP_DF; | ||
955 | #if ENABLE_FEATURE_TRACEROUTE_USE_ICMP | ||
956 | useicmp = op & USAGE_OP_USE_ICMP; | ||
957 | #endif | ||
958 | nflag = op & USAGE_OP_ADDR_NUM; | ||
959 | #if ENABLE_FEATURE_TRACEROUTE_VERBOSE | ||
960 | verbose = op & USAGE_OP_VERBOSE; | ||
961 | #endif | ||
962 | if (op & USAGE_OP_IP_CHKSUM) { | ||
963 | doipcksum = 0; | ||
964 | bb_error_msg("warning: ip checksums disabled"); | ||
965 | } | ||
966 | if (tos_str) | ||
967 | tos = xatoul_range(tos_str, 0, 255); | ||
968 | if (max_ttl_str) | ||
969 | max_ttl = xatoul_range(max_ttl_str, 1, 255); | ||
970 | if (port_str) | ||
971 | port = xatou16(port_str); | ||
972 | if (nprobes_str) | ||
973 | nprobes = xatoul_range(nprobes_str, 1, INT_MAX); | ||
974 | if (source) { | ||
975 | /* | ||
976 | * set the ip source address of the outbound | ||
977 | * probe (e.g., on a multi-homed host). | ||
978 | */ | ||
979 | if (getuid()) bb_error_msg_and_die("-s %s: permission denied", source); | ||
980 | } | ||
981 | if (waittime_str) | ||
982 | waittime = xatoul_range(waittime_str, 2, 24 * 60 * 60); | ||
983 | if (pausemsecs_str) | ||
984 | pausemsecs = xatoul_range(pausemsecs_str, 0, 60 * 60 * 1000); | ||
985 | if (first_ttl_str) | ||
986 | first_ttl = xatoul_range(first_ttl_str, 1, 255); | ||
987 | |||
988 | #if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE | ||
989 | if (sourse_route_list) { | ||
990 | llist_t *l_sr; | ||
991 | |||
992 | for(l_sr = sourse_route_list; l_sr; ) { | ||
993 | if (lsrr >= NGATEWAYS) | ||
994 | bb_error_msg_and_die("no more than %d gateways", NGATEWAYS); | ||
995 | getaddr(gwlist + lsrr, l_sr->data); | ||
996 | ++lsrr; | ||
997 | l_sr = l_sr->link; | ||
998 | free(sourse_route_list); | ||
999 | sourse_route_list = l_sr; | ||
1000 | } | ||
1001 | optlen = (lsrr + 1) * sizeof(gwlist[0]); | ||
1002 | } | ||
1003 | #endif | ||
1004 | |||
1005 | if (first_ttl > max_ttl) { | ||
1006 | bb_error_msg_and_die( | ||
1007 | "first ttl (%d) may not be greater than max ttl (%d)", | ||
1008 | first_ttl, max_ttl); | ||
1009 | } | ||
1010 | |||
1011 | minpacket = sizeof(*outip) + sizeof(*outdata) + optlen; | ||
1012 | |||
1013 | #if ENABLE_FEATURE_TRACEROUTE_USE_ICMP | ||
1014 | if (useicmp) | ||
1015 | minpacket += 8; /* XXX magic number */ | ||
1016 | else | ||
1017 | #endif | ||
1018 | minpacket += sizeof(*outudp); | ||
1019 | packlen = minpacket; /* minimum sized packet */ | ||
1020 | |||
1021 | /* Process destination and optional packet size */ | ||
1022 | switch (argc - optind) { | ||
1023 | |||
1024 | case 2: | ||
1025 | packlen = xatoul_range(argv[optind + 1], minpacket, maxpacket); | ||
1026 | /* Fall through */ | ||
1027 | |||
1028 | case 1: | ||
1029 | hostname = argv[optind]; | ||
1030 | hi = gethostinfo(hostname); | ||
1031 | setsin(to, hi->addrs[0]); | ||
1032 | if (hi->n > 1) | ||
1033 | bb_error_msg("warning: %s has multiple addresses; using %s", | ||
1034 | hostname, inet_ntoa(to->sin_addr)); | ||
1035 | hostname = hi->name; | ||
1036 | hi->name = NULL; | ||
1037 | freehostinfo(hi); | ||
1038 | break; | ||
1039 | |||
1040 | default: | ||
1041 | bb_show_usage(); | ||
1042 | } | ||
1043 | |||
1044 | /* Insure the socket fds won't be 0, 1 or 2 */ | ||
1045 | do n = xopen(bb_dev_null, O_RDONLY); while (n < 2); | ||
1046 | if (n > 2) | ||
1047 | close(n); | ||
1048 | |||
1049 | s = xsocket(AF_INET, SOCK_RAW, IPPROTO_ICMP); | ||
1050 | |||
1051 | #if TRACEROUTE_SO_DEBUG | ||
1052 | if (op & USAGE_OP_DEBUG) | ||
1053 | (void)setsockopt(s, SOL_SOCKET, SO_DEBUG, (char *)&on, | ||
1054 | sizeof(on)); | ||
1055 | #endif | ||
1056 | if (op & USAGE_OP_BYPASS_ROUTE) | ||
1057 | (void)setsockopt(s, SOL_SOCKET, SO_DONTROUTE, (char *)&on, | ||
1058 | sizeof(on)); | ||
1059 | |||
1060 | sndsock = xsocket(AF_INET, SOCK_RAW, IPPROTO_RAW); | ||
1061 | |||
1062 | #if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE | ||
1063 | #if defined(IP_OPTIONS) | ||
1064 | if (lsrr > 0) { | ||
1065 | unsigned char optlist[MAX_IPOPTLEN]; | ||
1066 | |||
1067 | /* final hop */ | ||
1068 | gwlist[lsrr] = to->sin_addr.s_addr; | ||
1069 | ++lsrr; | ||
1070 | |||
1071 | /* force 4 byte alignment */ | ||
1072 | optlist[0] = IPOPT_NOP; | ||
1073 | /* loose source route option */ | ||
1074 | optlist[1] = IPOPT_LSRR; | ||
1075 | i = lsrr * sizeof(gwlist[0]); | ||
1076 | optlist[2] = i + 3; | ||
1077 | /* Pointer to LSRR addresses */ | ||
1078 | optlist[3] = IPOPT_MINOFF; | ||
1079 | memcpy(optlist + 4, gwlist, i); | ||
1080 | |||
1081 | if ((setsockopt(sndsock, IPPROTO_IP, IP_OPTIONS, | ||
1082 | (char *)optlist, i + sizeof(gwlist[0]))) < 0) { | ||
1083 | bb_perror_msg_and_die("IP_OPTIONS"); | ||
1084 | } | ||
1085 | } | ||
1086 | #endif /* IP_OPTIONS */ | ||
1087 | #endif /* CONFIG_FEATURE_TRACEROUTE_SOURCE_ROUTE */ | ||
1088 | |||
1089 | #ifdef SO_SNDBUF | ||
1090 | if (setsockopt(sndsock, SOL_SOCKET, SO_SNDBUF, (char *)&packlen, | ||
1091 | sizeof(packlen)) < 0) { | ||
1092 | bb_perror_msg_and_die("SO_SNDBUF"); | ||
1093 | } | ||
1094 | #endif | ||
1095 | #ifdef IP_HDRINCL | ||
1096 | if (setsockopt(sndsock, IPPROTO_IP, IP_HDRINCL, (char *)&on, | ||
1097 | sizeof(on)) < 0 && errno != ENOPROTOOPT) { | ||
1098 | bb_perror_msg_and_die("IP_HDRINCL"); | ||
1099 | } | ||
1100 | #else | ||
1101 | #ifdef IP_TOS | ||
1102 | if (tos_str && setsockopt(sndsock, IPPROTO_IP, IP_TOS, | ||
1103 | (char *)&tos, sizeof(tos)) < 0) { | ||
1104 | bb_perror_msg_and_die("setsockopt tos %d", tos); | ||
1105 | } | ||
1106 | #endif | ||
1107 | #endif | ||
1108 | #if TRACEROUTE_SO_DEBUG | ||
1109 | if (op & USAGE_OP_DEBUG) | ||
1110 | (void)setsockopt(sndsock, SOL_SOCKET, SO_DEBUG, (char *)&on, | ||
1111 | sizeof(on)); | ||
1112 | #endif | ||
1113 | if (op & USAGE_OP_BYPASS_ROUTE) | ||
1114 | (void)setsockopt(sndsock, SOL_SOCKET, SO_DONTROUTE, (char *)&on, | ||
1115 | sizeof(on)); | ||
1116 | |||
1117 | /* Revert to non-privileged user after opening sockets */ | ||
1118 | xsetgid(getgid()); | ||
1119 | xsetuid(getuid()); | ||
1120 | |||
1121 | outip = (struct ip *)xzalloc(packlen); | ||
1122 | |||
1123 | outip->ip_v = IPVERSION; | ||
1124 | if (tos_str) | ||
1125 | outip->ip_tos = tos; | ||
1126 | outip->ip_len = htons(packlen); | ||
1127 | outip->ip_off = htons(off); | ||
1128 | outp = (unsigned char *)(outip + 1); | ||
1129 | outip->ip_dst = to->sin_addr; | ||
1130 | |||
1131 | outip->ip_hl = (outp - (unsigned char *)outip) >> 2; | ||
1132 | ident = (getpid() & 0xffff) | 0x8000; | ||
1133 | #if ENABLE_FEATURE_TRACEROUTE_USE_ICMP | ||
1134 | if (useicmp) { | ||
1135 | outip->ip_p = IPPROTO_ICMP; | ||
1136 | |||
1137 | outicmp = (struct icmp *)outp; | ||
1138 | outicmp->icmp_type = ICMP_ECHO; | ||
1139 | outicmp->icmp_id = htons(ident); | ||
1140 | |||
1141 | outdata = (struct outdata *)(outp + 8); /* XXX magic number */ | ||
1142 | } else | ||
1143 | #endif | ||
1144 | { | ||
1145 | outip->ip_p = IPPROTO_UDP; | ||
1146 | |||
1147 | outudp = (struct udphdr *)outp; | ||
1148 | outudp->source = htons(ident); | ||
1149 | outudp->len = | ||
1150 | htons((u_short)(packlen - (sizeof(*outip) + optlen))); | ||
1151 | outdata = (struct outdata *)(outudp + 1); | ||
1152 | } | ||
1153 | |||
1154 | /* Get the interface address list */ | ||
1155 | n = ifaddrlist(&al); | ||
1156 | |||
1157 | /* Look for a specific device */ | ||
1158 | if (device != NULL) { | ||
1159 | for (i = n; i > 0; --i, ++al) | ||
1160 | if (strcmp(device, al->device) == 0) | ||
1161 | break; | ||
1162 | if (i <= 0) { | ||
1163 | bb_error_msg_and_die("can't find interface %s", device); | ||
1164 | } | ||
1165 | } | ||
1166 | |||
1167 | /* Determine our source address */ | ||
1168 | if (source == NULL) { | ||
1169 | /* | ||
1170 | * If a device was specified, use the interface address. | ||
1171 | * Otherwise, try to determine our source address. | ||
1172 | */ | ||
1173 | if (device != NULL) | ||
1174 | setsin(from, al->addr); | ||
1175 | findsaddr(to, from); | ||
1176 | } else { | ||
1177 | hi = gethostinfo(source); | ||
1178 | source = hi->name; | ||
1179 | hi->name = NULL; | ||
1180 | /* | ||
1181 | * If the device was specified make sure it | ||
1182 | * corresponds to the source address specified. | ||
1183 | * Otherwise, use the first address (and warn if | ||
1184 | * there are more than one). | ||
1185 | */ | ||
1186 | if (device != NULL) { | ||
1187 | for (i = hi->n, ap = hi->addrs; i > 0; --i, ++ap) | ||
1188 | if (*ap == al->addr) | ||
1189 | break; | ||
1190 | if (i <= 0) { | ||
1191 | bb_error_msg_and_die( | ||
1192 | "%s is not on interface %s", | ||
1193 | source, device); | ||
1194 | } | ||
1195 | setsin(from, *ap); | ||
1196 | } else { | ||
1197 | setsin(from, hi->addrs[0]); | ||
1198 | if (hi->n > 1) | ||
1199 | bb_error_msg( | ||
1200 | "Warning: %s has multiple addresses; using %s", | ||
1201 | source, inet_ntoa(from->sin_addr)); | ||
1202 | } | ||
1203 | freehostinfo(hi); | ||
1204 | } | ||
1205 | |||
1206 | outip->ip_src = from->sin_addr; | ||
1207 | #ifndef IP_HDRINCL | ||
1208 | xbind(sndsock, (struct sockaddr *)from, sizeof(*from)); | ||
1209 | #endif | ||
1210 | |||
1211 | fprintf(stderr, "traceroute to %s (%s)", hostname, inet_ntoa(to->sin_addr)); | ||
1212 | if (source) | ||
1213 | fprintf(stderr, " from %s", source); | ||
1214 | fprintf(stderr, ", %d hops max, %d byte packets\n", max_ttl, packlen); | ||
1215 | (void)fflush(stderr); | ||
1216 | |||
1217 | for (ttl = first_ttl; ttl <= max_ttl; ++ttl) { | ||
1218 | u_int32_t lastaddr = 0; | ||
1219 | int gotlastaddr = 0; | ||
1220 | int got_there = 0; | ||
1221 | int unreachable = 0; | ||
1222 | int sentfirst = 0; | ||
1223 | |||
1224 | printf("%2d ", ttl); | ||
1225 | for (probe = 0; probe < nprobes; ++probe) { | ||
1226 | int cc; | ||
1227 | struct timeval t1, t2; | ||
1228 | struct timezone tz; | ||
1229 | struct ip *ip; | ||
1230 | |||
1231 | if (sentfirst && pausemsecs > 0) | ||
1232 | usleep(pausemsecs * 1000); | ||
1233 | (void)gettimeofday(&t1, &tz); | ||
1234 | send_probe(++seq, ttl, &t1); | ||
1235 | ++sentfirst; | ||
1236 | while ((cc = wait_for_reply(s, from, &t1)) != 0) { | ||
1237 | (void)gettimeofday(&t2, &tz); | ||
1238 | i = packet_ok(packet, cc, from, seq); | ||
1239 | /* Skip short packet */ | ||
1240 | if (i == 0) | ||
1241 | continue; | ||
1242 | if (!gotlastaddr || | ||
1243 | from->sin_addr.s_addr != lastaddr) { | ||
1244 | print(packet, cc, from); | ||
1245 | lastaddr = from->sin_addr.s_addr; | ||
1246 | ++gotlastaddr; | ||
1247 | } | ||
1248 | printf(" %.3f ms", deltaT(&t1, &t2)); | ||
1249 | ip = (struct ip *)packet; | ||
1250 | if (op & USAGE_OP_TTL_FLAG) | ||
1251 | printf(" (%d)", ip->ip_ttl); | ||
1252 | if (i == -2) { | ||
1253 | if (ip->ip_ttl <= 1) | ||
1254 | printf(" !"); | ||
1255 | ++got_there; | ||
1256 | break; | ||
1257 | } | ||
1258 | /* time exceeded in transit */ | ||
1259 | if (i == -1) | ||
1260 | break; | ||
1261 | code = i - 1; | ||
1262 | switch (code) { | ||
1263 | |||
1264 | case ICMP_UNREACH_PORT: | ||
1265 | if (ip->ip_ttl <= 1) | ||
1266 | printf(" !"); | ||
1267 | ++got_there; | ||
1268 | break; | ||
1269 | |||
1270 | case ICMP_UNREACH_NET: | ||
1271 | ++unreachable; | ||
1272 | printf(" !N"); | ||
1273 | break; | ||
1274 | |||
1275 | case ICMP_UNREACH_HOST: | ||
1276 | ++unreachable; | ||
1277 | printf(" !H"); | ||
1278 | break; | ||
1279 | |||
1280 | case ICMP_UNREACH_PROTOCOL: | ||
1281 | ++got_there; | ||
1282 | printf(" !P"); | ||
1283 | break; | ||
1284 | |||
1285 | case ICMP_UNREACH_NEEDFRAG: | ||
1286 | ++unreachable; | ||
1287 | printf(" !F-%d", pmtu); | ||
1288 | break; | ||
1289 | |||
1290 | case ICMP_UNREACH_SRCFAIL: | ||
1291 | ++unreachable; | ||
1292 | printf(" !S"); | ||
1293 | break; | ||
1294 | |||
1295 | case ICMP_UNREACH_FILTER_PROHIB: | ||
1296 | case ICMP_UNREACH_NET_PROHIB: /* misuse */ | ||
1297 | ++unreachable; | ||
1298 | printf(" !A"); | ||
1299 | break; | ||
1300 | |||
1301 | case ICMP_UNREACH_HOST_PROHIB: | ||
1302 | ++unreachable; | ||
1303 | printf(" !C"); | ||
1304 | break; | ||
1305 | |||
1306 | case ICMP_UNREACH_HOST_PRECEDENCE: | ||
1307 | ++unreachable; | ||
1308 | printf(" !V"); | ||
1309 | break; | ||
1310 | |||
1311 | case ICMP_UNREACH_PRECEDENCE_CUTOFF: | ||
1312 | ++unreachable; | ||
1313 | printf(" !C"); | ||
1314 | break; | ||
1315 | |||
1316 | case ICMP_UNREACH_NET_UNKNOWN: | ||
1317 | case ICMP_UNREACH_HOST_UNKNOWN: | ||
1318 | ++unreachable; | ||
1319 | printf(" !U"); | ||
1320 | break; | ||
1321 | |||
1322 | case ICMP_UNREACH_ISOLATED: | ||
1323 | ++unreachable; | ||
1324 | printf(" !I"); | ||
1325 | break; | ||
1326 | |||
1327 | case ICMP_UNREACH_TOSNET: | ||
1328 | case ICMP_UNREACH_TOSHOST: | ||
1329 | ++unreachable; | ||
1330 | printf(" !T"); | ||
1331 | break; | ||
1332 | |||
1333 | default: | ||
1334 | ++unreachable; | ||
1335 | printf(" !<%d>", code); | ||
1336 | break; | ||
1337 | } | ||
1338 | break; | ||
1339 | } | ||
1340 | if (cc == 0) | ||
1341 | printf(" *"); | ||
1342 | (void)fflush(stdout); | ||
1343 | } | ||
1344 | putchar('\n'); | ||
1345 | if (got_there || | ||
1346 | (unreachable > 0 && unreachable >= nprobes - 1)) | ||
1347 | break; | ||
1348 | } | ||
1349 | return 0; | ||
1350 | } | ||
diff --git a/networking/udhcp/Config.in b/networking/udhcp/Config.in new file mode 100644 index 000000000..f633473eb --- /dev/null +++ b/networking/udhcp/Config.in | |||
@@ -0,0 +1,67 @@ | |||
1 | # | ||
2 | # For a description of the syntax of this configuration file, | ||
3 | # see scripts/kbuild/config-language.txt. | ||
4 | # | ||
5 | |||
6 | config APP_UDHCPD | ||
7 | bool "udhcp Server (udhcpd)" | ||
8 | default n | ||
9 | help | ||
10 | uDHCPd is a DHCP server geared primarily toward embedded systems, | ||
11 | while striving to be fully functional and RFC compliant. | ||
12 | |||
13 | See http://udhcp.busybox.net for further details. | ||
14 | |||
15 | config APP_DHCPRELAY | ||
16 | bool "dhcprelay" | ||
17 | default n | ||
18 | depends on APP_UDHCPD | ||
19 | help | ||
20 | dhcprelay listens for dhcp requests on one or more interfaces | ||
21 | and forwards these requests to a different interface or dhcp | ||
22 | server. | ||
23 | |||
24 | config APP_DUMPLEASES | ||
25 | bool "Lease display utility (dumpleases)" | ||
26 | default n | ||
27 | depends on APP_UDHCPD | ||
28 | help | ||
29 | dumpleases displays the leases written out by the udhcpd server. | ||
30 | Lease times are stored in the file by time remaining in lease, or | ||
31 | by the absolute time that it expires in seconds from epoch. | ||
32 | |||
33 | See http://udhcp.busybox.net for further details. | ||
34 | |||
35 | config APP_UDHCPC | ||
36 | bool "udhcp Client (udhcpc)" | ||
37 | default n | ||
38 | help | ||
39 | uDHCPc is a DHCP client geared primarily toward embedded systems, | ||
40 | while striving to be fully functional and RFC compliant. | ||
41 | |||
42 | The udhcp client negotiates a lease with the DHCP server and | ||
43 | notifies a set of scripts when a lease is obtained or lost. | ||
44 | |||
45 | See http://udhcp.busybox.net for further details. | ||
46 | |||
47 | config FEATURE_UDHCP_SYSLOG | ||
48 | bool "Log udhcp messages to syslog" | ||
49 | default n | ||
50 | depends on APP_UDHCPD || APP_UDHCPC | ||
51 | select FEATURE_SYSLOG | ||
52 | help | ||
53 | If not daemonized, udhcpd prints its messages to stdout/stderr. | ||
54 | If this option is selected, it will also log them to syslog. | ||
55 | |||
56 | See http://udhcp.busybox.net for further details. | ||
57 | |||
58 | config FEATURE_UDHCP_DEBUG | ||
59 | bool "Compile udhcp with noisy debugging messages" | ||
60 | default n | ||
61 | depends on APP_UDHCPD || APP_UDHCPC | ||
62 | help | ||
63 | If selected, udhcpd will output extra debugging output. If using | ||
64 | this option, compile uDHCP with "-g", and do not fork the daemon to | ||
65 | the background. | ||
66 | |||
67 | See http://udhcp.busybox.net for further details. | ||
diff --git a/networking/udhcp/Kbuild b/networking/udhcp/Kbuild new file mode 100644 index 000000000..dc2c01f61 --- /dev/null +++ b/networking/udhcp/Kbuild | |||
@@ -0,0 +1,18 @@ | |||
1 | # Makefile for busybox | ||
2 | # | ||
3 | # Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org> | ||
4 | # | ||
5 | # Licensed under the GPL v2 or later, see the file LICENSE in this tarball. | ||
6 | # | ||
7 | |||
8 | lib-y:= | ||
9 | lib-$(CONFIG_APP_UDHCPC) += common.o options.o packet.o pidfile.o \ | ||
10 | signalpipe.o socket.o | ||
11 | lib-$(CONFIG_APP_UDHCPD) += common.o options.o packet.o pidfile.o \ | ||
12 | signalpipe.o socket.o | ||
13 | lib-$(CONFIG_APP_UDHCPC) += dhcpc.o clientpacket.o clientsocket.o \ | ||
14 | script.o | ||
15 | lib-$(CONFIG_APP_UDHCPD) += dhcpd.o arpping.o files.o leases.o \ | ||
16 | serverpacket.o static_leases.o | ||
17 | lib-$(CONFIG_APP_DUMPLEASES) += dumpleases.o | ||
18 | lib-$(CONFIG_APP_DHCPRELAY) += dhcprelay.o | ||
diff --git a/networking/udhcp/arpping.c b/networking/udhcp/arpping.c new file mode 100644 index 000000000..9c8b9c562 --- /dev/null +++ b/networking/udhcp/arpping.c | |||
@@ -0,0 +1,114 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * arpping.c | ||
4 | * | ||
5 | * Mostly stolen from: dhcpcd - DHCP client daemon | ||
6 | * by Yoichi Hariguchi <yoichi@fore.com> | ||
7 | */ | ||
8 | |||
9 | #include <netinet/if_ether.h> | ||
10 | #include <net/if_arp.h> | ||
11 | |||
12 | #include "common.h" | ||
13 | #include "dhcpd.h" | ||
14 | |||
15 | |||
16 | struct arpMsg { | ||
17 | /* Ethernet header */ | ||
18 | u_char h_dest[6]; /* destination ether addr */ | ||
19 | u_char h_source[6]; /* source ether addr */ | ||
20 | u_short h_proto; /* packet type ID field */ | ||
21 | |||
22 | /* ARP packet */ | ||
23 | uint16_t htype; /* hardware type (must be ARPHRD_ETHER) */ | ||
24 | uint16_t ptype; /* protocol type (must be ETH_P_IP) */ | ||
25 | uint8_t hlen; /* hardware address length (must be 6) */ | ||
26 | uint8_t plen; /* protocol address length (must be 4) */ | ||
27 | uint16_t operation; /* ARP opcode */ | ||
28 | uint8_t sHaddr[6]; /* sender's hardware address */ | ||
29 | uint8_t sInaddr[4]; /* sender's IP address */ | ||
30 | uint8_t tHaddr[6]; /* target's hardware address */ | ||
31 | uint8_t tInaddr[4]; /* target's IP address */ | ||
32 | uint8_t pad[18]; /* pad for min. Ethernet payload (60 bytes) */ | ||
33 | } ATTRIBUTE_PACKED; | ||
34 | |||
35 | /* args: yiaddr - what IP to ping | ||
36 | * ip - our ip | ||
37 | * mac - our arp address | ||
38 | * interface - interface to use | ||
39 | * retn: 1 addr free | ||
40 | * 0 addr used | ||
41 | * -1 error | ||
42 | */ | ||
43 | |||
44 | /* FIXME: match response against chaddr */ | ||
45 | int arpping(uint32_t yiaddr, uint32_t ip, uint8_t *mac, char *interface) | ||
46 | { | ||
47 | int timeout = 2; | ||
48 | int s; /* socket */ | ||
49 | int rv = 1; /* return value */ | ||
50 | struct sockaddr addr; /* for interface name */ | ||
51 | struct arpMsg arp; | ||
52 | fd_set fdset; | ||
53 | struct timeval tm; | ||
54 | time_t prevTime; | ||
55 | |||
56 | |||
57 | s = socket(PF_PACKET, SOCK_PACKET, htons(ETH_P_ARP)); | ||
58 | if (s == -1) { | ||
59 | bb_perror_msg(bb_msg_can_not_create_raw_socket); | ||
60 | return -1; | ||
61 | } | ||
62 | |||
63 | if (setsockopt_broadcast(s) == -1) { | ||
64 | bb_perror_msg("cannot setsocketopt on raw socket"); | ||
65 | close(s); | ||
66 | return -1; | ||
67 | } | ||
68 | |||
69 | /* send arp request */ | ||
70 | memset(&arp, 0, sizeof(arp)); | ||
71 | memcpy(arp.h_dest, MAC_BCAST_ADDR, 6); /* MAC DA */ | ||
72 | memcpy(arp.h_source, mac, 6); /* MAC SA */ | ||
73 | arp.h_proto = htons(ETH_P_ARP); /* protocol type (Ethernet) */ | ||
74 | arp.htype = htons(ARPHRD_ETHER); /* hardware type */ | ||
75 | arp.ptype = htons(ETH_P_IP); /* protocol type (ARP message) */ | ||
76 | arp.hlen = 6; /* hardware address length */ | ||
77 | arp.plen = 4; /* protocol address length */ | ||
78 | arp.operation = htons(ARPOP_REQUEST); /* ARP op code */ | ||
79 | memcpy(arp.sInaddr, &ip, sizeof(ip)); /* source IP address */ | ||
80 | memcpy(arp.sHaddr, mac, 6); /* source hardware address */ | ||
81 | memcpy(arp.tInaddr, &yiaddr, sizeof(yiaddr)); /* target IP address */ | ||
82 | |||
83 | memset(&addr, 0, sizeof(addr)); | ||
84 | strcpy(addr.sa_data, interface); | ||
85 | if (sendto(s, &arp, sizeof(arp), 0, &addr, sizeof(addr)) < 0) | ||
86 | rv = 0; | ||
87 | |||
88 | /* wait arp reply, and check it */ | ||
89 | tm.tv_usec = 0; | ||
90 | prevTime = uptime(); | ||
91 | while (timeout > 0) { | ||
92 | FD_ZERO(&fdset); | ||
93 | FD_SET(s, &fdset); | ||
94 | tm.tv_sec = timeout; | ||
95 | if (select(s + 1, &fdset, (fd_set *) NULL, (fd_set *) NULL, &tm) < 0) { | ||
96 | bb_perror_msg("error on ARPING request"); | ||
97 | if (errno != EINTR) rv = 0; | ||
98 | } else if (FD_ISSET(s, &fdset)) { | ||
99 | if (recv(s, &arp, sizeof(arp), 0) < 0 ) rv = 0; | ||
100 | if (arp.operation == htons(ARPOP_REPLY) && | ||
101 | memcmp(arp.tHaddr, mac, 6) == 0 && | ||
102 | *((uint32_t *) arp.sInaddr) == yiaddr) { | ||
103 | DEBUG("Valid arp reply received for this address"); | ||
104 | rv = 0; | ||
105 | break; | ||
106 | } | ||
107 | } | ||
108 | timeout -= uptime() - prevTime; | ||
109 | prevTime = uptime(); | ||
110 | } | ||
111 | close(s); | ||
112 | DEBUG("%salid arp replies for this address", rv ? "No v" : "V"); | ||
113 | return rv; | ||
114 | } | ||
diff --git a/networking/udhcp/clientpacket.c b/networking/udhcp/clientpacket.c new file mode 100644 index 000000000..15cbda2f5 --- /dev/null +++ b/networking/udhcp/clientpacket.c | |||
@@ -0,0 +1,224 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* clientpacket.c | ||
3 | * | ||
4 | * Packet generation and dispatching functions for the DHCP client. | ||
5 | * | ||
6 | * Russ Dill <Russ.Dill@asu.edu> July 2001 | ||
7 | * | ||
8 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | ||
9 | */ | ||
10 | |||
11 | #include <features.h> | ||
12 | #if (__GLIBC__ >= 2 && __GLIBC_MINOR__ >= 1) || defined _NEWLIB_VERSION | ||
13 | #include <netpacket/packet.h> | ||
14 | #include <net/ethernet.h> | ||
15 | #else | ||
16 | #include <asm/types.h> | ||
17 | #include <linux/if_packet.h> | ||
18 | #include <linux/if_ether.h> | ||
19 | #endif | ||
20 | |||
21 | #include "common.h" | ||
22 | #include "dhcpd.h" | ||
23 | #include "dhcpc.h" | ||
24 | #include "options.h" | ||
25 | |||
26 | |||
27 | /* Create a random xid */ | ||
28 | unsigned long random_xid(void) | ||
29 | { | ||
30 | static int initialized; | ||
31 | if (!initialized) { | ||
32 | unsigned long seed; | ||
33 | |||
34 | if (open_read_close("/dev/urandom", &seed, sizeof(seed)) < 0) { | ||
35 | bb_info_msg("Cannot load seed " | ||
36 | "from /dev/urandom: %s", strerror(errno)); | ||
37 | seed = time(0); | ||
38 | } | ||
39 | srand(seed); | ||
40 | initialized++; | ||
41 | } | ||
42 | return rand(); | ||
43 | } | ||
44 | |||
45 | |||
46 | /* initialize a packet with the proper defaults */ | ||
47 | static void init_packet(struct dhcpMessage *packet, char type) | ||
48 | { | ||
49 | udhcp_init_header(packet, type); | ||
50 | memcpy(packet->chaddr, client_config.arp, 6); | ||
51 | if (client_config.clientid) | ||
52 | add_option_string(packet->options, client_config.clientid); | ||
53 | if (client_config.hostname) add_option_string(packet->options, client_config.hostname); | ||
54 | if (client_config.fqdn) add_option_string(packet->options, client_config.fqdn); | ||
55 | add_option_string(packet->options, client_config.vendorclass); | ||
56 | } | ||
57 | |||
58 | |||
59 | /* Add a parameter request list for stubborn DHCP servers. Pull the data | ||
60 | * from the struct in options.c. Don't do bounds checking here because it | ||
61 | * goes towards the head of the packet. */ | ||
62 | static void add_requests(struct dhcpMessage *packet) | ||
63 | { | ||
64 | int end = end_option(packet->options); | ||
65 | int i, len = 0; | ||
66 | |||
67 | packet->options[end + OPT_CODE] = DHCP_PARAM_REQ; | ||
68 | for (i = 0; dhcp_options[i].code; i++) | ||
69 | if (dhcp_options[i].flags & OPTION_REQ) | ||
70 | packet->options[end + OPT_DATA + len++] = dhcp_options[i].code; | ||
71 | packet->options[end + OPT_LEN] = len; | ||
72 | packet->options[end + OPT_DATA + len] = DHCP_END; | ||
73 | |||
74 | } | ||
75 | |||
76 | |||
77 | /* Broadcast a DHCP discover packet to the network, with an optionally requested IP */ | ||
78 | int send_discover(unsigned long xid, unsigned long requested) | ||
79 | { | ||
80 | struct dhcpMessage packet; | ||
81 | |||
82 | init_packet(&packet, DHCPDISCOVER); | ||
83 | packet.xid = xid; | ||
84 | if (requested) | ||
85 | add_simple_option(packet.options, DHCP_REQUESTED_IP, requested); | ||
86 | |||
87 | add_requests(&packet); | ||
88 | bb_info_msg("Sending discover..."); | ||
89 | return udhcp_raw_packet(&packet, INADDR_ANY, CLIENT_PORT, INADDR_BROADCAST, | ||
90 | SERVER_PORT, MAC_BCAST_ADDR, client_config.ifindex); | ||
91 | } | ||
92 | |||
93 | |||
94 | /* Broadcasts a DHCP request message */ | ||
95 | int send_selecting(unsigned long xid, unsigned long server, unsigned long requested) | ||
96 | { | ||
97 | struct dhcpMessage packet; | ||
98 | struct in_addr addr; | ||
99 | |||
100 | init_packet(&packet, DHCPREQUEST); | ||
101 | packet.xid = xid; | ||
102 | |||
103 | add_simple_option(packet.options, DHCP_REQUESTED_IP, requested); | ||
104 | add_simple_option(packet.options, DHCP_SERVER_ID, server); | ||
105 | |||
106 | add_requests(&packet); | ||
107 | addr.s_addr = requested; | ||
108 | bb_info_msg("Sending select for %s...", inet_ntoa(addr)); | ||
109 | return udhcp_raw_packet(&packet, INADDR_ANY, CLIENT_PORT, INADDR_BROADCAST, | ||
110 | SERVER_PORT, MAC_BCAST_ADDR, client_config.ifindex); | ||
111 | } | ||
112 | |||
113 | |||
114 | /* Unicasts or broadcasts a DHCP renew message */ | ||
115 | int send_renew(unsigned long xid, unsigned long server, unsigned long ciaddr) | ||
116 | { | ||
117 | struct dhcpMessage packet; | ||
118 | int ret = 0; | ||
119 | |||
120 | init_packet(&packet, DHCPREQUEST); | ||
121 | packet.xid = xid; | ||
122 | packet.ciaddr = ciaddr; | ||
123 | |||
124 | add_requests(&packet); | ||
125 | bb_info_msg("Sending renew..."); | ||
126 | if (server) | ||
127 | ret = udhcp_kernel_packet(&packet, ciaddr, CLIENT_PORT, server, SERVER_PORT); | ||
128 | else ret = udhcp_raw_packet(&packet, INADDR_ANY, CLIENT_PORT, INADDR_BROADCAST, | ||
129 | SERVER_PORT, MAC_BCAST_ADDR, client_config.ifindex); | ||
130 | return ret; | ||
131 | } | ||
132 | |||
133 | |||
134 | /* Unicasts a DHCP release message */ | ||
135 | int send_release(unsigned long server, unsigned long ciaddr) | ||
136 | { | ||
137 | struct dhcpMessage packet; | ||
138 | |||
139 | init_packet(&packet, DHCPRELEASE); | ||
140 | packet.xid = random_xid(); | ||
141 | packet.ciaddr = ciaddr; | ||
142 | |||
143 | add_simple_option(packet.options, DHCP_REQUESTED_IP, ciaddr); | ||
144 | add_simple_option(packet.options, DHCP_SERVER_ID, server); | ||
145 | |||
146 | bb_info_msg("Sending release..."); | ||
147 | return udhcp_kernel_packet(&packet, ciaddr, CLIENT_PORT, server, SERVER_PORT); | ||
148 | } | ||
149 | |||
150 | |||
151 | /* return -1 on errors that are fatal for the socket, -2 for those that aren't */ | ||
152 | int get_raw_packet(struct dhcpMessage *payload, int fd) | ||
153 | { | ||
154 | int bytes; | ||
155 | struct udp_dhcp_packet packet; | ||
156 | uint32_t source, dest; | ||
157 | uint16_t check; | ||
158 | |||
159 | memset(&packet, 0, sizeof(struct udp_dhcp_packet)); | ||
160 | bytes = read(fd, &packet, sizeof(struct udp_dhcp_packet)); | ||
161 | if (bytes < 0) { | ||
162 | DEBUG("Cannot read on raw listening socket - ignoring"); | ||
163 | usleep(500000); /* possible down interface, looping condition */ | ||
164 | return -1; | ||
165 | } | ||
166 | |||
167 | if (bytes < (int) (sizeof(struct iphdr) + sizeof(struct udphdr))) { | ||
168 | DEBUG("Message too short, ignoring"); | ||
169 | return -2; | ||
170 | } | ||
171 | |||
172 | if (bytes < ntohs(packet.ip.tot_len)) { | ||
173 | DEBUG("Truncated packet"); | ||
174 | return -2; | ||
175 | } | ||
176 | |||
177 | /* ignore any extra garbage bytes */ | ||
178 | bytes = ntohs(packet.ip.tot_len); | ||
179 | |||
180 | /* Make sure its the right packet for us, and that it passes sanity checks */ | ||
181 | if (packet.ip.protocol != IPPROTO_UDP || packet.ip.version != IPVERSION | ||
182 | || packet.ip.ihl != sizeof(packet.ip) >> 2 | ||
183 | || packet.udp.dest != htons(CLIENT_PORT) | ||
184 | || bytes > (int) sizeof(struct udp_dhcp_packet) | ||
185 | || ntohs(packet.udp.len) != (uint16_t)(bytes - sizeof(packet.ip)) | ||
186 | ) { | ||
187 | DEBUG("Unrelated/bogus packet"); | ||
188 | return -2; | ||
189 | } | ||
190 | |||
191 | /* check IP checksum */ | ||
192 | check = packet.ip.check; | ||
193 | packet.ip.check = 0; | ||
194 | if (check != udhcp_checksum(&(packet.ip), sizeof(packet.ip))) { | ||
195 | DEBUG("bad IP header checksum, ignoring"); | ||
196 | return -1; | ||
197 | } | ||
198 | |||
199 | /* verify the UDP checksum by replacing the header with a psuedo header */ | ||
200 | source = packet.ip.saddr; | ||
201 | dest = packet.ip.daddr; | ||
202 | check = packet.udp.check; | ||
203 | packet.udp.check = 0; | ||
204 | memset(&packet.ip, 0, sizeof(packet.ip)); | ||
205 | |||
206 | packet.ip.protocol = IPPROTO_UDP; | ||
207 | packet.ip.saddr = source; | ||
208 | packet.ip.daddr = dest; | ||
209 | packet.ip.tot_len = packet.udp.len; /* cheat on the psuedo-header */ | ||
210 | if (check && check != udhcp_checksum(&packet, bytes)) { | ||
211 | bb_error_msg("packet with bad UDP checksum received, ignoring"); | ||
212 | return -2; | ||
213 | } | ||
214 | |||
215 | memcpy(payload, &(packet.data), bytes - (sizeof(packet.ip) + sizeof(packet.udp))); | ||
216 | |||
217 | if (ntohl(payload->cookie) != DHCP_MAGIC) { | ||
218 | bb_error_msg("received bogus message (bad magic) - ignoring"); | ||
219 | return -2; | ||
220 | } | ||
221 | DEBUG("oooooh!!! got some!"); | ||
222 | return bytes - (sizeof(packet.ip) + sizeof(packet.udp)); | ||
223 | |||
224 | } | ||
diff --git a/networking/udhcp/clientsocket.c b/networking/udhcp/clientsocket.c new file mode 100644 index 000000000..852061968 --- /dev/null +++ b/networking/udhcp/clientsocket.c | |||
@@ -0,0 +1,59 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * clientsocket.c -- DHCP client socket creation | ||
4 | * | ||
5 | * udhcp client | ||
6 | * | ||
7 | * Russ Dill <Russ.Dill@asu.edu> July 2001 | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License as published by | ||
11 | * the Free Software Foundation; either version 2 of the License, or | ||
12 | * (at your option) any later version. | ||
13 | * | ||
14 | * This program is distributed in the hope that it will be useful, | ||
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
17 | * GNU General Public License for more details. | ||
18 | * | ||
19 | * You should have received a copy of the GNU General Public License | ||
20 | * along with this program; if not, write to the Free Software | ||
21 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
22 | */ | ||
23 | |||
24 | #include <features.h> | ||
25 | #if (__GLIBC__ >= 2 && __GLIBC_MINOR__ >= 1) || defined(_NEWLIB_VERSION) | ||
26 | #include <netpacket/packet.h> | ||
27 | #include <net/ethernet.h> | ||
28 | #else | ||
29 | #include <asm/types.h> | ||
30 | #include <linux/if_packet.h> | ||
31 | #include <linux/if_ether.h> | ||
32 | #endif | ||
33 | |||
34 | #include "common.h" | ||
35 | |||
36 | |||
37 | int raw_socket(int ifindex) | ||
38 | { | ||
39 | int fd; | ||
40 | struct sockaddr_ll sock; | ||
41 | |||
42 | DEBUG("Opening raw socket on ifindex %d", ifindex); | ||
43 | fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP)); | ||
44 | if (fd < 0) { | ||
45 | bb_perror_msg("socket"); | ||
46 | return -1; | ||
47 | } | ||
48 | |||
49 | sock.sll_family = AF_PACKET; | ||
50 | sock.sll_protocol = htons(ETH_P_IP); | ||
51 | sock.sll_ifindex = ifindex; | ||
52 | if (bind(fd, (struct sockaddr *) &sock, sizeof(sock)) < 0) { | ||
53 | bb_perror_msg("bind"); | ||
54 | close(fd); | ||
55 | return -1; | ||
56 | } | ||
57 | |||
58 | return fd; | ||
59 | } | ||
diff --git a/networking/udhcp/common.c b/networking/udhcp/common.c new file mode 100644 index 000000000..3e916f422 --- /dev/null +++ b/networking/udhcp/common.c | |||
@@ -0,0 +1,75 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* common.c | ||
3 | * | ||
4 | * Functions for debugging and logging as well as some other | ||
5 | * simple helper functions. | ||
6 | * | ||
7 | * Russ Dill <Russ.Dill@asu.edu> 2001-2003 | ||
8 | * Rewritten by Vladimir Oleynik <dzo@simtreas.ru> (C) 2003 | ||
9 | * | ||
10 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | ||
11 | */ | ||
12 | |||
13 | #include <syslog.h> | ||
14 | |||
15 | #include "common.h" | ||
16 | |||
17 | |||
18 | long uptime(void) | ||
19 | { | ||
20 | struct sysinfo info; | ||
21 | sysinfo(&info); | ||
22 | return info.uptime; | ||
23 | } | ||
24 | |||
25 | /* | ||
26 | * This function makes sure our first socket calls | ||
27 | * aren't going to fd 1 (printf badness...) and are | ||
28 | * not later closed by daemon() | ||
29 | */ | ||
30 | static inline void sanitize_fds(void) | ||
31 | { | ||
32 | int fd = xopen(bb_dev_null, O_RDWR); | ||
33 | while (fd < 3) | ||
34 | fd = dup(fd); | ||
35 | close(fd); | ||
36 | } | ||
37 | |||
38 | |||
39 | void udhcp_background(const char *pidfile) | ||
40 | { | ||
41 | #ifdef __uClinux__ | ||
42 | bb_error_msg("cannot background in uclinux (yet)"); | ||
43 | #else /* __uClinux__ */ | ||
44 | int pid_fd; | ||
45 | |||
46 | /* hold lock during fork. */ | ||
47 | pid_fd = pidfile_acquire(pidfile); | ||
48 | setsid(); | ||
49 | xdaemon(0, 0); | ||
50 | logmode &= ~LOGMODE_STDIO; | ||
51 | pidfile_write_release(pid_fd); | ||
52 | #endif /* __uClinux__ */ | ||
53 | } | ||
54 | |||
55 | void udhcp_start_log_and_pid(const char *pidfile) | ||
56 | { | ||
57 | int pid_fd; | ||
58 | |||
59 | /* Make sure our syslog fd isn't overwritten */ | ||
60 | sanitize_fds(); | ||
61 | |||
62 | /* do some other misc startup stuff while we are here to save bytes */ | ||
63 | pid_fd = pidfile_acquire(pidfile); | ||
64 | pidfile_write_release(pid_fd); | ||
65 | |||
66 | /* equivelent of doing a fflush after every \n */ | ||
67 | setlinebuf(stdout); | ||
68 | |||
69 | if (ENABLE_FEATURE_UDHCP_SYSLOG) { | ||
70 | openlog(applet_name, LOG_PID, LOG_LOCAL0); | ||
71 | logmode |= LOGMODE_SYSLOG; | ||
72 | } | ||
73 | |||
74 | bb_info_msg("%s (v%s) started", applet_name, BB_VER); | ||
75 | } | ||
diff --git a/networking/udhcp/common.h b/networking/udhcp/common.h new file mode 100644 index 000000000..70a769342 --- /dev/null +++ b/networking/udhcp/common.h | |||
@@ -0,0 +1,108 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* common.h | ||
3 | * | ||
4 | * Russ Dill <Russ.Dill@asu.edu> September 2001 | ||
5 | * Rewritten by Vladimir Oleynik <dzo@simtreas.ru> (C) 2003 | ||
6 | * | ||
7 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | ||
8 | */ | ||
9 | |||
10 | #ifndef _COMMON_H | ||
11 | #define _COMMON_H | ||
12 | |||
13 | #include "busybox.h" | ||
14 | |||
15 | #ifdef CONFIG_INSTALL_NO_USR | ||
16 | # define DEFAULT_SCRIPT "/share/udhcpc/default.script" | ||
17 | #else | ||
18 | # define DEFAULT_SCRIPT "/usr/share/udhcpc/default.script" | ||
19 | #endif | ||
20 | |||
21 | #define COMBINED_BINARY | ||
22 | |||
23 | |||
24 | /*** packet.h ***/ | ||
25 | |||
26 | #include <netinet/udp.h> | ||
27 | #include <netinet/ip.h> | ||
28 | |||
29 | struct dhcpMessage { | ||
30 | uint8_t op; | ||
31 | uint8_t htype; | ||
32 | uint8_t hlen; | ||
33 | uint8_t hops; | ||
34 | uint32_t xid; | ||
35 | uint16_t secs; | ||
36 | uint16_t flags; | ||
37 | uint32_t ciaddr; | ||
38 | uint32_t yiaddr; | ||
39 | uint32_t siaddr; | ||
40 | uint32_t giaddr; | ||
41 | uint8_t chaddr[16]; | ||
42 | uint8_t sname[64]; | ||
43 | uint8_t file[128]; | ||
44 | uint32_t cookie; | ||
45 | uint8_t options[308]; /* 312 - cookie */ | ||
46 | }; | ||
47 | |||
48 | struct udp_dhcp_packet { | ||
49 | struct iphdr ip; | ||
50 | struct udphdr udp; | ||
51 | struct dhcpMessage data; | ||
52 | }; | ||
53 | |||
54 | void udhcp_init_header(struct dhcpMessage *packet, char type); | ||
55 | int udhcp_get_packet(struct dhcpMessage *packet, int fd); | ||
56 | uint16_t udhcp_checksum(void *addr, int count); | ||
57 | int udhcp_raw_packet(struct dhcpMessage *payload, | ||
58 | uint32_t source_ip, int source_port, | ||
59 | uint32_t dest_ip, int dest_port, uint8_t *dest_arp, int ifindex); | ||
60 | int udhcp_kernel_packet(struct dhcpMessage *payload, | ||
61 | uint32_t source_ip, int source_port, | ||
62 | uint32_t dest_ip, int dest_port); | ||
63 | |||
64 | |||
65 | /**/ | ||
66 | |||
67 | void udhcp_background(const char *pidfile); | ||
68 | void udhcp_start_log_and_pid(const char *pidfile); | ||
69 | |||
70 | void udhcp_run_script(struct dhcpMessage *packet, const char *name); | ||
71 | |||
72 | // Still need to clean these up... | ||
73 | |||
74 | /* from pidfile.h */ | ||
75 | #define pidfile_acquire udhcp_pidfile_acquire | ||
76 | #define pidfile_write_release udhcp_pidfile_write_release | ||
77 | /* from options.h */ | ||
78 | #define get_option udhcp_get_option | ||
79 | #define end_option udhcp_end_option | ||
80 | #define add_option_string udhcp_add_option_string | ||
81 | #define add_simple_option udhcp_add_simple_option | ||
82 | #define option_lengths udhcp_option_lengths | ||
83 | /* from socket.h */ | ||
84 | #define listen_socket udhcp_listen_socket | ||
85 | #define read_interface udhcp_read_interface | ||
86 | /* from dhcpc.h */ | ||
87 | #define client_config udhcp_client_config | ||
88 | /* from dhcpd.h */ | ||
89 | #define server_config udhcp_server_config | ||
90 | |||
91 | long uptime(void); | ||
92 | void udhcp_sp_setup(void); | ||
93 | int udhcp_sp_fd_set(fd_set *rfds, int extra_fd); | ||
94 | int udhcp_sp_read(fd_set *rfds); | ||
95 | int raw_socket(int ifindex); | ||
96 | int read_interface(char *interface, int *ifindex, uint32_t *addr, uint8_t *arp); | ||
97 | int listen_socket(uint32_t ip, int port, char *inf); | ||
98 | int pidfile_acquire(const char *pidfile); | ||
99 | void pidfile_write_release(int pid_fd); | ||
100 | int arpping(uint32_t yiaddr, uint32_t ip, uint8_t *arp, char *interface); | ||
101 | |||
102 | #if ENABLE_FEATURE_UDHCP_DEBUG | ||
103 | # define DEBUG(str, args...) bb_info_msg(str, ## args) | ||
104 | #else | ||
105 | # define DEBUG(str, args...) do {;} while (0) | ||
106 | #endif | ||
107 | |||
108 | #endif | ||
diff --git a/networking/udhcp/dhcpc.c b/networking/udhcp/dhcpc.c new file mode 100644 index 000000000..71315ff0a --- /dev/null +++ b/networking/udhcp/dhcpc.c | |||
@@ -0,0 +1,509 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* dhcpc.c | ||
3 | * | ||
4 | * udhcp DHCP client | ||
5 | * | ||
6 | * Russ Dill <Russ.Dill@asu.edu> July 2001 | ||
7 | * | ||
8 | * Licensed under the GPL v2 or later, see the file LICENSE in this tarball. | ||
9 | */ | ||
10 | |||
11 | #include <getopt.h> | ||
12 | |||
13 | #include "common.h" | ||
14 | #include "dhcpd.h" | ||
15 | #include "dhcpc.h" | ||
16 | #include "options.h" | ||
17 | |||
18 | |||
19 | static int state; | ||
20 | /* Something is definitely wrong here. IPv4 addresses | ||
21 | * in variables of type long?? BTW, we use inet_ntoa() | ||
22 | * in the code. Manpage says that struct in_addr has a member of type long (!) | ||
23 | * which holds IPv4 address, and the struct is passed by value (!!) | ||
24 | */ | ||
25 | static unsigned long requested_ip; /* = 0 */ | ||
26 | static unsigned long server_addr; | ||
27 | static unsigned long timeout; | ||
28 | static int packet_num; /* = 0 */ | ||
29 | static int fd = -1; | ||
30 | |||
31 | #define LISTEN_NONE 0 | ||
32 | #define LISTEN_KERNEL 1 | ||
33 | #define LISTEN_RAW 2 | ||
34 | static int listen_mode; | ||
35 | |||
36 | struct client_config_t client_config; | ||
37 | |||
38 | |||
39 | /* just a little helper */ | ||
40 | static void change_mode(int new_mode) | ||
41 | { | ||
42 | DEBUG("entering %s listen mode", | ||
43 | new_mode ? (new_mode == 1 ? "kernel" : "raw") : "none"); | ||
44 | if (fd >= 0) close(fd); | ||
45 | fd = -1; | ||
46 | listen_mode = new_mode; | ||
47 | } | ||
48 | |||
49 | |||
50 | /* perform a renew */ | ||
51 | static void perform_renew(void) | ||
52 | { | ||
53 | bb_info_msg("Performing a DHCP renew"); | ||
54 | switch (state) { | ||
55 | case BOUND: | ||
56 | change_mode(LISTEN_KERNEL); | ||
57 | case RENEWING: | ||
58 | case REBINDING: | ||
59 | state = RENEW_REQUESTED; | ||
60 | break; | ||
61 | case RENEW_REQUESTED: /* impatient are we? fine, square 1 */ | ||
62 | udhcp_run_script(NULL, "deconfig"); | ||
63 | case REQUESTING: | ||
64 | case RELEASED: | ||
65 | change_mode(LISTEN_RAW); | ||
66 | state = INIT_SELECTING; | ||
67 | break; | ||
68 | case INIT_SELECTING: | ||
69 | break; | ||
70 | } | ||
71 | |||
72 | /* start things over */ | ||
73 | packet_num = 0; | ||
74 | |||
75 | /* Kill any timeouts because the user wants this to hurry along */ | ||
76 | timeout = 0; | ||
77 | } | ||
78 | |||
79 | |||
80 | /* perform a release */ | ||
81 | static void perform_release(void) | ||
82 | { | ||
83 | char buffer[16]; | ||
84 | struct in_addr temp_addr; | ||
85 | |||
86 | /* send release packet */ | ||
87 | if (state == BOUND || state == RENEWING || state == REBINDING) { | ||
88 | temp_addr.s_addr = server_addr; | ||
89 | sprintf(buffer, "%s", inet_ntoa(temp_addr)); | ||
90 | temp_addr.s_addr = requested_ip; | ||
91 | bb_info_msg("Unicasting a release of %s to %s", | ||
92 | inet_ntoa(temp_addr), buffer); | ||
93 | send_release(server_addr, requested_ip); /* unicast */ | ||
94 | udhcp_run_script(NULL, "deconfig"); | ||
95 | } | ||
96 | bb_info_msg("Entering released state"); | ||
97 | |||
98 | change_mode(LISTEN_NONE); | ||
99 | state = RELEASED; | ||
100 | timeout = 0x7fffffff; | ||
101 | } | ||
102 | |||
103 | |||
104 | static void client_background(void) | ||
105 | { | ||
106 | udhcp_background(client_config.pidfile); | ||
107 | client_config.foreground = 1; /* Do not fork again. */ | ||
108 | client_config.background_if_no_lease = 0; | ||
109 | } | ||
110 | |||
111 | |||
112 | static uint8_t* alloc_dhcp_option(int code, const char *str, int extra) | ||
113 | { | ||
114 | uint8_t *storage; | ||
115 | int len = strlen(str); | ||
116 | if (len > 255) len = 255; | ||
117 | storage = xzalloc(len + extra + OPT_DATA); | ||
118 | storage[OPT_CODE] = code; | ||
119 | storage[OPT_LEN] = len + extra; | ||
120 | memcpy(storage + extra + OPT_DATA, str, len); | ||
121 | return storage; | ||
122 | } | ||
123 | |||
124 | |||
125 | int udhcpc_main(int argc, char *argv[]) | ||
126 | { | ||
127 | uint8_t *temp, *message; | ||
128 | char *str_c, *str_V, *str_h, *str_F, *str_r, *str_T, *str_t; | ||
129 | unsigned long t1 = 0, t2 = 0, xid = 0; | ||
130 | unsigned long start = 0, lease = 0; | ||
131 | long now; | ||
132 | unsigned opt; | ||
133 | int max_fd; | ||
134 | int sig; | ||
135 | int retval; | ||
136 | int len; | ||
137 | int no_clientid = 0; | ||
138 | fd_set rfds; | ||
139 | struct timeval tv; | ||
140 | struct dhcpMessage packet; | ||
141 | struct in_addr temp_addr; | ||
142 | |||
143 | enum { | ||
144 | OPT_c = 1 << 0, | ||
145 | OPT_C = 1 << 1, | ||
146 | OPT_V = 1 << 2, | ||
147 | OPT_f = 1 << 3, | ||
148 | OPT_b = 1 << 4, | ||
149 | OPT_H = 1 << 5, | ||
150 | OPT_h = 1 << 6, | ||
151 | OPT_F = 1 << 7, | ||
152 | OPT_i = 1 << 8, | ||
153 | OPT_n = 1 << 9, | ||
154 | OPT_p = 1 << 10, | ||
155 | OPT_q = 1 << 11, | ||
156 | OPT_R = 1 << 12, | ||
157 | OPT_r = 1 << 13, | ||
158 | OPT_s = 1 << 14, | ||
159 | OPT_T = 1 << 15, | ||
160 | OPT_t = 1 << 16, | ||
161 | OPT_v = 1 << 17, | ||
162 | }; | ||
163 | #if ENABLE_GETOPT_LONG | ||
164 | static const struct option arg_options[] = { | ||
165 | { "clientid", required_argument, 0, 'c' }, | ||
166 | { "clientid-none", no_argument, 0, 'C' }, | ||
167 | { "vendorclass", required_argument, 0, 'V' }, | ||
168 | { "foreground", no_argument, 0, 'f' }, | ||
169 | { "background", no_argument, 0, 'b' }, | ||
170 | { "hostname", required_argument, 0, 'H' }, | ||
171 | { "hostname", required_argument, 0, 'h' }, | ||
172 | { "fqdn", required_argument, 0, 'F' }, | ||
173 | { "interface", required_argument, 0, 'i' }, | ||
174 | { "now", no_argument, 0, 'n' }, | ||
175 | { "pidfile", required_argument, 0, 'p' }, | ||
176 | { "quit", no_argument, 0, 'q' }, | ||
177 | { "release", no_argument, 0, 'R' }, | ||
178 | { "request", required_argument, 0, 'r' }, | ||
179 | { "script", required_argument, 0, 's' }, | ||
180 | { "timeout", required_argument, 0, 'T' }, | ||
181 | { "version", no_argument, 0, 'v' }, | ||
182 | { "retries", required_argument, 0, 't' }, | ||
183 | { 0, 0, 0, 0 } | ||
184 | }; | ||
185 | #endif | ||
186 | /* Default options. */ | ||
187 | client_config.interface = "eth0"; | ||
188 | client_config.script = DEFAULT_SCRIPT; | ||
189 | client_config.retries = 3; | ||
190 | client_config.timeout = 3; | ||
191 | |||
192 | /* Parse command line */ | ||
193 | opt_complementary = "?:c--C:C--c" // mutually exclusive | ||
194 | ":hH:Hh"; // -h and -H are the same | ||
195 | #if ENABLE_GETOPT_LONG | ||
196 | applet_long_options = arg_options; | ||
197 | #endif | ||
198 | opt = getopt32(argc, argv, "c:CV:fbH:h:F:i:np:qRr:s:T:t:v", | ||
199 | &str_c, &str_V, &str_h, &str_h, &str_F, | ||
200 | &client_config.interface, &client_config.pidfile, &str_r, | ||
201 | &client_config.script, &str_T, &str_t | ||
202 | ); | ||
203 | |||
204 | if (opt & OPT_c) | ||
205 | client_config.clientid = alloc_dhcp_option(DHCP_CLIENT_ID, str_c, 0); | ||
206 | if (opt & OPT_C) | ||
207 | no_clientid = 1; | ||
208 | if (opt & OPT_V) | ||
209 | client_config.vendorclass = alloc_dhcp_option(DHCP_VENDOR, str_V, 0); | ||
210 | if (opt & OPT_f) | ||
211 | client_config.foreground = 1; | ||
212 | if (opt & OPT_b) | ||
213 | client_config.background_if_no_lease = 1; | ||
214 | if (opt & OPT_h) | ||
215 | client_config.hostname = alloc_dhcp_option(DHCP_HOST_NAME, str_h, 0); | ||
216 | if (opt & OPT_F) { | ||
217 | client_config.fqdn = alloc_dhcp_option(DHCP_FQDN, str_F, 3); | ||
218 | /* Flags: 0000NEOS | ||
219 | S: 1 => Client requests Server to update A RR in DNS as well as PTR | ||
220 | O: 1 => Server indicates to client that DNS has been updated regardless | ||
221 | E: 1 => Name data is DNS format, i.e. <4>host<6>domain<4>com<0> not "host.domain.com" | ||
222 | N: 1 => Client requests Server to not update DNS | ||
223 | */ | ||
224 | client_config.fqdn[OPT_DATA + 0] = 0x1; | ||
225 | /* client_config.fqdn[OPT_DATA + 1] = 0; - redundant */ | ||
226 | /* client_config.fqdn[OPT_DATA + 2] = 0; - redundant */ | ||
227 | } | ||
228 | // if (opt & OPT_i) client_config.interface = ... | ||
229 | if (opt & OPT_n) | ||
230 | client_config.abort_if_no_lease = 1; | ||
231 | // if (opt & OPT_p) client_config.pidfile = ... | ||
232 | if (opt & OPT_q) | ||
233 | client_config.quit_after_lease = 1; | ||
234 | if (opt & OPT_R) | ||
235 | client_config.release_on_quit = 1; | ||
236 | if (opt & OPT_r) | ||
237 | requested_ip = inet_addr(str_r); | ||
238 | // if (opt & OPT_s) client_config.script = ... | ||
239 | if (opt & OPT_T) | ||
240 | client_config.timeout = xatoi_u(str_T); | ||
241 | if (opt & OPT_t) | ||
242 | client_config.retries = xatoi_u(str_t); | ||
243 | if (opt & OPT_v) { | ||
244 | printf("version %s\n\n", BB_VER); | ||
245 | return 0; | ||
246 | } | ||
247 | |||
248 | /* Start the log, sanitize fd's, and write a pid file */ | ||
249 | udhcp_start_log_and_pid(client_config.pidfile); | ||
250 | |||
251 | if (read_interface(client_config.interface, &client_config.ifindex, | ||
252 | NULL, client_config.arp) < 0) | ||
253 | return 1; | ||
254 | |||
255 | /* if not set, and not suppressed, setup the default client ID */ | ||
256 | if (!client_config.clientid && !no_clientid) { | ||
257 | client_config.clientid = alloc_dhcp_option(DHCP_CLIENT_ID, "", 7); | ||
258 | client_config.clientid[OPT_DATA] = 1; | ||
259 | memcpy(client_config.clientid + OPT_DATA+1, client_config.arp, 6); | ||
260 | } | ||
261 | |||
262 | if (!client_config.vendorclass) | ||
263 | client_config.vendorclass = alloc_dhcp_option(DHCP_VENDOR, "udhcp "BB_VER, 0); | ||
264 | |||
265 | /* setup the signal pipe */ | ||
266 | udhcp_sp_setup(); | ||
267 | |||
268 | state = INIT_SELECTING; | ||
269 | udhcp_run_script(NULL, "deconfig"); | ||
270 | change_mode(LISTEN_RAW); | ||
271 | |||
272 | for (;;) { | ||
273 | tv.tv_sec = timeout - uptime(); | ||
274 | tv.tv_usec = 0; | ||
275 | |||
276 | if (listen_mode != LISTEN_NONE && fd < 0) { | ||
277 | if (listen_mode == LISTEN_KERNEL) | ||
278 | fd = listen_socket(INADDR_ANY, CLIENT_PORT, client_config.interface); | ||
279 | else | ||
280 | fd = raw_socket(client_config.ifindex); | ||
281 | if (fd < 0) { | ||
282 | bb_perror_msg("FATAL: cannot listen on socket"); | ||
283 | return 0; | ||
284 | } | ||
285 | } | ||
286 | max_fd = udhcp_sp_fd_set(&rfds, fd); | ||
287 | |||
288 | if (tv.tv_sec > 0) { | ||
289 | DEBUG("Waiting on select..."); | ||
290 | retval = select(max_fd + 1, &rfds, NULL, NULL, &tv); | ||
291 | } else retval = 0; /* If we already timed out, fall through */ | ||
292 | |||
293 | now = uptime(); | ||
294 | if (retval == 0) { | ||
295 | /* timeout dropped to zero */ | ||
296 | switch (state) { | ||
297 | case INIT_SELECTING: | ||
298 | if (packet_num < client_config.retries) { | ||
299 | if (packet_num == 0) | ||
300 | xid = random_xid(); | ||
301 | |||
302 | /* send discover packet */ | ||
303 | send_discover(xid, requested_ip); /* broadcast */ | ||
304 | |||
305 | timeout = now + client_config.timeout; | ||
306 | packet_num++; | ||
307 | } else { | ||
308 | udhcp_run_script(NULL, "leasefail"); | ||
309 | if (client_config.background_if_no_lease) { | ||
310 | bb_info_msg("No lease, forking to background"); | ||
311 | client_background(); | ||
312 | } else if (client_config.abort_if_no_lease) { | ||
313 | bb_info_msg("No lease, failing"); | ||
314 | return 1; | ||
315 | } | ||
316 | /* wait to try again */ | ||
317 | packet_num = 0; | ||
318 | timeout = now + 60; | ||
319 | } | ||
320 | break; | ||
321 | case RENEW_REQUESTED: | ||
322 | case REQUESTING: | ||
323 | if (packet_num < client_config.retries) { | ||
324 | /* send request packet */ | ||
325 | if (state == RENEW_REQUESTED) | ||
326 | send_renew(xid, server_addr, requested_ip); /* unicast */ | ||
327 | else send_selecting(xid, server_addr, requested_ip); /* broadcast */ | ||
328 | |||
329 | timeout = now + ((packet_num == 2) ? 10 : 2); | ||
330 | packet_num++; | ||
331 | } else { | ||
332 | /* timed out, go back to init state */ | ||
333 | if (state == RENEW_REQUESTED) udhcp_run_script(NULL, "deconfig"); | ||
334 | state = INIT_SELECTING; | ||
335 | timeout = now; | ||
336 | packet_num = 0; | ||
337 | change_mode(LISTEN_RAW); | ||
338 | } | ||
339 | break; | ||
340 | case BOUND: | ||
341 | /* Lease is starting to run out, time to enter renewing state */ | ||
342 | state = RENEWING; | ||
343 | change_mode(LISTEN_KERNEL); | ||
344 | DEBUG("Entering renew state"); | ||
345 | /* fall right through */ | ||
346 | case RENEWING: | ||
347 | /* Either set a new T1, or enter REBINDING state */ | ||
348 | if ((t2 - t1) <= (lease / 14400 + 1)) { | ||
349 | /* timed out, enter rebinding state */ | ||
350 | state = REBINDING; | ||
351 | timeout = now + (t2 - t1); | ||
352 | DEBUG("Entering rebinding state"); | ||
353 | } else { | ||
354 | /* send a request packet */ | ||
355 | send_renew(xid, server_addr, requested_ip); /* unicast */ | ||
356 | |||
357 | t1 = (t2 - t1) / 2 + t1; | ||
358 | timeout = t1 + start; | ||
359 | } | ||
360 | break; | ||
361 | case REBINDING: | ||
362 | /* Either set a new T2, or enter INIT state */ | ||
363 | if ((lease - t2) <= (lease / 14400 + 1)) { | ||
364 | /* timed out, enter init state */ | ||
365 | state = INIT_SELECTING; | ||
366 | bb_info_msg("Lease lost, entering init state"); | ||
367 | udhcp_run_script(NULL, "deconfig"); | ||
368 | timeout = now; | ||
369 | packet_num = 0; | ||
370 | change_mode(LISTEN_RAW); | ||
371 | } else { | ||
372 | /* send a request packet */ | ||
373 | send_renew(xid, 0, requested_ip); /* broadcast */ | ||
374 | |||
375 | t2 = (lease - t2) / 2 + t2; | ||
376 | timeout = t2 + start; | ||
377 | } | ||
378 | break; | ||
379 | case RELEASED: | ||
380 | /* yah, I know, *you* say it would never happen */ | ||
381 | timeout = 0x7fffffff; | ||
382 | break; | ||
383 | } | ||
384 | } else if (retval > 0 && listen_mode != LISTEN_NONE && FD_ISSET(fd, &rfds)) { | ||
385 | /* a packet is ready, read it */ | ||
386 | |||
387 | if (listen_mode == LISTEN_KERNEL) | ||
388 | len = udhcp_get_packet(&packet, fd); | ||
389 | else len = get_raw_packet(&packet, fd); | ||
390 | |||
391 | if (len == -1 && errno != EINTR) { | ||
392 | DEBUG("error on read, %s, reopening socket", strerror(errno)); | ||
393 | change_mode(listen_mode); /* just close and reopen */ | ||
394 | } | ||
395 | if (len < 0) continue; | ||
396 | |||
397 | if (packet.xid != xid) { | ||
398 | DEBUG("Ignoring XID %lx (our xid is %lx)", | ||
399 | (unsigned long) packet.xid, xid); | ||
400 | continue; | ||
401 | } | ||
402 | |||
403 | /* Ignore packets that aren't for us */ | ||
404 | if (memcmp(packet.chaddr, client_config.arp, 6)) { | ||
405 | DEBUG("Packet does not have our chaddr - ignoring"); | ||
406 | continue; | ||
407 | } | ||
408 | |||
409 | if ((message = get_option(&packet, DHCP_MESSAGE_TYPE)) == NULL) { | ||
410 | bb_error_msg("cannot get option from packet - ignoring"); | ||
411 | continue; | ||
412 | } | ||
413 | |||
414 | switch (state) { | ||
415 | case INIT_SELECTING: | ||
416 | /* Must be a DHCPOFFER to one of our xid's */ | ||
417 | if (*message == DHCPOFFER) { | ||
418 | temp = get_option(&packet, DHCP_SERVER_ID); | ||
419 | if (temp) { | ||
420 | server_addr = *(uint32_t*)temp; | ||
421 | xid = packet.xid; | ||
422 | requested_ip = packet.yiaddr; | ||
423 | |||
424 | /* enter requesting state */ | ||
425 | state = REQUESTING; | ||
426 | timeout = now; | ||
427 | packet_num = 0; | ||
428 | } else { | ||
429 | bb_error_msg("no server ID in message"); | ||
430 | } | ||
431 | } | ||
432 | break; | ||
433 | case RENEW_REQUESTED: | ||
434 | case REQUESTING: | ||
435 | case RENEWING: | ||
436 | case REBINDING: | ||
437 | if (*message == DHCPACK) { | ||
438 | temp = get_option(&packet, DHCP_LEASE_TIME); | ||
439 | if (!temp) { | ||
440 | bb_error_msg("no lease time with ACK, using 1 hour lease"); | ||
441 | lease = 60 * 60; | ||
442 | } else { | ||
443 | lease = ntohl(*(uint32_t*)temp); | ||
444 | } | ||
445 | |||
446 | /* enter bound state */ | ||
447 | t1 = lease / 2; | ||
448 | |||
449 | /* little fixed point for n * .875 */ | ||
450 | t2 = (lease * 0x7) >> 3; | ||
451 | temp_addr.s_addr = packet.yiaddr; | ||
452 | bb_info_msg("Lease of %s obtained, lease time %ld", | ||
453 | inet_ntoa(temp_addr), lease); | ||
454 | start = now; | ||
455 | timeout = t1 + start; | ||
456 | requested_ip = packet.yiaddr; | ||
457 | udhcp_run_script(&packet, | ||
458 | ((state == RENEWING || state == REBINDING) ? "renew" : "bound")); | ||
459 | |||
460 | state = BOUND; | ||
461 | change_mode(LISTEN_NONE); | ||
462 | if (client_config.quit_after_lease) { | ||
463 | if (client_config.release_on_quit) | ||
464 | perform_release(); | ||
465 | return 0; | ||
466 | } | ||
467 | if (!client_config.foreground) | ||
468 | client_background(); | ||
469 | |||
470 | } else if (*message == DHCPNAK) { | ||
471 | /* return to init state */ | ||
472 | bb_info_msg("Received DHCP NAK"); | ||
473 | udhcp_run_script(&packet, "nak"); | ||
474 | if (state != REQUESTING) | ||
475 | udhcp_run_script(NULL, "deconfig"); | ||
476 | state = INIT_SELECTING; | ||
477 | timeout = now; | ||
478 | requested_ip = 0; | ||
479 | packet_num = 0; | ||
480 | change_mode(LISTEN_RAW); | ||
481 | sleep(3); /* avoid excessive network traffic */ | ||
482 | } | ||
483 | break; | ||
484 | /* case BOUND, RELEASED: - ignore all packets */ | ||
485 | } | ||
486 | } else if (retval > 0 && (sig = udhcp_sp_read(&rfds))) { | ||
487 | switch (sig) { | ||
488 | case SIGUSR1: | ||
489 | perform_renew(); | ||
490 | break; | ||
491 | case SIGUSR2: | ||
492 | perform_release(); | ||
493 | break; | ||
494 | case SIGTERM: | ||
495 | bb_info_msg("Received SIGTERM"); | ||
496 | if (client_config.release_on_quit) | ||
497 | perform_release(); | ||
498 | return 0; | ||
499 | } | ||
500 | } else if (retval == -1 && errno == EINTR) { | ||
501 | /* a signal was caught */ | ||
502 | } else { | ||
503 | /* An error occured */ | ||
504 | bb_perror_msg("select"); | ||
505 | } | ||
506 | |||
507 | } | ||
508 | return 0; | ||
509 | } | ||
diff --git a/networking/udhcp/dhcpc.h b/networking/udhcp/dhcpc.h new file mode 100644 index 000000000..fd17917d0 --- /dev/null +++ b/networking/udhcp/dhcpc.h | |||
@@ -0,0 +1,50 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* dhcpc.h */ | ||
3 | #ifndef _DHCPC_H | ||
4 | #define _DHCPC_H | ||
5 | |||
6 | #define INIT_SELECTING 0 | ||
7 | #define REQUESTING 1 | ||
8 | #define BOUND 2 | ||
9 | #define RENEWING 3 | ||
10 | #define REBINDING 4 | ||
11 | #define INIT_REBOOT 5 | ||
12 | #define RENEW_REQUESTED 6 | ||
13 | #define RELEASED 7 | ||
14 | |||
15 | struct client_config_t { | ||
16 | /* TODO: combine flag fields into single "unsigned opt" */ | ||
17 | /* (can be set directly to the result of getopt32) */ | ||
18 | char foreground; /* Do not fork */ | ||
19 | char quit_after_lease; /* Quit after obtaining lease */ | ||
20 | char release_on_quit; /* perform release on quit */ | ||
21 | char abort_if_no_lease; /* Abort if no lease */ | ||
22 | char background_if_no_lease; /* Fork to background if no lease */ | ||
23 | char *interface; /* The name of the interface to use */ | ||
24 | char *pidfile; /* Optionally store the process ID */ | ||
25 | char *script; /* User script to run at dhcp events */ | ||
26 | uint8_t *clientid; /* Optional client id to use */ | ||
27 | uint8_t *vendorclass; /* Optional vendor class-id to use */ | ||
28 | uint8_t *hostname; /* Optional hostname to use */ | ||
29 | uint8_t *fqdn; /* Optional fully qualified domain name to use */ | ||
30 | int ifindex; /* Index number of the interface to use */ | ||
31 | int retries; /* Max number of request packets */ | ||
32 | int timeout; /* Number of seconds to try to get a lease */ | ||
33 | uint8_t arp[6]; /* Our arp address */ | ||
34 | }; | ||
35 | |||
36 | extern struct client_config_t client_config; | ||
37 | |||
38 | |||
39 | /*** clientpacket.h ***/ | ||
40 | |||
41 | unsigned long random_xid(void); | ||
42 | int send_discover(unsigned long xid, unsigned long requested); | ||
43 | int send_selecting(unsigned long xid, unsigned long server, unsigned long requested); | ||
44 | int send_renew(unsigned long xid, unsigned long server, unsigned long ciaddr); | ||
45 | int send_renew(unsigned long xid, unsigned long server, unsigned long ciaddr); | ||
46 | int send_release(unsigned long server, unsigned long ciaddr); | ||
47 | int get_raw_packet(struct dhcpMessage *payload, int fd); | ||
48 | |||
49 | |||
50 | #endif | ||
diff --git a/networking/udhcp/dhcpd.c b/networking/udhcp/dhcpd.c new file mode 100644 index 000000000..74380367f --- /dev/null +++ b/networking/udhcp/dhcpd.c | |||
@@ -0,0 +1,226 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* dhcpd.c | ||
3 | * | ||
4 | * udhcp Server | ||
5 | * Copyright (C) 1999 Matthew Ramsay <matthewr@moreton.com.au> | ||
6 | * Chris Trew <ctrew@moreton.com.au> | ||
7 | * | ||
8 | * Rewrite by Russ Dill <Russ.Dill@asu.edu> July 2001 | ||
9 | * | ||
10 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | ||
11 | */ | ||
12 | |||
13 | #include "common.h" | ||
14 | #include "dhcpd.h" | ||
15 | #include "options.h" | ||
16 | |||
17 | |||
18 | /* globals */ | ||
19 | struct dhcpOfferedAddr *leases; | ||
20 | struct server_config_t server_config; | ||
21 | |||
22 | |||
23 | int udhcpd_main(int argc, char *argv[]) | ||
24 | { | ||
25 | fd_set rfds; | ||
26 | struct timeval tv; | ||
27 | int server_socket = -1, bytes, retval, max_sock; | ||
28 | struct dhcpMessage packet; | ||
29 | uint8_t *state, *server_id, *requested; | ||
30 | uint32_t server_id_align, requested_align, static_lease_ip; | ||
31 | unsigned long timeout_end, num_ips; | ||
32 | struct option_set *option; | ||
33 | struct dhcpOfferedAddr *lease, static_lease; | ||
34 | |||
35 | read_config(argc < 2 ? DHCPD_CONF_FILE : argv[1]); | ||
36 | |||
37 | /* Start the log, sanitize fd's, and write a pid file */ | ||
38 | udhcp_start_log_and_pid(server_config.pidfile); | ||
39 | |||
40 | if ((option = find_option(server_config.options, DHCP_LEASE_TIME))) { | ||
41 | memcpy(&server_config.lease, option->data + 2, 4); | ||
42 | server_config.lease = ntohl(server_config.lease); | ||
43 | } | ||
44 | else server_config.lease = LEASE_TIME; | ||
45 | |||
46 | /* Sanity check */ | ||
47 | num_ips = ntohl(server_config.end) - ntohl(server_config.start) + 1; | ||
48 | if (server_config.max_leases > num_ips) { | ||
49 | bb_error_msg("max_leases value (%lu) not sane, " | ||
50 | "setting to %lu instead", | ||
51 | server_config.max_leases, num_ips); | ||
52 | server_config.max_leases = num_ips; | ||
53 | } | ||
54 | |||
55 | leases = xzalloc(server_config.max_leases * sizeof(struct dhcpOfferedAddr)); | ||
56 | read_leases(server_config.lease_file); | ||
57 | |||
58 | if (read_interface(server_config.interface, &server_config.ifindex, | ||
59 | &server_config.server, server_config.arp) < 0) | ||
60 | return 1; | ||
61 | |||
62 | if (!ENABLE_FEATURE_UDHCP_DEBUG) | ||
63 | udhcp_background(server_config.pidfile); /* hold lock during fork. */ | ||
64 | |||
65 | /* Setup the signal pipe */ | ||
66 | udhcp_sp_setup(); | ||
67 | |||
68 | timeout_end = time(0) + server_config.auto_time; | ||
69 | while (1) { /* loop until universe collapses */ | ||
70 | |||
71 | if (server_socket < 0) { | ||
72 | server_socket = listen_socket(INADDR_ANY, SERVER_PORT, server_config.interface); | ||
73 | if (server_socket < 0) { | ||
74 | bb_perror_msg("FATAL: cannot create server socket"); | ||
75 | return 2; | ||
76 | } | ||
77 | } | ||
78 | |||
79 | max_sock = udhcp_sp_fd_set(&rfds, server_socket); | ||
80 | if (server_config.auto_time) { | ||
81 | tv.tv_sec = timeout_end - time(0); | ||
82 | tv.tv_usec = 0; | ||
83 | } | ||
84 | if (!server_config.auto_time || tv.tv_sec > 0) { | ||
85 | retval = select(max_sock + 1, &rfds, NULL, NULL, | ||
86 | server_config.auto_time ? &tv : NULL); | ||
87 | } else retval = 0; /* If we already timed out, fall through */ | ||
88 | |||
89 | if (retval == 0) { | ||
90 | write_leases(); | ||
91 | timeout_end = time(0) + server_config.auto_time; | ||
92 | continue; | ||
93 | } else if (retval < 0 && errno != EINTR) { | ||
94 | DEBUG("error on select"); | ||
95 | continue; | ||
96 | } | ||
97 | |||
98 | switch (udhcp_sp_read(&rfds)) { | ||
99 | case SIGUSR1: | ||
100 | bb_info_msg("Received a SIGUSR1"); | ||
101 | write_leases(); | ||
102 | /* why not just reset the timeout, eh */ | ||
103 | timeout_end = time(0) + server_config.auto_time; | ||
104 | continue; | ||
105 | case SIGTERM: | ||
106 | bb_info_msg("Received a SIGTERM"); | ||
107 | return 0; | ||
108 | case 0: break; /* no signal */ | ||
109 | default: continue; /* signal or error (probably EINTR) */ | ||
110 | } | ||
111 | |||
112 | if ((bytes = udhcp_get_packet(&packet, server_socket)) < 0) { /* this waits for a packet - idle */ | ||
113 | if (bytes == -1 && errno != EINTR) { | ||
114 | DEBUG("error on read, %s, reopening socket", strerror(errno)); | ||
115 | close(server_socket); | ||
116 | server_socket = -1; | ||
117 | } | ||
118 | continue; | ||
119 | } | ||
120 | |||
121 | if ((state = get_option(&packet, DHCP_MESSAGE_TYPE)) == NULL) { | ||
122 | bb_error_msg("cannot get option from packet, ignoring"); | ||
123 | continue; | ||
124 | } | ||
125 | |||
126 | /* Look for a static lease */ | ||
127 | static_lease_ip = getIpByMac(server_config.static_leases, &packet.chaddr); | ||
128 | |||
129 | if (static_lease_ip) { | ||
130 | bb_info_msg("Found static lease: %x", static_lease_ip); | ||
131 | |||
132 | memcpy(&static_lease.chaddr, &packet.chaddr, 16); | ||
133 | static_lease.yiaddr = static_lease_ip; | ||
134 | static_lease.expires = 0; | ||
135 | |||
136 | lease = &static_lease; | ||
137 | |||
138 | } else { | ||
139 | lease = find_lease_by_chaddr(packet.chaddr); | ||
140 | } | ||
141 | |||
142 | switch (state[0]) { | ||
143 | case DHCPDISCOVER: | ||
144 | DEBUG("Received DISCOVER"); | ||
145 | |||
146 | if (sendOffer(&packet) < 0) { | ||
147 | bb_error_msg("send OFFER failed"); | ||
148 | } | ||
149 | break; | ||
150 | case DHCPREQUEST: | ||
151 | DEBUG("received REQUEST"); | ||
152 | |||
153 | requested = get_option(&packet, DHCP_REQUESTED_IP); | ||
154 | server_id = get_option(&packet, DHCP_SERVER_ID); | ||
155 | |||
156 | if (requested) memcpy(&requested_align, requested, 4); | ||
157 | if (server_id) memcpy(&server_id_align, server_id, 4); | ||
158 | |||
159 | if (lease) { | ||
160 | if (server_id) { | ||
161 | /* SELECTING State */ | ||
162 | DEBUG("server_id = %08x", ntohl(server_id_align)); | ||
163 | if (server_id_align == server_config.server && requested && | ||
164 | requested_align == lease->yiaddr) { | ||
165 | sendACK(&packet, lease->yiaddr); | ||
166 | } | ||
167 | } else { | ||
168 | if (requested) { | ||
169 | /* INIT-REBOOT State */ | ||
170 | if (lease->yiaddr == requested_align) | ||
171 | sendACK(&packet, lease->yiaddr); | ||
172 | else sendNAK(&packet); | ||
173 | } else { | ||
174 | /* RENEWING or REBINDING State */ | ||
175 | if (lease->yiaddr == packet.ciaddr) | ||
176 | sendACK(&packet, lease->yiaddr); | ||
177 | else { | ||
178 | /* don't know what to do!!!! */ | ||
179 | sendNAK(&packet); | ||
180 | } | ||
181 | } | ||
182 | } | ||
183 | |||
184 | /* what to do if we have no record of the client */ | ||
185 | } else if (server_id) { | ||
186 | /* SELECTING State */ | ||
187 | |||
188 | } else if (requested) { | ||
189 | /* INIT-REBOOT State */ | ||
190 | if ((lease = find_lease_by_yiaddr(requested_align))) { | ||
191 | if (lease_expired(lease)) { | ||
192 | /* probably best if we drop this lease */ | ||
193 | memset(lease->chaddr, 0, 16); | ||
194 | /* make some contention for this address */ | ||
195 | } else sendNAK(&packet); | ||
196 | } else if (requested_align < server_config.start || | ||
197 | requested_align > server_config.end) { | ||
198 | sendNAK(&packet); | ||
199 | } /* else remain silent */ | ||
200 | |||
201 | } else { | ||
202 | /* RENEWING or REBINDING State */ | ||
203 | } | ||
204 | break; | ||
205 | case DHCPDECLINE: | ||
206 | DEBUG("Received DECLINE"); | ||
207 | if (lease) { | ||
208 | memset(lease->chaddr, 0, 16); | ||
209 | lease->expires = time(0) + server_config.decline_time; | ||
210 | } | ||
211 | break; | ||
212 | case DHCPRELEASE: | ||
213 | DEBUG("Received RELEASE"); | ||
214 | if (lease) lease->expires = time(0); | ||
215 | break; | ||
216 | case DHCPINFORM: | ||
217 | DEBUG("Received INFORM"); | ||
218 | send_inform(&packet); | ||
219 | break; | ||
220 | default: | ||
221 | bb_info_msg("Unsupported DHCP message (%02x) - ignoring", state[0]); | ||
222 | } | ||
223 | } | ||
224 | |||
225 | return 0; | ||
226 | } | ||
diff --git a/networking/udhcp/dhcpd.h b/networking/udhcp/dhcpd.h new file mode 100644 index 000000000..40959e4ae --- /dev/null +++ b/networking/udhcp/dhcpd.h | |||
@@ -0,0 +1,190 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* dhcpd.h */ | ||
3 | #ifndef _DHCPD_H | ||
4 | #define _DHCPD_H | ||
5 | |||
6 | /************************************/ | ||
7 | /* Defaults _you_ may want to tweak */ | ||
8 | /************************************/ | ||
9 | |||
10 | /* the period of time the client is allowed to use that address */ | ||
11 | #define LEASE_TIME (60*60*24*10) /* 10 days of seconds */ | ||
12 | #define LEASES_FILE "/var/lib/misc/udhcpd.leases" | ||
13 | |||
14 | /* where to find the DHCP server configuration file */ | ||
15 | #define DHCPD_CONF_FILE "/etc/udhcpd.conf" | ||
16 | |||
17 | /*****************************************************************/ | ||
18 | /* Do not modify below here unless you know what you are doing!! */ | ||
19 | /*****************************************************************/ | ||
20 | |||
21 | /* DHCP protocol -- see RFC 2131 */ | ||
22 | #define SERVER_PORT 67 | ||
23 | #define CLIENT_PORT 68 | ||
24 | |||
25 | #define DHCP_MAGIC 0x63825363 | ||
26 | |||
27 | /* DHCP option codes (partial list) */ | ||
28 | #define DHCP_PADDING 0x00 | ||
29 | #define DHCP_SUBNET 0x01 | ||
30 | #define DHCP_TIME_OFFSET 0x02 | ||
31 | #define DHCP_ROUTER 0x03 | ||
32 | #define DHCP_TIME_SERVER 0x04 | ||
33 | #define DHCP_NAME_SERVER 0x05 | ||
34 | #define DHCP_DNS_SERVER 0x06 | ||
35 | #define DHCP_LOG_SERVER 0x07 | ||
36 | #define DHCP_COOKIE_SERVER 0x08 | ||
37 | #define DHCP_LPR_SERVER 0x09 | ||
38 | #define DHCP_HOST_NAME 0x0c | ||
39 | #define DHCP_BOOT_SIZE 0x0d | ||
40 | #define DHCP_DOMAIN_NAME 0x0f | ||
41 | #define DHCP_SWAP_SERVER 0x10 | ||
42 | #define DHCP_ROOT_PATH 0x11 | ||
43 | #define DHCP_IP_TTL 0x17 | ||
44 | #define DHCP_MTU 0x1a | ||
45 | #define DHCP_BROADCAST 0x1c | ||
46 | #define DHCP_NTP_SERVER 0x2a | ||
47 | #define DHCP_WINS_SERVER 0x2c | ||
48 | #define DHCP_REQUESTED_IP 0x32 | ||
49 | #define DHCP_LEASE_TIME 0x33 | ||
50 | #define DHCP_OPTION_OVER 0x34 | ||
51 | #define DHCP_MESSAGE_TYPE 0x35 | ||
52 | #define DHCP_SERVER_ID 0x36 | ||
53 | #define DHCP_PARAM_REQ 0x37 | ||
54 | #define DHCP_MESSAGE 0x38 | ||
55 | #define DHCP_MAX_SIZE 0x39 | ||
56 | #define DHCP_T1 0x3a | ||
57 | #define DHCP_T2 0x3b | ||
58 | #define DHCP_VENDOR 0x3c | ||
59 | #define DHCP_CLIENT_ID 0x3d | ||
60 | #define DHCP_FQDN 0x51 | ||
61 | |||
62 | #define DHCP_END 0xFF | ||
63 | |||
64 | |||
65 | #define BOOTREQUEST 1 | ||
66 | #define BOOTREPLY 2 | ||
67 | |||
68 | #define ETH_10MB 1 | ||
69 | #define ETH_10MB_LEN 6 | ||
70 | |||
71 | #define DHCPDISCOVER 1 | ||
72 | #define DHCPOFFER 2 | ||
73 | #define DHCPREQUEST 3 | ||
74 | #define DHCPDECLINE 4 | ||
75 | #define DHCPACK 5 | ||
76 | #define DHCPNAK 6 | ||
77 | #define DHCPRELEASE 7 | ||
78 | #define DHCPINFORM 8 | ||
79 | |||
80 | #define BROADCAST_FLAG 0x8000 | ||
81 | |||
82 | #define OPTION_FIELD 0 | ||
83 | #define FILE_FIELD 1 | ||
84 | #define SNAME_FIELD 2 | ||
85 | |||
86 | /* miscellaneous defines */ | ||
87 | #define MAC_BCAST_ADDR (uint8_t *) "\xff\xff\xff\xff\xff\xff" | ||
88 | #define OPT_CODE 0 | ||
89 | #define OPT_LEN 1 | ||
90 | #define OPT_DATA 2 | ||
91 | |||
92 | struct option_set { | ||
93 | uint8_t *data; | ||
94 | struct option_set *next; | ||
95 | }; | ||
96 | |||
97 | struct static_lease { | ||
98 | uint8_t *mac; | ||
99 | uint32_t *ip; | ||
100 | struct static_lease *next; | ||
101 | }; | ||
102 | |||
103 | struct server_config_t { | ||
104 | uint32_t server; /* Our IP, in network order */ | ||
105 | uint32_t start; /* Start address of leases, network order */ | ||
106 | uint32_t end; /* End of leases, network order */ | ||
107 | struct option_set *options; /* List of DHCP options loaded from the config file */ | ||
108 | char *interface; /* The name of the interface to use */ | ||
109 | int ifindex; /* Index number of the interface to use */ | ||
110 | uint8_t arp[6]; /* Our arp address */ | ||
111 | unsigned long lease; /* lease time in seconds (host order) */ | ||
112 | unsigned long max_leases; /* maximum number of leases (including reserved address) */ | ||
113 | char remaining; /* should the lease file be interpreted as lease time remaining, or | ||
114 | * as the time the lease expires */ | ||
115 | unsigned long auto_time; /* how long should udhcpd wait before writing a config file. | ||
116 | * if this is zero, it will only write one on SIGUSR1 */ | ||
117 | unsigned long decline_time; /* how long an address is reserved if a client returns a | ||
118 | * decline message */ | ||
119 | unsigned long conflict_time; /* how long an arp conflict offender is leased for */ | ||
120 | unsigned long offer_time; /* how long an offered address is reserved */ | ||
121 | unsigned long min_lease; /* minimum lease a client can request*/ | ||
122 | char *lease_file; | ||
123 | char *pidfile; | ||
124 | char *notify_file; /* What to run whenever leases are written */ | ||
125 | uint32_t siaddr; /* next server bootp option */ | ||
126 | char *sname; /* bootp server name */ | ||
127 | char *boot_file; /* bootp boot file option */ | ||
128 | struct static_lease *static_leases; /* List of ip/mac pairs to assign static leases */ | ||
129 | }; | ||
130 | |||
131 | extern struct server_config_t server_config; | ||
132 | extern struct dhcpOfferedAddr *leases; | ||
133 | |||
134 | |||
135 | /*** leases.h ***/ | ||
136 | |||
137 | struct dhcpOfferedAddr { | ||
138 | uint8_t chaddr[16]; | ||
139 | uint32_t yiaddr; /* network order */ | ||
140 | uint32_t expires; /* host order */ | ||
141 | }; | ||
142 | |||
143 | extern uint8_t blank_chaddr[]; | ||
144 | |||
145 | void clear_lease(uint8_t *chaddr, uint32_t yiaddr); | ||
146 | struct dhcpOfferedAddr *add_lease(uint8_t *chaddr, uint32_t yiaddr, unsigned long lease); | ||
147 | int lease_expired(struct dhcpOfferedAddr *lease); | ||
148 | struct dhcpOfferedAddr *oldest_expired_lease(void); | ||
149 | struct dhcpOfferedAddr *find_lease_by_chaddr(uint8_t *chaddr); | ||
150 | struct dhcpOfferedAddr *find_lease_by_yiaddr(uint32_t yiaddr); | ||
151 | uint32_t find_address(int check_expired); | ||
152 | |||
153 | |||
154 | /*** static_leases.h ***/ | ||
155 | |||
156 | /* Config file will pass static lease info to this function which will add it | ||
157 | * to a data structure that can be searched later */ | ||
158 | int addStaticLease(struct static_lease **lease_struct, uint8_t *mac, uint32_t *ip); | ||
159 | /* Check to see if a mac has an associated static lease */ | ||
160 | uint32_t getIpByMac(struct static_lease *lease_struct, void *arg); | ||
161 | /* Check to see if an ip is reserved as a static ip */ | ||
162 | uint32_t reservedIp(struct static_lease *lease_struct, uint32_t ip); | ||
163 | /* Print out static leases just to check what's going on (debug code) */ | ||
164 | void printStaticLeases(struct static_lease **lease_struct); | ||
165 | |||
166 | |||
167 | /*** serverpacket.h ***/ | ||
168 | |||
169 | int sendOffer(struct dhcpMessage *oldpacket); | ||
170 | int sendNAK(struct dhcpMessage *oldpacket); | ||
171 | int sendACK(struct dhcpMessage *oldpacket, uint32_t yiaddr); | ||
172 | int send_inform(struct dhcpMessage *oldpacket); | ||
173 | |||
174 | |||
175 | /*** files.h ***/ | ||
176 | |||
177 | struct config_keyword { | ||
178 | const char *keyword; | ||
179 | int (* const handler)(const char *line, void *var); | ||
180 | void *var; | ||
181 | const char *def; | ||
182 | }; | ||
183 | |||
184 | int read_config(const char *file); | ||
185 | void write_leases(void); | ||
186 | void read_leases(const char *file); | ||
187 | struct option_set *find_option(struct option_set *opt_list, char code); | ||
188 | |||
189 | |||
190 | #endif | ||
diff --git a/networking/udhcp/dhcprelay.c b/networking/udhcp/dhcprelay.c new file mode 100644 index 000000000..e3a816886 --- /dev/null +++ b/networking/udhcp/dhcprelay.c | |||
@@ -0,0 +1,340 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* Port to Busybox Copyright (C) 2006 Jesse Dutton <jessedutton@gmail.com> | ||
3 | * | ||
4 | * Licensed under GPL v2, see file LICENSE in this tarball for details. | ||
5 | * | ||
6 | * DHCP Relay for 'DHCPv4 Configuration of IPSec Tunnel Mode' support | ||
7 | * Copyright (C) 2002 Mario Strasser <mast@gmx.net>, | ||
8 | * Zuercher Hochschule Winterthur, | ||
9 | * Netbeat AG | ||
10 | * Upstream has GPL v2 or later | ||
11 | */ | ||
12 | |||
13 | #include "common.h" | ||
14 | #include "dhcpd.h" | ||
15 | #include "options.h" | ||
16 | |||
17 | /* constants */ | ||
18 | #define SELECT_TIMEOUT 5 /* select timeout in sec. */ | ||
19 | #define MAX_LIFETIME 2*60 /* lifetime of an xid entry in sec. */ | ||
20 | #define MAX_INTERFACES 9 | ||
21 | |||
22 | |||
23 | /* This list holds information about clients. The xid_* functions manipulate this list. */ | ||
24 | static struct xid_item { | ||
25 | u_int32_t xid; | ||
26 | struct sockaddr_in ip; | ||
27 | int client; | ||
28 | time_t timestamp; | ||
29 | struct xid_item *next; | ||
30 | } dhcprelay_xid_list = {0, {0}, 0, 0, NULL}; | ||
31 | |||
32 | |||
33 | static struct xid_item * xid_add(u_int32_t xid, struct sockaddr_in *ip, int client) | ||
34 | { | ||
35 | struct xid_item *item; | ||
36 | |||
37 | /* create new xid entry */ | ||
38 | item = xmalloc(sizeof(struct xid_item)); | ||
39 | |||
40 | /* add xid entry */ | ||
41 | item->ip = *ip; | ||
42 | item->xid = xid; | ||
43 | item->client = client; | ||
44 | item->timestamp = time(NULL); | ||
45 | item->next = dhcprelay_xid_list.next; | ||
46 | dhcprelay_xid_list.next = item; | ||
47 | |||
48 | return item; | ||
49 | } | ||
50 | |||
51 | |||
52 | static void xid_expire(void) | ||
53 | { | ||
54 | struct xid_item *item = dhcprelay_xid_list.next; | ||
55 | struct xid_item *last = &dhcprelay_xid_list; | ||
56 | time_t current_time = time(NULL); | ||
57 | |||
58 | while (item != NULL) { | ||
59 | if ((current_time-item->timestamp) > MAX_LIFETIME) { | ||
60 | last->next = item->next; | ||
61 | free(item); | ||
62 | item = last->next; | ||
63 | } else { | ||
64 | last = item; | ||
65 | item = item->next; | ||
66 | } | ||
67 | } | ||
68 | } | ||
69 | |||
70 | static struct xid_item * xid_find(u_int32_t xid) | ||
71 | { | ||
72 | struct xid_item *item = dhcprelay_xid_list.next; | ||
73 | while (item != NULL) { | ||
74 | if (item->xid == xid) { | ||
75 | return item; | ||
76 | } | ||
77 | item = item->next; | ||
78 | } | ||
79 | return NULL; | ||
80 | } | ||
81 | |||
82 | static void xid_del(u_int32_t xid) | ||
83 | { | ||
84 | struct xid_item *item = dhcprelay_xid_list.next; | ||
85 | struct xid_item *last = &dhcprelay_xid_list; | ||
86 | while (item != NULL) { | ||
87 | if (item->xid == xid) { | ||
88 | last->next = item->next; | ||
89 | free(item); | ||
90 | item = last->next; | ||
91 | } else { | ||
92 | last = item; | ||
93 | item = item->next; | ||
94 | } | ||
95 | } | ||
96 | } | ||
97 | |||
98 | |||
99 | /** | ||
100 | * get_dhcp_packet_type - gets the message type of a dhcp packet | ||
101 | * p - pointer to the dhcp packet | ||
102 | * returns the message type on success, -1 otherwise | ||
103 | */ | ||
104 | static int get_dhcp_packet_type(struct dhcpMessage *p) | ||
105 | { | ||
106 | u_char *op; | ||
107 | |||
108 | /* it must be either a BOOTREQUEST or a BOOTREPLY */ | ||
109 | if (p->op != BOOTREQUEST && p->op != BOOTREPLY) | ||
110 | return -1; | ||
111 | /* get message type option */ | ||
112 | op = get_option(p, DHCP_MESSAGE_TYPE); | ||
113 | if (op != NULL) | ||
114 | return op[0]; | ||
115 | return -1; | ||
116 | } | ||
117 | |||
118 | /** | ||
119 | * signal_handler - handles signals ;-) | ||
120 | * sig - sent signal | ||
121 | */ | ||
122 | static int dhcprelay_stopflag; | ||
123 | static void dhcprelay_signal_handler(int sig) | ||
124 | { | ||
125 | dhcprelay_stopflag = 1; | ||
126 | } | ||
127 | |||
128 | /** | ||
129 | * get_client_devices - parses the devices list | ||
130 | * dev_list - comma separated list of devices | ||
131 | * returns array | ||
132 | */ | ||
133 | static char ** get_client_devices(char *dev_list, int *client_number) | ||
134 | { | ||
135 | char *s, *list, **client_dev; | ||
136 | int i, cn; | ||
137 | |||
138 | /* copy list */ | ||
139 | list = xstrdup(dev_list); | ||
140 | if (list == NULL) return NULL; | ||
141 | |||
142 | /* get number of items */ | ||
143 | for (s = dev_list, cn = 1; *s; s++) | ||
144 | if (*s == ',') | ||
145 | cn++; | ||
146 | |||
147 | client_dev = xzalloc(cn * sizeof(*client_dev)); | ||
148 | |||
149 | /* parse list */ | ||
150 | s = strtok(list, ","); | ||
151 | i = 0; | ||
152 | while (s != NULL) { | ||
153 | client_dev[i++] = xstrdup(s); | ||
154 | s = strtok(NULL, ","); | ||
155 | } | ||
156 | |||
157 | /* free copy and exit */ | ||
158 | free(list); | ||
159 | *client_number = cn; | ||
160 | return client_dev; | ||
161 | } | ||
162 | |||
163 | |||
164 | /* Creates listen sockets (in fds) and returns the number allocated. */ | ||
165 | static int init_sockets(char **client, int num_clients, | ||
166 | char *server, int *fds, int *max_socket) | ||
167 | { | ||
168 | int i; | ||
169 | |||
170 | /* talk to real server on bootps */ | ||
171 | fds[0] = listen_socket(htonl(INADDR_ANY), 67, server); | ||
172 | if (fds[0] < 0) return -1; | ||
173 | *max_socket = fds[0]; | ||
174 | |||
175 | /* array starts at 1 since server is 0 */ | ||
176 | num_clients++; | ||
177 | |||
178 | for (i=1; i < num_clients; i++) { | ||
179 | /* listen for clients on bootps */ | ||
180 | fds[i] = listen_socket(htonl(INADDR_ANY), 67, client[i-1]); | ||
181 | if (fds[i] < 0) return -1; | ||
182 | if (fds[i] > *max_socket) *max_socket = fds[i]; | ||
183 | } | ||
184 | |||
185 | return i; | ||
186 | } | ||
187 | |||
188 | |||
189 | /** | ||
190 | * pass_on() - forwards dhcp packets from client to server | ||
191 | * p - packet to send | ||
192 | * client - number of the client | ||
193 | */ | ||
194 | static void pass_on(struct dhcpMessage *p, int packet_len, int client, int *fds, | ||
195 | struct sockaddr_in *client_addr, struct sockaddr_in *server_addr) | ||
196 | { | ||
197 | int res, type; | ||
198 | struct xid_item *item; | ||
199 | |||
200 | /* check packet_type */ | ||
201 | type = get_dhcp_packet_type(p); | ||
202 | if (type != DHCPDISCOVER && type != DHCPREQUEST | ||
203 | && type != DHCPDECLINE && type != DHCPRELEASE | ||
204 | && type != DHCPINFORM | ||
205 | ) { | ||
206 | return; | ||
207 | } | ||
208 | |||
209 | /* create new xid entry */ | ||
210 | item = xid_add(p->xid, client_addr, client); | ||
211 | |||
212 | /* forward request to LAN (server) */ | ||
213 | res = sendto(fds[0], p, packet_len, 0, (struct sockaddr*)server_addr, | ||
214 | sizeof(struct sockaddr_in)); | ||
215 | if (res != packet_len) { | ||
216 | bb_perror_msg("pass_on"); | ||
217 | return; | ||
218 | } | ||
219 | } | ||
220 | |||
221 | /** | ||
222 | * pass_back() - forwards dhcp packets from server to client | ||
223 | * p - packet to send | ||
224 | */ | ||
225 | static void pass_back(struct dhcpMessage *p, int packet_len, int *fds) | ||
226 | { | ||
227 | int res, type; | ||
228 | struct xid_item *item; | ||
229 | |||
230 | /* check xid */ | ||
231 | item = xid_find(p->xid); | ||
232 | if (!item) { | ||
233 | return; | ||
234 | } | ||
235 | |||
236 | /* check packet type */ | ||
237 | type = get_dhcp_packet_type(p); | ||
238 | if (type != DHCPOFFER && type != DHCPACK && type != DHCPNAK) { | ||
239 | return; | ||
240 | } | ||
241 | |||
242 | if (item->ip.sin_addr.s_addr == htonl(INADDR_ANY)) | ||
243 | item->ip.sin_addr.s_addr = htonl(INADDR_BROADCAST); | ||
244 | if (item->client > MAX_INTERFACES) | ||
245 | return; | ||
246 | res = sendto(fds[item->client], p, packet_len, 0, (struct sockaddr*)(&item->ip), | ||
247 | sizeof(item->ip)); | ||
248 | if (res != packet_len) { | ||
249 | bb_perror_msg("pass_back"); | ||
250 | return; | ||
251 | } | ||
252 | |||
253 | /* remove xid entry */ | ||
254 | xid_del(p->xid); | ||
255 | } | ||
256 | |||
257 | static void dhcprelay_loop(int *fds, int num_sockets, int max_socket, char **clients, | ||
258 | struct sockaddr_in *server_addr, uint32_t gw_ip) | ||
259 | { | ||
260 | struct dhcpMessage dhcp_msg; | ||
261 | fd_set rfds; | ||
262 | size_t packlen, addr_size; | ||
263 | struct sockaddr_in client_addr; | ||
264 | struct timeval tv; | ||
265 | int i; | ||
266 | |||
267 | while (!dhcprelay_stopflag) { | ||
268 | FD_ZERO(&rfds); | ||
269 | for (i = 0; i < num_sockets; i++) | ||
270 | FD_SET(fds[i], &rfds); | ||
271 | tv.tv_sec = SELECT_TIMEOUT; | ||
272 | tv.tv_usec = 0; | ||
273 | if (select(max_socket + 1, &rfds, NULL, NULL, &tv) > 0) { | ||
274 | /* server */ | ||
275 | if (FD_ISSET(fds[0], &rfds)) { | ||
276 | packlen = udhcp_get_packet(&dhcp_msg, fds[0]); | ||
277 | if (packlen > 0) { | ||
278 | pass_back(&dhcp_msg, packlen, fds); | ||
279 | } | ||
280 | } | ||
281 | for (i = 1; i < num_sockets; i++) { | ||
282 | /* clients */ | ||
283 | if (!FD_ISSET(fds[i], &rfds)) | ||
284 | continue; | ||
285 | addr_size = sizeof(struct sockaddr_in); | ||
286 | packlen = recvfrom(fds[i], &dhcp_msg, sizeof(dhcp_msg), 0, | ||
287 | (struct sockaddr *)(&client_addr), &addr_size); | ||
288 | if (packlen <= 0) | ||
289 | continue; | ||
290 | if (read_interface(clients[i-1], NULL, &dhcp_msg.giaddr, NULL) < 0) | ||
291 | dhcp_msg.giaddr = gw_ip; | ||
292 | pass_on(&dhcp_msg, packlen, i, fds, &client_addr, server_addr); | ||
293 | } | ||
294 | } | ||
295 | xid_expire(); | ||
296 | } | ||
297 | } | ||
298 | |||
299 | int dhcprelay_main(int argc, char **argv) | ||
300 | { | ||
301 | int i, num_sockets, max_socket, fds[MAX_INTERFACES]; | ||
302 | uint32_t gw_ip; | ||
303 | char **clients; | ||
304 | struct sockaddr_in server_addr; | ||
305 | |||
306 | server_addr.sin_family = AF_INET; | ||
307 | server_addr.sin_port = htons(67); | ||
308 | if (argc == 4) { | ||
309 | if (!inet_aton(argv[3], &server_addr.sin_addr)) | ||
310 | bb_perror_msg_and_die("didn't grok server"); | ||
311 | } else if (argc == 3) { | ||
312 | server_addr.sin_addr.s_addr = htonl(INADDR_BROADCAST); | ||
313 | } else { | ||
314 | bb_show_usage(); | ||
315 | } | ||
316 | clients = get_client_devices(argv[1], &num_sockets); | ||
317 | if (!clients) return 0; | ||
318 | |||
319 | signal(SIGTERM, dhcprelay_signal_handler); | ||
320 | signal(SIGQUIT, dhcprelay_signal_handler); | ||
321 | signal(SIGINT, dhcprelay_signal_handler); | ||
322 | |||
323 | num_sockets = init_sockets(clients, num_sockets, argv[2], fds, &max_socket); | ||
324 | if (num_sockets == -1) | ||
325 | bb_perror_msg_and_die("init_sockets() failed"); | ||
326 | |||
327 | if (read_interface(argv[2], NULL, &gw_ip, NULL) == -1) | ||
328 | return 1; | ||
329 | |||
330 | dhcprelay_loop(fds, num_sockets, max_socket, clients, &server_addr, gw_ip); | ||
331 | |||
332 | if (ENABLE_FEATURE_CLEAN_UP) { | ||
333 | for (i = 0; i < num_sockets; i++) { | ||
334 | close(fds[i]); | ||
335 | free(clients[i]); | ||
336 | } | ||
337 | } | ||
338 | |||
339 | return 0; | ||
340 | } | ||
diff --git a/networking/udhcp/dumpleases.c b/networking/udhcp/dumpleases.c new file mode 100644 index 000000000..a0e81bb13 --- /dev/null +++ b/networking/udhcp/dumpleases.c | |||
@@ -0,0 +1,74 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * Licensed under the GPL v2 or later, see the file LICENSE in this tarball. | ||
4 | */ | ||
5 | #include <getopt.h> | ||
6 | |||
7 | #include "common.h" | ||
8 | #include "dhcpd.h" | ||
9 | |||
10 | |||
11 | #define REMAINING 0 | ||
12 | #define ABSOLUTE 1 | ||
13 | |||
14 | int dumpleases_main(int argc, char *argv[]) | ||
15 | { | ||
16 | int fp; | ||
17 | int i, c, mode = REMAINING; | ||
18 | unsigned long expires; | ||
19 | const char *file = LEASES_FILE; | ||
20 | struct dhcpOfferedAddr lease; | ||
21 | struct in_addr addr; | ||
22 | |||
23 | static const struct option options[] = { | ||
24 | {"absolute", 0, 0, 'a'}, | ||
25 | {"remaining", 0, 0, 'r'}, | ||
26 | {"file", 1, 0, 'f'}, | ||
27 | {0, 0, 0, 0} | ||
28 | }; | ||
29 | |||
30 | while (1) { | ||
31 | int option_index = 0; | ||
32 | c = getopt_long(argc, argv, "arf:", options, &option_index); | ||
33 | if (c == -1) break; | ||
34 | |||
35 | switch (c) { | ||
36 | case 'a': mode = ABSOLUTE; break; | ||
37 | case 'r': mode = REMAINING; break; | ||
38 | case 'f': | ||
39 | file = optarg; | ||
40 | break; | ||
41 | default: | ||
42 | bb_show_usage(); | ||
43 | } | ||
44 | } | ||
45 | |||
46 | fp = xopen(file, O_RDONLY); | ||
47 | |||
48 | printf("Mac Address IP-Address Expires %s\n", mode == REMAINING ? "in" : "at"); | ||
49 | /* "00:00:00:00:00:00 255.255.255.255 Wed Jun 30 21:49:08 1993" */ | ||
50 | while (full_read(fp, &lease, sizeof(lease)) == sizeof(lease)) { | ||
51 | printf(":%02x"+1, lease.chaddr[0]); | ||
52 | for (i = 1; i < 6; i++) { | ||
53 | printf(":%02x", lease.chaddr[i]); | ||
54 | } | ||
55 | addr.s_addr = lease.yiaddr; | ||
56 | printf(" %-15s ", inet_ntoa(addr)); | ||
57 | expires = ntohl(lease.expires); | ||
58 | if (mode == REMAINING) { | ||
59 | if (!expires) | ||
60 | printf("expired\n"); | ||
61 | else { | ||
62 | unsigned d, h, m; | ||
63 | d = expires / (24*60*60); expires %= (24*60*60); | ||
64 | h = expires / (60*60); expires %= (60*60); | ||
65 | m = expires / 60; expires %= 60; | ||
66 | if (d) printf("%u days ", d); | ||
67 | printf("%02u:%02u:%02u\n", h, m, (unsigned)expires); | ||
68 | } | ||
69 | } else fputs(ctime(&expires), stdout); | ||
70 | } | ||
71 | /* close(fp); */ | ||
72 | |||
73 | return 0; | ||
74 | } | ||
diff --git a/networking/udhcp/files.c b/networking/udhcp/files.c new file mode 100644 index 000000000..5e399e1f8 --- /dev/null +++ b/networking/udhcp/files.c | |||
@@ -0,0 +1,395 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * files.c -- DHCP server file manipulation * | ||
4 | * Rewrite by Russ Dill <Russ.Dill@asu.edu> July 2001 | ||
5 | */ | ||
6 | |||
7 | #include <netinet/ether.h> | ||
8 | |||
9 | #include "common.h" | ||
10 | #include "dhcpd.h" | ||
11 | #include "options.h" | ||
12 | |||
13 | |||
14 | /* | ||
15 | * Domain names may have 254 chars, and string options can be 254 | ||
16 | * chars long. However, 80 bytes will be enough for most, and won't | ||
17 | * hog up memory. If you have a special application, change it | ||
18 | */ | ||
19 | #define READ_CONFIG_BUF_SIZE 80 | ||
20 | |||
21 | /* on these functions, make sure you datatype matches */ | ||
22 | static int read_ip(const char *line, void *arg) | ||
23 | { | ||
24 | struct in_addr *addr = arg; | ||
25 | struct hostent *host; | ||
26 | int retval = 1; | ||
27 | |||
28 | if (!inet_aton(line, addr)) { | ||
29 | host = gethostbyname(line); | ||
30 | if (host) | ||
31 | addr->s_addr = *((unsigned long *) host->h_addr_list[0]); | ||
32 | else retval = 0; | ||
33 | } | ||
34 | return retval; | ||
35 | } | ||
36 | |||
37 | static int read_mac(const char *line, void *arg) | ||
38 | { | ||
39 | uint8_t *mac_bytes = arg; | ||
40 | struct ether_addr *temp_ether_addr; | ||
41 | int retval = 1; | ||
42 | |||
43 | temp_ether_addr = ether_aton(line); | ||
44 | |||
45 | if (temp_ether_addr == NULL) | ||
46 | retval = 0; | ||
47 | else | ||
48 | memcpy(mac_bytes, temp_ether_addr, 6); | ||
49 | |||
50 | return retval; | ||
51 | } | ||
52 | |||
53 | |||
54 | static int read_str(const char *line, void *arg) | ||
55 | { | ||
56 | char **dest = arg; | ||
57 | |||
58 | free(*dest); | ||
59 | *dest = strdup(line); | ||
60 | |||
61 | return 1; | ||
62 | } | ||
63 | |||
64 | |||
65 | static int read_u32(const char *line, void *arg) | ||
66 | { | ||
67 | *((uint32_t*)arg) = bb_strtou32(line, NULL, 10); | ||
68 | return errno == 0; | ||
69 | } | ||
70 | |||
71 | |||
72 | static int read_yn(const char *line, void *arg) | ||
73 | { | ||
74 | char *dest = arg; | ||
75 | int retval = 1; | ||
76 | |||
77 | if (!strcasecmp("yes", line)) | ||
78 | *dest = 1; | ||
79 | else if (!strcasecmp("no", line)) | ||
80 | *dest = 0; | ||
81 | else retval = 0; | ||
82 | |||
83 | return retval; | ||
84 | } | ||
85 | |||
86 | |||
87 | /* find option 'code' in opt_list */ | ||
88 | struct option_set *find_option(struct option_set *opt_list, char code) | ||
89 | { | ||
90 | while (opt_list && opt_list->data[OPT_CODE] < code) | ||
91 | opt_list = opt_list->next; | ||
92 | |||
93 | if (opt_list && opt_list->data[OPT_CODE] == code) return opt_list; | ||
94 | else return NULL; | ||
95 | } | ||
96 | |||
97 | |||
98 | /* add an option to the opt_list */ | ||
99 | static void attach_option(struct option_set **opt_list, | ||
100 | const struct dhcp_option *option, char *buffer, int length) | ||
101 | { | ||
102 | struct option_set *existing, *new, **curr; | ||
103 | |||
104 | /* add it to an existing option */ | ||
105 | existing = find_option(*opt_list, option->code); | ||
106 | if (existing) { | ||
107 | DEBUG("Attaching option %s to existing member of list", option->name); | ||
108 | if (option->flags & OPTION_LIST) { | ||
109 | if (existing->data[OPT_LEN] + length <= 255) { | ||
110 | existing->data = realloc(existing->data, | ||
111 | existing->data[OPT_LEN] + length + 2); | ||
112 | memcpy(existing->data + existing->data[OPT_LEN] + 2, buffer, length); | ||
113 | existing->data[OPT_LEN] += length; | ||
114 | } /* else, ignore the data, we could put this in a second option in the future */ | ||
115 | } /* else, ignore the new data */ | ||
116 | } else { | ||
117 | DEBUG("Attaching option %s to list", option->name); | ||
118 | |||
119 | /* make a new option */ | ||
120 | new = xmalloc(sizeof(struct option_set)); | ||
121 | new->data = xmalloc(length + 2); | ||
122 | new->data[OPT_CODE] = option->code; | ||
123 | new->data[OPT_LEN] = length; | ||
124 | memcpy(new->data + 2, buffer, length); | ||
125 | |||
126 | curr = opt_list; | ||
127 | while (*curr && (*curr)->data[OPT_CODE] < option->code) | ||
128 | curr = &(*curr)->next; | ||
129 | |||
130 | new->next = *curr; | ||
131 | *curr = new; | ||
132 | } | ||
133 | } | ||
134 | |||
135 | |||
136 | /* read a dhcp option and add it to opt_list */ | ||
137 | static int read_opt(const char *const_line, void *arg) | ||
138 | { | ||
139 | struct option_set **opt_list = arg; | ||
140 | char *opt, *val, *endptr; | ||
141 | const struct dhcp_option *option; | ||
142 | int retval = 0, length; | ||
143 | char buffer[8]; | ||
144 | char *line; | ||
145 | uint16_t *result_u16 = (uint16_t *) buffer; | ||
146 | uint32_t *result_u32 = (uint32_t *) buffer; | ||
147 | |||
148 | /* Cheat, the only const line we'll actually get is "" */ | ||
149 | line = (char *) const_line; | ||
150 | opt = strtok(line, " \t="); | ||
151 | if (!opt) return 0; | ||
152 | |||
153 | option = dhcp_options; | ||
154 | while (1) { | ||
155 | if (!option->code) | ||
156 | return 0; | ||
157 | if (!strcasecmp(option->name, opt)) | ||
158 | break; | ||
159 | option++; | ||
160 | } | ||
161 | |||
162 | do { | ||
163 | val = strtok(NULL, ", \t"); | ||
164 | if (!val) break; | ||
165 | length = option_lengths[option->flags & TYPE_MASK]; | ||
166 | retval = 0; | ||
167 | opt = buffer; /* new meaning for variable opt */ | ||
168 | switch (option->flags & TYPE_MASK) { | ||
169 | case OPTION_IP: | ||
170 | retval = read_ip(val, buffer); | ||
171 | break; | ||
172 | case OPTION_IP_PAIR: | ||
173 | retval = read_ip(val, buffer); | ||
174 | if (!(val = strtok(NULL, ", \t/-"))) retval = 0; | ||
175 | if (retval) retval = read_ip(val, buffer + 4); | ||
176 | break; | ||
177 | case OPTION_STRING: | ||
178 | length = strlen(val); | ||
179 | if (length > 0) { | ||
180 | if (length > 254) length = 254; | ||
181 | opt = val; | ||
182 | retval = 1; | ||
183 | } | ||
184 | break; | ||
185 | case OPTION_BOOLEAN: | ||
186 | retval = read_yn(val, buffer); | ||
187 | break; | ||
188 | case OPTION_U8: | ||
189 | buffer[0] = strtoul(val, &endptr, 0); | ||
190 | retval = (endptr[0] == '\0'); | ||
191 | break; | ||
192 | case OPTION_U16: | ||
193 | *result_u16 = htons(strtoul(val, &endptr, 0)); | ||
194 | retval = (endptr[0] == '\0'); | ||
195 | break; | ||
196 | case OPTION_S16: | ||
197 | *result_u16 = htons(strtol(val, &endptr, 0)); | ||
198 | retval = (endptr[0] == '\0'); | ||
199 | break; | ||
200 | case OPTION_U32: | ||
201 | *result_u32 = htonl(strtoul(val, &endptr, 0)); | ||
202 | retval = (endptr[0] == '\0'); | ||
203 | break; | ||
204 | case OPTION_S32: | ||
205 | *result_u32 = htonl(strtol(val, &endptr, 0)); | ||
206 | retval = (endptr[0] == '\0'); | ||
207 | break; | ||
208 | default: | ||
209 | break; | ||
210 | } | ||
211 | if (retval) | ||
212 | attach_option(opt_list, option, opt, length); | ||
213 | } while (retval && option->flags & OPTION_LIST); | ||
214 | return retval; | ||
215 | } | ||
216 | |||
217 | static int read_staticlease(const char *const_line, void *arg) | ||
218 | { | ||
219 | char *line; | ||
220 | char *mac_string; | ||
221 | char *ip_string; | ||
222 | uint8_t *mac_bytes; | ||
223 | uint32_t *ip; | ||
224 | |||
225 | |||
226 | /* Allocate memory for addresses */ | ||
227 | mac_bytes = xmalloc(sizeof(unsigned char) * 8); | ||
228 | ip = xmalloc(sizeof(uint32_t)); | ||
229 | |||
230 | /* Read mac */ | ||
231 | line = (char *) const_line; | ||
232 | mac_string = strtok(line, " \t"); | ||
233 | read_mac(mac_string, mac_bytes); | ||
234 | |||
235 | /* Read ip */ | ||
236 | ip_string = strtok(NULL, " \t"); | ||
237 | read_ip(ip_string, ip); | ||
238 | |||
239 | addStaticLease(arg, mac_bytes, ip); | ||
240 | |||
241 | if (ENABLE_FEATURE_UDHCP_DEBUG) printStaticLeases(arg); | ||
242 | |||
243 | return 1; | ||
244 | } | ||
245 | |||
246 | |||
247 | static const struct config_keyword keywords[] = { | ||
248 | /* keyword handler variable address default */ | ||
249 | {"start", read_ip, &(server_config.start), "192.168.0.20"}, | ||
250 | {"end", read_ip, &(server_config.end), "192.168.0.254"}, | ||
251 | {"interface", read_str, &(server_config.interface), "eth0"}, | ||
252 | {"option", read_opt, &(server_config.options), ""}, | ||
253 | {"opt", read_opt, &(server_config.options), ""}, | ||
254 | {"max_leases", read_u32, &(server_config.max_leases), "254"}, | ||
255 | {"remaining", read_yn, &(server_config.remaining), "yes"}, | ||
256 | {"auto_time", read_u32, &(server_config.auto_time), "7200"}, | ||
257 | {"decline_time",read_u32, &(server_config.decline_time),"3600"}, | ||
258 | {"conflict_time",read_u32,&(server_config.conflict_time),"3600"}, | ||
259 | {"offer_time", read_u32, &(server_config.offer_time), "60"}, | ||
260 | {"min_lease", read_u32, &(server_config.min_lease), "60"}, | ||
261 | {"lease_file", read_str, &(server_config.lease_file), LEASES_FILE}, | ||
262 | {"pidfile", read_str, &(server_config.pidfile), "/var/run/udhcpd.pid"}, | ||
263 | {"notify_file", read_str, &(server_config.notify_file), ""}, | ||
264 | {"siaddr", read_ip, &(server_config.siaddr), "0.0.0.0"}, | ||
265 | {"sname", read_str, &(server_config.sname), ""}, | ||
266 | {"boot_file", read_str, &(server_config.boot_file), ""}, | ||
267 | {"static_lease",read_staticlease, &(server_config.static_leases), ""}, | ||
268 | /*ADDME: static lease */ | ||
269 | {"", NULL, NULL, ""} | ||
270 | }; | ||
271 | |||
272 | |||
273 | int read_config(const char *file) | ||
274 | { | ||
275 | FILE *in; | ||
276 | char buffer[READ_CONFIG_BUF_SIZE], *token, *line; | ||
277 | int i, lm = 0; | ||
278 | |||
279 | for (i = 0; keywords[i].keyword[0]; i++) | ||
280 | if (keywords[i].def[0]) | ||
281 | keywords[i].handler(keywords[i].def, keywords[i].var); | ||
282 | |||
283 | in = fopen(file, "r"); | ||
284 | if (!in) { | ||
285 | bb_error_msg("cannot open config file: %s", file); | ||
286 | return 0; | ||
287 | } | ||
288 | |||
289 | while (fgets(buffer, READ_CONFIG_BUF_SIZE, in)) { | ||
290 | char debug_orig[READ_CONFIG_BUF_SIZE]; | ||
291 | char *p; | ||
292 | |||
293 | lm++; | ||
294 | p = strchr(buffer, '\n'); | ||
295 | if (p) *p = '\0'; | ||
296 | if (ENABLE_FEATURE_UDHCP_DEBUG) strcpy(debug_orig, buffer); | ||
297 | p = strchr(buffer, '#'); | ||
298 | if (p) *p = '\0'; | ||
299 | |||
300 | if (!(token = strtok(buffer, " \t"))) continue; | ||
301 | if (!(line = strtok(NULL, ""))) continue; | ||
302 | |||
303 | /* eat leading whitespace */ | ||
304 | line = skip_whitespace(line); | ||
305 | /* eat trailing whitespace */ | ||
306 | i = strlen(line) - 1; | ||
307 | while (i >= 0 && isspace(line[i])) | ||
308 | line[i--] = '\0'; | ||
309 | |||
310 | for (i = 0; keywords[i].keyword[0]; i++) | ||
311 | if (!strcasecmp(token, keywords[i].keyword)) | ||
312 | if (!keywords[i].handler(line, keywords[i].var)) { | ||
313 | bb_error_msg("cannot parse line %d of %s", lm, file); | ||
314 | if (ENABLE_FEATURE_UDHCP_DEBUG) | ||
315 | bb_error_msg("cannot parse '%s'", debug_orig); | ||
316 | /* reset back to the default value */ | ||
317 | keywords[i].handler(keywords[i].def, keywords[i].var); | ||
318 | } | ||
319 | } | ||
320 | fclose(in); | ||
321 | return 1; | ||
322 | } | ||
323 | |||
324 | |||
325 | void write_leases(void) | ||
326 | { | ||
327 | int fp; | ||
328 | unsigned i; | ||
329 | time_t curr = time(0); | ||
330 | unsigned long tmp_time; | ||
331 | |||
332 | fp = open(server_config.lease_file, O_WRONLY|O_CREAT|O_TRUNC, 0666); | ||
333 | if (fp < 0) { | ||
334 | bb_error_msg("cannot open %s for writing", server_config.lease_file); | ||
335 | return; | ||
336 | } | ||
337 | |||
338 | for (i = 0; i < server_config.max_leases; i++) { | ||
339 | if (leases[i].yiaddr != 0) { | ||
340 | |||
341 | /* screw with the time in the struct, for easier writing */ | ||
342 | tmp_time = leases[i].expires; | ||
343 | |||
344 | if (server_config.remaining) { | ||
345 | if (lease_expired(&(leases[i]))) | ||
346 | leases[i].expires = 0; | ||
347 | else leases[i].expires -= curr; | ||
348 | } /* else stick with the time we got */ | ||
349 | leases[i].expires = htonl(leases[i].expires); | ||
350 | // FIXME: error check?? | ||
351 | full_write(fp, &leases[i], sizeof(leases[i])); | ||
352 | |||
353 | /* then restore it when done */ | ||
354 | leases[i].expires = tmp_time; | ||
355 | } | ||
356 | } | ||
357 | close(fp); | ||
358 | |||
359 | if (server_config.notify_file) { | ||
360 | char *cmd = xasprintf("%s %s", server_config.notify_file, server_config.lease_file); | ||
361 | system(cmd); | ||
362 | free(cmd); | ||
363 | } | ||
364 | } | ||
365 | |||
366 | |||
367 | void read_leases(const char *file) | ||
368 | { | ||
369 | int fp; | ||
370 | unsigned int i = 0; | ||
371 | struct dhcpOfferedAddr lease; | ||
372 | |||
373 | fp = open(file, O_RDONLY); | ||
374 | if (fp < 0) { | ||
375 | bb_error_msg("cannot open %s for reading", file); | ||
376 | return; | ||
377 | } | ||
378 | |||
379 | while (i < server_config.max_leases | ||
380 | && full_read(fp, &lease, sizeof(lease)) == sizeof(lease) | ||
381 | ) { | ||
382 | /* ADDME: is it a static lease */ | ||
383 | if (lease.yiaddr >= server_config.start && lease.yiaddr <= server_config.end) { | ||
384 | lease.expires = ntohl(lease.expires); | ||
385 | if (!server_config.remaining) lease.expires -= time(0); | ||
386 | if (!(add_lease(lease.chaddr, lease.yiaddr, lease.expires))) { | ||
387 | bb_error_msg("too many leases while loading %s", file); | ||
388 | break; | ||
389 | } | ||
390 | i++; | ||
391 | } | ||
392 | } | ||
393 | DEBUG("Read %d leases", i); | ||
394 | close(fp); | ||
395 | } | ||
diff --git a/networking/udhcp/leases.c b/networking/udhcp/leases.c new file mode 100644 index 000000000..2f7847d74 --- /dev/null +++ b/networking/udhcp/leases.c | |||
@@ -0,0 +1,145 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * leases.c -- tools to manage DHCP leases | ||
4 | * Russ Dill <Russ.Dill@asu.edu> July 2001 | ||
5 | */ | ||
6 | |||
7 | #include "common.h" | ||
8 | #include "dhcpd.h" | ||
9 | |||
10 | |||
11 | uint8_t blank_chaddr[] = {[0 ... 15] = 0}; | ||
12 | |||
13 | /* clear every lease out that chaddr OR yiaddr matches and is nonzero */ | ||
14 | void clear_lease(uint8_t *chaddr, uint32_t yiaddr) | ||
15 | { | ||
16 | unsigned int i, j; | ||
17 | |||
18 | for (j = 0; j < 16 && !chaddr[j]; j++); | ||
19 | |||
20 | for (i = 0; i < server_config.max_leases; i++) | ||
21 | if ((j != 16 && !memcmp(leases[i].chaddr, chaddr, 16)) || | ||
22 | (yiaddr && leases[i].yiaddr == yiaddr)) { | ||
23 | memset(&(leases[i]), 0, sizeof(struct dhcpOfferedAddr)); | ||
24 | } | ||
25 | } | ||
26 | |||
27 | |||
28 | /* add a lease into the table, clearing out any old ones */ | ||
29 | struct dhcpOfferedAddr *add_lease(uint8_t *chaddr, uint32_t yiaddr, unsigned long lease) | ||
30 | { | ||
31 | struct dhcpOfferedAddr *oldest; | ||
32 | |||
33 | /* clean out any old ones */ | ||
34 | clear_lease(chaddr, yiaddr); | ||
35 | |||
36 | oldest = oldest_expired_lease(); | ||
37 | |||
38 | if (oldest) { | ||
39 | memcpy(oldest->chaddr, chaddr, 16); | ||
40 | oldest->yiaddr = yiaddr; | ||
41 | oldest->expires = time(0) + lease; | ||
42 | } | ||
43 | |||
44 | return oldest; | ||
45 | } | ||
46 | |||
47 | |||
48 | /* true if a lease has expired */ | ||
49 | int lease_expired(struct dhcpOfferedAddr *lease) | ||
50 | { | ||
51 | return (lease->expires < (unsigned long) time(0)); | ||
52 | } | ||
53 | |||
54 | |||
55 | /* Find the oldest expired lease, NULL if there are no expired leases */ | ||
56 | struct dhcpOfferedAddr *oldest_expired_lease(void) | ||
57 | { | ||
58 | struct dhcpOfferedAddr *oldest = NULL; | ||
59 | unsigned long oldest_lease = time(0); | ||
60 | unsigned int i; | ||
61 | |||
62 | |||
63 | for (i = 0; i < server_config.max_leases; i++) | ||
64 | if (oldest_lease > leases[i].expires) { | ||
65 | oldest_lease = leases[i].expires; | ||
66 | oldest = &(leases[i]); | ||
67 | } | ||
68 | return oldest; | ||
69 | |||
70 | } | ||
71 | |||
72 | |||
73 | /* Find the first lease that matches chaddr, NULL if no match */ | ||
74 | struct dhcpOfferedAddr *find_lease_by_chaddr(uint8_t *chaddr) | ||
75 | { | ||
76 | unsigned int i; | ||
77 | |||
78 | for (i = 0; i < server_config.max_leases; i++) | ||
79 | if (!memcmp(leases[i].chaddr, chaddr, 16)) return &(leases[i]); | ||
80 | |||
81 | return NULL; | ||
82 | } | ||
83 | |||
84 | |||
85 | /* Find the first lease that matches yiaddr, NULL is no match */ | ||
86 | struct dhcpOfferedAddr *find_lease_by_yiaddr(uint32_t yiaddr) | ||
87 | { | ||
88 | unsigned int i; | ||
89 | |||
90 | for (i = 0; i < server_config.max_leases; i++) | ||
91 | if (leases[i].yiaddr == yiaddr) return &(leases[i]); | ||
92 | |||
93 | return NULL; | ||
94 | } | ||
95 | |||
96 | |||
97 | /* check is an IP is taken, if it is, add it to the lease table */ | ||
98 | static int check_ip(uint32_t addr) | ||
99 | { | ||
100 | struct in_addr temp; | ||
101 | |||
102 | if (arpping(addr, server_config.server, server_config.arp, server_config.interface) == 0) { | ||
103 | temp.s_addr = addr; | ||
104 | bb_info_msg("%s belongs to someone, reserving it for %ld seconds", | ||
105 | inet_ntoa(temp), server_config.conflict_time); | ||
106 | add_lease(blank_chaddr, addr, server_config.conflict_time); | ||
107 | return 1; | ||
108 | } else return 0; | ||
109 | } | ||
110 | |||
111 | |||
112 | /* find an assignable address, it check_expired is true, we check all the expired leases as well. | ||
113 | * Maybe this should try expired leases by age... */ | ||
114 | uint32_t find_address(int check_expired) | ||
115 | { | ||
116 | uint32_t addr, ret; | ||
117 | struct dhcpOfferedAddr *lease = NULL; | ||
118 | |||
119 | addr = ntohl(server_config.start); /* addr is in host order here */ | ||
120 | for (;addr <= ntohl(server_config.end); addr++) { | ||
121 | |||
122 | /* ie, 192.168.55.0 */ | ||
123 | if (!(addr & 0xFF)) continue; | ||
124 | |||
125 | /* ie, 192.168.55.255 */ | ||
126 | if ((addr & 0xFF) == 0xFF) continue; | ||
127 | |||
128 | /* Only do if it isn't an assigned as a static lease */ | ||
129 | if (!reservedIp(server_config.static_leases, htonl(addr))) { | ||
130 | |||
131 | /* lease is not taken */ | ||
132 | ret = htonl(addr); | ||
133 | lease = find_lease_by_yiaddr(ret); | ||
134 | |||
135 | /* no lease or it expired and we are checking for expired leases */ | ||
136 | if ( (!lease || (check_expired && lease_expired(lease))) | ||
137 | && /* and it isn't on the network */ !check_ip(ret) | ||
138 | ) { | ||
139 | return ret; | ||
140 | break; | ||
141 | } | ||
142 | } | ||
143 | } | ||
144 | return 0; | ||
145 | } | ||
diff --git a/networking/udhcp/options.c b/networking/udhcp/options.c new file mode 100644 index 000000000..4a46da579 --- /dev/null +++ b/networking/udhcp/options.c | |||
@@ -0,0 +1,174 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * options.c -- DHCP server option packet tools | ||
4 | * Rewrite by Russ Dill <Russ.Dill@asu.edu> July 2001 | ||
5 | */ | ||
6 | |||
7 | #include "common.h" | ||
8 | #include "dhcpd.h" | ||
9 | #include "options.h" | ||
10 | |||
11 | |||
12 | /* supported options are easily added here */ | ||
13 | const struct dhcp_option dhcp_options[] = { | ||
14 | /* name[10] flags code */ | ||
15 | {"subnet", OPTION_IP | OPTION_REQ, 0x01}, | ||
16 | {"timezone", OPTION_S32, 0x02}, | ||
17 | {"router", OPTION_IP | OPTION_LIST | OPTION_REQ, 0x03}, | ||
18 | {"timesvr", OPTION_IP | OPTION_LIST, 0x04}, | ||
19 | {"namesvr", OPTION_IP | OPTION_LIST, 0x05}, | ||
20 | {"dns", OPTION_IP | OPTION_LIST | OPTION_REQ, 0x06}, | ||
21 | {"logsvr", OPTION_IP | OPTION_LIST, 0x07}, | ||
22 | {"cookiesvr", OPTION_IP | OPTION_LIST, 0x08}, | ||
23 | {"lprsvr", OPTION_IP | OPTION_LIST, 0x09}, | ||
24 | {"hostname", OPTION_STRING | OPTION_REQ, 0x0c}, | ||
25 | {"bootsize", OPTION_U16, 0x0d}, | ||
26 | {"domain", OPTION_STRING | OPTION_REQ, 0x0f}, | ||
27 | {"swapsvr", OPTION_IP, 0x10}, | ||
28 | {"rootpath", OPTION_STRING, 0x11}, | ||
29 | {"ipttl", OPTION_U8, 0x17}, | ||
30 | {"mtu", OPTION_U16, 0x1a}, | ||
31 | {"broadcast", OPTION_IP | OPTION_REQ, 0x1c}, | ||
32 | {"nisdomain", OPTION_STRING | OPTION_REQ, 0x28}, | ||
33 | {"nissrv", OPTION_IP | OPTION_LIST | OPTION_REQ, 0x29}, | ||
34 | {"ntpsrv", OPTION_IP | OPTION_LIST | OPTION_REQ, 0x2a}, | ||
35 | {"wins", OPTION_IP | OPTION_LIST, 0x2c}, | ||
36 | {"requestip", OPTION_IP, 0x32}, | ||
37 | {"lease", OPTION_U32, 0x33}, | ||
38 | {"dhcptype", OPTION_U8, 0x35}, | ||
39 | {"serverid", OPTION_IP, 0x36}, | ||
40 | {"message", OPTION_STRING, 0x38}, | ||
41 | {"vendorclass", OPTION_STRING, 0x3C}, | ||
42 | {"clientid", OPTION_STRING, 0x3D}, | ||
43 | {"tftp", OPTION_STRING, 0x42}, | ||
44 | {"bootfile", OPTION_STRING, 0x43}, | ||
45 | {"userclass", OPTION_STRING, 0x4D}, | ||
46 | /* MSIE's "Web Proxy Autodiscovery Protocol" support */ | ||
47 | {"wpad", OPTION_STRING, 0xfc}, | ||
48 | {"", 0x00, 0x00} | ||
49 | }; | ||
50 | |||
51 | /* Lengths of the different option types */ | ||
52 | const unsigned char option_lengths[] = { | ||
53 | [OPTION_IP] = 4, | ||
54 | [OPTION_IP_PAIR] = 8, | ||
55 | [OPTION_BOOLEAN] = 1, | ||
56 | [OPTION_STRING] = 1, | ||
57 | [OPTION_U8] = 1, | ||
58 | [OPTION_U16] = 2, | ||
59 | [OPTION_S16] = 2, | ||
60 | [OPTION_U32] = 4, | ||
61 | [OPTION_S32] = 4 | ||
62 | }; | ||
63 | |||
64 | |||
65 | /* get an option with bounds checking (warning, not aligned). */ | ||
66 | uint8_t *get_option(struct dhcpMessage *packet, int code) | ||
67 | { | ||
68 | int i, length; | ||
69 | uint8_t *optionptr; | ||
70 | int over = 0, done = 0, curr = OPTION_FIELD; | ||
71 | |||
72 | optionptr = packet->options; | ||
73 | i = 0; | ||
74 | length = 308; | ||
75 | while (!done) { | ||
76 | if (i >= length) { | ||
77 | bb_error_msg("bogus packet, option fields too long"); | ||
78 | return NULL; | ||
79 | } | ||
80 | if (optionptr[i + OPT_CODE] == code) { | ||
81 | if (i + 1 + optionptr[i + OPT_LEN] >= length) { | ||
82 | bb_error_msg("bogus packet, option fields too long"); | ||
83 | return NULL; | ||
84 | } | ||
85 | return optionptr + i + 2; | ||
86 | } | ||
87 | switch (optionptr[i + OPT_CODE]) { | ||
88 | case DHCP_PADDING: | ||
89 | i++; | ||
90 | break; | ||
91 | case DHCP_OPTION_OVER: | ||
92 | if (i + 1 + optionptr[i + OPT_LEN] >= length) { | ||
93 | bb_error_msg("bogus packet, option fields too long"); | ||
94 | return NULL; | ||
95 | } | ||
96 | over = optionptr[i + 3]; | ||
97 | i += optionptr[OPT_LEN] + 2; | ||
98 | break; | ||
99 | case DHCP_END: | ||
100 | if (curr == OPTION_FIELD && over & FILE_FIELD) { | ||
101 | optionptr = packet->file; | ||
102 | i = 0; | ||
103 | length = 128; | ||
104 | curr = FILE_FIELD; | ||
105 | } else if (curr == FILE_FIELD && over & SNAME_FIELD) { | ||
106 | optionptr = packet->sname; | ||
107 | i = 0; | ||
108 | length = 64; | ||
109 | curr = SNAME_FIELD; | ||
110 | } else done = 1; | ||
111 | break; | ||
112 | default: | ||
113 | i += optionptr[OPT_LEN + i] + 2; | ||
114 | } | ||
115 | } | ||
116 | return NULL; | ||
117 | } | ||
118 | |||
119 | |||
120 | /* return the position of the 'end' option (no bounds checking) */ | ||
121 | int end_option(uint8_t *optionptr) | ||
122 | { | ||
123 | int i = 0; | ||
124 | |||
125 | while (optionptr[i] != DHCP_END) { | ||
126 | if (optionptr[i] == DHCP_PADDING) i++; | ||
127 | else i += optionptr[i + OPT_LEN] + 2; | ||
128 | } | ||
129 | return i; | ||
130 | } | ||
131 | |||
132 | |||
133 | /* add an option string to the options (an option string contains an option code, | ||
134 | * length, then data) */ | ||
135 | int add_option_string(uint8_t *optionptr, uint8_t *string) | ||
136 | { | ||
137 | int end = end_option(optionptr); | ||
138 | |||
139 | /* end position + string length + option code/length + end option */ | ||
140 | if (end + string[OPT_LEN] + 2 + 1 >= 308) { | ||
141 | bb_error_msg("option 0x%02x did not fit into the packet", | ||
142 | string[OPT_CODE]); | ||
143 | return 0; | ||
144 | } | ||
145 | DEBUG("adding option 0x%02x", string[OPT_CODE]); | ||
146 | memcpy(optionptr + end, string, string[OPT_LEN] + 2); | ||
147 | optionptr[end + string[OPT_LEN] + 2] = DHCP_END; | ||
148 | return string[OPT_LEN] + 2; | ||
149 | } | ||
150 | |||
151 | |||
152 | /* add a one to four byte option to a packet */ | ||
153 | int add_simple_option(uint8_t *optionptr, uint8_t code, uint32_t data) | ||
154 | { | ||
155 | const struct dhcp_option *dh; | ||
156 | |||
157 | for (dh = dhcp_options; dh->code; dh++) { | ||
158 | if (dh->code == code) { | ||
159 | uint8_t option[6], len; | ||
160 | |||
161 | option[OPT_CODE] = code; | ||
162 | len = option_lengths[dh->flags & TYPE_MASK]; | ||
163 | option[OPT_LEN] = len; | ||
164 | if (BB_BIG_ENDIAN) data <<= 8 * (4 - len); | ||
165 | /* This memcpy is for broken processors which can't | ||
166 | * handle a simple unaligned 32-bit assignment */ | ||
167 | memcpy(&option[OPT_DATA], &data, 4); | ||
168 | return add_option_string(optionptr, option); | ||
169 | } | ||
170 | } | ||
171 | |||
172 | bb_error_msg("cannot add option 0x%02x", code); | ||
173 | return 0; | ||
174 | } | ||
diff --git a/networking/udhcp/options.h b/networking/udhcp/options.h new file mode 100644 index 000000000..588504e5d --- /dev/null +++ b/networking/udhcp/options.h | |||
@@ -0,0 +1,37 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* options.h */ | ||
3 | #ifndef _OPTIONS_H | ||
4 | #define _OPTIONS_H | ||
5 | |||
6 | #define TYPE_MASK 0x0F | ||
7 | |||
8 | enum { | ||
9 | OPTION_IP=1, | ||
10 | OPTION_IP_PAIR, | ||
11 | OPTION_STRING, | ||
12 | OPTION_BOOLEAN, | ||
13 | OPTION_U8, | ||
14 | OPTION_U16, | ||
15 | OPTION_S16, | ||
16 | OPTION_U32, | ||
17 | OPTION_S32 | ||
18 | }; | ||
19 | |||
20 | #define OPTION_REQ 0x10 /* have the client request this option */ | ||
21 | #define OPTION_LIST 0x20 /* There can be a list of 1 or more of these */ | ||
22 | |||
23 | struct dhcp_option { | ||
24 | char name[12]; | ||
25 | char flags; | ||
26 | uint8_t code; | ||
27 | }; | ||
28 | |||
29 | extern const struct dhcp_option dhcp_options[]; | ||
30 | extern const unsigned char option_lengths[]; | ||
31 | |||
32 | uint8_t *get_option(struct dhcpMessage *packet, int code); | ||
33 | int end_option(uint8_t *optionptr); | ||
34 | int add_option_string(uint8_t *optionptr, uint8_t *string); | ||
35 | int add_simple_option(uint8_t *optionptr, uint8_t code, uint32_t data); | ||
36 | |||
37 | #endif | ||
diff --git a/networking/udhcp/packet.c b/networking/udhcp/packet.c new file mode 100644 index 000000000..dec9d0ab3 --- /dev/null +++ b/networking/udhcp/packet.c | |||
@@ -0,0 +1,211 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | |||
3 | #include <netinet/in.h> | ||
4 | #if (__GLIBC__ >= 2 && __GLIBC_MINOR__ >= 1) || defined _NEWLIB_VERSION | ||
5 | #include <netpacket/packet.h> | ||
6 | #include <net/ethernet.h> | ||
7 | #else | ||
8 | #include <asm/types.h> | ||
9 | #include <linux/if_packet.h> | ||
10 | #include <linux/if_ether.h> | ||
11 | #endif | ||
12 | |||
13 | #include "common.h" | ||
14 | #include "dhcpd.h" | ||
15 | #include "options.h" | ||
16 | |||
17 | |||
18 | void udhcp_init_header(struct dhcpMessage *packet, char type) | ||
19 | { | ||
20 | memset(packet, 0, sizeof(struct dhcpMessage)); | ||
21 | switch (type) { | ||
22 | case DHCPDISCOVER: | ||
23 | case DHCPREQUEST: | ||
24 | case DHCPRELEASE: | ||
25 | case DHCPINFORM: | ||
26 | packet->op = BOOTREQUEST; | ||
27 | break; | ||
28 | case DHCPOFFER: | ||
29 | case DHCPACK: | ||
30 | case DHCPNAK: | ||
31 | packet->op = BOOTREPLY; | ||
32 | } | ||
33 | packet->htype = ETH_10MB; | ||
34 | packet->hlen = ETH_10MB_LEN; | ||
35 | packet->cookie = htonl(DHCP_MAGIC); | ||
36 | packet->options[0] = DHCP_END; | ||
37 | add_simple_option(packet->options, DHCP_MESSAGE_TYPE, type); | ||
38 | } | ||
39 | |||
40 | |||
41 | /* read a packet from socket fd, return -1 on read error, -2 on packet error */ | ||
42 | int udhcp_get_packet(struct dhcpMessage *packet, int fd) | ||
43 | { | ||
44 | static const char broken_vendors[][8] = { | ||
45 | "MSFT 98", | ||
46 | "" | ||
47 | }; | ||
48 | int bytes; | ||
49 | int i; | ||
50 | char unsigned *vendor; | ||
51 | |||
52 | memset(packet, 0, sizeof(struct dhcpMessage)); | ||
53 | bytes = read(fd, packet, sizeof(struct dhcpMessage)); | ||
54 | if (bytes < 0) { | ||
55 | DEBUG("cannot read on listening socket, ignoring"); | ||
56 | return -1; | ||
57 | } | ||
58 | |||
59 | if (ntohl(packet->cookie) != DHCP_MAGIC) { | ||
60 | bb_error_msg("received bogus message, ignoring"); | ||
61 | return -2; | ||
62 | } | ||
63 | DEBUG("Received a packet"); | ||
64 | |||
65 | if (packet->op == BOOTREQUEST && (vendor = get_option(packet, DHCP_VENDOR))) { | ||
66 | for (i = 0; broken_vendors[i][0]; i++) { | ||
67 | if (vendor[OPT_LEN - 2] == (uint8_t)strlen(broken_vendors[i]) | ||
68 | && !strncmp((char*)vendor, broken_vendors[i], vendor[OPT_LEN - 2]) | ||
69 | ) { | ||
70 | DEBUG("broken client (%s), forcing broadcast", | ||
71 | broken_vendors[i]); | ||
72 | packet->flags |= htons(BROADCAST_FLAG); | ||
73 | } | ||
74 | } | ||
75 | } | ||
76 | |||
77 | return bytes; | ||
78 | } | ||
79 | |||
80 | |||
81 | uint16_t udhcp_checksum(void *addr, int count) | ||
82 | { | ||
83 | /* Compute Internet Checksum for "count" bytes | ||
84 | * beginning at location "addr". | ||
85 | */ | ||
86 | int32_t sum = 0; | ||
87 | uint16_t *source = (uint16_t *) addr; | ||
88 | |||
89 | while (count > 1) { | ||
90 | /* This is the inner loop */ | ||
91 | sum += *source++; | ||
92 | count -= 2; | ||
93 | } | ||
94 | |||
95 | /* Add left-over byte, if any */ | ||
96 | if (count > 0) { | ||
97 | /* Make sure that the left-over byte is added correctly both | ||
98 | * with little and big endian hosts */ | ||
99 | uint16_t tmp = 0; | ||
100 | *(uint8_t *) (&tmp) = * (uint8_t *) source; | ||
101 | sum += tmp; | ||
102 | } | ||
103 | /* Fold 32-bit sum to 16 bits */ | ||
104 | while (sum >> 16) | ||
105 | sum = (sum & 0xffff) + (sum >> 16); | ||
106 | |||
107 | return ~sum; | ||
108 | } | ||
109 | |||
110 | |||
111 | /* Construct a ip/udp header for a packet, and specify the source and dest hardware address */ | ||
112 | void BUG_sizeof_struct_udp_dhcp_packet_must_be_576(void); | ||
113 | int udhcp_raw_packet(struct dhcpMessage *payload, | ||
114 | uint32_t source_ip, int source_port, | ||
115 | uint32_t dest_ip, int dest_port, uint8_t *dest_arp, int ifindex) | ||
116 | { | ||
117 | int fd; | ||
118 | int result; | ||
119 | struct sockaddr_ll dest; | ||
120 | struct udp_dhcp_packet packet; | ||
121 | |||
122 | fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP)); | ||
123 | if (fd < 0) { | ||
124 | bb_perror_msg("socket"); | ||
125 | return -1; | ||
126 | } | ||
127 | |||
128 | memset(&dest, 0, sizeof(dest)); | ||
129 | memset(&packet, 0, sizeof(packet)); | ||
130 | |||
131 | dest.sll_family = AF_PACKET; | ||
132 | dest.sll_protocol = htons(ETH_P_IP); | ||
133 | dest.sll_ifindex = ifindex; | ||
134 | dest.sll_halen = 6; | ||
135 | memcpy(dest.sll_addr, dest_arp, 6); | ||
136 | if (bind(fd, (struct sockaddr *)&dest, sizeof(struct sockaddr_ll)) < 0) { | ||
137 | bb_perror_msg("bind"); | ||
138 | close(fd); | ||
139 | return -1; | ||
140 | } | ||
141 | |||
142 | packet.ip.protocol = IPPROTO_UDP; | ||
143 | packet.ip.saddr = source_ip; | ||
144 | packet.ip.daddr = dest_ip; | ||
145 | packet.udp.source = htons(source_port); | ||
146 | packet.udp.dest = htons(dest_port); | ||
147 | packet.udp.len = htons(sizeof(packet.udp) + sizeof(struct dhcpMessage)); /* cheat on the psuedo-header */ | ||
148 | packet.ip.tot_len = packet.udp.len; | ||
149 | memcpy(&(packet.data), payload, sizeof(struct dhcpMessage)); | ||
150 | packet.udp.check = udhcp_checksum(&packet, sizeof(struct udp_dhcp_packet)); | ||
151 | |||
152 | packet.ip.tot_len = htons(sizeof(struct udp_dhcp_packet)); | ||
153 | packet.ip.ihl = sizeof(packet.ip) >> 2; | ||
154 | packet.ip.version = IPVERSION; | ||
155 | packet.ip.ttl = IPDEFTTL; | ||
156 | packet.ip.check = udhcp_checksum(&(packet.ip), sizeof(packet.ip)); | ||
157 | |||
158 | if (sizeof(struct udp_dhcp_packet) != 576) | ||
159 | BUG_sizeof_struct_udp_dhcp_packet_must_be_576(); | ||
160 | |||
161 | result = sendto(fd, &packet, sizeof(struct udp_dhcp_packet), 0, | ||
162 | (struct sockaddr *) &dest, sizeof(dest)); | ||
163 | if (result <= 0) { | ||
164 | bb_perror_msg("sendto"); | ||
165 | } | ||
166 | close(fd); | ||
167 | return result; | ||
168 | } | ||
169 | |||
170 | |||
171 | /* Let the kernel do all the work for packet generation */ | ||
172 | int udhcp_kernel_packet(struct dhcpMessage *payload, | ||
173 | uint32_t source_ip, int source_port, | ||
174 | uint32_t dest_ip, int dest_port) | ||
175 | { | ||
176 | int fd, result; | ||
177 | struct sockaddr_in client; | ||
178 | |||
179 | fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); | ||
180 | if (fd < 0) | ||
181 | return -1; | ||
182 | |||
183 | if (setsockopt_reuseaddr(fd) == -1) { | ||
184 | close(fd); | ||
185 | return -1; | ||
186 | } | ||
187 | |||
188 | memset(&client, 0, sizeof(client)); | ||
189 | client.sin_family = AF_INET; | ||
190 | client.sin_port = htons(source_port); | ||
191 | client.sin_addr.s_addr = source_ip; | ||
192 | |||
193 | if (bind(fd, (struct sockaddr *)&client, sizeof(struct sockaddr)) == -1) { | ||
194 | close(fd); | ||
195 | return -1; | ||
196 | } | ||
197 | |||
198 | memset(&client, 0, sizeof(client)); | ||
199 | client.sin_family = AF_INET; | ||
200 | client.sin_port = htons(dest_port); | ||
201 | client.sin_addr.s_addr = dest_ip; | ||
202 | |||
203 | if (connect(fd, (struct sockaddr *)&client, sizeof(struct sockaddr)) == -1) { | ||
204 | close(fd); | ||
205 | return -1; | ||
206 | } | ||
207 | |||
208 | result = write(fd, payload, sizeof(struct dhcpMessage)); | ||
209 | close(fd); | ||
210 | return result; | ||
211 | } | ||
diff --git a/networking/udhcp/pidfile.c b/networking/udhcp/pidfile.c new file mode 100644 index 000000000..bcb2608c5 --- /dev/null +++ b/networking/udhcp/pidfile.c | |||
@@ -0,0 +1,66 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* pidfile.c | ||
3 | * | ||
4 | * Functions to assist in the writing and removing of pidfiles. | ||
5 | * | ||
6 | * Russ Dill <Russ.Dill@asu.edu> September 2001 | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published by | ||
10 | * the Free Software Foundation; either version 2 of the License, or | ||
11 | * (at your option) any later version. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, | ||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | * GNU General Public License for more details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU General Public License | ||
19 | * along with this program; if not, write to the Free Software | ||
20 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
21 | */ | ||
22 | |||
23 | #include "common.h" | ||
24 | |||
25 | |||
26 | static const char *saved_pidfile; | ||
27 | |||
28 | static void pidfile_delete(void) | ||
29 | { | ||
30 | if (saved_pidfile) unlink(saved_pidfile); | ||
31 | } | ||
32 | |||
33 | |||
34 | int pidfile_acquire(const char *pidfile) | ||
35 | { | ||
36 | int pid_fd; | ||
37 | if (!pidfile) return -1; | ||
38 | |||
39 | pid_fd = open(pidfile, O_CREAT|O_WRONLY|O_TRUNC, 0644); | ||
40 | if (pid_fd < 0) { | ||
41 | bb_perror_msg("cannot open pidfile %s", pidfile); | ||
42 | } else { | ||
43 | lockf(pid_fd, F_LOCK, 0); | ||
44 | if (!saved_pidfile) | ||
45 | atexit(pidfile_delete); | ||
46 | saved_pidfile = pidfile; | ||
47 | } | ||
48 | |||
49 | return pid_fd; | ||
50 | } | ||
51 | |||
52 | |||
53 | void pidfile_write_release(int pid_fd) | ||
54 | { | ||
55 | FILE *out; | ||
56 | |||
57 | if (pid_fd < 0) return; | ||
58 | |||
59 | out = fdopen(pid_fd, "w"); | ||
60 | if (out) { | ||
61 | fprintf(out, "%d\n", getpid()); | ||
62 | fclose(out); | ||
63 | } | ||
64 | lockf(pid_fd, F_UNLCK, 0); | ||
65 | close(pid_fd); | ||
66 | } | ||
diff --git a/networking/udhcp/script.c b/networking/udhcp/script.c new file mode 100644 index 000000000..07f68362c --- /dev/null +++ b/networking/udhcp/script.c | |||
@@ -0,0 +1,213 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* script.c | ||
3 | * | ||
4 | * Functions to call the DHCP client notification scripts | ||
5 | * | ||
6 | * Russ Dill <Russ.Dill@asu.edu> July 2001 | ||
7 | * | ||
8 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | ||
9 | */ | ||
10 | |||
11 | #include "common.h" | ||
12 | #include "dhcpd.h" | ||
13 | #include "dhcpc.h" | ||
14 | #include "options.h" | ||
15 | |||
16 | |||
17 | /* get a rough idea of how long an option will be (rounding up...) */ | ||
18 | static const int max_option_length[] = { | ||
19 | [OPTION_IP] = sizeof("255.255.255.255 "), | ||
20 | [OPTION_IP_PAIR] = sizeof("255.255.255.255 ") * 2, | ||
21 | [OPTION_STRING] = 1, | ||
22 | [OPTION_BOOLEAN] = sizeof("yes "), | ||
23 | [OPTION_U8] = sizeof("255 "), | ||
24 | [OPTION_U16] = sizeof("65535 "), | ||
25 | [OPTION_S16] = sizeof("-32768 "), | ||
26 | [OPTION_U32] = sizeof("4294967295 "), | ||
27 | [OPTION_S32] = sizeof("-2147483684 "), | ||
28 | }; | ||
29 | |||
30 | |||
31 | static inline int upper_length(int length, int opt_index) | ||
32 | { | ||
33 | return max_option_length[opt_index] * | ||
34 | (length / option_lengths[opt_index]); | ||
35 | } | ||
36 | |||
37 | |||
38 | static int sprintip(char *dest, char *pre, uint8_t *ip) | ||
39 | { | ||
40 | return sprintf(dest, "%s%d.%d.%d.%d", pre, ip[0], ip[1], ip[2], ip[3]); | ||
41 | } | ||
42 | |||
43 | |||
44 | /* really simple implementation, just count the bits */ | ||
45 | static int mton(struct in_addr *mask) | ||
46 | { | ||
47 | int i; | ||
48 | unsigned long bits = ntohl(mask->s_addr); | ||
49 | /* too bad one can't check the carry bit, etc in c bit | ||
50 | * shifting */ | ||
51 | for (i = 0; i < 32 && !((bits >> i) & 1); i++); | ||
52 | return 32 - i; | ||
53 | } | ||
54 | |||
55 | |||
56 | /* Fill dest with the text of option 'option'. */ | ||
57 | static void fill_options(char *dest, uint8_t *option, | ||
58 | const struct dhcp_option *type_p) | ||
59 | { | ||
60 | int type, optlen; | ||
61 | uint16_t val_u16; | ||
62 | int16_t val_s16; | ||
63 | uint32_t val_u32; | ||
64 | int32_t val_s32; | ||
65 | int len = option[OPT_LEN - 2]; | ||
66 | |||
67 | dest += sprintf(dest, "%s=", type_p->name); | ||
68 | |||
69 | type = type_p->flags & TYPE_MASK; | ||
70 | optlen = option_lengths[type]; | ||
71 | for (;;) { | ||
72 | switch (type) { | ||
73 | case OPTION_IP_PAIR: | ||
74 | dest += sprintip(dest, "", option); | ||
75 | *(dest++) = '/'; | ||
76 | option += 4; | ||
77 | optlen = 4; | ||
78 | case OPTION_IP: /* Works regardless of host byte order. */ | ||
79 | dest += sprintip(dest, "", option); | ||
80 | break; | ||
81 | case OPTION_BOOLEAN: | ||
82 | dest += sprintf(dest, *option ? "yes" : "no"); | ||
83 | break; | ||
84 | case OPTION_U8: | ||
85 | dest += sprintf(dest, "%u", *option); | ||
86 | break; | ||
87 | case OPTION_U16: | ||
88 | memcpy(&val_u16, option, 2); | ||
89 | dest += sprintf(dest, "%u", ntohs(val_u16)); | ||
90 | break; | ||
91 | case OPTION_S16: | ||
92 | memcpy(&val_s16, option, 2); | ||
93 | dest += sprintf(dest, "%d", ntohs(val_s16)); | ||
94 | break; | ||
95 | case OPTION_U32: | ||
96 | memcpy(&val_u32, option, 4); | ||
97 | dest += sprintf(dest, "%lu", (unsigned long) ntohl(val_u32)); | ||
98 | break; | ||
99 | case OPTION_S32: | ||
100 | memcpy(&val_s32, option, 4); | ||
101 | dest += sprintf(dest, "%ld", (long) ntohl(val_s32)); | ||
102 | break; | ||
103 | case OPTION_STRING: | ||
104 | memcpy(dest, option, len); | ||
105 | dest[len] = '\0'; | ||
106 | return; /* Short circuit this case */ | ||
107 | } | ||
108 | option += optlen; | ||
109 | len -= optlen; | ||
110 | if (len <= 0) break; | ||
111 | dest += sprintf(dest, " "); | ||
112 | } | ||
113 | } | ||
114 | |||
115 | |||
116 | /* put all the parameters into an environment */ | ||
117 | static char **fill_envp(struct dhcpMessage *packet) | ||
118 | { | ||
119 | int num_options = 0; | ||
120 | int i, j; | ||
121 | char **envp; | ||
122 | uint8_t *temp; | ||
123 | struct in_addr subnet; | ||
124 | char over = 0; | ||
125 | |||
126 | if (packet == NULL) | ||
127 | num_options = 0; | ||
128 | else { | ||
129 | for (i = 0; dhcp_options[i].code; i++) | ||
130 | if (get_option(packet, dhcp_options[i].code)) { | ||
131 | num_options++; | ||
132 | if (dhcp_options[i].code == DHCP_SUBNET) | ||
133 | num_options++; /* for mton */ | ||
134 | } | ||
135 | if (packet->siaddr) num_options++; | ||
136 | if ((temp = get_option(packet, DHCP_OPTION_OVER))) | ||
137 | over = *temp; | ||
138 | if (!(over & FILE_FIELD) && packet->file[0]) num_options++; | ||
139 | if (!(over & SNAME_FIELD) && packet->sname[0]) num_options++; | ||
140 | } | ||
141 | |||
142 | envp = xzalloc(sizeof(char *) * (num_options + 5)); | ||
143 | j = 0; | ||
144 | envp[j++] = xasprintf("interface=%s", client_config.interface); | ||
145 | envp[j++] = xasprintf("PATH=%s", | ||
146 | getenv("PATH") ? : "/bin:/usr/bin:/sbin:/usr/sbin"); | ||
147 | envp[j++] = xasprintf("HOME=%s", getenv("HOME") ? : "/"); | ||
148 | |||
149 | if (packet == NULL) return envp; | ||
150 | |||
151 | envp[j] = xmalloc(sizeof("ip=255.255.255.255")); | ||
152 | sprintip(envp[j++], "ip=", (uint8_t *) &packet->yiaddr); | ||
153 | |||
154 | for (i = 0; dhcp_options[i].code; i++) { | ||
155 | temp = get_option(packet, dhcp_options[i].code); | ||
156 | if (!temp) | ||
157 | continue; | ||
158 | envp[j] = xmalloc(upper_length(temp[OPT_LEN - 2], | ||
159 | dhcp_options[i].flags & TYPE_MASK) + strlen(dhcp_options[i].name) + 2); | ||
160 | fill_options(envp[j++], temp, &dhcp_options[i]); | ||
161 | |||
162 | /* Fill in a subnet bits option for things like /24 */ | ||
163 | if (dhcp_options[i].code == DHCP_SUBNET) { | ||
164 | memcpy(&subnet, temp, 4); | ||
165 | envp[j++] = xasprintf("mask=%d", mton(&subnet)); | ||
166 | } | ||
167 | } | ||
168 | if (packet->siaddr) { | ||
169 | envp[j] = xmalloc(sizeof("siaddr=255.255.255.255")); | ||
170 | sprintip(envp[j++], "siaddr=", (uint8_t *) &packet->siaddr); | ||
171 | } | ||
172 | if (!(over & FILE_FIELD) && packet->file[0]) { | ||
173 | /* watch out for invalid packets */ | ||
174 | packet->file[sizeof(packet->file) - 1] = '\0'; | ||
175 | envp[j++] = xasprintf("boot_file=%s", packet->file); | ||
176 | } | ||
177 | if (!(over & SNAME_FIELD) && packet->sname[0]) { | ||
178 | /* watch out for invalid packets */ | ||
179 | packet->sname[sizeof(packet->sname) - 1] = '\0'; | ||
180 | envp[j++] = xasprintf("sname=%s", packet->sname); | ||
181 | } | ||
182 | return envp; | ||
183 | } | ||
184 | |||
185 | |||
186 | /* Call a script with a par file and env vars */ | ||
187 | void udhcp_run_script(struct dhcpMessage *packet, const char *name) | ||
188 | { | ||
189 | int pid; | ||
190 | char **envp, **curr; | ||
191 | |||
192 | if (client_config.script == NULL) | ||
193 | return; | ||
194 | |||
195 | DEBUG("vfork'ing and execle'ing %s", client_config.script); | ||
196 | |||
197 | envp = fill_envp(packet); | ||
198 | /* call script */ | ||
199 | pid = vfork(); | ||
200 | if (pid) { | ||
201 | waitpid(pid, NULL, 0); | ||
202 | for (curr = envp; *curr; curr++) free(*curr); | ||
203 | free(envp); | ||
204 | return; | ||
205 | } else if (pid == 0) { | ||
206 | /* close fd's? */ | ||
207 | /* exec script */ | ||
208 | execle(client_config.script, client_config.script, | ||
209 | name, NULL, envp); | ||
210 | bb_perror_msg("script %s failed", client_config.script); | ||
211 | exit(1); | ||
212 | } | ||
213 | } | ||
diff --git a/networking/udhcp/serverpacket.c b/networking/udhcp/serverpacket.c new file mode 100644 index 000000000..8889fda86 --- /dev/null +++ b/networking/udhcp/serverpacket.c | |||
@@ -0,0 +1,261 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* serverpacket.c | ||
3 | * | ||
4 | * Construct and send DHCP server packets | ||
5 | * | ||
6 | * Russ Dill <Russ.Dill@asu.edu> July 2001 | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published by | ||
10 | * the Free Software Foundation; either version 2 of the License, or | ||
11 | * (at your option) any later version. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, | ||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | * GNU General Public License for more details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU General Public License | ||
19 | * along with this program; if not, write to the Free Software | ||
20 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
21 | */ | ||
22 | |||
23 | #include "common.h" | ||
24 | #include "dhcpd.h" | ||
25 | #include "options.h" | ||
26 | |||
27 | |||
28 | /* send a packet to giaddr using the kernel ip stack */ | ||
29 | static int send_packet_to_relay(struct dhcpMessage *payload) | ||
30 | { | ||
31 | DEBUG("Forwarding packet to relay"); | ||
32 | |||
33 | return udhcp_kernel_packet(payload, server_config.server, SERVER_PORT, | ||
34 | payload->giaddr, SERVER_PORT); | ||
35 | } | ||
36 | |||
37 | |||
38 | /* send a packet to a specific arp address and ip address by creating our own ip packet */ | ||
39 | static int send_packet_to_client(struct dhcpMessage *payload, int force_broadcast) | ||
40 | { | ||
41 | uint8_t *chaddr; | ||
42 | uint32_t ciaddr; | ||
43 | |||
44 | if (force_broadcast) { | ||
45 | DEBUG("broadcasting packet to client (NAK)"); | ||
46 | ciaddr = INADDR_BROADCAST; | ||
47 | chaddr = MAC_BCAST_ADDR; | ||
48 | } else if (payload->ciaddr) { | ||
49 | DEBUG("unicasting packet to client ciaddr"); | ||
50 | ciaddr = payload->ciaddr; | ||
51 | chaddr = payload->chaddr; | ||
52 | } else if (ntohs(payload->flags) & BROADCAST_FLAG) { | ||
53 | DEBUG("broadcasting packet to client (requested)"); | ||
54 | ciaddr = INADDR_BROADCAST; | ||
55 | chaddr = MAC_BCAST_ADDR; | ||
56 | } else { | ||
57 | DEBUG("unicasting packet to client yiaddr"); | ||
58 | ciaddr = payload->yiaddr; | ||
59 | chaddr = payload->chaddr; | ||
60 | } | ||
61 | return udhcp_raw_packet(payload, server_config.server, SERVER_PORT, | ||
62 | ciaddr, CLIENT_PORT, chaddr, server_config.ifindex); | ||
63 | } | ||
64 | |||
65 | |||
66 | /* send a dhcp packet, if force broadcast is set, the packet will be broadcast to the client */ | ||
67 | static int send_packet(struct dhcpMessage *payload, int force_broadcast) | ||
68 | { | ||
69 | int ret; | ||
70 | |||
71 | if (payload->giaddr) | ||
72 | ret = send_packet_to_relay(payload); | ||
73 | else ret = send_packet_to_client(payload, force_broadcast); | ||
74 | return ret; | ||
75 | } | ||
76 | |||
77 | |||
78 | static void init_packet(struct dhcpMessage *packet, struct dhcpMessage *oldpacket, char type) | ||
79 | { | ||
80 | udhcp_init_header(packet, type); | ||
81 | packet->xid = oldpacket->xid; | ||
82 | memcpy(packet->chaddr, oldpacket->chaddr, 16); | ||
83 | packet->flags = oldpacket->flags; | ||
84 | packet->giaddr = oldpacket->giaddr; | ||
85 | packet->ciaddr = oldpacket->ciaddr; | ||
86 | add_simple_option(packet->options, DHCP_SERVER_ID, server_config.server); | ||
87 | } | ||
88 | |||
89 | |||
90 | /* add in the bootp options */ | ||
91 | static void add_bootp_options(struct dhcpMessage *packet) | ||
92 | { | ||
93 | packet->siaddr = server_config.siaddr; | ||
94 | if (server_config.sname) | ||
95 | strncpy((char*)packet->sname, server_config.sname, sizeof(packet->sname) - 1); | ||
96 | if (server_config.boot_file) | ||
97 | strncpy((char*)packet->file, server_config.boot_file, sizeof(packet->file) - 1); | ||
98 | } | ||
99 | |||
100 | |||
101 | /* send a DHCP OFFER to a DHCP DISCOVER */ | ||
102 | int sendOffer(struct dhcpMessage *oldpacket) | ||
103 | { | ||
104 | struct dhcpMessage packet; | ||
105 | struct dhcpOfferedAddr *lease = NULL; | ||
106 | uint32_t req_align, lease_time_align = server_config.lease; | ||
107 | uint8_t *req, *lease_time; | ||
108 | struct option_set *curr; | ||
109 | struct in_addr addr; | ||
110 | |||
111 | uint32_t static_lease_ip; | ||
112 | |||
113 | init_packet(&packet, oldpacket, DHCPOFFER); | ||
114 | |||
115 | static_lease_ip = getIpByMac(server_config.static_leases, oldpacket->chaddr); | ||
116 | |||
117 | /* ADDME: if static, short circuit */ | ||
118 | if (!static_lease_ip) { | ||
119 | /* the client is in our lease/offered table */ | ||
120 | lease = find_lease_by_chaddr(oldpacket->chaddr); | ||
121 | if (lease) { | ||
122 | if (!lease_expired(lease)) | ||
123 | lease_time_align = lease->expires - time(0); | ||
124 | packet.yiaddr = lease->yiaddr; | ||
125 | |||
126 | /* Or the client has a requested ip */ | ||
127 | } else if ((req = get_option(oldpacket, DHCP_REQUESTED_IP)) | ||
128 | /* Don't look here (ugly hackish thing to do) */ | ||
129 | && memcpy(&req_align, req, 4) | ||
130 | /* and the ip is in the lease range */ | ||
131 | && ntohl(req_align) >= ntohl(server_config.start) | ||
132 | && ntohl(req_align) <= ntohl(server_config.end) | ||
133 | && !static_lease_ip /* Check that its not a static lease */ | ||
134 | /* and is not already taken/offered */ | ||
135 | && (!(lease = find_lease_by_yiaddr(req_align)) | ||
136 | /* or its taken, but expired */ /* ADDME: or maybe in here */ | ||
137 | || lease_expired(lease)) | ||
138 | ) { | ||
139 | packet.yiaddr = req_align; /* FIXME: oh my, is there a host using this IP? */ | ||
140 | /* otherwise, find a free IP */ | ||
141 | } else { | ||
142 | /* Is it a static lease? (No, because find_address skips static lease) */ | ||
143 | packet.yiaddr = find_address(0); | ||
144 | /* try for an expired lease */ | ||
145 | if (!packet.yiaddr) packet.yiaddr = find_address(1); | ||
146 | } | ||
147 | |||
148 | if (!packet.yiaddr) { | ||
149 | bb_error_msg("no IP addresses to give - OFFER abandoned"); | ||
150 | return -1; | ||
151 | } | ||
152 | if (!add_lease(packet.chaddr, packet.yiaddr, server_config.offer_time)) { | ||
153 | bb_error_msg("lease pool is full - OFFER abandoned"); | ||
154 | return -1; | ||
155 | } | ||
156 | lease_time = get_option(oldpacket, DHCP_LEASE_TIME); | ||
157 | if (lease_time) { | ||
158 | memcpy(&lease_time_align, lease_time, 4); | ||
159 | lease_time_align = ntohl(lease_time_align); | ||
160 | if (lease_time_align > server_config.lease) | ||
161 | lease_time_align = server_config.lease; | ||
162 | } | ||
163 | |||
164 | /* Make sure we aren't just using the lease time from the previous offer */ | ||
165 | if (lease_time_align < server_config.min_lease) | ||
166 | lease_time_align = server_config.lease; | ||
167 | /* ADDME: end of short circuit */ | ||
168 | } else { | ||
169 | /* It is a static lease... use it */ | ||
170 | packet.yiaddr = static_lease_ip; | ||
171 | } | ||
172 | |||
173 | add_simple_option(packet.options, DHCP_LEASE_TIME, htonl(lease_time_align)); | ||
174 | |||
175 | curr = server_config.options; | ||
176 | while (curr) { | ||
177 | if (curr->data[OPT_CODE] != DHCP_LEASE_TIME) | ||
178 | add_option_string(packet.options, curr->data); | ||
179 | curr = curr->next; | ||
180 | } | ||
181 | |||
182 | add_bootp_options(&packet); | ||
183 | |||
184 | addr.s_addr = packet.yiaddr; | ||
185 | bb_info_msg("Sending OFFER of %s", inet_ntoa(addr)); | ||
186 | return send_packet(&packet, 0); | ||
187 | } | ||
188 | |||
189 | |||
190 | int sendNAK(struct dhcpMessage *oldpacket) | ||
191 | { | ||
192 | struct dhcpMessage packet; | ||
193 | |||
194 | init_packet(&packet, oldpacket, DHCPNAK); | ||
195 | |||
196 | DEBUG("Sending NAK"); | ||
197 | return send_packet(&packet, 1); | ||
198 | } | ||
199 | |||
200 | |||
201 | int sendACK(struct dhcpMessage *oldpacket, uint32_t yiaddr) | ||
202 | { | ||
203 | struct dhcpMessage packet; | ||
204 | struct option_set *curr; | ||
205 | uint8_t *lease_time; | ||
206 | uint32_t lease_time_align = server_config.lease; | ||
207 | struct in_addr addr; | ||
208 | |||
209 | init_packet(&packet, oldpacket, DHCPACK); | ||
210 | packet.yiaddr = yiaddr; | ||
211 | |||
212 | if ((lease_time = get_option(oldpacket, DHCP_LEASE_TIME))) { | ||
213 | memcpy(&lease_time_align, lease_time, 4); | ||
214 | lease_time_align = ntohl(lease_time_align); | ||
215 | if (lease_time_align > server_config.lease) | ||
216 | lease_time_align = server_config.lease; | ||
217 | else if (lease_time_align < server_config.min_lease) | ||
218 | lease_time_align = server_config.lease; | ||
219 | } | ||
220 | |||
221 | add_simple_option(packet.options, DHCP_LEASE_TIME, htonl(lease_time_align)); | ||
222 | |||
223 | curr = server_config.options; | ||
224 | while (curr) { | ||
225 | if (curr->data[OPT_CODE] != DHCP_LEASE_TIME) | ||
226 | add_option_string(packet.options, curr->data); | ||
227 | curr = curr->next; | ||
228 | } | ||
229 | |||
230 | add_bootp_options(&packet); | ||
231 | |||
232 | addr.s_addr = packet.yiaddr; | ||
233 | bb_info_msg("Sending ACK to %s", inet_ntoa(addr)); | ||
234 | |||
235 | if (send_packet(&packet, 0) < 0) | ||
236 | return -1; | ||
237 | |||
238 | add_lease(packet.chaddr, packet.yiaddr, lease_time_align); | ||
239 | |||
240 | return 0; | ||
241 | } | ||
242 | |||
243 | |||
244 | int send_inform(struct dhcpMessage *oldpacket) | ||
245 | { | ||
246 | struct dhcpMessage packet; | ||
247 | struct option_set *curr; | ||
248 | |||
249 | init_packet(&packet, oldpacket, DHCPACK); | ||
250 | |||
251 | curr = server_config.options; | ||
252 | while (curr) { | ||
253 | if (curr->data[OPT_CODE] != DHCP_LEASE_TIME) | ||
254 | add_option_string(packet.options, curr->data); | ||
255 | curr = curr->next; | ||
256 | } | ||
257 | |||
258 | add_bootp_options(&packet); | ||
259 | |||
260 | return send_packet(&packet, 0); | ||
261 | } | ||
diff --git a/networking/udhcp/signalpipe.c b/networking/udhcp/signalpipe.c new file mode 100644 index 000000000..361596580 --- /dev/null +++ b/networking/udhcp/signalpipe.c | |||
@@ -0,0 +1,77 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* signalpipe.c | ||
3 | * | ||
4 | * Signal pipe infrastructure. A reliable way of delivering signals. | ||
5 | * | ||
6 | * Russ Dill <Russ.Dill@asu.edu> December 2003 | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published by | ||
10 | * the Free Software Foundation; either version 2 of the License, or | ||
11 | * (at your option) any later version. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, | ||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | * GNU General Public License for more details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU General Public License | ||
19 | * along with this program; if not, write to the Free Software | ||
20 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
21 | */ | ||
22 | |||
23 | #include "common.h" | ||
24 | |||
25 | |||
26 | static int signal_pipe[2]; | ||
27 | |||
28 | static void signal_handler(int sig) | ||
29 | { | ||
30 | if (send(signal_pipe[1], &sig, sizeof(sig), MSG_DONTWAIT) < 0) | ||
31 | bb_perror_msg("cannot send signal"); | ||
32 | } | ||
33 | |||
34 | |||
35 | /* Call this before doing anything else. Sets up the socket pair | ||
36 | * and installs the signal handler */ | ||
37 | void udhcp_sp_setup(void) | ||
38 | { | ||
39 | socketpair(AF_UNIX, SOCK_STREAM, 0, signal_pipe); | ||
40 | fcntl(signal_pipe[0], F_SETFD, FD_CLOEXEC); | ||
41 | fcntl(signal_pipe[1], F_SETFD, FD_CLOEXEC); | ||
42 | signal(SIGUSR1, signal_handler); | ||
43 | signal(SIGUSR2, signal_handler); | ||
44 | signal(SIGTERM, signal_handler); | ||
45 | } | ||
46 | |||
47 | |||
48 | /* Quick little function to setup the rfds. Will return the | ||
49 | * max_fd for use with select. Limited in that you can only pass | ||
50 | * one extra fd */ | ||
51 | int udhcp_sp_fd_set(fd_set *rfds, int extra_fd) | ||
52 | { | ||
53 | FD_ZERO(rfds); | ||
54 | FD_SET(signal_pipe[0], rfds); | ||
55 | if (extra_fd >= 0) { | ||
56 | fcntl(extra_fd, F_SETFD, FD_CLOEXEC); | ||
57 | FD_SET(extra_fd, rfds); | ||
58 | } | ||
59 | return signal_pipe[0] > extra_fd ? signal_pipe[0] : extra_fd; | ||
60 | } | ||
61 | |||
62 | |||
63 | /* Read a signal from the signal pipe. Returns 0 if there is | ||
64 | * no signal, -1 on error (and sets errno appropriately), and | ||
65 | * your signal on success */ | ||
66 | int udhcp_sp_read(fd_set *rfds) | ||
67 | { | ||
68 | int sig; | ||
69 | |||
70 | if (!FD_ISSET(signal_pipe[0], rfds)) | ||
71 | return 0; | ||
72 | |||
73 | if (read(signal_pipe[0], &sig, sizeof(sig)) < 0) | ||
74 | return -1; | ||
75 | |||
76 | return sig; | ||
77 | } | ||
diff --git a/networking/udhcp/socket.c b/networking/udhcp/socket.c new file mode 100644 index 000000000..2bae68f27 --- /dev/null +++ b/networking/udhcp/socket.c | |||
@@ -0,0 +1,130 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * socket.c -- DHCP server client/server socket creation | ||
4 | * | ||
5 | * udhcp client/server | ||
6 | * Copyright (C) 1999 Matthew Ramsay <matthewr@moreton.com.au> | ||
7 | * Chris Trew <ctrew@moreton.com.au> | ||
8 | * | ||
9 | * Rewrite by Russ Dill <Russ.Dill@asu.edu> July 2001 | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or modify | ||
12 | * it under the terms of the GNU General Public License as published by | ||
13 | * the Free Software Foundation; either version 2 of the License, or | ||
14 | * (at your option) any later version. | ||
15 | * | ||
16 | * This program is distributed in the hope that it will be useful, | ||
17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
19 | * GNU General Public License for more details. | ||
20 | * | ||
21 | * You should have received a copy of the GNU General Public License | ||
22 | * along with this program; if not, write to the Free Software | ||
23 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
24 | */ | ||
25 | |||
26 | #include <net/if.h> | ||
27 | #include <features.h> | ||
28 | #if (__GLIBC__ >= 2 && __GLIBC_MINOR__ >= 1) || defined _NEWLIB_VERSION | ||
29 | #include <netpacket/packet.h> | ||
30 | #include <net/ethernet.h> | ||
31 | #else | ||
32 | #include <asm/types.h> | ||
33 | #include <linux/if_packet.h> | ||
34 | #include <linux/if_ether.h> | ||
35 | #endif | ||
36 | |||
37 | #include "common.h" | ||
38 | |||
39 | |||
40 | int read_interface(char *interface, int *ifindex, uint32_t *addr, uint8_t *arp) | ||
41 | { | ||
42 | int fd; | ||
43 | struct ifreq ifr; | ||
44 | struct sockaddr_in *our_ip; | ||
45 | |||
46 | memset(&ifr, 0, sizeof(struct ifreq)); | ||
47 | fd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); | ||
48 | if (fd < 0) { | ||
49 | bb_perror_msg("socket failed"); | ||
50 | return -1; | ||
51 | } | ||
52 | |||
53 | ifr.ifr_addr.sa_family = AF_INET; | ||
54 | strncpy(ifr.ifr_name, interface, sizeof(ifr.ifr_name)); | ||
55 | if (addr) { | ||
56 | if (ioctl(fd, SIOCGIFADDR, &ifr) != 0) { | ||
57 | bb_perror_msg("SIOCGIFADDR failed, is the interface up and configured?"); | ||
58 | close(fd); | ||
59 | return -1; | ||
60 | } | ||
61 | our_ip = (struct sockaddr_in *) &ifr.ifr_addr; | ||
62 | *addr = our_ip->sin_addr.s_addr; | ||
63 | DEBUG("%s (our ip) = %s", ifr.ifr_name, inet_ntoa(our_ip->sin_addr)); | ||
64 | } | ||
65 | |||
66 | if (ifindex) { | ||
67 | if (ioctl(fd, SIOCGIFINDEX, &ifr) != 0) { | ||
68 | bb_perror_msg("SIOCGIFINDEX failed"); | ||
69 | close(fd); | ||
70 | return -1; | ||
71 | } | ||
72 | DEBUG("adapter index %d", ifr.ifr_ifindex); | ||
73 | *ifindex = ifr.ifr_ifindex; | ||
74 | } | ||
75 | |||
76 | if (arp) { | ||
77 | if (ioctl(fd, SIOCGIFHWADDR, &ifr) != 0) { | ||
78 | bb_perror_msg("SIOCGIFHWADDR failed"); | ||
79 | close(fd); | ||
80 | return -1; | ||
81 | } | ||
82 | memcpy(arp, ifr.ifr_hwaddr.sa_data, 6); | ||
83 | DEBUG("adapter hardware address %02x:%02x:%02x:%02x:%02x:%02x", | ||
84 | arp[0], arp[1], arp[2], arp[3], arp[4], arp[5]); | ||
85 | } | ||
86 | |||
87 | return 0; | ||
88 | } | ||
89 | |||
90 | |||
91 | int listen_socket(uint32_t ip, int port, char *inf) | ||
92 | { | ||
93 | struct ifreq interface; | ||
94 | int fd; | ||
95 | struct sockaddr_in addr; | ||
96 | |||
97 | DEBUG("Opening listen socket on 0x%08x:%d %s", ip, port, inf); | ||
98 | fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); | ||
99 | if (fd < 0) { | ||
100 | bb_perror_msg("socket"); | ||
101 | return -1; | ||
102 | } | ||
103 | |||
104 | memset(&addr, 0, sizeof(addr)); | ||
105 | addr.sin_family = AF_INET; | ||
106 | addr.sin_port = htons(port); | ||
107 | addr.sin_addr.s_addr = ip; | ||
108 | |||
109 | if (setsockopt_reuseaddr(fd) == -1) { | ||
110 | close(fd); | ||
111 | return -1; | ||
112 | } | ||
113 | if (setsockopt_broadcast(fd) == -1) { | ||
114 | close(fd); | ||
115 | return -1; | ||
116 | } | ||
117 | |||
118 | strncpy(interface.ifr_name, inf, IFNAMSIZ); | ||
119 | if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, (char *)&interface, sizeof(interface)) < 0) { | ||
120 | close(fd); | ||
121 | return -1; | ||
122 | } | ||
123 | |||
124 | if (bind(fd, (struct sockaddr *)&addr, sizeof(struct sockaddr)) == -1) { | ||
125 | close(fd); | ||
126 | return -1; | ||
127 | } | ||
128 | |||
129 | return fd; | ||
130 | } | ||
diff --git a/networking/udhcp/static_leases.c b/networking/udhcp/static_leases.c new file mode 100644 index 000000000..aabfb81aa --- /dev/null +++ b/networking/udhcp/static_leases.c | |||
@@ -0,0 +1,99 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * static_leases.c -- Couple of functions to assist with storing and | ||
4 | * retrieving data for static leases | ||
5 | * | ||
6 | * Wade Berrier <wberrier@myrealbox.com> September 2004 | ||
7 | * | ||
8 | */ | ||
9 | |||
10 | #include "common.h" | ||
11 | #include "dhcpd.h" | ||
12 | |||
13 | |||
14 | /* Takes the address of the pointer to the static_leases linked list, | ||
15 | * Address to a 6 byte mac address | ||
16 | * Address to a 4 byte ip address */ | ||
17 | int addStaticLease(struct static_lease **lease_struct, uint8_t *mac, uint32_t *ip) | ||
18 | { | ||
19 | struct static_lease *cur; | ||
20 | struct static_lease *new_static_lease; | ||
21 | |||
22 | /* Build new node */ | ||
23 | new_static_lease = xmalloc(sizeof(struct static_lease)); | ||
24 | new_static_lease->mac = mac; | ||
25 | new_static_lease->ip = ip; | ||
26 | new_static_lease->next = NULL; | ||
27 | |||
28 | /* If it's the first node to be added... */ | ||
29 | if (*lease_struct == NULL) { | ||
30 | *lease_struct = new_static_lease; | ||
31 | } else { | ||
32 | cur = *lease_struct; | ||
33 | while (cur->next) { | ||
34 | cur = cur->next; | ||
35 | } | ||
36 | |||
37 | cur->next = new_static_lease; | ||
38 | } | ||
39 | |||
40 | return 1; | ||
41 | } | ||
42 | |||
43 | /* Check to see if a mac has an associated static lease */ | ||
44 | uint32_t getIpByMac(struct static_lease *lease_struct, void *arg) | ||
45 | { | ||
46 | uint32_t return_ip; | ||
47 | struct static_lease *cur = lease_struct; | ||
48 | uint8_t *mac = arg; | ||
49 | |||
50 | return_ip = 0; | ||
51 | |||
52 | while (cur) { | ||
53 | /* If the client has the correct mac */ | ||
54 | if (memcmp(cur->mac, mac, 6) == 0) { | ||
55 | return_ip = *(cur->ip); | ||
56 | } | ||
57 | |||
58 | cur = cur->next; | ||
59 | } | ||
60 | |||
61 | return return_ip; | ||
62 | } | ||
63 | |||
64 | /* Check to see if an ip is reserved as a static ip */ | ||
65 | uint32_t reservedIp(struct static_lease *lease_struct, uint32_t ip) | ||
66 | { | ||
67 | struct static_lease *cur = lease_struct; | ||
68 | |||
69 | uint32_t return_val = 0; | ||
70 | |||
71 | while (cur) { | ||
72 | /* If the client has the correct ip */ | ||
73 | if (*cur->ip == ip) | ||
74 | return_val = 1; | ||
75 | |||
76 | cur = cur->next; | ||
77 | } | ||
78 | |||
79 | return return_val; | ||
80 | } | ||
81 | |||
82 | #if ENABLE_FEATURE_UDHCP_DEBUG | ||
83 | /* Print out static leases just to check what's going on */ | ||
84 | /* Takes the address of the pointer to the static_leases linked list */ | ||
85 | void printStaticLeases(struct static_lease **arg) | ||
86 | { | ||
87 | /* Get a pointer to the linked list */ | ||
88 | struct static_lease *cur = *arg; | ||
89 | |||
90 | while (cur) { | ||
91 | /* printf("PrintStaticLeases: Lease mac Address: %x\n", cur->mac); */ | ||
92 | printf("PrintStaticLeases: Lease mac Value: %x\n", *(cur->mac)); | ||
93 | /* printf("PrintStaticLeases: Lease ip Address: %x\n", cur->ip); */ | ||
94 | printf("PrintStaticLeases: Lease ip Value: %x\n", *(cur->ip)); | ||
95 | |||
96 | cur = cur->next; | ||
97 | } | ||
98 | } | ||
99 | #endif | ||
diff --git a/networking/vconfig.c b/networking/vconfig.c new file mode 100644 index 000000000..003c1a8f7 --- /dev/null +++ b/networking/vconfig.c | |||
@@ -0,0 +1,164 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * vconfig implementation for busybox | ||
4 | * | ||
5 | * Copyright (C) 2001 Manuel Novoa III <mjn3@codepoet.org> | ||
6 | * | ||
7 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | ||
8 | */ | ||
9 | |||
10 | /* BB_AUDIT SUSv3 N/A */ | ||
11 | |||
12 | #include "busybox.h" | ||
13 | #include <net/if.h> | ||
14 | |||
15 | /* Stuff from linux/if_vlan.h, kernel version 2.4.23 */ | ||
16 | enum vlan_ioctl_cmds { | ||
17 | ADD_VLAN_CMD, | ||
18 | DEL_VLAN_CMD, | ||
19 | SET_VLAN_INGRESS_PRIORITY_CMD, | ||
20 | SET_VLAN_EGRESS_PRIORITY_CMD, | ||
21 | GET_VLAN_INGRESS_PRIORITY_CMD, | ||
22 | GET_VLAN_EGRESS_PRIORITY_CMD, | ||
23 | SET_VLAN_NAME_TYPE_CMD, | ||
24 | SET_VLAN_FLAG_CMD | ||
25 | }; | ||
26 | enum vlan_name_types { | ||
27 | VLAN_NAME_TYPE_PLUS_VID, /* Name will look like: vlan0005 */ | ||
28 | VLAN_NAME_TYPE_RAW_PLUS_VID, /* name will look like: eth1.0005 */ | ||
29 | VLAN_NAME_TYPE_PLUS_VID_NO_PAD, /* Name will look like: vlan5 */ | ||
30 | VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD, /* Name will look like: eth0.5 */ | ||
31 | VLAN_NAME_TYPE_HIGHEST | ||
32 | }; | ||
33 | |||
34 | struct vlan_ioctl_args { | ||
35 | int cmd; /* Should be one of the vlan_ioctl_cmds enum above. */ | ||
36 | char device1[24]; | ||
37 | |||
38 | union { | ||
39 | char device2[24]; | ||
40 | int VID; | ||
41 | unsigned int skb_priority; | ||
42 | unsigned int name_type; | ||
43 | unsigned int bind_type; | ||
44 | unsigned int flag; /* Matches vlan_dev_info flags */ | ||
45 | } u; | ||
46 | |||
47 | short vlan_qos; | ||
48 | }; | ||
49 | |||
50 | #define VLAN_GROUP_ARRAY_LEN 4096 | ||
51 | #define SIOCSIFVLAN 0x8983 /* Set 802.1Q VLAN options */ | ||
52 | |||
53 | /* On entry, table points to the length of the current string plus | ||
54 | * nul terminator plus data length for the subsequent entry. The | ||
55 | * return value is the last data entry for the matching string. */ | ||
56 | static const char *xfind_str(const char *table, const char *str) | ||
57 | { | ||
58 | while (strcasecmp(str, table+1) != 0) { | ||
59 | if (!*(table += table[0])) { | ||
60 | bb_show_usage(); | ||
61 | } | ||
62 | } | ||
63 | return table - 1; | ||
64 | } | ||
65 | |||
66 | static const char cmds[] = { | ||
67 | 4, ADD_VLAN_CMD, 7, | ||
68 | 'a', 'd', 'd', 0, | ||
69 | 3, DEL_VLAN_CMD, 7, | ||
70 | 'r', 'e', 'm', 0, | ||
71 | 3, SET_VLAN_NAME_TYPE_CMD, 17, | ||
72 | 's', 'e', 't', '_', | ||
73 | 'n', 'a', 'm', 'e', '_', | ||
74 | 't', 'y', 'p', 'e', 0, | ||
75 | 5, SET_VLAN_FLAG_CMD, 12, | ||
76 | 's', 'e', 't', '_', | ||
77 | 'f', 'l', 'a', 'g', 0, | ||
78 | 5, SET_VLAN_EGRESS_PRIORITY_CMD, 18, | ||
79 | 's', 'e', 't', '_', | ||
80 | 'e', 'g', 'r', 'e', 's', 's', '_', | ||
81 | 'm', 'a', 'p', 0, | ||
82 | 5, SET_VLAN_INGRESS_PRIORITY_CMD, 16, | ||
83 | 's', 'e', 't', '_', | ||
84 | 'i', 'n', 'g', 'r', 'e', 's', 's', '_', | ||
85 | 'm', 'a', 'p', 0, | ||
86 | }; | ||
87 | |||
88 | static const char name_types[] = { | ||
89 | VLAN_NAME_TYPE_PLUS_VID, 16, | ||
90 | 'V', 'L', 'A', 'N', | ||
91 | '_', 'P', 'L', 'U', 'S', '_', 'V', 'I', 'D', | ||
92 | 0, | ||
93 | VLAN_NAME_TYPE_PLUS_VID_NO_PAD, 22, | ||
94 | 'V', 'L', 'A', 'N', | ||
95 | '_', 'P', 'L', 'U', 'S', '_', 'V', 'I', 'D', | ||
96 | '_', 'N', 'O', '_', 'P', 'A', 'D', 0, | ||
97 | VLAN_NAME_TYPE_RAW_PLUS_VID, 15, | ||
98 | 'D', 'E', 'V', | ||
99 | '_', 'P', 'L', 'U', 'S', '_', 'V', 'I', 'D', | ||
100 | 0, | ||
101 | VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD, 20, | ||
102 | 'D', 'E', 'V', | ||
103 | '_', 'P', 'L', 'U', 'S', '_', 'V', 'I', 'D', | ||
104 | '_', 'N', 'O', '_', 'P', 'A', 'D', 0, | ||
105 | }; | ||
106 | |||
107 | static const char conf_file_name[] = "/proc/net/vlan/config"; | ||
108 | |||
109 | int vconfig_main(int argc, char **argv) | ||
110 | { | ||
111 | struct vlan_ioctl_args ifr; | ||
112 | const char *p; | ||
113 | int fd; | ||
114 | |||
115 | if (argc < 3) { | ||
116 | bb_show_usage(); | ||
117 | } | ||
118 | |||
119 | /* Don't bother closing the filedes. It will be closed on cleanup. */ | ||
120 | /* Will die if 802.1q is not present */ | ||
121 | xopen(conf_file_name, O_RDONLY); | ||
122 | |||
123 | memset(&ifr, 0, sizeof(struct vlan_ioctl_args)); | ||
124 | |||
125 | ++argv; | ||
126 | p = xfind_str(cmds+2, *argv); | ||
127 | ifr.cmd = *p; | ||
128 | if (argc != p[-1]) { | ||
129 | bb_show_usage(); | ||
130 | } | ||
131 | |||
132 | if (ifr.cmd == SET_VLAN_NAME_TYPE_CMD) { /* set_name_type */ | ||
133 | ifr.u.name_type = *xfind_str(name_types+1, argv[1]); | ||
134 | } else { | ||
135 | if (strlen(argv[1]) >= IF_NAMESIZE) { | ||
136 | bb_error_msg_and_die("if_name >= %d chars", IF_NAMESIZE); | ||
137 | } | ||
138 | strcpy(ifr.device1, argv[1]); | ||
139 | p = argv[2]; | ||
140 | |||
141 | /* I suppose one could try to combine some of the function calls below, | ||
142 | * since ifr.u.flag, ifr.u.VID, and ifr.u.skb_priority are all same-sized | ||
143 | * (unsigned) int members of a unions. But because of the range checking, | ||
144 | * doing so wouldn't save that much space and would also make maintainence | ||
145 | * more of a pain. */ | ||
146 | if (ifr.cmd == SET_VLAN_FLAG_CMD) { /* set_flag */ | ||
147 | ifr.u.flag = xatoul_range(p, 0, 1); | ||
148 | /* DM: in order to set reorder header, qos must be set */ | ||
149 | ifr.vlan_qos = xatoul_range(argv[3], 0, 7); | ||
150 | } else if (ifr.cmd == ADD_VLAN_CMD) { /* add */ | ||
151 | ifr.u.VID = xatoul_range(p, 0, VLAN_GROUP_ARRAY_LEN-1); | ||
152 | } else if (ifr.cmd != DEL_VLAN_CMD) { /* set_{egress|ingress}_map */ | ||
153 | ifr.u.skb_priority = xatou(p); | ||
154 | ifr.vlan_qos = xatoul_range(argv[3], 0, 7); | ||
155 | } | ||
156 | } | ||
157 | |||
158 | fd = xsocket(AF_INET, SOCK_STREAM, 0); | ||
159 | if (ioctl(fd, SIOCSIFVLAN, &ifr) < 0) { | ||
160 | bb_perror_msg_and_die("ioctl error for %s", *argv); | ||
161 | } | ||
162 | |||
163 | return 0; | ||
164 | } | ||
diff --git a/networking/wget.c b/networking/wget.c new file mode 100644 index 000000000..028e18c73 --- /dev/null +++ b/networking/wget.c | |||
@@ -0,0 +1,832 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * wget - retrieve a file using HTTP or FTP | ||
4 | * | ||
5 | * Chip Rosenthal Covad Communications <chip@laserlink.net> | ||
6 | * | ||
7 | */ | ||
8 | |||
9 | /* We want libc to give us xxx64 functions also */ | ||
10 | /* http://www.unix.org/version2/whatsnew/lfs20mar.html */ | ||
11 | #define _LARGEFILE64_SOURCE 1 | ||
12 | |||
13 | #include "busybox.h" | ||
14 | #include <getopt.h> /* for struct option */ | ||
15 | |||
16 | struct host_info { | ||
17 | // May be used if we ever will want to free() all xstrdup()s... | ||
18 | /* char *allocated; */ | ||
19 | char *host; | ||
20 | int port; | ||
21 | char *path; | ||
22 | int is_ftp; | ||
23 | char *user; | ||
24 | }; | ||
25 | |||
26 | static void parse_url(char *url, struct host_info *h); | ||
27 | static FILE *open_socket(struct sockaddr_in *s_in); | ||
28 | static char *gethdr(char *buf, size_t bufsiz, FILE *fp, int *istrunc); | ||
29 | static int ftpcmd(char *s1, char *s2, FILE *fp, char *buf); | ||
30 | |||
31 | /* Globals (can be accessed from signal handlers */ | ||
32 | static off_t content_len; /* Content-length of the file */ | ||
33 | static off_t beg_range; /* Range at which continue begins */ | ||
34 | #if ENABLE_FEATURE_WGET_STATUSBAR | ||
35 | static off_t transferred; /* Number of bytes transferred so far */ | ||
36 | #endif | ||
37 | static int chunked; /* chunked transfer encoding */ | ||
38 | #if ENABLE_FEATURE_WGET_STATUSBAR | ||
39 | static void progressmeter(int flag); | ||
40 | static char *curfile; /* Name of current file being transferred */ | ||
41 | static struct timeval start; /* Time a transfer started */ | ||
42 | enum { | ||
43 | STALLTIME = 5 /* Seconds when xfer considered "stalled" */ | ||
44 | }; | ||
45 | #else | ||
46 | static void progressmeter(int flag) {} | ||
47 | #endif | ||
48 | |||
49 | /* Read NMEMB elements of SIZE bytes into PTR from STREAM. Returns the | ||
50 | * number of elements read, and a short count if an eof or non-interrupt | ||
51 | * error is encountered. */ | ||
52 | static size_t safe_fread(void *ptr, size_t size, size_t nmemb, FILE *stream) | ||
53 | { | ||
54 | size_t ret = 0; | ||
55 | |||
56 | do { | ||
57 | clearerr(stream); | ||
58 | ret += fread((char *)ptr + (ret * size), size, nmemb - ret, stream); | ||
59 | } while (ret < nmemb && ferror(stream) && errno == EINTR); | ||
60 | |||
61 | return ret; | ||
62 | } | ||
63 | |||
64 | /* Read a line or SIZE - 1 bytes into S, whichever is less, from STREAM. | ||
65 | * Returns S, or NULL if an eof or non-interrupt error is encountered. */ | ||
66 | static char *safe_fgets(char *s, int size, FILE *stream) | ||
67 | { | ||
68 | char *ret; | ||
69 | |||
70 | do { | ||
71 | clearerr(stream); | ||
72 | ret = fgets(s, size, stream); | ||
73 | } while (ret == NULL && ferror(stream) && errno == EINTR); | ||
74 | |||
75 | return ret; | ||
76 | } | ||
77 | |||
78 | #if ENABLE_FEATURE_WGET_AUTHENTICATION | ||
79 | /* | ||
80 | * Base64-encode character string and return the string. | ||
81 | */ | ||
82 | static char *base64enc(unsigned char *p, char *buf, int len) | ||
83 | { | ||
84 | bb_uuencode(p, buf, len, bb_uuenc_tbl_base64); | ||
85 | return buf; | ||
86 | } | ||
87 | #endif | ||
88 | |||
89 | int wget_main(int argc, char **argv) | ||
90 | { | ||
91 | char buf[512]; | ||
92 | struct host_info server, target; | ||
93 | struct sockaddr_in s_in; | ||
94 | int n, status; | ||
95 | int port; | ||
96 | int try = 5; | ||
97 | unsigned opt; | ||
98 | char *s; | ||
99 | char *proxy = 0; | ||
100 | char *dir_prefix = NULL; | ||
101 | #if ENABLE_FEATURE_WGET_LONG_OPTIONS | ||
102 | char *extra_headers = NULL; | ||
103 | llist_t *headers_llist = NULL; | ||
104 | #endif | ||
105 | |||
106 | /* server.allocated = target.allocated = NULL; */ | ||
107 | |||
108 | FILE *sfp = NULL; /* socket to web/ftp server */ | ||
109 | FILE *dfp = NULL; /* socket to ftp server (data) */ | ||
110 | char *fname_out = NULL; /* where to direct output (-O) */ | ||
111 | int got_clen = 0; /* got content-length: from server */ | ||
112 | int output_fd = -1; | ||
113 | int use_proxy = 1; /* Use proxies if env vars are set */ | ||
114 | const char *proxy_flag = "on"; /* Use proxies if env vars are set */ | ||
115 | const char *user_agent = "Wget";/* Content of the "User-Agent" header field */ | ||
116 | |||
117 | /* | ||
118 | * Crack command line. | ||
119 | */ | ||
120 | enum { | ||
121 | WGET_OPT_CONTINUE = 0x1, | ||
122 | WGET_OPT_QUIET = 0x2, | ||
123 | WGET_OPT_OUTNAME = 0x4, | ||
124 | WGET_OPT_PREFIX = 0x8, | ||
125 | WGET_OPT_PROXY = 0x10, | ||
126 | WGET_OPT_USER_AGENT = 0x20, | ||
127 | WGET_OPT_PASSIVE = 0x40, | ||
128 | WGET_OPT_HEADER = 0x80, | ||
129 | }; | ||
130 | #if ENABLE_FEATURE_WGET_LONG_OPTIONS | ||
131 | static const struct option wget_long_options[] = { | ||
132 | // name, has_arg, flag, val | ||
133 | { "continue", no_argument, NULL, 'c' }, | ||
134 | { "quiet", no_argument, NULL, 'q' }, | ||
135 | { "output-document", required_argument, NULL, 'O' }, | ||
136 | { "directory-prefix", required_argument, NULL, 'P' }, | ||
137 | { "proxy", required_argument, NULL, 'Y' }, | ||
138 | { "user-agent", required_argument, NULL, 'U' }, | ||
139 | { "passive-ftp", no_argument, NULL, 0xff }, | ||
140 | { "header", required_argument, NULL, 0xfe }, | ||
141 | { 0, 0, 0, 0 } | ||
142 | }; | ||
143 | applet_long_options = wget_long_options; | ||
144 | #endif | ||
145 | opt_complementary = "-1" USE_FEATURE_WGET_LONG_OPTIONS(":\xfe::"); | ||
146 | opt = getopt32(argc, argv, "cqO:P:Y:U:", | ||
147 | &fname_out, &dir_prefix, | ||
148 | &proxy_flag, &user_agent | ||
149 | USE_FEATURE_WGET_LONG_OPTIONS(, &headers_llist) | ||
150 | ); | ||
151 | if (strcmp(proxy_flag, "off") == 0) { | ||
152 | /* Use the proxy if necessary. */ | ||
153 | use_proxy = 0; | ||
154 | } | ||
155 | #if ENABLE_FEATURE_WGET_LONG_OPTIONS | ||
156 | if (headers_llist) { | ||
157 | int size = 1; | ||
158 | char *cp; | ||
159 | llist_t *ll = headers_llist = rev_llist(headers_llist); | ||
160 | while (ll) { | ||
161 | size += strlen(ll->data) + 2; | ||
162 | ll = ll->link; | ||
163 | } | ||
164 | extra_headers = cp = xmalloc(size); | ||
165 | while (headers_llist) { | ||
166 | cp += sprintf(cp, "%s\r\n", headers_llist->data); | ||
167 | headers_llist = headers_llist->link; | ||
168 | } | ||
169 | } | ||
170 | #endif | ||
171 | |||
172 | parse_url(argv[optind], &target); | ||
173 | server.host = target.host; | ||
174 | server.port = target.port; | ||
175 | |||
176 | /* | ||
177 | * Use the proxy if necessary. | ||
178 | */ | ||
179 | if (use_proxy) { | ||
180 | proxy = getenv(target.is_ftp ? "ftp_proxy" : "http_proxy"); | ||
181 | if (proxy && *proxy) { | ||
182 | parse_url(proxy, &server); | ||
183 | } else { | ||
184 | use_proxy = 0; | ||
185 | } | ||
186 | } | ||
187 | |||
188 | /* Guess an output filename */ | ||
189 | if (!fname_out) { | ||
190 | // Dirty hack. Needed because bb_get_last_path_component | ||
191 | // will destroy trailing / by storing '\0' in last byte! | ||
192 | if (*target.path && target.path[strlen(target.path)-1] != '/') { | ||
193 | fname_out = | ||
194 | #if ENABLE_FEATURE_WGET_STATUSBAR | ||
195 | curfile = | ||
196 | #endif | ||
197 | bb_get_last_path_component(target.path); | ||
198 | } | ||
199 | if (!fname_out || !fname_out[0]) { | ||
200 | fname_out = | ||
201 | #if ENABLE_FEATURE_WGET_STATUSBAR | ||
202 | curfile = | ||
203 | #endif | ||
204 | "index.html"; | ||
205 | } | ||
206 | if (dir_prefix != NULL) | ||
207 | fname_out = concat_path_file(dir_prefix, fname_out); | ||
208 | #if ENABLE_FEATURE_WGET_STATUSBAR | ||
209 | } else { | ||
210 | curfile = bb_get_last_path_component(fname_out); | ||
211 | #endif | ||
212 | } | ||
213 | /* Impossible? | ||
214 | if ((opt & WGET_OPT_CONTINUE) && !fname_out) | ||
215 | bb_error_msg_and_die("cannot specify continue (-c) without a filename (-O)"); */ | ||
216 | |||
217 | /* | ||
218 | * Determine where to start transfer. | ||
219 | */ | ||
220 | if (fname_out[0] == '-' && !fname_out[1]) { | ||
221 | output_fd = 1; | ||
222 | opt &= ~WGET_OPT_CONTINUE; | ||
223 | } | ||
224 | if (opt & WGET_OPT_CONTINUE) { | ||
225 | output_fd = open(fname_out, O_WRONLY); | ||
226 | if (output_fd >= 0) { | ||
227 | beg_range = xlseek(output_fd, 0, SEEK_END); | ||
228 | } | ||
229 | /* File doesn't exist. We do not create file here yet. | ||
230 | We are not sure it exists on remove side */ | ||
231 | } | ||
232 | |||
233 | /* We want to do exactly _one_ DNS lookup, since some | ||
234 | * sites (i.e. ftp.us.debian.org) use round-robin DNS | ||
235 | * and we want to connect to only one IP... */ | ||
236 | bb_lookup_host(&s_in, server.host); | ||
237 | s_in.sin_port = server.port; | ||
238 | if (!(opt & WGET_OPT_QUIET)) { | ||
239 | fprintf(stderr, "Connecting to %s[%s]:%d\n", | ||
240 | server.host, inet_ntoa(s_in.sin_addr), ntohs(server.port)); | ||
241 | } | ||
242 | |||
243 | if (use_proxy || !target.is_ftp) { | ||
244 | /* | ||
245 | * HTTP session | ||
246 | */ | ||
247 | do { | ||
248 | got_clen = chunked = 0; | ||
249 | |||
250 | if (!--try) | ||
251 | bb_error_msg_and_die("too many redirections"); | ||
252 | |||
253 | /* | ||
254 | * Open socket to http server | ||
255 | */ | ||
256 | if (sfp) fclose(sfp); | ||
257 | sfp = open_socket(&s_in); | ||
258 | |||
259 | /* | ||
260 | * Send HTTP request. | ||
261 | */ | ||
262 | if (use_proxy) { | ||
263 | const char *format = "GET %stp://%s:%d/%s HTTP/1.1\r\n"; | ||
264 | #if ENABLE_FEATURE_WGET_IP6_LITERAL | ||
265 | if (strchr(target.host, ':')) | ||
266 | format = "GET %stp://[%s]:%d/%s HTTP/1.1\r\n"; | ||
267 | #endif | ||
268 | fprintf(sfp, format, | ||
269 | target.is_ftp ? "f" : "ht", target.host, | ||
270 | ntohs(target.port), target.path); | ||
271 | } else { | ||
272 | fprintf(sfp, "GET /%s HTTP/1.1\r\n", target.path); | ||
273 | } | ||
274 | |||
275 | fprintf(sfp, "Host: %s\r\nUser-Agent: %s\r\n", target.host, | ||
276 | user_agent); | ||
277 | |||
278 | #if ENABLE_FEATURE_WGET_AUTHENTICATION | ||
279 | if (target.user) { | ||
280 | fprintf(sfp, "Authorization: Basic %s\r\n", | ||
281 | base64enc((unsigned char*)target.user, buf, sizeof(buf))); | ||
282 | } | ||
283 | if (use_proxy && server.user) { | ||
284 | fprintf(sfp, "Proxy-Authorization: Basic %s\r\n", | ||
285 | base64enc((unsigned char*)server.user, buf, sizeof(buf))); | ||
286 | } | ||
287 | #endif | ||
288 | |||
289 | if (beg_range) | ||
290 | fprintf(sfp, "Range: bytes=%"OFF_FMT"d-\r\n", beg_range); | ||
291 | #if ENABLE_FEATURE_WGET_LONG_OPTIONS | ||
292 | if (extra_headers) | ||
293 | fputs(extra_headers, sfp); | ||
294 | #endif | ||
295 | fprintf(sfp, "Connection: close\r\n\r\n"); | ||
296 | |||
297 | /* | ||
298 | * Retrieve HTTP response line and check for "200" status code. | ||
299 | */ | ||
300 | read_response: | ||
301 | if (fgets(buf, sizeof(buf), sfp) == NULL) | ||
302 | bb_error_msg_and_die("no response from server"); | ||
303 | |||
304 | s = buf; | ||
305 | while (*s != '\0' && !isspace(*s)) ++s; | ||
306 | s = skip_whitespace(s); | ||
307 | // FIXME: no error check | ||
308 | // xatou wouldn't work: "200 OK" | ||
309 | status = atoi(s); | ||
310 | switch (status) { | ||
311 | case 0: | ||
312 | case 100: | ||
313 | while (gethdr(buf, sizeof(buf), sfp, &n) != NULL) | ||
314 | /* eat all remaining headers */; | ||
315 | goto read_response; | ||
316 | case 200: | ||
317 | break; | ||
318 | case 300: /* redirection */ | ||
319 | case 301: | ||
320 | case 302: | ||
321 | case 303: | ||
322 | break; | ||
323 | case 206: | ||
324 | if (beg_range) | ||
325 | break; | ||
326 | /*FALLTHRU*/ | ||
327 | default: | ||
328 | /* Show first line only and kill any ESC tricks */ | ||
329 | buf[strcspn(buf, "\n\r\x1b")] = '\0'; | ||
330 | bb_error_msg_and_die("server returned error: %s", buf); | ||
331 | } | ||
332 | |||
333 | /* | ||
334 | * Retrieve HTTP headers. | ||
335 | */ | ||
336 | while ((s = gethdr(buf, sizeof(buf), sfp, &n)) != NULL) { | ||
337 | if (strcasecmp(buf, "content-length") == 0) { | ||
338 | content_len = BB_STRTOOFF(s, NULL, 10); | ||
339 | if (errno || content_len < 0) { | ||
340 | bb_error_msg_and_die("content-length %s is garbage", s); | ||
341 | } | ||
342 | got_clen = 1; | ||
343 | continue; | ||
344 | } | ||
345 | if (strcasecmp(buf, "transfer-encoding") == 0) { | ||
346 | if (strcasecmp(s, "chunked") != 0) | ||
347 | bb_error_msg_and_die("server wants to do %s transfer encoding", s); | ||
348 | chunked = got_clen = 1; | ||
349 | } | ||
350 | if (strcasecmp(buf, "location") == 0) { | ||
351 | if (s[0] == '/') | ||
352 | /* free(target.allocated); */ | ||
353 | target.path = /* target.allocated = */ xstrdup(s+1); | ||
354 | else { | ||
355 | parse_url(s, &target); | ||
356 | if (use_proxy == 0) { | ||
357 | server.host = target.host; | ||
358 | server.port = target.port; | ||
359 | } | ||
360 | bb_lookup_host(&s_in, server.host); | ||
361 | s_in.sin_port = server.port; | ||
362 | break; | ||
363 | } | ||
364 | } | ||
365 | } | ||
366 | } while(status >= 300); | ||
367 | |||
368 | dfp = sfp; | ||
369 | |||
370 | } else { | ||
371 | |||
372 | /* | ||
373 | * FTP session | ||
374 | */ | ||
375 | if (!target.user) | ||
376 | target.user = xstrdup("anonymous:busybox@"); | ||
377 | |||
378 | sfp = open_socket(&s_in); | ||
379 | if (ftpcmd(NULL, NULL, sfp, buf) != 220) | ||
380 | bb_error_msg_and_die("%s", buf+4); | ||
381 | |||
382 | /* | ||
383 | * Splitting username:password pair, | ||
384 | * trying to log in | ||
385 | */ | ||
386 | s = strchr(target.user, ':'); | ||
387 | if (s) | ||
388 | *(s++) = '\0'; | ||
389 | switch (ftpcmd("USER ", target.user, sfp, buf)) { | ||
390 | case 230: | ||
391 | break; | ||
392 | case 331: | ||
393 | if (ftpcmd("PASS ", s, sfp, buf) == 230) | ||
394 | break; | ||
395 | /* FALLTHRU (failed login) */ | ||
396 | default: | ||
397 | bb_error_msg_and_die("ftp login: %s", buf+4); | ||
398 | } | ||
399 | |||
400 | ftpcmd("TYPE I", NULL, sfp, buf); | ||
401 | |||
402 | /* | ||
403 | * Querying file size | ||
404 | */ | ||
405 | if (ftpcmd("SIZE ", target.path, sfp, buf) == 213) { | ||
406 | content_len = BB_STRTOOFF(buf+4, NULL, 10); | ||
407 | if (errno || content_len < 0) { | ||
408 | bb_error_msg_and_die("SIZE value is garbage"); | ||
409 | } | ||
410 | got_clen = 1; | ||
411 | } | ||
412 | |||
413 | /* | ||
414 | * Entering passive mode | ||
415 | */ | ||
416 | if (ftpcmd("PASV", NULL, sfp, buf) != 227) { | ||
417 | pasv_error: | ||
418 | bb_error_msg_and_die("bad response to %s: %s", "PASV", buf); | ||
419 | } | ||
420 | // Response is "227 garbageN1,N2,N3,N4,P1,P2[)garbage] | ||
421 | // Server's IP is N1.N2.N3.N4 (we ignore it) | ||
422 | // Server's port for data connection is P1*256+P2 | ||
423 | s = strrchr(buf, ')'); | ||
424 | if (s) s[0] = '\0'; | ||
425 | s = strrchr(buf, ','); | ||
426 | if (!s) goto pasv_error; | ||
427 | port = xatou_range(s+1, 0, 255); | ||
428 | *s = '\0'; | ||
429 | s = strrchr(buf, ','); | ||
430 | if (!s) goto pasv_error; | ||
431 | port += xatou_range(s+1, 0, 255) * 256; | ||
432 | s_in.sin_port = htons(port); | ||
433 | dfp = open_socket(&s_in); | ||
434 | |||
435 | if (beg_range) { | ||
436 | sprintf(buf, "REST %"OFF_FMT"d", beg_range); | ||
437 | if (ftpcmd(buf, NULL, sfp, buf) == 350) | ||
438 | content_len -= beg_range; | ||
439 | } | ||
440 | |||
441 | if (ftpcmd("RETR ", target.path, sfp, buf) > 150) | ||
442 | bb_error_msg_and_die("bad response to RETR: %s", buf); | ||
443 | } | ||
444 | |||
445 | |||
446 | /* | ||
447 | * Retrieve file | ||
448 | */ | ||
449 | if (chunked) { | ||
450 | fgets(buf, sizeof(buf), dfp); | ||
451 | content_len = STRTOOFF(buf, NULL, 16); | ||
452 | /* FIXME: error check?? */ | ||
453 | } | ||
454 | |||
455 | /* Do it before progressmeter (want to have nice error message) */ | ||
456 | if (output_fd < 0) | ||
457 | output_fd = xopen(fname_out, | ||
458 | O_WRONLY|O_CREAT|O_EXCL|O_TRUNC); | ||
459 | |||
460 | if (!(opt & WGET_OPT_QUIET)) | ||
461 | progressmeter(-1); | ||
462 | |||
463 | do { | ||
464 | while (content_len > 0 || !got_clen) { | ||
465 | unsigned rdsz = sizeof(buf); | ||
466 | if (content_len < sizeof(buf) && (chunked || got_clen)) | ||
467 | rdsz = (unsigned)content_len; | ||
468 | n = safe_fread(buf, 1, rdsz, dfp); | ||
469 | if (n <= 0) | ||
470 | break; | ||
471 | if (full_write(output_fd, buf, n) != n) { | ||
472 | bb_perror_msg_and_die(bb_msg_write_error); | ||
473 | } | ||
474 | #if ENABLE_FEATURE_WGET_STATUSBAR | ||
475 | transferred += n; | ||
476 | #endif | ||
477 | if (got_clen) { | ||
478 | content_len -= n; | ||
479 | } | ||
480 | } | ||
481 | |||
482 | if (chunked) { | ||
483 | safe_fgets(buf, sizeof(buf), dfp); /* This is a newline */ | ||
484 | safe_fgets(buf, sizeof(buf), dfp); | ||
485 | content_len = STRTOOFF(buf, NULL, 16); | ||
486 | /* FIXME: error check? */ | ||
487 | if (content_len == 0) { | ||
488 | chunked = 0; /* all done! */ | ||
489 | } | ||
490 | } | ||
491 | |||
492 | if (n == 0 && ferror(dfp)) { | ||
493 | bb_perror_msg_and_die(bb_msg_read_error); | ||
494 | } | ||
495 | } while (chunked); | ||
496 | |||
497 | if (!(opt & WGET_OPT_QUIET)) | ||
498 | progressmeter(1); | ||
499 | |||
500 | if ((use_proxy == 0) && target.is_ftp) { | ||
501 | fclose(dfp); | ||
502 | if (ftpcmd(NULL, NULL, sfp, buf) != 226) | ||
503 | bb_error_msg_and_die("ftp error: %s", buf+4); | ||
504 | ftpcmd("QUIT", NULL, sfp, buf); | ||
505 | } | ||
506 | exit(EXIT_SUCCESS); | ||
507 | } | ||
508 | |||
509 | |||
510 | static void parse_url(char *src_url, struct host_info *h) | ||
511 | { | ||
512 | char *url, *p, *cp, *sp, *up, *pp; | ||
513 | |||
514 | /* h->allocated = */ url = xstrdup(src_url); | ||
515 | |||
516 | if (strncmp(url, "http://", 7) == 0) { | ||
517 | h->port = bb_lookup_port("http", "tcp", 80); | ||
518 | h->host = url + 7; | ||
519 | h->is_ftp = 0; | ||
520 | } else if (strncmp(url, "ftp://", 6) == 0) { | ||
521 | h->port = bb_lookup_port("ftp", "tcp", 21); | ||
522 | h->host = url + 6; | ||
523 | h->is_ftp = 1; | ||
524 | } else | ||
525 | bb_error_msg_and_die("not an http or ftp url: %s", url); | ||
526 | |||
527 | // FYI: | ||
528 | // "Real" wget 'http://busybox.net?var=a/b' sends this request: | ||
529 | // 'GET /?var=a/b HTTP 1.0' | ||
530 | // and saves 'index.html?var=a%2Fb' (we save 'b') | ||
531 | // wget 'http://busybox.net?login=john@doe': | ||
532 | // request: 'GET /?login=john@doe HTTP/1.0' | ||
533 | // saves: 'index.html?login=john@doe' (we save '?login=john@doe') | ||
534 | // wget 'http://busybox.net#test/test': | ||
535 | // request: 'GET / HTTP/1.0' | ||
536 | // saves: 'index.html' (we save 'test') | ||
537 | // | ||
538 | // We also don't add unique .N suffix if file exists... | ||
539 | sp = strchr(h->host, '/'); | ||
540 | p = strchr(h->host, '?'); if (!sp || (p && sp > p)) sp = p; | ||
541 | p = strchr(h->host, '#'); if (!sp || (p && sp > p)) sp = p; | ||
542 | if (!sp) { | ||
543 | h->path = ""; | ||
544 | } else if (*sp == '/') { | ||
545 | *sp++ = '\0'; | ||
546 | h->path = sp; | ||
547 | } else { // '#' or '?' | ||
548 | // http://busybox.net?login=john@doe is a valid URL | ||
549 | // memmove converts to: | ||
550 | // http:/busybox.nett?login=john@doe... | ||
551 | memmove(h->host-1, h->host, sp - h->host); | ||
552 | h->host--; | ||
553 | sp[-1] = '\0'; | ||
554 | h->path = sp; | ||
555 | } | ||
556 | |||
557 | up = strrchr(h->host, '@'); | ||
558 | if (up != NULL) { | ||
559 | h->user = h->host; | ||
560 | *up++ = '\0'; | ||
561 | h->host = up; | ||
562 | } else | ||
563 | h->user = NULL; | ||
564 | |||
565 | pp = h->host; | ||
566 | |||
567 | #if ENABLE_FEATURE_WGET_IP6_LITERAL | ||
568 | if (h->host[0] == '[') { | ||
569 | char *ep; | ||
570 | |||
571 | ep = h->host + 1; | ||
572 | while (*ep == ':' || isxdigit(*ep)) | ||
573 | ep++; | ||
574 | if (*ep == ']') { | ||
575 | h->host++; | ||
576 | *ep = '\0'; | ||
577 | pp = ep + 1; | ||
578 | } | ||
579 | } | ||
580 | #endif | ||
581 | |||
582 | cp = strchr(pp, ':'); | ||
583 | if (cp != NULL) { | ||
584 | *cp++ = '\0'; | ||
585 | h->port = htons(xatou16(cp)); | ||
586 | } | ||
587 | } | ||
588 | |||
589 | |||
590 | static FILE *open_socket(struct sockaddr_in *s_in) | ||
591 | { | ||
592 | FILE *fp; | ||
593 | |||
594 | /* glibc 2.4 seems to try seeking on it - ??! */ | ||
595 | /* hopefully it understands what ESPIPE means... */ | ||
596 | fp = fdopen(xconnect_tcp_v4(s_in), "r+"); | ||
597 | if (fp == NULL) | ||
598 | bb_perror_msg_and_die("fdopen"); | ||
599 | |||
600 | return fp; | ||
601 | } | ||
602 | |||
603 | |||
604 | static char *gethdr(char *buf, size_t bufsiz, FILE *fp, int *istrunc) | ||
605 | { | ||
606 | char *s, *hdrval; | ||
607 | int c; | ||
608 | |||
609 | *istrunc = 0; | ||
610 | |||
611 | /* retrieve header line */ | ||
612 | if (fgets(buf, bufsiz, fp) == NULL) | ||
613 | return NULL; | ||
614 | |||
615 | /* see if we are at the end of the headers */ | ||
616 | for (s = buf; *s == '\r'; ++s) | ||
617 | ; | ||
618 | if (s[0] == '\n') | ||
619 | return NULL; | ||
620 | |||
621 | /* convert the header name to lower case */ | ||
622 | for (s = buf; isalnum(*s) || *s == '-'; ++s) | ||
623 | *s = tolower(*s); | ||
624 | |||
625 | /* verify we are at the end of the header name */ | ||
626 | if (*s != ':') | ||
627 | bb_error_msg_and_die("bad header line: %s", buf); | ||
628 | |||
629 | /* locate the start of the header value */ | ||
630 | for (*s++ = '\0'; *s == ' ' || *s == '\t'; ++s) | ||
631 | ; | ||
632 | hdrval = s; | ||
633 | |||
634 | /* locate the end of header */ | ||
635 | while (*s != '\0' && *s != '\r' && *s != '\n') | ||
636 | ++s; | ||
637 | |||
638 | /* end of header found */ | ||
639 | if (*s != '\0') { | ||
640 | *s = '\0'; | ||
641 | return hdrval; | ||
642 | } | ||
643 | |||
644 | /* Rats! The buffer isn't big enough to hold the entire header value. */ | ||
645 | while (c = getc(fp), c != EOF && c != '\n') | ||
646 | ; | ||
647 | *istrunc = 1; | ||
648 | return hdrval; | ||
649 | } | ||
650 | |||
651 | static int ftpcmd(char *s1, char *s2, FILE *fp, char *buf) | ||
652 | { | ||
653 | int result; | ||
654 | if (s1) { | ||
655 | if (!s2) s2 = ""; | ||
656 | fprintf(fp, "%s%s\r\n", s1, s2); | ||
657 | fflush(fp); | ||
658 | } | ||
659 | |||
660 | do { | ||
661 | char *buf_ptr; | ||
662 | |||
663 | if (fgets(buf, 510, fp) == NULL) { | ||
664 | bb_perror_msg_and_die("error getting response"); | ||
665 | } | ||
666 | buf_ptr = strstr(buf, "\r\n"); | ||
667 | if (buf_ptr) { | ||
668 | *buf_ptr = '\0'; | ||
669 | } | ||
670 | } while (!isdigit(buf[0]) || buf[3] != ' '); | ||
671 | |||
672 | buf[3] = '\0'; | ||
673 | result = xatoi_u(buf); | ||
674 | buf[3] = ' '; | ||
675 | return result; | ||
676 | } | ||
677 | |||
678 | #if ENABLE_FEATURE_WGET_STATUSBAR | ||
679 | /* Stuff below is from BSD rcp util.c, as added to openshh. | ||
680 | * Original copyright notice is retained at the end of this file. | ||
681 | */ | ||
682 | static int | ||
683 | getttywidth(void) | ||
684 | { | ||
685 | int width; | ||
686 | get_terminal_width_height(0, &width, NULL); | ||
687 | return width; | ||
688 | } | ||
689 | |||
690 | static void | ||
691 | updateprogressmeter(int ignore) | ||
692 | { | ||
693 | int save_errno = errno; | ||
694 | |||
695 | progressmeter(0); | ||
696 | errno = save_errno; | ||
697 | } | ||
698 | |||
699 | static void alarmtimer(int iwait) | ||
700 | { | ||
701 | struct itimerval itv; | ||
702 | |||
703 | itv.it_value.tv_sec = iwait; | ||
704 | itv.it_value.tv_usec = 0; | ||
705 | itv.it_interval = itv.it_value; | ||
706 | setitimer(ITIMER_REAL, &itv, NULL); | ||
707 | } | ||
708 | |||
709 | |||
710 | static void | ||
711 | progressmeter(int flag) | ||
712 | { | ||
713 | static struct timeval lastupdate; | ||
714 | static off_t lastsize, totalsize; | ||
715 | |||
716 | struct timeval now, td, tvwait; | ||
717 | off_t abbrevsize; | ||
718 | int elapsed, ratio, barlength, i; | ||
719 | char buf[256]; | ||
720 | |||
721 | if (flag == -1) { /* first call to progressmeter */ | ||
722 | gettimeofday(&start, (struct timezone *) 0); | ||
723 | lastupdate = start; | ||
724 | lastsize = 0; | ||
725 | totalsize = content_len + beg_range; /* as content_len changes.. */ | ||
726 | } | ||
727 | |||
728 | gettimeofday(&now, (struct timezone *) 0); | ||
729 | ratio = 100; | ||
730 | if (totalsize != 0 && !chunked) { | ||
731 | /* long long helps to have working ETA even if !LFS */ | ||
732 | ratio = (int) (100 * (unsigned long long)(transferred+beg_range) / totalsize); | ||
733 | ratio = MIN(ratio, 100); | ||
734 | } | ||
735 | |||
736 | fprintf(stderr, "\r%-20.20s%4d%% ", curfile, ratio); | ||
737 | |||
738 | barlength = getttywidth() - 51; | ||
739 | if (barlength > 0 && barlength < sizeof(buf)) { | ||
740 | i = barlength * ratio / 100; | ||
741 | memset(buf, '*', i); | ||
742 | memset(buf + i, ' ', barlength - i); | ||
743 | buf[barlength] = '\0'; | ||
744 | fprintf(stderr, "|%s|", buf); | ||
745 | } | ||
746 | i = 0; | ||
747 | abbrevsize = transferred + beg_range; | ||
748 | while (abbrevsize >= 100000) { | ||
749 | i++; | ||
750 | abbrevsize >>= 10; | ||
751 | } | ||
752 | /* see http://en.wikipedia.org/wiki/Tera */ | ||
753 | fprintf(stderr, "%6d %c%c ", (int)abbrevsize, " KMGTPEZY"[i], i?'B':' '); | ||
754 | |||
755 | timersub(&now, &lastupdate, &tvwait); | ||
756 | if (transferred > lastsize) { | ||
757 | lastupdate = now; | ||
758 | lastsize = transferred; | ||
759 | if (tvwait.tv_sec >= STALLTIME) | ||
760 | timeradd(&start, &tvwait, &start); | ||
761 | tvwait.tv_sec = 0; | ||
762 | } | ||
763 | timersub(&now, &start, &td); | ||
764 | elapsed = td.tv_sec; | ||
765 | |||
766 | if (tvwait.tv_sec >= STALLTIME) { | ||
767 | fprintf(stderr, " - stalled -"); | ||
768 | } else { | ||
769 | off_t to_download = totalsize - beg_range; | ||
770 | if (transferred <= 0 || elapsed <= 0 || transferred > to_download || chunked) { | ||
771 | fprintf(stderr, "--:--:-- ETA"); | ||
772 | } else { | ||
773 | /* to_download / (transferred/elapsed) - elapsed: */ | ||
774 | int eta = (int) ((unsigned long long)to_download*elapsed/transferred - elapsed); | ||
775 | /* (long long helps to have working ETA even if !LFS) */ | ||
776 | i = eta % 3600; | ||
777 | fprintf(stderr, "%02d:%02d:%02d ETA", eta / 3600, i / 60, i % 60); | ||
778 | } | ||
779 | } | ||
780 | |||
781 | if (flag == -1) { /* first call to progressmeter */ | ||
782 | struct sigaction sa; | ||
783 | sa.sa_handler = updateprogressmeter; | ||
784 | sigemptyset(&sa.sa_mask); | ||
785 | sa.sa_flags = SA_RESTART; | ||
786 | sigaction(SIGALRM, &sa, NULL); | ||
787 | alarmtimer(1); | ||
788 | } else if (flag == 1) { /* last call to progressmeter */ | ||
789 | alarmtimer(0); | ||
790 | transferred = 0; | ||
791 | putc('\n', stderr); | ||
792 | } | ||
793 | } | ||
794 | #endif | ||
795 | |||
796 | /* Original copyright notice which applies to the CONFIG_FEATURE_WGET_STATUSBAR stuff, | ||
797 | * much of which was blatantly stolen from openssh. */ | ||
798 | |||
799 | /*- | ||
800 | * Copyright (c) 1992, 1993 | ||
801 | * The Regents of the University of California. All rights reserved. | ||
802 | * | ||
803 | * Redistribution and use in source and binary forms, with or without | ||
804 | * modification, are permitted provided that the following conditions | ||
805 | * are met: | ||
806 | * 1. Redistributions of source code must retain the above copyright | ||
807 | * notice, this list of conditions and the following disclaimer. | ||
808 | * 2. Redistributions in binary form must reproduce the above copyright | ||
809 | * notice, this list of conditions and the following disclaimer in the | ||
810 | * documentation and/or other materials provided with the distribution. | ||
811 | * | ||
812 | * 3. <BSD Advertising Clause omitted per the July 22, 1999 licensing change | ||
813 | * ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change> | ||
814 | * | ||
815 | * 4. Neither the name of the University nor the names of its contributors | ||
816 | * may be used to endorse or promote products derived from this software | ||
817 | * without specific prior written permission. | ||
818 | * | ||
819 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | ||
820 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||
821 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | ||
822 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | ||
823 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||
824 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | ||
825 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | ||
826 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | ||
827 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | ||
828 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | ||
829 | * SUCH DAMAGE. | ||
830 | * | ||
831 | * $Id: wget.c,v 1.75 2004/10/08 08:27:40 andersen Exp $ | ||
832 | */ | ||
diff --git a/networking/zcip.c b/networking/zcip.c new file mode 100644 index 000000000..27e281c93 --- /dev/null +++ b/networking/zcip.c | |||
@@ -0,0 +1,546 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * RFC3927 ZeroConf IPv4 Link-Local addressing | ||
4 | * (see <http://www.zeroconf.org/>) | ||
5 | * | ||
6 | * Copyright (C) 2003 by Arthur van Hoff (avh@strangeberry.com) | ||
7 | * Copyright (C) 2004 by David Brownell | ||
8 | * | ||
9 | * Licensed under the GPL v2 or later, see the file LICENSE in this tarball. | ||
10 | */ | ||
11 | |||
12 | /* | ||
13 | * ZCIP just manages the 169.254.*.* addresses. That network is not | ||
14 | * routed at the IP level, though various proxies or bridges can | ||
15 | * certainly be used. Its naming is built over multicast DNS. | ||
16 | */ | ||
17 | |||
18 | //#define DEBUG | ||
19 | |||
20 | // TODO: | ||
21 | // - more real-world usage/testing, especially daemon mode | ||
22 | // - kernel packet filters to reduce scheduling noise | ||
23 | // - avoid silent script failures, especially under load... | ||
24 | // - link status monitoring (restart on link-up; stop on link-down) | ||
25 | |||
26 | #include "busybox.h" | ||
27 | #include <syslog.h> | ||
28 | #include <poll.h> | ||
29 | #include <sys/wait.h> | ||
30 | #include <netinet/ether.h> | ||
31 | #include <net/ethernet.h> | ||
32 | #include <net/if.h> | ||
33 | #include <net/if_arp.h> | ||
34 | |||
35 | #include <linux/if_packet.h> | ||
36 | #include <linux/sockios.h> | ||
37 | |||
38 | |||
39 | struct arp_packet { | ||
40 | struct ether_header hdr; | ||
41 | struct ether_arp arp; | ||
42 | } ATTRIBUTE_PACKED; | ||
43 | |||
44 | enum { | ||
45 | /* 169.254.0.0 */ | ||
46 | LINKLOCAL_ADDR = 0xa9fe0000, | ||
47 | |||
48 | /* protocol timeout parameters, specified in seconds */ | ||
49 | PROBE_WAIT = 1, | ||
50 | PROBE_MIN = 1, | ||
51 | PROBE_MAX = 2, | ||
52 | PROBE_NUM = 3, | ||
53 | MAX_CONFLICTS = 10, | ||
54 | RATE_LIMIT_INTERVAL = 60, | ||
55 | ANNOUNCE_WAIT = 2, | ||
56 | ANNOUNCE_NUM = 2, | ||
57 | ANNOUNCE_INTERVAL = 2, | ||
58 | DEFEND_INTERVAL = 10 | ||
59 | }; | ||
60 | |||
61 | /* States during the configuration process. */ | ||
62 | enum { | ||
63 | PROBE = 0, | ||
64 | RATE_LIMIT_PROBE, | ||
65 | ANNOUNCE, | ||
66 | MONITOR, | ||
67 | DEFEND | ||
68 | }; | ||
69 | |||
70 | #define VDBG(fmt,args...) \ | ||
71 | do { } while (0) | ||
72 | |||
73 | static unsigned opts; | ||
74 | #define FOREGROUND (opts & 1) | ||
75 | #define QUIT (opts & 2) | ||
76 | |||
77 | /** | ||
78 | * Pick a random link local IP address on 169.254/16, except that | ||
79 | * the first and last 256 addresses are reserved. | ||
80 | */ | ||
81 | static void pick(struct in_addr *ip) | ||
82 | { | ||
83 | unsigned tmp; | ||
84 | |||
85 | /* use cheaper math than lrand48() mod N */ | ||
86 | do { | ||
87 | tmp = (lrand48() >> 16) & IN_CLASSB_HOST; | ||
88 | } while (tmp > (IN_CLASSB_HOST - 0x0200)); | ||
89 | ip->s_addr = htonl((LINKLOCAL_ADDR + 0x0100) + tmp); | ||
90 | } | ||
91 | |||
92 | /* TODO: we need a flag to direct bb_[p]error_msg output to stderr. */ | ||
93 | |||
94 | /** | ||
95 | * Broadcast an ARP packet. | ||
96 | */ | ||
97 | static void arp(int fd, struct sockaddr *saddr, int op, | ||
98 | const struct ether_addr *source_addr, struct in_addr source_ip, | ||
99 | const struct ether_addr *target_addr, struct in_addr target_ip) | ||
100 | { | ||
101 | struct arp_packet p; | ||
102 | memset(&p, 0, sizeof(p)); | ||
103 | |||
104 | // ether header | ||
105 | p.hdr.ether_type = htons(ETHERTYPE_ARP); | ||
106 | memcpy(p.hdr.ether_shost, source_addr, ETH_ALEN); | ||
107 | memset(p.hdr.ether_dhost, 0xff, ETH_ALEN); | ||
108 | |||
109 | // arp request | ||
110 | p.arp.arp_hrd = htons(ARPHRD_ETHER); | ||
111 | p.arp.arp_pro = htons(ETHERTYPE_IP); | ||
112 | p.arp.arp_hln = ETH_ALEN; | ||
113 | p.arp.arp_pln = 4; | ||
114 | p.arp.arp_op = htons(op); | ||
115 | memcpy(&p.arp.arp_sha, source_addr, ETH_ALEN); | ||
116 | memcpy(&p.arp.arp_spa, &source_ip, sizeof (p.arp.arp_spa)); | ||
117 | memcpy(&p.arp.arp_tha, target_addr, ETH_ALEN); | ||
118 | memcpy(&p.arp.arp_tpa, &target_ip, sizeof (p.arp.arp_tpa)); | ||
119 | |||
120 | // send it | ||
121 | if (sendto(fd, &p, sizeof (p), 0, saddr, sizeof (*saddr)) < 0) { | ||
122 | bb_perror_msg("sendto"); | ||
123 | //return -errno; | ||
124 | } | ||
125 | // Currently all callers ignore errors, that's why returns are | ||
126 | // commented out... | ||
127 | //return 0; | ||
128 | } | ||
129 | |||
130 | /** | ||
131 | * Run a script. | ||
132 | */ | ||
133 | static int run(char *script, char *arg, char *intf, struct in_addr *ip) | ||
134 | { | ||
135 | int pid, status; | ||
136 | char *why; | ||
137 | |||
138 | if(1) { //always true: if (script != NULL) | ||
139 | VDBG("%s run %s %s\n", intf, script, arg); | ||
140 | if (ip != NULL) { | ||
141 | char *addr = inet_ntoa(*ip); | ||
142 | setenv("ip", addr, 1); | ||
143 | bb_info_msg("%s %s %s", arg, intf, addr); | ||
144 | } | ||
145 | |||
146 | pid = vfork(); | ||
147 | if (pid < 0) { // error | ||
148 | why = "vfork"; | ||
149 | goto bad; | ||
150 | } else if (pid == 0) { // child | ||
151 | execl(script, script, arg, NULL); | ||
152 | bb_perror_msg("execl"); | ||
153 | _exit(EXIT_FAILURE); | ||
154 | } | ||
155 | |||
156 | if (waitpid(pid, &status, 0) <= 0) { | ||
157 | why = "waitpid"; | ||
158 | goto bad; | ||
159 | } | ||
160 | if (WEXITSTATUS(status) != 0) { | ||
161 | bb_error_msg("script %s failed, exit=%d", | ||
162 | script, WEXITSTATUS(status)); | ||
163 | return -errno; | ||
164 | } | ||
165 | } | ||
166 | return 0; | ||
167 | bad: | ||
168 | status = -errno; | ||
169 | bb_perror_msg("%s %s, %s", arg, intf, why); | ||
170 | return status; | ||
171 | } | ||
172 | |||
173 | |||
174 | /** | ||
175 | * Return milliseconds of random delay, up to "secs" seconds. | ||
176 | */ | ||
177 | static unsigned ATTRIBUTE_ALWAYS_INLINE ms_rdelay(unsigned secs) | ||
178 | { | ||
179 | return lrand48() % (secs * 1000); | ||
180 | } | ||
181 | |||
182 | /** | ||
183 | * main program | ||
184 | */ | ||
185 | |||
186 | /* Used to be auto variables on main() stack, but | ||
187 | * most of them were zero-inited. Moving them to bss | ||
188 | * is more space-efficient. | ||
189 | */ | ||
190 | static const struct in_addr null_ip; // = { 0 }; | ||
191 | static const struct ether_addr null_addr; // = { {0, 0, 0, 0, 0, 0} }; | ||
192 | |||
193 | static struct sockaddr saddr; // memset(0); | ||
194 | static struct in_addr ip; // = { 0 }; | ||
195 | static struct ifreq ifr; //memset(0); | ||
196 | |||
197 | static char *intf; // = NULL; | ||
198 | static char *script; // = NULL; | ||
199 | static suseconds_t timeout; // = 0; // milliseconds | ||
200 | static unsigned conflicts; // = 0; | ||
201 | static unsigned nprobes; // = 0; | ||
202 | static unsigned nclaims; // = 0; | ||
203 | static int ready; // = 0; | ||
204 | static int verbose; // = 0; | ||
205 | static int state = PROBE; | ||
206 | |||
207 | int zcip_main(int argc, char *argv[]) | ||
208 | { | ||
209 | struct ether_addr eth_addr; | ||
210 | char *why; | ||
211 | int fd; | ||
212 | |||
213 | // parse commandline: prog [options] ifname script | ||
214 | char *r_opt; | ||
215 | opt_complementary = "vv:vf"; // -v accumulates and implies -f | ||
216 | opts = getopt32(argc, argv, "fqr:v", &r_opt, &verbose); | ||
217 | if (!FOREGROUND) { | ||
218 | /* Do it early, before all bb_xx_msg calls */ | ||
219 | logmode = LOGMODE_SYSLOG; | ||
220 | openlog(applet_name, 0, LOG_DAEMON); | ||
221 | } | ||
222 | if (opts & 4) { // -r n.n.n.n | ||
223 | if (inet_aton(r_opt, &ip) == 0 | ||
224 | || (ntohl(ip.s_addr) & IN_CLASSB_NET) != LINKLOCAL_ADDR) { | ||
225 | bb_error_msg_and_die("invalid link address"); | ||
226 | } | ||
227 | } | ||
228 | argc -= optind; | ||
229 | argv += optind; | ||
230 | if (argc != 2) | ||
231 | bb_show_usage(); | ||
232 | intf = argv[0]; | ||
233 | script = argv[1]; | ||
234 | setenv("interface", intf, 1); | ||
235 | |||
236 | // initialize the interface (modprobe, ifup, etc) | ||
237 | if (run(script, "init", intf, NULL) < 0) | ||
238 | return EXIT_FAILURE; | ||
239 | |||
240 | // initialize saddr | ||
241 | //memset(&saddr, 0, sizeof (saddr)); | ||
242 | safe_strncpy(saddr.sa_data, intf, sizeof (saddr.sa_data)); | ||
243 | |||
244 | // open an ARP socket | ||
245 | fd = xsocket(PF_PACKET, SOCK_PACKET, htons(ETH_P_ARP)); | ||
246 | // bind to the interface's ARP socket | ||
247 | xbind(fd, &saddr, sizeof (saddr)); | ||
248 | |||
249 | // get the interface's ethernet address | ||
250 | //memset(&ifr, 0, sizeof (ifr)); | ||
251 | strncpy(ifr.ifr_name, intf, sizeof (ifr.ifr_name)); | ||
252 | if (ioctl(fd, SIOCGIFHWADDR, &ifr) < 0) { | ||
253 | bb_perror_msg_and_die("get ethernet address"); | ||
254 | } | ||
255 | memcpy(ð_addr, &ifr.ifr_hwaddr.sa_data, ETH_ALEN); | ||
256 | |||
257 | // start with some stable ip address, either a function of | ||
258 | // the hardware address or else the last address we used. | ||
259 | // NOTE: the sequence of addresses we try changes only | ||
260 | // depending on when we detect conflicts. | ||
261 | // (SVID 3 bogon: who says that "short" is always 16 bits?) | ||
262 | seed48( (unsigned short*)&ifr.ifr_hwaddr.sa_data ); | ||
263 | if (ip.s_addr == 0) | ||
264 | pick(&ip); | ||
265 | |||
266 | // FIXME cases to handle: | ||
267 | // - zcip already running! | ||
268 | // - link already has local address... just defend/update | ||
269 | |||
270 | // daemonize now; don't delay system startup | ||
271 | if (!FOREGROUND) { | ||
272 | setsid(); | ||
273 | xdaemon(0, 0); | ||
274 | bb_info_msg("start, interface %s", intf); | ||
275 | } | ||
276 | |||
277 | // run the dynamic address negotiation protocol, | ||
278 | // restarting after address conflicts: | ||
279 | // - start with some address we want to try | ||
280 | // - short random delay | ||
281 | // - arp probes to see if another host else uses it | ||
282 | // - arp announcements that we're claiming it | ||
283 | // - use it | ||
284 | // - defend it, within limits | ||
285 | while (1) { | ||
286 | struct pollfd fds[1]; | ||
287 | struct timeval tv1; | ||
288 | struct arp_packet p; | ||
289 | |||
290 | int source_ip_conflict = 0; | ||
291 | int target_ip_conflict = 0; | ||
292 | |||
293 | fds[0].fd = fd; | ||
294 | fds[0].events = POLLIN; | ||
295 | fds[0].revents = 0; | ||
296 | |||
297 | // poll, being ready to adjust current timeout | ||
298 | if (!timeout) { | ||
299 | timeout = ms_rdelay(PROBE_WAIT); | ||
300 | // FIXME setsockopt(fd, SO_ATTACH_FILTER, ...) to | ||
301 | // make the kernel filter out all packets except | ||
302 | // ones we'd care about. | ||
303 | } | ||
304 | // set tv1 to the point in time when we timeout | ||
305 | gettimeofday(&tv1, NULL); | ||
306 | tv1.tv_usec += (timeout % 1000) * 1000; | ||
307 | while (tv1.tv_usec > 1000000) { | ||
308 | tv1.tv_usec -= 1000000; | ||
309 | tv1.tv_sec++; | ||
310 | } | ||
311 | tv1.tv_sec += timeout / 1000; | ||
312 | |||
313 | VDBG("...wait %ld %s nprobes=%d, nclaims=%d\n", | ||
314 | timeout, intf, nprobes, nclaims); | ||
315 | switch (poll(fds, 1, timeout)) { | ||
316 | |||
317 | // timeout | ||
318 | case 0: | ||
319 | VDBG("state = %d\n", state); | ||
320 | switch (state) { | ||
321 | case PROBE: | ||
322 | // timeouts in the PROBE state mean no conflicting ARP packets | ||
323 | // have been received, so we can progress through the states | ||
324 | if (nprobes < PROBE_NUM) { | ||
325 | nprobes++; | ||
326 | VDBG("probe/%d %s@%s\n", | ||
327 | nprobes, intf, inet_ntoa(ip)); | ||
328 | arp(fd, &saddr, ARPOP_REQUEST, | ||
329 | ð_addr, null_ip, | ||
330 | &null_addr, ip); | ||
331 | timeout = PROBE_MIN * 1000; | ||
332 | timeout += ms_rdelay(PROBE_MAX | ||
333 | - PROBE_MIN); | ||
334 | } | ||
335 | else { | ||
336 | // Switch to announce state. | ||
337 | state = ANNOUNCE; | ||
338 | nclaims = 0; | ||
339 | VDBG("announce/%d %s@%s\n", | ||
340 | nclaims, intf, inet_ntoa(ip)); | ||
341 | arp(fd, &saddr, ARPOP_REQUEST, | ||
342 | ð_addr, ip, | ||
343 | ð_addr, ip); | ||
344 | timeout = ANNOUNCE_INTERVAL * 1000; | ||
345 | } | ||
346 | break; | ||
347 | case RATE_LIMIT_PROBE: | ||
348 | // timeouts in the RATE_LIMIT_PROBE state mean no conflicting ARP packets | ||
349 | // have been received, so we can move immediately to the announce state | ||
350 | state = ANNOUNCE; | ||
351 | nclaims = 0; | ||
352 | VDBG("announce/%d %s@%s\n", | ||
353 | nclaims, intf, inet_ntoa(ip)); | ||
354 | arp(fd, &saddr, ARPOP_REQUEST, | ||
355 | ð_addr, ip, | ||
356 | ð_addr, ip); | ||
357 | timeout = ANNOUNCE_INTERVAL * 1000; | ||
358 | break; | ||
359 | case ANNOUNCE: | ||
360 | // timeouts in the ANNOUNCE state mean no conflicting ARP packets | ||
361 | // have been received, so we can progress through the states | ||
362 | if (nclaims < ANNOUNCE_NUM) { | ||
363 | nclaims++; | ||
364 | VDBG("announce/%d %s@%s\n", | ||
365 | nclaims, intf, inet_ntoa(ip)); | ||
366 | arp(fd, &saddr, ARPOP_REQUEST, | ||
367 | ð_addr, ip, | ||
368 | ð_addr, ip); | ||
369 | timeout = ANNOUNCE_INTERVAL * 1000; | ||
370 | } | ||
371 | else { | ||
372 | // Switch to monitor state. | ||
373 | state = MONITOR; | ||
374 | // link is ok to use earlier | ||
375 | // FIXME update filters | ||
376 | run(script, "config", intf, &ip); | ||
377 | ready = 1; | ||
378 | conflicts = 0; | ||
379 | timeout = -1; // Never timeout in the monitor state. | ||
380 | |||
381 | // NOTE: all other exit paths | ||
382 | // should deconfig ... | ||
383 | if (QUIT) | ||
384 | return EXIT_SUCCESS; | ||
385 | } | ||
386 | break; | ||
387 | case DEFEND: | ||
388 | // We won! No ARP replies, so just go back to monitor. | ||
389 | state = MONITOR; | ||
390 | timeout = -1; | ||
391 | conflicts = 0; | ||
392 | break; | ||
393 | default: | ||
394 | // Invalid, should never happen. Restart the whole protocol. | ||
395 | state = PROBE; | ||
396 | pick(&ip); | ||
397 | timeout = 0; | ||
398 | nprobes = 0; | ||
399 | nclaims = 0; | ||
400 | break; | ||
401 | } // switch (state) | ||
402 | break; // case 0 (timeout) | ||
403 | // packets arriving | ||
404 | case 1: | ||
405 | // We need to adjust the timeout in case we didn't receive | ||
406 | // a conflicting packet. | ||
407 | if (timeout > 0) { | ||
408 | struct timeval tv2; | ||
409 | |||
410 | gettimeofday(&tv2, NULL); | ||
411 | if (timercmp(&tv1, &tv2, <)) { | ||
412 | // Current time is greater than the expected timeout time. | ||
413 | // Should never happen. | ||
414 | VDBG("missed an expected timeout\n"); | ||
415 | timeout = 0; | ||
416 | } else { | ||
417 | VDBG("adjusting timeout\n"); | ||
418 | timersub(&tv1, &tv2, &tv1); | ||
419 | timeout = 1000 * tv1.tv_sec | ||
420 | + tv1.tv_usec / 1000; | ||
421 | } | ||
422 | } | ||
423 | |||
424 | if ((fds[0].revents & POLLIN) == 0) { | ||
425 | if (fds[0].revents & POLLERR) { | ||
426 | // FIXME: links routinely go down; | ||
427 | // this shouldn't necessarily exit. | ||
428 | bb_error_msg("%s: poll error", intf); | ||
429 | if (ready) { | ||
430 | run(script, "deconfig", | ||
431 | intf, &ip); | ||
432 | } | ||
433 | return EXIT_FAILURE; | ||
434 | } | ||
435 | continue; | ||
436 | } | ||
437 | |||
438 | // read ARP packet | ||
439 | if (recv(fd, &p, sizeof (p), 0) < 0) { | ||
440 | why = "recv"; | ||
441 | goto bad; | ||
442 | } | ||
443 | if (p.hdr.ether_type != htons(ETHERTYPE_ARP)) | ||
444 | continue; | ||
445 | |||
446 | #ifdef DEBUG | ||
447 | { | ||
448 | struct ether_addr * sha = (struct ether_addr *) p.arp.arp_sha; | ||
449 | struct ether_addr * tha = (struct ether_addr *) p.arp.arp_tha; | ||
450 | struct in_addr * spa = (struct in_addr *) p.arp.arp_spa; | ||
451 | struct in_addr * tpa = (struct in_addr *) p.arp.arp_tpa; | ||
452 | VDBG("%s recv arp type=%d, op=%d,\n", | ||
453 | intf, ntohs(p.hdr.ether_type), | ||
454 | ntohs(p.arp.arp_op)); | ||
455 | VDBG("\tsource=%s %s\n", | ||
456 | ether_ntoa(sha), | ||
457 | inet_ntoa(*spa)); | ||
458 | VDBG("\ttarget=%s %s\n", | ||
459 | ether_ntoa(tha), | ||
460 | inet_ntoa(*tpa)); | ||
461 | } | ||
462 | #endif | ||
463 | if (p.arp.arp_op != htons(ARPOP_REQUEST) | ||
464 | && p.arp.arp_op != htons(ARPOP_REPLY)) | ||
465 | continue; | ||
466 | |||
467 | if (memcmp(p.arp.arp_spa, &ip.s_addr, sizeof(struct in_addr)) == 0 && | ||
468 | memcmp(ð_addr, &p.arp.arp_sha, ETH_ALEN) != 0) { | ||
469 | source_ip_conflict = 1; | ||
470 | } | ||
471 | if (memcmp(p.arp.arp_tpa, &ip.s_addr, sizeof(struct in_addr)) == 0 && | ||
472 | p.arp.arp_op == htons(ARPOP_REQUEST) && | ||
473 | memcmp(ð_addr, &p.arp.arp_tha, ETH_ALEN) != 0) { | ||
474 | target_ip_conflict = 1; | ||
475 | } | ||
476 | |||
477 | VDBG("state = %d, source ip conflict = %d, target ip conflict = %d\n", | ||
478 | state, source_ip_conflict, target_ip_conflict); | ||
479 | switch (state) { | ||
480 | case PROBE: | ||
481 | case ANNOUNCE: | ||
482 | // When probing or announcing, check for source IP conflicts | ||
483 | // and other hosts doing ARP probes (target IP conflicts). | ||
484 | if (source_ip_conflict || target_ip_conflict) { | ||
485 | conflicts++; | ||
486 | if (conflicts >= MAX_CONFLICTS) { | ||
487 | VDBG("%s ratelimit\n", intf); | ||
488 | timeout = RATE_LIMIT_INTERVAL * 1000; | ||
489 | state = RATE_LIMIT_PROBE; | ||
490 | } | ||
491 | |||
492 | // restart the whole protocol | ||
493 | pick(&ip); | ||
494 | timeout = 0; | ||
495 | nprobes = 0; | ||
496 | nclaims = 0; | ||
497 | } | ||
498 | break; | ||
499 | case MONITOR: | ||
500 | // If a conflict, we try to defend with a single ARP probe. | ||
501 | if (source_ip_conflict) { | ||
502 | VDBG("monitor conflict -- defending\n"); | ||
503 | state = DEFEND; | ||
504 | timeout = DEFEND_INTERVAL * 1000; | ||
505 | arp(fd, &saddr, | ||
506 | ARPOP_REQUEST, | ||
507 | ð_addr, ip, | ||
508 | ð_addr, ip); | ||
509 | } | ||
510 | break; | ||
511 | case DEFEND: | ||
512 | // Well, we tried. Start over (on conflict). | ||
513 | if (source_ip_conflict) { | ||
514 | state = PROBE; | ||
515 | VDBG("defend conflict -- starting over\n"); | ||
516 | ready = 0; | ||
517 | run(script, "deconfig", intf, &ip); | ||
518 | |||
519 | // restart the whole protocol | ||
520 | pick(&ip); | ||
521 | timeout = 0; | ||
522 | nprobes = 0; | ||
523 | nclaims = 0; | ||
524 | } | ||
525 | break; | ||
526 | default: | ||
527 | // Invalid, should never happen. Restart the whole protocol. | ||
528 | VDBG("invalid state -- starting over\n"); | ||
529 | state = PROBE; | ||
530 | pick(&ip); | ||
531 | timeout = 0; | ||
532 | nprobes = 0; | ||
533 | nclaims = 0; | ||
534 | break; | ||
535 | } // switch state | ||
536 | |||
537 | break; // case 1 (packets arriving) | ||
538 | default: | ||
539 | why = "poll"; | ||
540 | goto bad; | ||
541 | } // switch poll | ||
542 | } | ||
543 | bad: | ||
544 | bb_perror_msg("%s, %s", intf, why); | ||
545 | return EXIT_FAILURE; | ||
546 | } | ||