1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23#include "common.h"
24
25#define SERVER_PORT 67
26
27
28#define MAX_LIFETIME 2*60
29
30#define SELECT_TIMEOUT (MAX_LIFETIME / 8)
31
32
33struct xid_item {
34 unsigned timestamp;
35 unsigned iface_no;
36 uint32_t xid;
37 struct sockaddr_in ip;
38 struct xid_item *next;
39} FIX_ALIASING;
40
41#define dhcprelay_xid_list (*(struct xid_item*)bb_common_bufsiz1)
42#define INIT_G() do { setup_common_bufsiz(); } while (0)
43
44static struct xid_item *xid_add(uint32_t xid, struct sockaddr_in *ip, unsigned iface_no)
45{
46 struct xid_item *item;
47
48
49 item = xmalloc(sizeof(struct xid_item));
50
51
52 item->ip = *ip;
53 item->xid = xid;
54 item->iface_no = iface_no;
55 item->timestamp = monotonic_sec();
56 item->next = dhcprelay_xid_list.next;
57 dhcprelay_xid_list.next = item;
58
59 return item;
60}
61
62static void xid_expire(void)
63{
64 struct xid_item *item = dhcprelay_xid_list.next;
65 struct xid_item *last = &dhcprelay_xid_list;
66 unsigned current_time = monotonic_sec();
67
68 while (item != NULL) {
69 if ((current_time - item->timestamp) > MAX_LIFETIME) {
70 last->next = item->next;
71 free(item);
72 item = last->next;
73 } else {
74 last = item;
75 item = item->next;
76 }
77 }
78}
79
80static struct xid_item *xid_find(uint32_t xid)
81{
82 struct xid_item *item = dhcprelay_xid_list.next;
83 while (item != NULL) {
84 if (item->xid == xid) {
85 break;
86 }
87 item = item->next;
88 }
89 return item;
90}
91
92static void xid_del(uint32_t xid)
93{
94 struct xid_item *item = dhcprelay_xid_list.next;
95 struct xid_item *last = &dhcprelay_xid_list;
96 while (item != NULL) {
97 if (item->xid == xid) {
98 last->next = item->next;
99 free(item);
100 item = last->next;
101 } else {
102 last = item;
103 item = item->next;
104 }
105 }
106}
107
108
109
110
111
112
113static int get_dhcp_packet_type(struct dhcp_packet *p)
114{
115 uint8_t *op;
116
117
118 if (p->op != BOOTREQUEST && p->op != BOOTREPLY)
119 return -1;
120
121 op = udhcp_get_option(p, DHCP_MESSAGE_TYPE);
122 if (op != NULL)
123 return op[0];
124 return -1;
125}
126
127
128
129
130
131static char **make_iface_list(char **client_and_server_ifaces, unsigned *client_number)
132{
133 char *s, **iface_list;
134 unsigned i, cn;
135
136
137 cn = 2;
138 s = client_and_server_ifaces[0];
139 while (*s) {
140 if (*s == ',')
141 cn++;
142 s++;
143 }
144 *client_number = cn;
145
146
147 iface_list = xzalloc(cn * sizeof(iface_list[0]));
148
149 iface_list[0] = client_and_server_ifaces[1];
150
151 i = 1;
152 s = xstrdup(client_and_server_ifaces[0]);
153 goto store_client_iface_name;
154
155 while (i < cn) {
156 if (*s++ == ',') {
157 s[-1] = '\0';
158 store_client_iface_name:
159 iface_list[i++] = s;
160 }
161 }
162
163 return iface_list;
164}
165
166
167
168
169static unsigned init_sockets(char **iface_list, unsigned num_clients, int *fds)
170{
171 unsigned i, n;
172
173 n = 0;
174 for (i = 0; i < num_clients; i++) {
175 fds[i] = udhcp_listen_socket( SERVER_PORT, iface_list[i]);
176 if (n < fds[i])
177 n = fds[i];
178 }
179 return n;
180}
181
182static int sendto_ip4(int sock, const void *msg, int msg_len, struct sockaddr_in *to)
183{
184 int err;
185
186 errno = 0;
187 err = sendto(sock, msg, msg_len, 0, (struct sockaddr*) to, sizeof(*to));
188 err -= msg_len;
189 if (err)
190 bb_simple_perror_msg("sendto");
191 return err;
192}
193
194
195
196
197
198
199static void pass_to_server(struct dhcp_packet *p, int packet_len, unsigned from_iface_no, int *fds,
200 struct sockaddr_in *client_addr, struct sockaddr_in *server_addr)
201{
202 int type;
203
204
205 type = get_dhcp_packet_type(p);
206
207 if (type != DHCPDISCOVER && type != DHCPREQUEST
208 && type != DHCPDECLINE && type != DHCPRELEASE
209 && type != DHCPINFORM
210 ) {
211 return;
212 }
213
214
215 xid_add(p->xid, client_addr, from_iface_no);
216
217
218
219
220
221
222
223
224
225 sendto_ip4(fds[0], p, packet_len, server_addr);
226}
227
228
229
230
231
232static void pass_to_client(struct dhcp_packet *p, int packet_len, int *fds)
233{
234 int type;
235 struct xid_item *item;
236
237
238 type = get_dhcp_packet_type(p);
239
240 if (type != DHCPOFFER && type != DHCPACK && type != DHCPNAK) {
241 return;
242 }
243
244
245 item = xid_find(p->xid);
246 if (!item) {
247 return;
248 }
249
250
251
252
253
254
255
256 if (item->ip.sin_addr.s_addr == htonl(INADDR_ANY))
257 item->ip.sin_addr.s_addr = htonl(INADDR_BROADCAST);
258
259 sendto_ip4(fds[item->iface_no], p, packet_len, &item->ip);
260
261
262
263 xid_del(p->xid);
264}
265
266int dhcprelay_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
267int dhcprelay_main(int argc UNUSED_PARAM, char **argv)
268{
269 struct sockaddr_in server_addr;
270 char **iface_list;
271 int *fds;
272 unsigned num_sockets, max_socket;
273 uint32_t our_nip;
274
275 INIT_G();
276
277 server_addr.sin_family = AF_INET;
278 server_addr.sin_addr.s_addr = htonl(INADDR_BROADCAST);
279 server_addr.sin_port = htons(SERVER_PORT);
280
281
282 if (!argv[1] || !argv[2])
283 bb_show_usage();
284 if (argv[3]) {
285 if (!inet_aton(argv[3], &server_addr.sin_addr))
286 bb_simple_perror_msg_and_die("bad server IP");
287 }
288
289 iface_list = make_iface_list(argv + 1, &num_sockets);
290
291 fds = xmalloc(num_sockets * sizeof(fds[0]));
292
293
294 max_socket = init_sockets(iface_list, num_sockets, fds);
295
296
297 if (udhcp_read_interface(argv[2], NULL, &our_nip, NULL))
298 return 1;
299
300
301 while (1) {
302
303
304 fd_set rfds;
305 struct timeval tv;
306 unsigned i;
307
308 FD_ZERO(&rfds);
309 for (i = 0; i < num_sockets; i++)
310 FD_SET(fds[i], &rfds);
311 tv.tv_sec = SELECT_TIMEOUT;
312 tv.tv_usec = 0;
313 if (select(max_socket + 1, &rfds, NULL, NULL, &tv) > 0) {
314 int packlen;
315 struct dhcp_packet dhcp_msg;
316
317
318 if (FD_ISSET(fds[0], &rfds)) {
319 packlen = udhcp_recv_kernel_packet(&dhcp_msg, fds[0]);
320
321
322 if (packlen > 0) {
323 pass_to_client(&dhcp_msg, packlen, fds);
324 }
325 }
326
327
328 for (i = 1; i < num_sockets; i++) {
329 struct sockaddr_in client_addr;
330 socklen_t addr_size;
331
332 if (!FD_ISSET(fds[i], &rfds))
333 continue;
334
335 addr_size = sizeof(client_addr);
336 packlen = recvfrom(fds[i], &dhcp_msg, sizeof(dhcp_msg), 0,
337 (struct sockaddr *)(&client_addr), &addr_size);
338 if (packlen <= 0)
339 continue;
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384 if (udhcp_read_interface(iface_list[i], NULL, &dhcp_msg.gateway_nip, NULL)) {
385
386
387 dhcp_msg.gateway_nip = our_nip;
388 }
389
390 pass_to_server(&dhcp_msg, packlen, i, fds, &client_addr, &server_addr);
391 }
392 }
393 xid_expire();
394 }
395
396
397}
398