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