aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenis Vlasenko <vda.linux@googlemail.com>2006-11-20 19:40:36 +0000
committerDenis Vlasenko <vda.linux@googlemail.com>2006-11-20 19:40:36 +0000
commit736230e2098e55720a1e235b1c13bdd1a3372c86 (patch)
tree8269cafd5207e1d2d3f3870c83b2e0b2bdc3bf42
parentc9cdeaa3fef3f93eb259957d36c760ec8902e0c1 (diff)
downloadbusybox-w32-736230e2098e55720a1e235b1c13bdd1a3372c86.tar.gz
busybox-w32-736230e2098e55720a1e235b1c13bdd1a3372c86.tar.bz2
busybox-w32-736230e2098e55720a1e235b1c13bdd1a3372c86.zip
dhcprelay: new applet
-rw-r--r--include/applets.h1
-rw-r--r--include/usage.h10
-rw-r--r--networking/ifupdown.c9
-rw-r--r--networking/udhcp/Config.in9
-rw-r--r--networking/udhcp/Kbuild1
-rw-r--r--networking/udhcp/dhcprelay.c339
-rw-r--r--networking/udhcp/socket.c30
7 files changed, 386 insertions, 13 deletions
diff --git a/include/applets.h b/include/applets.h
index 78fa68861..1d6cc932e 100644
--- a/include/applets.h
+++ b/include/applets.h
@@ -94,6 +94,7 @@ USE_DELGROUP(APPLET_ODDNAME(delgroup, deluser, _BB_DIR_BIN, _BB_SUID_NEVER, delg
94USE_DELUSER(APPLET(deluser, _BB_DIR_BIN, _BB_SUID_NEVER)) 94USE_DELUSER(APPLET(deluser, _BB_DIR_BIN, _BB_SUID_NEVER))
95USE_DEVFSD(APPLET(devfsd, _BB_DIR_SBIN, _BB_SUID_NEVER)) 95USE_DEVFSD(APPLET(devfsd, _BB_DIR_SBIN, _BB_SUID_NEVER))
96USE_DF(APPLET(df, _BB_DIR_BIN, _BB_SUID_NEVER)) 96USE_DF(APPLET(df, _BB_DIR_BIN, _BB_SUID_NEVER))
97USE_APP_DHCPRELAY(APPLET(dhcprelay, _BB_DIR_USR_SBIN, _BB_SUID_NEVER))
97USE_DIFF(APPLET(diff, _BB_DIR_USR_BIN, _BB_SUID_NEVER)) 98USE_DIFF(APPLET(diff, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
98USE_DIRNAME(APPLET(dirname, _BB_DIR_USR_BIN, _BB_SUID_NEVER)) 99USE_DIRNAME(APPLET(dirname, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
99USE_DMESG(APPLET(dmesg, _BB_DIR_BIN, _BB_SUID_NEVER)) 100USE_DMESG(APPLET(dmesg, _BB_DIR_BIN, _BB_SUID_NEVER))
diff --git a/include/usage.h b/include/usage.h
index 9b2d11a4d..7898473a6 100644
--- a/include/usage.h
+++ b/include/usage.h
@@ -539,6 +539,16 @@ USE_FEATURE_DATE_ISOFMT( \
539 "Filesystem 1k-blocks Used Available Use% Mounted on\n" \ 539 "Filesystem 1k-blocks Used Available Use% Mounted on\n" \
540 "/dev/sda3 8690864 8553540 137324 98% /\n" 540 "/dev/sda3 8690864 8553540 137324 98% /\n"
541 541
542#define dhcprelay_trivial_usage \
543 "[client_device_list] [server_device]"
544#define dhcprelay_full_usage \
545 "Relays dhcp requests from client devices to server device"
546
547#define dhcprelay_trivial_usage \
548 "[client_device_list] [server_device]"
549#define dhcprelay_full_usage \
550 "Relays dhcp requests from client devices to server device"
551
542#define diff_trivial_usage \ 552#define diff_trivial_usage \
543 "[-abdiNqrTstw] [-L LABEL] [-S FILE] [-U LINES] FILE1 FILE2" 553 "[-abdiNqrTstw] [-L LABEL] [-S FILE] [-U LINES] FILE1 FILE2"
544#define diff_full_usage \ 554#define diff_full_usage \
diff --git a/networking/ifupdown.c b/networking/ifupdown.c
index ede2f8997..f72e653b3 100644
--- a/networking/ifupdown.c
+++ b/networking/ifupdown.c
@@ -598,6 +598,9 @@ static const struct address_family_t *get_address_family(const struct address_fa
598{ 598{
599 int i; 599 int i;
600 600
601 if (!name)
602 return NULL;
603
601 for (i = 0; af[i]; i++) { 604 for (i = 0; af[i]; i++) {
602 if (strcmp(af[i]->name, name) == 0) { 605 if (strcmp(af[i]->name, name) == 0) {
603 return af[i]; 606 return af[i];
@@ -610,6 +613,9 @@ static const struct method_t *get_method(const struct address_family_t *af, char
610{ 613{
611 int i; 614 int i;
612 615
616 if (!name)
617 return NULL;
618
613 for (i = 0; i < af->n_methods; i++) { 619 for (i = 0; i < af->n_methods; i++) {
614 if (strcmp(af->method[i].name, name) == 0) { 620 if (strcmp(af->method[i].name, name) == 0) {
615 return &af->method[i]; 621 return &af->method[i];
@@ -620,6 +626,9 @@ static const struct method_t *get_method(const struct address_family_t *af, char
620 626
621static const llist_t *find_list_string(const llist_t *list, const char *string) 627static const llist_t *find_list_string(const llist_t *list, const char *string)
622{ 628{
629 if (string == NULL)
630 return NULL;
631
623 while (list) { 632 while (list) {
624 if (strcmp(list->data, string) == 0) { 633 if (strcmp(list->data, string) == 0) {
625 return list; 634 return list;
diff --git a/networking/udhcp/Config.in b/networking/udhcp/Config.in
index 13dbcee9c..f633473eb 100644
--- a/networking/udhcp/Config.in
+++ b/networking/udhcp/Config.in
@@ -12,6 +12,15 @@ config APP_UDHCPD
12 12
13 See http://udhcp.busybox.net for further details. 13 See http://udhcp.busybox.net for further details.
14 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
15config APP_DUMPLEASES 24config APP_DUMPLEASES
16 bool "Lease display utility (dumpleases)" 25 bool "Lease display utility (dumpleases)"
17 default n 26 default n
diff --git a/networking/udhcp/Kbuild b/networking/udhcp/Kbuild
index 90047c174..dc2c01f61 100644
--- a/networking/udhcp/Kbuild
+++ b/networking/udhcp/Kbuild
@@ -15,3 +15,4 @@ lib-$(CONFIG_APP_UDHCPC) += dhcpc.o clientpacket.o clientsocket.o \
15lib-$(CONFIG_APP_UDHCPD) += dhcpd.o arpping.o files.o leases.o \ 15lib-$(CONFIG_APP_UDHCPD) += dhcpd.o arpping.o files.o leases.o \
16 serverpacket.o static_leases.o 16 serverpacket.o static_leases.o
17lib-$(CONFIG_APP_DUMPLEASES) += dumpleases.o 17lib-$(CONFIG_APP_DUMPLEASES) += dumpleases.o
18lib-$(CONFIG_APP_DHCPRELAY) += dhcprelay.o
diff --git a/networking/udhcp/dhcprelay.c b/networking/udhcp/dhcprelay.c
new file mode 100644
index 000000000..97bdcb084
--- /dev/null
+++ b/networking/udhcp/dhcprelay.c
@@ -0,0 +1,339 @@
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. */
24struct 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=1;
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; *s; s++) if (*s == ',')
144 cn++;
145
146 client_dev = xzalloc(cn * sizeof(*client_dev));
147
148 /* parse list */
149 s = strtok(list, ",");
150 i = 0;
151 while (s != NULL) {
152 client_dev[i++] = xstrdup(s);
153 s = strtok(NULL, ",");
154 }
155
156 /* free copy and exit */
157 free(list);
158 *client_number = cn;
159 return client_dev;
160}
161
162
163/* Creates listen sockets (in fds) and returns the number allocated. */
164static int init_sockets(char **client, int num_clients,
165 char *server, int *fds, int *max_socket)
166{
167 int i;
168
169 // talk to real server on bootps
170 fds[0] = listen_socket(htonl(INADDR_ANY), 67, server);
171 if (fds[0] < 0) return -1;
172 *max_socket = fds[0];
173
174 // array starts at 1 since server is 0
175 num_clients++;
176
177 for (i=1; i < num_clients; i++) {
178 // listen for clients on bootps
179 fds[i] = listen_socket(htonl(INADDR_ANY), 67, client[i-1]);
180 if (fds[i] < 0) return -1;
181 if (fds[i] > *max_socket) *max_socket = fds[i];
182 }
183
184 return i;
185}
186
187
188/**
189 * pass_on() - forwards dhcp packets from client to server
190 * p - packet to send
191 * client - number of the client
192 */
193static void pass_on(struct dhcpMessage *p, int packet_len, int client, int *fds,
194 struct sockaddr_in *client_addr, struct sockaddr_in *server_addr)
195{
196 int res, type;
197 struct xid_item *item;
198
199 /* check packet_type */
200 type = get_dhcp_packet_type(p);
201 if (type != DHCPDISCOVER && type != DHCPREQUEST
202 && type != DHCPDECLINE && type != DHCPRELEASE
203 && type != DHCPINFORM
204 ) {
205 return;
206 }
207
208 /* create new xid entry */
209 item = xid_add(p->xid, client_addr, client);
210
211 /* forward request to LAN (server) */
212 res = sendto(fds[0], p, packet_len, 0, (struct sockaddr*)server_addr,
213 sizeof(struct sockaddr_in));
214 if (res != packet_len) {
215 bb_perror_msg("pass_on");
216 return;
217 }
218}
219
220/**
221 * pass_back() - forwards dhcp packets from server to client
222 * p - packet to send
223 */
224static void pass_back(struct dhcpMessage *p, int packet_len, int *fds)
225{
226 int res, type;
227 struct xid_item *item;
228
229 /* check xid */
230 item = xid_find(p->xid);
231 if (!item) {
232 return;
233 }
234
235 /* check packet type */
236 type = get_dhcp_packet_type(p);
237 if (type != DHCPOFFER && type != DHCPACK && type != DHCPNAK) {
238 return;
239 }
240
241 if (item->ip.sin_addr.s_addr == htonl(INADDR_ANY))
242 item->ip.sin_addr.s_addr = htonl(INADDR_BROADCAST);
243 if (item->client > MAX_INTERFACES)
244 return;
245 res = sendto(fds[item->client], p, packet_len, 0, (struct sockaddr*)(&item->ip),
246 sizeof(item->ip));
247 if (res != packet_len) {
248 bb_perror_msg("pass_back");
249 return;
250 }
251
252 /* remove xid entry */
253 xid_del(p->xid);
254}
255
256static void dhcprelay_loop(int *fds, int num_sockets, int max_socket, char **clients,
257 struct sockaddr_in *server_addr, uint32_t gw_ip)
258{
259 struct dhcpMessage dhcp_msg;
260 fd_set rfds;
261 size_t packlen, addr_size;
262 struct sockaddr_in client_addr;
263 struct timeval tv;
264 int i;
265
266 while (!dhcprelay_stopflag) {
267 FD_ZERO(&rfds);
268 for (i = 0; i < num_sockets; i++)
269 FD_SET(fds[i], &rfds);
270 tv.tv_sec = SELECT_TIMEOUT;
271 tv.tv_usec = 0;
272 if (select(max_socket + 1, &rfds, NULL, NULL, &tv) > 0) {
273 /* server */
274 if (FD_ISSET(fds[0], &rfds)) {
275 packlen = udhcp_get_packet(&dhcp_msg, fds[0]);
276 if (packlen > 0) {
277 pass_back(&dhcp_msg, packlen, fds);
278 }
279 }
280 for (i = 1; i < num_sockets; i++) {
281 /* clients */
282 if (!FD_ISSET(fds[i], &rfds))
283 continue;
284 addr_size = sizeof(struct sockaddr_in);
285 packlen = recvfrom(fds[i], &dhcp_msg, sizeof(dhcp_msg), 0,
286 (struct sockaddr *)(&client_addr), &addr_size);
287 if (packlen <= 0)
288 continue;
289 if (read_interface(clients[i-1], NULL, &dhcp_msg.giaddr, NULL) < 0)
290 dhcp_msg.giaddr = gw_ip;
291 pass_on(&dhcp_msg, packlen, i, fds, &client_addr, server_addr);
292 }
293 }
294 xid_expire();
295 }
296}
297
298int dhcprelay_main(int argc, char **argv)
299{
300 int i, num_sockets, max_socket, fds[MAX_INTERFACES];
301 uint32_t gw_ip;
302 char **clients;
303 struct sockaddr_in server_addr;
304
305 server_addr.sin_family = AF_INET;
306 server_addr.sin_port = htons(67);
307 if (argc == 4) {
308 if (!inet_aton(argv[3], &server_addr.sin_addr))
309 bb_perror_msg_and_die("didn't grok server");
310 } else if (argc == 3) {
311 server_addr.sin_addr.s_addr = htonl(INADDR_BROADCAST);
312 } else {
313 bb_show_usage();
314 }
315 clients = get_client_devices(argv[1], &num_sockets);
316 if (!clients) return 0;
317
318 signal(SIGTERM, dhcprelay_signal_handler);
319 signal(SIGQUIT, dhcprelay_signal_handler);
320 signal(SIGINT, dhcprelay_signal_handler);
321
322 num_sockets = init_sockets(clients, num_sockets, argv[2], fds, &max_socket);
323 if (num_sockets == -1)
324 bb_perror_msg_and_die("init_sockets() failed");
325
326 if (read_interface(argv[2], NULL, &gw_ip, NULL) == -1)
327 return 1;
328
329 dhcprelay_loop(fds, num_sockets, max_socket, clients, &server_addr, gw_ip);
330
331 if (ENABLE_FEATURE_CLEAN_UP) {
332 for (i = 0; i < num_sockets; i++) {
333 close(fds[i]);
334 free(clients[i]);
335 }
336 }
337
338 return 0;
339}
diff --git a/networking/udhcp/socket.c b/networking/udhcp/socket.c
index ea2913172..c19131d65 100644
--- a/networking/udhcp/socket.c
+++ b/networking/udhcp/socket.c
@@ -63,23 +63,27 @@ int read_interface(char *interface, int *ifindex, uint32_t *addr, uint8_t *arp)
63 DEBUG("%s (our ip) = %s", ifr.ifr_name, inet_ntoa(our_ip->sin_addr)); 63 DEBUG("%s (our ip) = %s", ifr.ifr_name, inet_ntoa(our_ip->sin_addr));
64 } 64 }
65 65
66 if (ioctl(fd, SIOCGIFINDEX, &ifr) == 0) { 66 if (ifindex) {
67 bb_perror_msg("SIOCGIFINDEX failed"); 67 if (ioctl(fd, SIOCGIFINDEX, &ifr) == 0) {
68 close(fd); 68 bb_perror_msg("SIOCGIFINDEX failed");
69 return -1; 69 close(fd);
70 return -1;
71 }
72 DEBUG("adapter index %d", ifr.ifr_ifindex);
73 *ifindex = ifr.ifr_ifindex;
70 } 74 }
71 75
72 DEBUG("adapter index %d", ifr.ifr_ifindex); 76 if (arp) {
73 *ifindex = ifr.ifr_ifindex; 77 if (ioctl(fd, SIOCGIFHWADDR, &ifr) != 0) {
74 if (ioctl(fd, SIOCGIFHWADDR, &ifr) != 0) { 78 bb_perror_msg("SIOCGIFHWADDR failed");
75 bb_perror_msg("SIOCGIFHWADDR failed"); 79 close(fd);
76 close(fd); 80 return -1;
77 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]);
78 } 85 }
79 86
80 memcpy(arp, ifr.ifr_hwaddr.sa_data, 6);
81 DEBUG("adapter hardware address %02x:%02x:%02x:%02x:%02x:%02x",
82 arp[0], arp[1], arp[2], arp[3], arp[4], arp[5]);
83 return 0; 87 return 0;
84} 88}
85 89