summaryrefslogtreecommitdiff
path: root/networking
diff options
context:
space:
mode:
authorDenis Vlasenko <vda.linux@googlemail.com>2006-12-27 04:35:04 +0000
committerDenis Vlasenko <vda.linux@googlemail.com>2006-12-27 04:35:04 +0000
commit8d42f86b146871ae4c4cafd3801a85f381249a14 (patch)
treeb963999fc54eddb65f1929b894f868e24851fc9c /networking
downloadbusybox-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')
-rw-r--r--networking/Config.in720
-rw-r--r--networking/Kbuild39
-rw-r--r--networking/arping.c446
-rw-r--r--networking/dnsd.c456
-rw-r--r--networking/ether-wake.c284
-rw-r--r--networking/fakeidentd.c387
-rw-r--r--networking/ftpgetput.c340
-rw-r--r--networking/hostname.c102
-rw-r--r--networking/httpd.c1970
-rw-r--r--networking/httpd_index_cgi_example55
-rw-r--r--networking/ifconfig.c548
-rw-r--r--networking/ifupdown.c1270
-rw-r--r--networking/inetd.c1767
-rw-r--r--networking/interface.c1152
-rw-r--r--networking/ip.c48
-rw-r--r--networking/ipaddr.c25
-rw-r--r--networking/ipcalc.c194
-rw-r--r--networking/iplink.c25
-rw-r--r--networking/iproute.c25
-rw-r--r--networking/iprule.c25
-rw-r--r--networking/iptunnel.c25
-rw-r--r--networking/libiproute/Kbuild66
-rw-r--r--networking/libiproute/ip_common.h34
-rw-r--r--networking/libiproute/ip_parse_common_args.c77
-rw-r--r--networking/libiproute/ipaddress.c824
-rw-r--r--networking/libiproute/iplink.c354
-rw-r--r--networking/libiproute/iproute.c858
-rw-r--r--networking/libiproute/iprule.c331
-rw-r--r--networking/libiproute/iptunnel.c546
-rw-r--r--networking/libiproute/libnetlink.c396
-rw-r--r--networking/libiproute/libnetlink.h43
-rw-r--r--networking/libiproute/linux/pkt_sched.h414
-rw-r--r--networking/libiproute/ll_addr.c85
-rw-r--r--networking/libiproute/ll_map.c186
-rw-r--r--networking/libiproute/ll_map.h13
-rw-r--r--networking/libiproute/ll_proto.c122
-rw-r--r--networking/libiproute/ll_types.c118
-rw-r--r--networking/libiproute/rt_names.c384
-rw-r--r--networking/libiproute/rt_names.h29
-rw-r--r--networking/libiproute/rtm_map.c111
-rw-r--r--networking/libiproute/rtm_map.h11
-rw-r--r--networking/libiproute/utils.c354
-rw-r--r--networking/libiproute/utils.h103
-rw-r--r--networking/nameif.c170
-rw-r--r--networking/nc.c201
-rw-r--r--networking/netstat.c613
-rw-r--r--networking/nslookup.c149
-rw-r--r--networking/ping.c440
-rw-r--r--networking/ping6.c480
-rw-r--r--networking/route.c703
-rw-r--r--networking/telnet.c714
-rw-r--r--networking/telnetd.c578
-rw-r--r--networking/tftp.c561
-rw-r--r--networking/traceroute.c1350
-rw-r--r--networking/udhcp/Config.in67
-rw-r--r--networking/udhcp/Kbuild18
-rw-r--r--networking/udhcp/arpping.c114
-rw-r--r--networking/udhcp/clientpacket.c224
-rw-r--r--networking/udhcp/clientsocket.c59
-rw-r--r--networking/udhcp/common.c75
-rw-r--r--networking/udhcp/common.h108
-rw-r--r--networking/udhcp/dhcpc.c509
-rw-r--r--networking/udhcp/dhcpc.h50
-rw-r--r--networking/udhcp/dhcpd.c226
-rw-r--r--networking/udhcp/dhcpd.h190
-rw-r--r--networking/udhcp/dhcprelay.c340
-rw-r--r--networking/udhcp/dumpleases.c74
-rw-r--r--networking/udhcp/files.c395
-rw-r--r--networking/udhcp/leases.c145
-rw-r--r--networking/udhcp/options.c174
-rw-r--r--networking/udhcp/options.h37
-rw-r--r--networking/udhcp/packet.c211
-rw-r--r--networking/udhcp/pidfile.c66
-rw-r--r--networking/udhcp/script.c213
-rw-r--r--networking/udhcp/serverpacket.c261
-rw-r--r--networking/udhcp/signalpipe.c77
-rw-r--r--networking/udhcp/socket.c130
-rw-r--r--networking/udhcp/static_leases.c99
-rw-r--r--networking/vconfig.c164
-rw-r--r--networking/wget.c832
-rw-r--r--networking/zcip.c546
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
6menu "Networking Utilities"
7
8config 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
15config ARPING
16 bool "arping"
17 default n
18 help
19 Ping hosts by ARP packets
20
21config DNSD
22 bool "dnsd"
23 default n
24 help
25 Small and static DNS server daemon.
26
27config ETHER_WAKE
28 bool "ether-wake"
29 default n
30 help
31 Send a magic packet to wake up sleeping machines.
32
33config 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
41config FTPGET
42 bool "ftpget"
43 default n
44 help
45 Retrieve a remote file via FTP.
46
47config FTPPUT
48 bool "ftpput"
49 default n
50 help
51 Store a remote file via FTP.
52
53config 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
60config HOSTNAME
61 bool "hostname"
62 default n
63 help
64 Show or set the system's host name
65
66config HTTPD
67 bool "httpd"
68 default n
69 help
70 Serve web pages via an HTTP server.
71
72config 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
80config 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
90config 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
98config 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
106config 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
114config 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
122config 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
133config 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
141config 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 "&#60Hello&#32World&#62".
150
151config IFCONFIG
152 bool "ifconfig"
153 default n
154 help
155 Ifconfig is used to configure the kernel-resident network interfaces.
156
157config 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
165config 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
173config 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
181config 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
190config 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
198config 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
215config 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
223config 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
237config 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
251config 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
258config 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
265config 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
273config 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
281config INETD
282 bool "inetd"
283 default n
284 select FEATURE_SYSLOG
285 help
286 Internet superserver daemon
287
288config 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
295config 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
302config 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
309config 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
316config 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
323config 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
331config 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
339config 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
346config FEATURE_IP_LINK
347 bool "ip link"
348 default y
349 depends on IP
350 help
351 Configure network devices with "ip".
352
353config 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
360config 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
367config 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
374config 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
388config IPADDR
389 bool
390 default y
391 depends on FEATURE_IP_SHORT_FORMS && FEATURE_IP_ADDRESS
392
393config IPLINK
394 bool
395 default y
396 depends on FEATURE_IP_SHORT_FORMS && FEATURE_IP_LINK
397
398config IPROUTE
399 bool
400 default y
401 depends on FEATURE_IP_SHORT_FORMS && FEATURE_IP_ROUTE
402
403config IPTUNNEL
404 bool
405 default y
406 depends on FEATURE_IP_SHORT_FORMS && FEATURE_IP_TUNNEL
407
408config IPRULE
409 bool
410 default y
411 depends on FEATURE_IP_SHORT_FORMS && FEATURE_IP_RULE
412
413config 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
420config 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
427config 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
434config 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
449config NC
450 bool "nc"
451 default n
452 help
453 A simple Unix utility which reads and writes data across network
454 connections.
455
456config 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
463config 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
472config NETSTAT
473 bool "netstat"
474 default n
475 help
476 netstat prints information about the Linux networking subsystem.
477
478config NSLOOKUP
479 bool "nslookup"
480 default n
481 help
482 nslookup is a tool to query Internet name servers.
483
484config 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
491config 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
499config 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
506config 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
514config ROUTE
515 bool "route"
516 default n
517 help
518 Route displays or manipulates the kernel's IP routing tables.
519
520config 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
527config 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
536config 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
546config 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
589config 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
596config 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
604config 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
612config 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
620config 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
627config 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
636config TRACEROUTE
637 bool "traceroute"
638 default n
639 help
640 Utility to trace the route of IP packets
641
642config 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
650config 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
658config 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
665source networking/udhcp/Config.in
666
667config VCONFIG
668 bool "vconfig"
669 default n
670 help
671 Creates, removes, and configures VLAN interfaces
672
673config 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
680config 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
687config FEATURE_WGET_AUTHENTICATION
688 bool "Enable HTTP authentication"
689 default y
690 depends on WGET
691 help
692 Support authenticated HTTP transfers.
693
694config 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
701config 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
708config 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
720endmenu
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
7lib-y:=
8lib-$(CONFIG_ARPING) += arping.o
9lib-$(CONFIG_DNSD) += dnsd.o
10lib-$(CONFIG_ETHER_WAKE) += ether-wake.o
11lib-$(CONFIG_FAKEIDENTD) += fakeidentd.o
12lib-$(CONFIG_FTPGET) += ftpgetput.o
13lib-$(CONFIG_FTPPUT) += ftpgetput.o
14lib-$(CONFIG_HOSTNAME) += hostname.o
15lib-$(CONFIG_HTTPD) += httpd.o
16lib-$(CONFIG_IFCONFIG) += ifconfig.o interface.o
17lib-$(CONFIG_IFUPDOWN) += ifupdown.o
18lib-$(CONFIG_INETD) += inetd.o
19lib-$(CONFIG_IP) += ip.o
20lib-$(CONFIG_IPCALC) += ipcalc.o
21lib-$(CONFIG_IPADDR) += ipaddr.o
22lib-$(CONFIG_IPLINK) += iplink.o
23lib-$(CONFIG_IPROUTE) += iproute.o
24lib-$(CONFIG_IPRULE) += iprule.o
25lib-$(CONFIG_IPTUNNEL) += iptunnel.o
26lib-$(CONFIG_NAMEIF) += nameif.o
27lib-$(CONFIG_NC) += nc.o
28lib-$(CONFIG_NETSTAT) += netstat.o
29lib-$(CONFIG_NSLOOKUP) += nslookup.o
30lib-$(CONFIG_PING) += ping.o
31lib-$(CONFIG_PING6) += ping6.o
32lib-$(CONFIG_ROUTE) += route.o
33lib-$(CONFIG_TELNET) += telnet.o
34lib-$(CONFIG_TELNETD) += telnetd.o
35lib-$(CONFIG_TFTP) += tftp.o
36lib-$(CONFIG_TRACEROUTE) += traceroute.o
37lib-$(CONFIG_VCONFIG) += vconfig.o
38lib-$(CONFIG_WGET) += wget.o
39lib-$(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
26static struct in_addr src;
27static struct in_addr dst;
28static struct sockaddr_ll me;
29static struct sockaddr_ll he;
30static struct timeval last;
31
32enum 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};
41static int cfg;
42
43static int s;
44static unsigned count = UINT_MAX;
45static unsigned timeout;
46static int sent;
47static int brd_sent;
48static int received;
49static int brd_recv;
50static 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 )
55static 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
99static 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
124static 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
146static 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
255int 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
22static 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
32enum {
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
53struct 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
59struct 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};
67struct dns_prop {
68 uint16_t type;
69 uint16_t class;
70};
71struct 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
78static struct dns_entry *dnsentry = NULL;
79// FIXME! unused! :(
80static int daemonmode;
81static uint32_t ttl = DEFAULT_TTL;
82
83/*
84 * Convert host name from C-string to dns length/string.
85 */
86static 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 */
98static 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 */
114static 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
135static 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 */
170static 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 */
198static 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 */
223static 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)
267static 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 */
363static void interrupt(int x)
364{
365 unlink(LOCK_FILE);
366 write(2, "interrupt exiting\n", 18);
367 exit(2);
368}
369
370int 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)
89void 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
104static inline void get_dest_addr(const char *arg, struct ether_addr *eaddr);
105static inline int get_fill(unsigned char *pkt, struct ether_addr *eaddr, int broadcast);
106static inline int get_wol_pw(const char *ethoptarg, unsigned char *wol_passwd);
107
108int 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*/
215static 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
233static 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
258static 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
21static const char ident_substr[] = " : USERID : UNIX : ";
22enum { 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 */
45static 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. */
52static struct {
53 const char *identuser;
54 fd_set readfds;
55 int conncnt;
56} G;
57
58/*
59 * Prototypes
60 */
61static void reply(int s, char *buf);
62static void replyError(int s, char *buf);
63
64static const char *nobodystr = "nobody"; /* this needs to be declared like this */
65static char *bind_ip_address = "0.0.0.0";
66
67static void movefd(int from, int to)
68{
69 if (from != to) {
70 dup2(from, to);
71 close(from);
72 }
73}
74
75static 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
102static 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. */
110static 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 */
130static 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
165static 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
193static 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
209static 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
218int 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
275deleteconn:
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
310static int parseAddrs(char *ptr, char **myaddr, char **heraddr);
311static 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
331static 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
340static int chmatch(char c, char *chars)
341{
342 for (; *chars; chars++)
343 if (c == *chars)
344 return 1;
345 return 0;
346}
347
348static 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
357static 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
19typedef struct ftp_host_info_s {
20 char *user;
21 char *password;
22 struct sockaddr_in *s_in;
23} ftp_host_info_t;
24
25static char verbose_flag = 0;
26static char do_continue = 0;
27
28static 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
56static 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
73static 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
107int ftp_receive(ftp_host_info_t *server, FILE *control_stream,
108 const char *local_path, char *server_path);
109#else
110static
111int 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
195int ftp_send(ftp_host_info_t *server, FILE *control_stream,
196 const char *server_path, char *local_path);
197#else
198static
199int 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
266static 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
276int 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
18static 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
47int 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 "&#60Hello&#32World&#62"
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
98static const char httpdVersion[] = "busybox httpd/1.35 6-Oct-2004";
99static const char default_path_httpd_conf[] = "/etc";
100static const char httpd_conf[] = "httpd.conf";
101static 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
118typedef struct HT_ACCESS {
119 char *after_colon;
120 struct HT_ACCESS *next;
121 char before_colon[1]; /* really bigger, must last */
122} Htaccess;
123
124typedef 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
131typedef 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
173static HttpdConfig *config;
174
175static const char request_GET[] = "GET"; /* size algorithmic optimize */
176
177static 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
199typedef 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
226typedef struct {
227 HttpResponseNum type;
228 const char *name;
229 const char *info;
230} HttpEnumString;
231
232static 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
261static 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
267static 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
304static 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
342static 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 ****************************************************************************/
386static 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 ****************************************************************************/
642static 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 ****************************************************************************/
681static 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 ****************************************************************************/
725static void setenv1(const char *name, const char *value)
726{
727 if (!value)
728 value = "";
729 setenv(name, value, 1);
730}
731static 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 ****************************************************************************/
758static 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 ****************************************************************************/
809static 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 ****************************************************************************/
853static 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 ****************************************************************************/
928static 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 ****************************************************************************/
969static 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 ****************************************************************************/
1247static 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
1302static 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
1346static 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) {
1400set_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
1423static 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 ****************************************************************************/
1436static 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 ****************************************************************************/
1739static 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 */
1797static 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
1817static 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
1830enum {
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
1853static 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
1861int 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 '/'
13test "${QUERY_STRING:0:1}" = "/" || exit 1
14# /../ is not allowed
15test "${QUERY_STRING%/../*}" = "$QUERY_STRING" || exit 1
16test "${QUERY_STRING%/..}" = "$QUERY_STRING" || exit 1
17
18# Outta cgi-bin...
19cd .. 2>/dev/null || exit 1
20# Strip leading '/', go to target dir
21cd "${QUERY_STRING:1}" 2>/dev/null || exit 1
22
23f=`dirname "$QUERY_STRING"`
24test "$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{
30printf "%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
41IFS='#'
42for 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// /&nbsp;}" # 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 }
53done
54printf "</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
66struct 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
165struct arg1opt {
166 const char *name;
167 int selector;
168 unsigned short ifr_offset;
169};
170
171struct 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
185static 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
215static 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
257static int in_ether(const char *bufp, struct sockaddr *sap);
258#endif
259
260#if ENABLE_FEATURE_IFCONFIG_STATUS
261extern int interface_opt_a;
262extern int display_interfaces(char *ifname);
263#endif
264
265/*
266 * Our main function.
267 */
268
269int 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. */
509static 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 */
35struct interface_defn_t;
36
37typedef int execfn(char *command);
38
39struct 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
46struct address_family_t
47{
48 char *name;
49 int n_methods;
50 const struct method_t *method;
51};
52
53struct 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
68struct variable_t
69{
70 char *name;
71 char *value;
72};
73
74struct 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
85struct 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:"
93enum {
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
106static char **__myenviron;
107
108static char *startup_PATH;
109
110#if ENABLE_FEATURE_IFUPDOWN_IPV4 || ENABLE_FEATURE_IFUPDOWN_IPV6
111
112#ifdef CONFIG_FEATURE_IFUPDOWN_IP
113
114static 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
122static 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
137static 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
151static 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
160static 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
185static 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 */
287static 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
307static 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
319static 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
328static 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
343static 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
353static 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
364static int v4tunnel_down(struct interface_defn_t * ifd, execfn * exec)
365{
366 return execute("ip tunnel del %iface%", ifd, exec);
367}
368#endif
369
370static 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
378static 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
386static 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
398static 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
410static 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
432static 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
446struct dhcp_client_t
447{
448 const char *name;
449 const char *startcmd;
450 const char *stopcmd;
451};
452
453static 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
473static 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
490static 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
506static int manual_up_down(struct interface_defn_t *ifd, execfn *exec)
507{
508 return 1;
509}
510
511static 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
518static int ppp_up(struct interface_defn_t *ifd, execfn *exec)
519{
520 return execute("pon [[%provider%]]", ifd, exec);
521}
522
523static int ppp_down(struct interface_defn_t *ifd, execfn *exec)
524{
525 return execute("poff [[%provider%]]", ifd, exec);
526}
527
528static 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
534static 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
540static 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
550static 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
558static 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
590static 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
605static 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
620static 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
634static 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
842static 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
866static 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
900static 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
925static 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
944static int check(char *str) {
945 return str != NULL;
946}
947
948static 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
958static 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
969static 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
1022static 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
1074static 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
1089int 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)
165static rlim_t rlim_ofile_cur = OPEN_MAX;
166static 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
184typedef 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
228static servtab_t *servtab;
229
230#ifdef INETD_FEATURE_ENABLED
231struct 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
241static void echo_stream(int, servtab_t *);
242static void echo_dg(int, servtab_t *);
243#endif
244 /* Internet /dev/null */
245#ifdef CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DISCARD
246static void discard_stream(int, servtab_t *);
247static void discard_dg(int, servtab_t *);
248#endif
249 /* Return 32 bit time since 1900 */
250#ifdef CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_TIME
251static void machtime_stream(int, servtab_t *);
252static void machtime_dg(int, servtab_t *);
253#endif
254 /* Return human-readable time */
255#ifdef CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME
256static void daytime_stream(int, servtab_t *);
257static void daytime_dg(int, servtab_t *);
258#endif
259 /* Familiar character generator */
260#ifdef CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN
261static void chargen_stream(int, servtab_t *);
262static void chargen_dg(int, servtab_t *);
263#endif
264
265static 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
295static int global_queuelen = 128;
296static int nsock, maxsock;
297static fd_set allsock;
298static int toomany;
299static int timingout;
300static struct servent *sp;
301static uid_t uid;
302
303static char *CONFIG = _PATH_INETDCONF;
304
305static FILE *fconfig;
306static char line[1024];
307static 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 */
313static char *xxstrdup(char *cp)
314{
315 return xstrdup(cp ? cp : "");
316}
317
318static 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
330static 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
341static 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
368static 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
379static 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
393static 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
420static 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
485static 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
498static 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
510again:
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
537static servtab_t *new_servtab(void)
538{
539 return xmalloc(sizeof(servtab_t));
540}
541
542static 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
573static 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
840static 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
858static 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
892static 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
1111static 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
1137static 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
1161static 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
1192static char **Argv;
1193static char *LastArg;
1194
1195static void
1196inetd_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
1222int
1223inetd_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);
1489do_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)
1509static 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 */
1523static void
1524echo_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 */
1541static void
1542echo_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 */
1563static void
1564discard_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 */
1578static void
1579discard_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
1590static char ring[128];
1591static char *endring;
1592
1593static void
1594initring(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 */
1607static void
1608chargen_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 */
1642static void
1643chargen_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
1687static 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 */
1699static void
1700machtime_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 */
1709static void
1710machtime_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 */
1735static 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 */
1748void
1749daytime_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
65struct 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. */
95struct 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. */
115static 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
129static 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. */
142static 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
154static 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. */
166static 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. */
184static 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
193static struct aftype unspec_aftype = {
194 "unspec", "UNSPEC", AF_UNSPEC, 0,
195 UNSPEC_print, UNSPEC_sprint, NULL, NULL,
196 NULL,
197};
198
199static 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. */
209static 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 */
223static 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
236struct 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
265struct 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
287int interface_opt_a; /* show all interfaces */
288
289static struct interface *int_list, *int_last;
290static int skfd = -1; /* generic raw socket desc. */
291
292
293static 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
334static 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 */
348except that the freshly added calls to xatoul() brf on ethernet aliases with
349uClibc with e.g.: ife->name='lo' name='eth0:1'
350static 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
374static 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
401static 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
449static 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
491static 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
497static 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
505static 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
539static 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
548static 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
591static 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
600static 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. */
617static 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
699static 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. */
718struct 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
729static const struct hwtype unspec_hwtype = {
730 .name = "unspec",
731 .title = "UNSPEC",
732 .type = -1,
733 .print = UNSPEC_print
734};
735
736static 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. */
751static 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
762static 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
772static 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
779static 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
788static const struct hwtype * const hwtypes[] = {
789 &loop_hwtype,
790 &ether_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
800static 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. */
814static 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 */
828static 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
839static const char TRext[] = "\0\0\0Ki\0Mi\0Gi\0Ti";
840
841static 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
864static 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
883static 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
903static 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
1095static 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
1108static 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 */
1119static 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
1136int display_interfaces(char *ifname);
1137int 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
20int 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
20int 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
25static 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
40static 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
54int 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
79int 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
20int 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
20int 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
20int 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
20int 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
8lib-y:=
9lib-$(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
25lib-$(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
35lib-$(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
46lib-$(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
55lib-$(CONFIG_IPTUNNEL) += \
56 ip_parse_common_args.o \
57 iptunnel.o \
58 rt_names.o \
59 utils.o
60
61lib-$(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
16extern int preferred_family;
17extern char * _SL_;
18
19extern void ip_parse_common_args(int *argcp, char ***argvp);
20extern int print_neigh(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg);
21extern int ipaddr_list_or_flush(int argc, char **argv, int flush);
22extern int iproute_monitor(int argc, char **argv);
23extern void iplink_usage(void) ATTRIBUTE_NORETURN;
24extern void ipneigh_reset_filter(void);
25extern int do_ipaddr(int argc, char **argv);
26extern int do_iproute(int argc, char **argv);
27extern int do_iprule(int argc, char **argv);
28extern int do_ipneigh(int argc, char **argv);
29extern int do_iptunnel(int argc, char **argv);
30extern int do_iplink(int argc, char **argv);
31extern int do_ipmonitor(int argc, char **argv);
32extern int do_multiaddr(int argc, char **argv);
33extern 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
25int preferred_family = AF_UNSPEC;
26int oneline = 0;
27char * _SL_ = NULL;
28
29void 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
29static 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
47static 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
79static 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
101static 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
194static 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
204static 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
361struct nlmsg_list
362{
363 struct nlmsg_list *next;
364 struct nlmsghdr h;
365};
366
367static 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
389static 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
409static void ipaddr_reset_filter(int _oneline)
410{
411 memset(&filter, 0, sizeof(filter));
412 filter.oneline = _oneline;
413}
414
415int 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
616static 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
625static 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
800int 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
32static 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
38static 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
58static 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
85static 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
106static 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
128static 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
150static 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
192static 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
209static 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
226static 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
334static 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
340int 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
33static 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
56static 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
66static 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
285static 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
426static 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
449static 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
472static void iproute_reset_filter(void)
473{
474 memset(&filter, 0, sizeof(filter));
475 filter.mdst.bitlen = -1;
476 filter.msrc.bitlen = -1;
477}
478
479static 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
641static 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
811int 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/*
28static void usage(void) __attribute__((noreturn));
29
30static 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*/
42static 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
161int 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
191int 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
304int 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
40static 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
55static 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
71static 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
88static 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
105static 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
126static 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
147static 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
336static 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
362static 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
382static 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
440static 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
498static 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
528int 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
26void rtnl_close(struct rtnl_handle *rth)
27{
28 close(rth->fd);
29}
30
31int 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
68int 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
89int 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
99int 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
123int 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
194skip_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
207int 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
322int 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
336int 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
351int 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
367int 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
384int 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
9struct rtnl_handle
10{
11 int fd;
12 struct sockaddr_nl local;
13 struct sockaddr_nl peer;
14 __u32 seq;
15 __u32 dump;
16};
17
18extern int rtnl_open(struct rtnl_handle *rth, unsigned subscriptions);
19extern void rtnl_close(struct rtnl_handle *rth);
20extern int rtnl_wilddump_request(struct rtnl_handle *rth, int fam, int type);
21extern int rtnl_dump_request(struct rtnl_handle *rth, int type, void *req, int len);
22extern 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);
27extern 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);
31extern int rtnl_send(struct rtnl_handle *rth, char *buf, int);
32
33
34extern int addattr32(struct nlmsghdr *n, int maxlen, int type, __u32 data);
35extern int addattr_l(struct nlmsghdr *n, int maxlen, int type, void *data, int alen);
36extern int rta_addattr32(struct rtattr *rta, int maxlen, int type, __u32 data);
37extern int rta_addattr_l(struct rtattr *rta, int maxlen, int type, void *data, int alen);
38
39extern 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
33struct 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
49struct 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
82struct 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
94struct 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
103struct 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
111struct 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
119struct 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
129enum
130{
131 TCA_CSZ_UNSPEC,
132 TCA_CSZ_PARMS,
133 TCA_CSZ_RTAB,
134 TCA_CSZ_PTAB,
135};
136
137/* TBF section */
138
139struct 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
148enum
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
163struct 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
183enum
184{
185 TCA_RED_UNSPEC,
186 TCA_RED_PARMS,
187 TCA_RED_STAB,
188};
189
190struct 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
202struct 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
214enum
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
223struct 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 */
247struct 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
258struct 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};
270struct 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};
285enum
286{
287 TCA_HTB_UNSPEC,
288 TCA_HTB_PARMS,
289 TCA_HTB_INIT,
290 TCA_HTB_CTAB,
291 TCA_HTB_RTAB,
292};
293struct 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
309struct 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
329struct 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
339struct 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
351struct tc_cbq_police
352{
353 unsigned char police;
354 unsigned char __res1;
355 unsigned short __res2;
356};
357
358struct tc_cbq_fopt
359{
360 __u32 split;
361 __u32 defmap;
362 __u32 defchange;
363};
364
365struct tc_cbq_xstats
366{
367 __u32 borrows;
368 __u32 overactions;
369 __s32 avgidle;
370 __s32 undertime;
371};
372
373enum
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
389enum {
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
402enum {
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
22const 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
46int 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
24struct 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
34static struct idxmap *idxmap[16];
35
36int 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
84const 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
98const char *ll_index_to_name(int idx)
99{
100 static char nbuf[16];
101
102 return ll_idx_n2a(idx, nbuf);
103}
104
105int 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
117unsigned 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
130int 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
176int 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
5extern int ll_remember_index(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg);
6extern int ll_init_map(struct rtnl_handle *rth);
7extern int ll_name_to_index(char *name);
8extern const char *ll_index_to_name(int idx);
9extern const char *ll_idx_n2a(int idx, char *buf);
10extern int ll_index_to_type(int idx);
11extern 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 },
26static 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
95const 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
109int 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
19const char * ll_type_n2a(int type, char *buf, int len)
20{
21#define __PF(f,n) { ARPHRD_##f, #n },
22static 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
19static 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
54static 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
72static int rtnl_rtprot_init;
73
74static 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
81const 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
97int 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
131static const char * rtnl_rtscope_tab[256] = {
132 "global",
133};
134
135static int rtnl_rtscope_init;
136
137static 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
148const 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
164int 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
198static const char * rtnl_rtrealm_tab[256] = {
199 "unknown",
200};
201
202static int rtnl_rtrealm_init;
203
204static 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
211int 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
244const 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
261static const char * rtnl_rtdsfield_tab[256] = {
262 "0",
263};
264
265static int rtnl_rtdsfield_init;
266
267static 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
274const 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
291int 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
324static int rtnl_rttable_init;
325static const char * rtnl_rttable_tab[256] = {
326 "unspec",
327};
328static 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
337const 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
353int 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
7extern const char* rtnl_rtprot_n2a(int id, char *buf, int len);
8extern const char* rtnl_rtscope_n2a(int id, char *buf, int len);
9extern const char* rtnl_rtrealm_n2a(int id, char *buf, int len);
10extern const char* rtnl_dsfield_n2a(int id, char *buf, int len);
11extern const char* rtnl_rttable_n2a(int id, char *buf, int len);
12extern int rtnl_rtprot_a2n(uint32_t *id, char *arg);
13extern int rtnl_rtscope_a2n(uint32_t *id, char *arg);
14extern int rtnl_rtrealm_a2n(uint32_t *id, char *arg);
15extern int rtnl_dsfield_a2n(uint32_t *id, char *arg);
16extern int rtnl_rttable_a2n(uint32_t *id, char *arg);
17
18
19extern const char * ll_type_n2a(int type, char *buf, int len);
20
21extern const char *ll_addr_n2a(unsigned char *addr, int alen, int type,
22 char *buf, int blen);
23extern int ll_addr_a2n(unsigned char *lladdr, int len, char *arg);
24
25extern const char * ll_proto_n2a(unsigned short id, char *buf, int len);
26extern 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
20char *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
54int 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
91int 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
5char *rtnl_rtntype_n2a(int id, char *buf, int len);
6int rtnl_rtntype_a2n(int *id, char *arg);
7
8int 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
22int 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
36int 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
50int 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
64int 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
78int 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
92int 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
106int 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
120int 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
164int 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
206int 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
217int 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
238void incomplete_command(void)
239{
240 bb_error_msg("command line is not complete, try option \"help\"");
241 exit(-1);
242}
243
244void 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
250void duparg(char *key, char *arg)
251{
252 bb_error_msg("duplicate \"%s\": \"%s\" is the second value", key, arg);
253 exit(-1);
254}
255
256void 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
262int matches(char *cmd, char *pattern)
263{
264 int len = strlen(cmd);
265
266 return strncmp(pattern, cmd, len);
267}
268
269int 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
297int __iproute2_hz_internal;
298
299int __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
317const 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
330const 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
13extern int preferred_family;
14extern int show_stats;
15extern int show_details;
16extern int show_raw;
17extern int resolve_hosts;
18extern int oneline;
19extern 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
31extern void incomplete_command(void) ATTRIBUTE_NORETURN;
32
33#define NEXT_ARG() do { argv++; if (--argc <= 0) incomplete_command(); } while(0)
34
35typedef 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
48struct dn_naddr
49{
50 unsigned short a_len;
51 unsigned char a_addr[DN_MAXADDL];
52};
53
54#define IPX_NODE_LEN 6
55
56struct ipx_addr {
57 uint32_t ipx_net;
58 uint8_t ipx_node[IPX_NODE_LEN];
59};
60
61extern __u32 get_addr32(char *name);
62extern int get_addr_1(inet_prefix *dst, char *arg, int family);
63extern int get_prefix_1(inet_prefix *dst, char *arg, int family);
64extern int get_addr(inet_prefix *dst, char *arg, int family);
65extern int get_prefix(inet_prefix *dst, char *arg, int family);
66
67extern int get_integer(int *val, char *arg, int base);
68extern 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
72extern int get_u32(__u32 *val, char *arg, int base);
73extern int get_u16(__u16 *val, char *arg, int base);
74extern int get_s16(__s16 *val, char *arg, int base);
75extern int get_u8(__u8 *val, char *arg, int base);
76extern int get_s8(__s8 *val, char *arg, int base);
77
78extern const char *format_host(int af, int len, void *addr, char *buf, int buflen);
79extern const char *rt_addr_n2a(int af, int len, void *addr, char *buf, int buflen);
80
81void invarg(const char * const, const char * const) ATTRIBUTE_NORETURN;
82void duparg(char *, char *) ATTRIBUTE_NORETURN;
83void duparg2(char *, char *) ATTRIBUTE_NORETURN;
84int matches(char *arg, char *pattern);
85extern int inet_addr_match(inet_prefix *a, inet_prefix *b, int bits);
86
87const char *dnet_ntop(int af, const void *addr, char *str, size_t len);
88int dnet_pton(int af, const char *src, void *addr);
89
90const char *ipx_ntop(int af, const void *addr, char *str, size_t len);
91int ipx_pton(int af, const char *src, void *addr);
92
93extern int __iproute2_hz_internal;
94extern int __get_hz(void);
95
96static __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
37typedef 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 */
45static 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
58int 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
12static void timeout(int signum)
13{
14 bb_error_msg_and_die("timed out");
15}
16
17int 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
18extern 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
31static 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
39enum {
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
53static 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
69typedef 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
81static 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
96static 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
119static 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
190static 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
285static 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
369static 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
497static 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
525int 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
50static 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
65static 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 */
101static 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 */
113static 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
123int 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
34enum {
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
44static void ping(const char *host);
45
46/* common routines */
47
48static 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
75static char *hostname;
76
77static void noresp(int ign)
78{
79 printf("No response from %s\n", hostname);
80 exit(EXIT_FAILURE);
81}
82
83static 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
140int 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:"
155enum {
156 OPT_QUIET = 1 << 0,
157};
158
159static struct sockaddr_in pingaddr;
160static struct sockaddr_in sourceaddr;
161static int pingsock = -1;
162static unsigned datalen; /* intentionally uninitialized to work around gcc bug */
163
164static unsigned long ntransmitted, nreceived, nrepeats, pingcount;
165static int myid;
166static unsigned long tmin = ULONG_MAX, tmax, tsum;
167static char rcvd_tbl[MAX_DUP_CHK / 8];
168
169static struct hostent *hostent;
170
171static void sendping(int);
172static void pingstats(int);
173static 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
183static 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
209static 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
246static 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
266static 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
333static 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*/
398static 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
416int 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) ...
431make 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
46enum {
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
56static void ping(const char *host);
57
58#ifndef CONFIG_FEATURE_FANCY_PING6
59
60/* simple version */
61
62static struct hostent *h;
63
64static void noresp(int ign)
65{
66 printf("No response from %s\n", h->h_name);
67 exit(EXIT_FAILURE);
68}
69
70static 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
127int 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:"
142enum {
143 OPT_QUIET = 1 << 0,
144 OPT_VERBOSE = 1 << 1,
145};
146
147static struct sockaddr_in6 pingaddr;
148static int pingsock = -1;
149static unsigned datalen; /* intentionally uninitialized to work around gcc bug */
150static int if_index;
151
152static unsigned long ntransmitted, nreceived, nrepeats, pingcount;
153static int myid;
154static unsigned long tmin = ULONG_MAX, tmax, tsum;
155static char rcvd_tbl[MAX_DUP_CHK / 8];
156
157static struct hostent *hostent;
158
159static void sendping(int);
160static void pingstats(int);
161static 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
171static 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
197static 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
244static 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
260static 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
322static 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
422int 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. */
76static 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
99static 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
124static 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
133static 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
155static 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
339static 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
442static 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
458static const char flagchars[] = /* Must agree with flagvals[]. */
459 "GHRDM"
460#ifdef CONFIG_FEATURE_IPV6
461 "DAC"
462#endif
463;
464
465static
466#ifndef CONFIG_FEATURE_IPV6
467__inline
468#endif
469void 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 */
483void displayroutes(int noresolve, int netstatfmt);
484void 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
548static 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. */
641static 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
648int 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
47enum {
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
64typedef unsigned char byte;
65
66/* use globals to reduce size ??? */ /* test this hypothesis later */
67static 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
87struct Globalvars * Gptr;
88#define G (*Gptr)
89#endif
90
91static void iacflush(void)
92{
93 write(G.netfd, G.iacbuf, G.iaclen);
94 G.iaclen = 0;
95}
96
97/* Function prototypes */
98static void rawmode(void);
99static void cookmode(void);
100static void do_linemode(void);
101static void will_charmode(void);
102static void telopt(byte c);
103static int subneg(byte c);
104
105/* Some globals */
106static const int one = 1;
107
108#ifdef CONFIG_FEATURE_TELNET_TTYPE
109static char *ttype;
110#endif
111
112#ifdef CONFIG_FEATURE_TELNET_AUTOLOGIN
113static const char *autologin;
114#endif
115
116#ifdef CONFIG_FEATURE_AUTOWIDTH
117static int win_width, win_height;
118#endif
119
120static void doexit(int ev)
121{
122 cookmode();
123 exit(ev);
124}
125
126static 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}
176static 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
217static 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
294static void putiac(int c)
295{
296 G.iacbuf[G.iaclen++] = c;
297}
298
299
300static 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
311static 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
332static 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
360static 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
383static char const escapecharis[] = "\r\nEscape character is ";
384
385static 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
407static 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
418static 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
431static 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
437static 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
464static 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
486static 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
500static 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
514static void to_naws(void)
515{
516 /* Tell server we will do NAWS */
517 putiac2(WILL, TELOPT_NAWS);
518 return;
519}
520#endif
521
522static 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
549static 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
578static void fgotsig(int sig)
579{
580 G.gotsig = sig;
581}
582
583
584static void rawmode(void)
585{
586 if (G.do_termios) tcsetattr(0, TCSADRAIN, &G.termios_raw);
587}
588
589static void cookmode(void)
590{
591 if (G.do_termios) tcsetattr(0, TCSADRAIN, &G.termios_def);
592}
593
594int 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
40typedef struct sockaddr_in6 sockaddr_type;
41#else
42typedef struct sockaddr_in sockaddr_type;
43#endif
44
45#if ENABLE_LOGIN
46static const char *loginpath = "/bin/login";
47#else
48static const char *loginpath = DEFAULT_SHELL;
49#endif
50
51static const char *issuefile = "/etc/issue.net";
52
53/* shell name and arguments */
54
55static const char *argv_init[2];
56
57/* structure that describes a session */
58
59struct 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
84static int maxfd;
85
86static 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 */
108static char *
109remove_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
175static int
176getpty(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
222static void
223send_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
235static struct tsession *
236make_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
321static void
322free_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 */
360void free_session(struct tsession *ts);
361
362#endif
363
364
365int
366telnetd_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
29static const char * const MODE_OCTET = "octet";
30#define MODE_OCTET_LEN 6 /* sizeof(MODE_OCTET)*/
31
32static 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
43static 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
65static 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
82static 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
125static 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
459int 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 */
239struct 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 */
250struct 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 */
268struct hostinfo {
269 char *name;
270 int n;
271 u_int32_t *addrs;
272};
273
274/* Data section of the probe packet */
275struct 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
281struct IFADDRLIST {
282 u_int32_t addr;
283 char device[sizeof(struct ifreq)];
284};
285
286
287static const char route[] = "/proc/net/route";
288
289/* last inbound (icmp) packet */
290static unsigned char packet[512] ATTRIBUTE_ALIGNED(32);
291
292static struct ip *outip; /* last output (udp) packet */
293static struct udphdr *outudp; /* last output (udp) packet */
294static struct outdata *outdata; /* last output (udp) packet */
295
296#if ENABLE_FEATURE_TRACEROUTE_USE_ICMP
297static 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) */
304static u_int32_t gwlist[NGATEWAYS + 1];
305#endif
306
307static int s; /* receive (icmp) socket file descriptor */
308static int sndsock; /* send (udp/icmp) socket file descriptor */
309
310static struct sockaddr_storage whereto; /* Who to try to reach */
311static struct sockaddr_storage wherefrom; /* Who we are */
312static int packlen; /* total length of packet */
313static int minpacket; /* min ip packet size */
314static int maxpacket = 32 * 1024; /* max ip packet size */
315static int pmtu; /* Path MTU Discovery (RFC1191) */
316
317static char *hostname;
318
319static u_short ident;
320static u_short port = 32768 + 666; /* start udp dest port # for probe packets */
321
322static int waittime = 5; /* time to wait for response (in seconds) */
323static int nflag; /* print addresses numerically */
324static int doipcksum = 1; /* calculate ip checksums by default */
325
326#if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE
327static int optlen; /* length of ip options */
328#else
329#define optlen 0
330#endif
331
332#if ENABLE_FEATURE_TRACEROUTE_USE_ICMP
333static int useicmp; /* use icmp echo instead of udp packets */
334#endif
335#if ENABLE_FEATURE_TRACEROUTE_VERBOSE
336static int verbose;
337#endif
338
339/*
340 * Return the interface list
341 */
342static int
343ifaddrlist(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
431static void
432setsin(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 */
446static void
447findsaddr(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 */
504static inline void
505tvsub(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
515static int
516wait_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 */
542static u_short
543in_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
575static void
576send_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
680static inline double
681deltaT(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 */
694static inline const char *
695pr_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
712static int
713packet_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 */
798static inline void
799inetname(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
816static inline void
817print(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
834static struct hostinfo *
835gethostinfo(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
866static void
867freehostinfo(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
876static void
877getaddr(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
888int
889traceroute_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
6config 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
15config 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
24config 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
35config 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
47config 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
58config 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
8lib-y:=
9lib-$(CONFIG_APP_UDHCPC) += common.o options.o packet.o pidfile.o \
10 signalpipe.o socket.o
11lib-$(CONFIG_APP_UDHCPD) += common.o options.o packet.o pidfile.o \
12 signalpipe.o socket.o
13lib-$(CONFIG_APP_UDHCPC) += dhcpc.o clientpacket.o clientsocket.o \
14 script.o
15lib-$(CONFIG_APP_UDHCPD) += dhcpd.o arpping.o files.o leases.o \
16 serverpacket.o static_leases.o
17lib-$(CONFIG_APP_DUMPLEASES) += dumpleases.o
18lib-$(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
16struct 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 */
45int 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 */
28unsigned 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 */
47static 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. */
62static 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 */
78int 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 */
95int 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 */
115int 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 */
135int 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 */
152int 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
37int 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
18long 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 */
30static 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
39void 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
55void 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
29struct 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
48struct udp_dhcp_packet {
49 struct iphdr ip;
50 struct udphdr udp;
51 struct dhcpMessage data;
52};
53
54void udhcp_init_header(struct dhcpMessage *packet, char type);
55int udhcp_get_packet(struct dhcpMessage *packet, int fd);
56uint16_t udhcp_checksum(void *addr, int count);
57int 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);
60int 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
67void udhcp_background(const char *pidfile);
68void udhcp_start_log_and_pid(const char *pidfile);
69
70void 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
91long uptime(void);
92void udhcp_sp_setup(void);
93int udhcp_sp_fd_set(fd_set *rfds, int extra_fd);
94int udhcp_sp_read(fd_set *rfds);
95int raw_socket(int ifindex);
96int read_interface(char *interface, int *ifindex, uint32_t *addr, uint8_t *arp);
97int listen_socket(uint32_t ip, int port, char *inf);
98int pidfile_acquire(const char *pidfile);
99void pidfile_write_release(int pid_fd);
100int 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
19static 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 */
25static unsigned long requested_ip; /* = 0 */
26static unsigned long server_addr;
27static unsigned long timeout;
28static int packet_num; /* = 0 */
29static int fd = -1;
30
31#define LISTEN_NONE 0
32#define LISTEN_KERNEL 1
33#define LISTEN_RAW 2
34static int listen_mode;
35
36struct client_config_t client_config;
37
38
39/* just a little helper */
40static 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 */
51static 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 */
81static 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
104static 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
112static 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
125int 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
15struct 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
36extern struct client_config_t client_config;
37
38
39/*** clientpacket.h ***/
40
41unsigned long random_xid(void);
42int send_discover(unsigned long xid, unsigned long requested);
43int send_selecting(unsigned long xid, unsigned long server, unsigned long requested);
44int send_renew(unsigned long xid, unsigned long server, unsigned long ciaddr);
45int send_renew(unsigned long xid, unsigned long server, unsigned long ciaddr);
46int send_release(unsigned long server, unsigned long ciaddr);
47int 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 */
19struct dhcpOfferedAddr *leases;
20struct server_config_t server_config;
21
22
23int 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
92struct option_set {
93 uint8_t *data;
94 struct option_set *next;
95};
96
97struct static_lease {
98 uint8_t *mac;
99 uint32_t *ip;
100 struct static_lease *next;
101};
102
103struct 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
131extern struct server_config_t server_config;
132extern struct dhcpOfferedAddr *leases;
133
134
135/*** leases.h ***/
136
137struct dhcpOfferedAddr {
138 uint8_t chaddr[16];
139 uint32_t yiaddr; /* network order */
140 uint32_t expires; /* host order */
141};
142
143extern uint8_t blank_chaddr[];
144
145void clear_lease(uint8_t *chaddr, uint32_t yiaddr);
146struct dhcpOfferedAddr *add_lease(uint8_t *chaddr, uint32_t yiaddr, unsigned long lease);
147int lease_expired(struct dhcpOfferedAddr *lease);
148struct dhcpOfferedAddr *oldest_expired_lease(void);
149struct dhcpOfferedAddr *find_lease_by_chaddr(uint8_t *chaddr);
150struct dhcpOfferedAddr *find_lease_by_yiaddr(uint32_t yiaddr);
151uint32_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 */
158int addStaticLease(struct static_lease **lease_struct, uint8_t *mac, uint32_t *ip);
159/* Check to see if a mac has an associated static lease */
160uint32_t getIpByMac(struct static_lease *lease_struct, void *arg);
161/* Check to see if an ip is reserved as a static ip */
162uint32_t reservedIp(struct static_lease *lease_struct, uint32_t ip);
163/* Print out static leases just to check what's going on (debug code) */
164void printStaticLeases(struct static_lease **lease_struct);
165
166
167/*** serverpacket.h ***/
168
169int sendOffer(struct dhcpMessage *oldpacket);
170int sendNAK(struct dhcpMessage *oldpacket);
171int sendACK(struct dhcpMessage *oldpacket, uint32_t yiaddr);
172int send_inform(struct dhcpMessage *oldpacket);
173
174
175/*** files.h ***/
176
177struct config_keyword {
178 const char *keyword;
179 int (* const handler)(const char *line, void *var);
180 void *var;
181 const char *def;
182};
183
184int read_config(const char *file);
185void write_leases(void);
186void read_leases(const char *file);
187struct 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. */
24static 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
33static 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
52static 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
70static 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
82static 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 */
104static 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 */
122static int dhcprelay_stopflag;
123static 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 */
133static 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. */
165static 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 */
194static 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 */
225static 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
257static 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
299int 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
14int 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 */
22static 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
37static 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
54static 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
65static 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
72static 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 */
88struct 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 */
99static 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 */
137static 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
217static 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
247static 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
273int 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
325void 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
367void 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
11uint8_t blank_chaddr[] = {[0 ... 15] = 0};
12
13/* clear every lease out that chaddr OR yiaddr matches and is nonzero */
14void 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 */
29struct 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 */
49int 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 */
56struct 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 */
74struct 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 */
86struct 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 */
98static 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... */
114uint32_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 */
13const 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 */
52const 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). */
66uint8_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) */
121int 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) */
135int 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 */
153int 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
8enum {
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
23struct dhcp_option {
24 char name[12];
25 char flags;
26 uint8_t code;
27};
28
29extern const struct dhcp_option dhcp_options[];
30extern const unsigned char option_lengths[];
31
32uint8_t *get_option(struct dhcpMessage *packet, int code);
33int end_option(uint8_t *optionptr);
34int add_option_string(uint8_t *optionptr, uint8_t *string);
35int 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
18void 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 */
42int 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
81uint16_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 */
112void BUG_sizeof_struct_udp_dhcp_packet_must_be_576(void);
113int 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 */
172int 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
26static const char *saved_pidfile;
27
28static void pidfile_delete(void)
29{
30 if (saved_pidfile) unlink(saved_pidfile);
31}
32
33
34int 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
53void 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...) */
18static 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
31static 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
38static 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 */
45static 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'. */
57static 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 */
117static 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 */
187void 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 */
29static 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 */
39static 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 */
67static 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
78static 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 */
91static 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 */
102int 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
190int 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
201int 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
244int 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
26static int signal_pipe[2];
27
28static 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 */
37void 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 */
51int 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 */
66int 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
40int 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
91int 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 */
17int 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 */
44uint32_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 */
65uint32_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 */
85void 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 */
16enum 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};
26enum 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
34struct 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. */
56static 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
66static 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
88static 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
107static const char conf_file_name[] = "/proc/net/vlan/config";
108
109int 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
16struct 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
26static void parse_url(char *url, struct host_info *h);
27static FILE *open_socket(struct sockaddr_in *s_in);
28static char *gethdr(char *buf, size_t bufsiz, FILE *fp, int *istrunc);
29static int ftpcmd(char *s1, char *s2, FILE *fp, char *buf);
30
31/* Globals (can be accessed from signal handlers */
32static off_t content_len; /* Content-length of the file */
33static off_t beg_range; /* Range at which continue begins */
34#if ENABLE_FEATURE_WGET_STATUSBAR
35static off_t transferred; /* Number of bytes transferred so far */
36#endif
37static int chunked; /* chunked transfer encoding */
38#if ENABLE_FEATURE_WGET_STATUSBAR
39static void progressmeter(int flag);
40static char *curfile; /* Name of current file being transferred */
41static struct timeval start; /* Time a transfer started */
42enum {
43 STALLTIME = 5 /* Seconds when xfer considered "stalled" */
44};
45#else
46static 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. */
52static 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. */
66static 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 */
82static 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
89int 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
510static 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
590static 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
604static 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
651static 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 */
682static int
683getttywidth(void)
684{
685 int width;
686 get_terminal_width_height(0, &width, NULL);
687 return width;
688}
689
690static void
691updateprogressmeter(int ignore)
692{
693 int save_errno = errno;
694
695 progressmeter(0);
696 errno = save_errno;
697}
698
699static 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
710static void
711progressmeter(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
39struct arp_packet {
40 struct ether_header hdr;
41 struct ether_arp arp;
42} ATTRIBUTE_PACKED;
43
44enum {
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. */
62enum {
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
73static 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 */
81static 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 */
97static 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 */
133static 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;
167bad:
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 */
177static 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 */
190static const struct in_addr null_ip; // = { 0 };
191static const struct ether_addr null_addr; // = { {0, 0, 0, 0, 0, 0} };
192
193static struct sockaddr saddr; // memset(0);
194static struct in_addr ip; // = { 0 };
195static struct ifreq ifr; //memset(0);
196
197static char *intf; // = NULL;
198static char *script; // = NULL;
199static suseconds_t timeout; // = 0; // milliseconds
200static unsigned conflicts; // = 0;
201static unsigned nprobes; // = 0;
202static unsigned nclaims; // = 0;
203static int ready; // = 0;
204static int verbose; // = 0;
205static int state = PROBE;
206
207int 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(&eth_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 &eth_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 &eth_addr, ip,
343 &eth_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 &eth_addr, ip,
356 &eth_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 &eth_addr, ip,
368 &eth_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(&eth_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(&eth_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 &eth_addr, ip,
508 &eth_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 }
543bad:
544 bb_perror_msg("%s, %s", intf, why);
545 return EXIT_FAILURE;
546}