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