source: MondoRescue/branches/3.3/mindi-busybox/networking/udhcp/dhcprelay.c@ 3803

Last change on this file since 3803 was 3621, checked in by Bruno Cornec, 10 years ago

New 3?3 banch for incorporation of latest busybox 1.25. Changing minor version to handle potential incompatibilities.

  • Property svn:eol-style set to native
File size: 10.1 KB
Line 
1/* vi: set sw=4 ts=4: */
2/* Port to Busybox Copyright (C) 2006 Jesse Dutton <jessedutton@gmail.com>
3 *
4 * Licensed under GPLv2, see file LICENSE in this source tree.
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//usage:#define dhcprelay_trivial_usage
14//usage: "CLIENT_IFACE[,CLIENT_IFACE2]... SERVER_IFACE [SERVER_IP]"
15//usage:#define dhcprelay_full_usage "\n\n"
16//usage: "Relay DHCP requests between clients and server"
17
18#include "common.h"
19
20#define SERVER_PORT 67
21
22/* lifetime of an xid entry in sec. */
23#define MAX_LIFETIME 2*60
24/* select timeout in sec. */
25#define SELECT_TIMEOUT (MAX_LIFETIME / 8)
26
27/* This list holds information about clients. The xid_* functions manipulate this list. */
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#define INIT_G() do { setup_common_bufsiz(); } while (0)
38
39static struct xid_item *xid_add(uint32_t xid, struct sockaddr_in *ip, int client)
40{
41 struct xid_item *item;
42
43 /* create new xid entry */
44 item = xmalloc(sizeof(struct xid_item));
45
46 /* add xid entry */
47 item->ip = *ip;
48 item->xid = xid;
49 item->client = client;
50 item->timestamp = monotonic_sec();
51 item->next = dhcprelay_xid_list.next;
52 dhcprelay_xid_list.next = item;
53
54 return item;
55}
56
57static void xid_expire(void)
58{
59 struct xid_item *item = dhcprelay_xid_list.next;
60 struct xid_item *last = &dhcprelay_xid_list;
61 unsigned current_time = monotonic_sec();
62
63 while (item != NULL) {
64 if ((current_time - item->timestamp) > MAX_LIFETIME) {
65 last->next = item->next;
66 free(item);
67 item = last->next;
68 } else {
69 last = item;
70 item = item->next;
71 }
72 }
73}
74
75static struct xid_item *xid_find(uint32_t xid)
76{
77 struct xid_item *item = dhcprelay_xid_list.next;
78 while (item != NULL) {
79 if (item->xid == xid) {
80 break;
81 }
82 item = item->next;
83 }
84 return item;
85}
86
87static void xid_del(uint32_t xid)
88{
89 struct xid_item *item = dhcprelay_xid_list.next;
90 struct xid_item *last = &dhcprelay_xid_list;
91 while (item != NULL) {
92 if (item->xid == xid) {
93 last->next = item->next;
94 free(item);
95 item = last->next;
96 } else {
97 last = item;
98 item = item->next;
99 }
100 }
101}
102
103/**
104 * get_dhcp_packet_type - gets the message type of a dhcp packet
105 * p - pointer to the dhcp packet
106 * returns the message type on success, -1 otherwise
107 */
108static int get_dhcp_packet_type(struct dhcp_packet *p)
109{
110 uint8_t *op;
111
112 /* it must be either a BOOTREQUEST or a BOOTREPLY */
113 if (p->op != BOOTREQUEST && p->op != BOOTREPLY)
114 return -1;
115 /* get message type option */
116 op = udhcp_get_option(p, DHCP_MESSAGE_TYPE);
117 if (op != NULL)
118 return op[0];
119 return -1;
120}
121
122/**
123 * make_iface_list - parses client/server interface names
124 * returns array
125 */
126static char **make_iface_list(char **client_and_server_ifaces, int *client_number)
127{
128 char *s, **iface_list;
129 int i, cn;
130
131 /* get number of items */
132 cn = 2; /* 1 server iface + at least 1 client one */
133 s = client_and_server_ifaces[0]; /* list of client ifaces */
134 while (*s) {
135 if (*s == ',')
136 cn++;
137 s++;
138 }
139 *client_number = cn;
140
141 /* create vector of pointers */
142 iface_list = xzalloc(cn * sizeof(iface_list[0]));
143
144 iface_list[0] = client_and_server_ifaces[1]; /* server iface */
145
146 i = 1;
147 s = xstrdup(client_and_server_ifaces[0]); /* list of client ifaces */
148 goto store_client_iface_name;
149
150 while (i < cn) {
151 if (*s++ == ',') {
152 s[-1] = '\0';
153 store_client_iface_name:
154 iface_list[i++] = s;
155 }
156 }
157
158 return iface_list;
159}
160
161/* Creates listen sockets (in fds) bound to client and server ifaces,
162 * and returns numerically max fd.
163 */
164static int init_sockets(char **iface_list, int num_clients, int *fds)
165{
166 int i, n;
167
168 n = 0;
169 for (i = 0; i < num_clients; i++) {
170 fds[i] = udhcp_listen_socket(/*INADDR_ANY,*/ SERVER_PORT, iface_list[i]);
171 if (n < fds[i])
172 n = fds[i];
173 }
174 return n;
175}
176
177static int sendto_ip4(int sock, const void *msg, int msg_len, struct sockaddr_in *to)
178{
179 int err;
180
181 errno = 0;
182 err = sendto(sock, msg, msg_len, 0, (struct sockaddr*) to, sizeof(*to));
183 err -= msg_len;
184 if (err)
185 bb_perror_msg("sendto");
186 return err;
187}
188
189/**
190 * pass_to_server() - forwards dhcp packets from client to server
191 * p - packet to send
192 * client - number of the client
193 */
194static void pass_to_server(struct dhcp_packet *p, int packet_len, int client, int *fds,
195 struct sockaddr_in *client_addr, struct sockaddr_in *server_addr)
196{
197 int type;
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 xid_add(p->xid, client_addr, client);
210
211 /* forward request to server */
212 /* note that we send from fds[0] which is bound to SERVER_PORT (67).
213 * IOW: we send _from_ SERVER_PORT! Although this may look strange,
214 * RFC 1542 not only allows, but prescribes this for BOOTP relays.
215 */
216 sendto_ip4(fds[0], p, packet_len, server_addr);
217}
218
219/**
220 * pass_to_client() - forwards dhcp packets from server to client
221 * p - packet to send
222 */
223static void pass_to_client(struct dhcp_packet *p, int packet_len, int *fds)
224{
225 int type;
226 struct xid_item *item;
227
228 /* check xid */
229 item = xid_find(p->xid);
230 if (!item) {
231 return;
232 }
233
234 /* check packet type */
235 type = get_dhcp_packet_type(p);
236 if (type != DHCPOFFER && type != DHCPACK && type != DHCPNAK) {
237 return;
238 }
239
240//TODO: also do it if (p->flags & htons(BROADCAST_FLAG)) is set!
241 if (item->ip.sin_addr.s_addr == htonl(INADDR_ANY))
242 item->ip.sin_addr.s_addr = htonl(INADDR_BROADCAST);
243
244 if (sendto_ip4(fds[item->client], p, packet_len, &item->ip) != 0) {
245 return; /* send error occurred */
246 }
247
248 /* remove xid entry */
249 xid_del(p->xid);
250}
251
252int dhcprelay_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
253int dhcprelay_main(int argc, char **argv)
254{
255 struct sockaddr_in server_addr;
256 char **iface_list;
257 int *fds;
258 int num_sockets, max_socket;
259 uint32_t our_nip;
260
261 INIT_G();
262
263 server_addr.sin_family = AF_INET;
264 server_addr.sin_addr.s_addr = htonl(INADDR_BROADCAST);
265 server_addr.sin_port = htons(SERVER_PORT);
266
267 /* dhcprelay CLIENT_IFACE1[,CLIENT_IFACE2...] SERVER_IFACE [SERVER_IP] */
268 if (argc == 4) {
269 if (!inet_aton(argv[3], &server_addr.sin_addr))
270 bb_perror_msg_and_die("bad server IP");
271 } else if (argc != 3) {
272 bb_show_usage();
273 }
274
275 iface_list = make_iface_list(argv + 1, &num_sockets);
276
277 fds = xmalloc(num_sockets * sizeof(fds[0]));
278
279 /* Create sockets and bind one to every iface */
280 max_socket = init_sockets(iface_list, num_sockets, fds);
281
282 /* Get our IP on server_iface */
283 if (udhcp_read_interface(argv[2], NULL, &our_nip, NULL))
284 return 1;
285
286 /* Main loop */
287 while (1) {
288// reinit stuff from time to time? go back to make_iface_list
289// every N minutes?
290 fd_set rfds;
291 struct timeval tv;
292 int i;
293
294 FD_ZERO(&rfds);
295 for (i = 0; i < num_sockets; i++)
296 FD_SET(fds[i], &rfds);
297 tv.tv_sec = SELECT_TIMEOUT;
298 tv.tv_usec = 0;
299 if (select(max_socket + 1, &rfds, NULL, NULL, &tv) > 0) {
300 int packlen;
301 struct dhcp_packet dhcp_msg;
302
303 /* server */
304 if (FD_ISSET(fds[0], &rfds)) {
305 packlen = udhcp_recv_kernel_packet(&dhcp_msg, fds[0]);
306 if (packlen > 0) {
307 pass_to_client(&dhcp_msg, packlen, fds);
308 }
309 }
310
311 /* clients */
312 for (i = 1; i < num_sockets; i++) {
313 struct sockaddr_in client_addr;
314 socklen_t addr_size;
315
316 if (!FD_ISSET(fds[i], &rfds))
317 continue;
318
319 addr_size = sizeof(client_addr);
320 packlen = recvfrom(fds[i], &dhcp_msg, sizeof(dhcp_msg), 0,
321 (struct sockaddr *)(&client_addr), &addr_size);
322 if (packlen <= 0)
323 continue;
324
325 /* Get our IP on corresponding client_iface */
326// RFC 1542
327// 4.1 General BOOTP Processing for Relay Agents
328// 4.1.1 BOOTREQUEST Messages
329// If the relay agent does decide to relay the request, it MUST examine
330// the 'giaddr' ("gateway" IP address) field. If this field is zero,
331// the relay agent MUST fill this field with the IP address of the
332// interface on which the request was received. If the interface has
333// more than one IP address logically associated with it, the relay
334// agent SHOULD choose one IP address associated with that interface and
335// use it consistently for all BOOTP messages it relays. If the
336// 'giaddr' field contains some non-zero value, the 'giaddr' field MUST
337// NOT be modified. The relay agent MUST NOT, under any circumstances,
338// fill the 'giaddr' field with a broadcast address as is suggested in
339// [1] (Section 8, sixth paragraph).
340
341// but why? what if server can't route such IP? Client ifaces may be, say, NATed!
342
343// 4.1.2 BOOTREPLY Messages
344// BOOTP relay agents relay BOOTREPLY messages only to BOOTP clients.
345// It is the responsibility of BOOTP servers to send BOOTREPLY messages
346// directly to the relay agent identified in the 'giaddr' field.
347// (yeah right, unless it is impossible... see comment above)
348// Therefore, a relay agent may assume that all BOOTREPLY messages it
349// receives are intended for BOOTP clients on its directly-connected
350// networks.
351//
352// When a relay agent receives a BOOTREPLY message, it should examine
353// the BOOTP 'giaddr', 'yiaddr', 'chaddr', 'htype', and 'hlen' fields.
354// These fields should provide adequate information for the relay agent
355// to deliver the BOOTREPLY message to the client.
356//
357// The 'giaddr' field can be used to identify the logical interface from
358// which the reply must be sent (i.e., the host or router interface
359// connected to the same network as the BOOTP client). If the content
360// of the 'giaddr' field does not match one of the relay agent's
361// directly-connected logical interfaces, the BOOTREPLY messsage MUST be
362// silently discarded.
363 if (udhcp_read_interface(iface_list[i], NULL, &dhcp_msg.gateway_nip, NULL)) {
364 /* Fall back to our IP on server iface */
365// this makes more sense!
366 dhcp_msg.gateway_nip = our_nip;
367 }
368// maybe dhcp_msg.hops++? drop packets with too many hops (RFC 1542 says 4 or 16)?
369 pass_to_server(&dhcp_msg, packlen, i, fds, &client_addr, &server_addr);
370 }
371 }
372 xid_expire();
373 } /* while (1) */
374
375 /* return 0; - not reached */
376}
Note: See TracBrowser for help on using the repository browser.