aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenys Vlasenko <vda.linux@googlemail.com>2010-10-06 01:45:24 +0200
committerDenys Vlasenko <vda.linux@googlemail.com>2010-10-06 01:45:24 +0200
commitc0f39b0fb288206036d2c442588cf597390c3622 (patch)
tree889f9bcfe5230ce7b2173dec73d99671c242dd98
parent28458c64db779c27ecd63f400ea9af0e3e6b555a (diff)
downloadbusybox-w32-c0f39b0fb288206036d2c442588cf597390c3622.tar.gz
busybox-w32-c0f39b0fb288206036d2c442588cf597390c3622.tar.bz2
busybox-w32-c0f39b0fb288206036d2c442588cf597390c3622.zip
dhcprelay: code shrink, and explain its workings a bit more
function old new delta sendto_ip4 - 55 +55 dhcprelay_main 1059 942 -117 ------------------------------------------------------------------------------ (add/remove: 1/0 grow/shrink: 0/1 up/down: 55/-117) Total: -62 bytes Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-rw-r--r--networking/udhcp/dhcprelay.c189
1 files changed, 118 insertions, 71 deletions
diff --git a/networking/udhcp/dhcprelay.c b/networking/udhcp/dhcprelay.c
index a2c7f359d..759a4ba03 100644
--- a/networking/udhcp/dhcprelay.c
+++ b/networking/udhcp/dhcprelay.c
@@ -11,9 +11,12 @@
11 */ 11 */
12#include "common.h" 12#include "common.h"
13 13
14#define SERVER_PORT 67 14#define SERVER_PORT 67
15#define SELECT_TIMEOUT 5 /* select timeout in sec. */ 15
16#define MAX_LIFETIME 2*60 /* lifetime of an xid entry in sec. */ 16/* lifetime of an xid entry in sec. */
17#define MAX_LIFETIME 2*60
18/* select timeout in sec. */
19#define SELECT_TIMEOUT (MAX_LIFETIME / 8)
17 20
18/* This list holds information about clients. The xid_* functions manipulate this list. */ 21/* This list holds information about clients. The xid_* functions manipulate this list. */
19struct xid_item { 22struct xid_item {
@@ -67,11 +70,11 @@ static struct xid_item *xid_find(uint32_t xid)
67 struct xid_item *item = dhcprelay_xid_list.next; 70 struct xid_item *item = dhcprelay_xid_list.next;
68 while (item != NULL) { 71 while (item != NULL) {
69 if (item->xid == xid) { 72 if (item->xid == xid) {
70 return item; 73 break;
71 } 74 }
72 item = item->next; 75 item = item->next;
73 } 76 }
74 return NULL; 77 return item;
75} 78}
76 79
77static void xid_del(uint32_t xid) 80static void xid_del(uint32_t xid)
@@ -110,62 +113,72 @@ static int get_dhcp_packet_type(struct dhcp_packet *p)
110} 113}
111 114
112/** 115/**
113 * get_client_devices - parses the devices list 116 * make_iface_list - parses client/server interface names
114 * dev_list - comma separated list of devices
115 * returns array 117 * returns array
116 */ 118 */
117static char **get_client_devices(char *dev_list, int *client_number) 119static char **make_iface_list(char **client_and_server_ifaces, int *client_number)
118{ 120{
119 char *s, **client_dev; 121 char *s, **iface_list;
120 int i, cn; 122 int i, cn;
121 123
122 /* copy list */ 124 /* get number of items */
123 dev_list = xstrdup(dev_list); 125 cn = 2; /* 1 server iface + at least 1 client one */
124 126 s = client_and_server_ifaces[0]; /* list of client ifaces */
125 /* get number of items, replace ',' with NULs */
126 s = dev_list;
127 cn = 1;
128 while (*s) { 127 while (*s) {
129 if (*s == ',') { 128 if (*s == ',')
130 *s = '\0';
131 cn++; 129 cn++;
132 }
133 s++; 130 s++;
134 } 131 }
135 *client_number = cn; 132 *client_number = cn;
136 133
137 /* create vector of pointers */ 134 /* create vector of pointers */
138 client_dev = xzalloc(cn * sizeof(*client_dev)); 135 iface_list = xzalloc(cn * sizeof(iface_list[0]));
139 client_dev[0] = dev_list; 136
137 iface_list[0] = client_and_server_ifaces[1]; /* server iface */
138
140 i = 1; 139 i = 1;
141 while (i != cn) { 140 s = xstrdup(client_and_server_ifaces[0]); /* list of client ifaces */
142 client_dev[i] = client_dev[i - 1] + strlen(client_dev[i - 1]) + 1; 141 goto store_client_iface_name;
143 i++; 142
143 while (i < cn) {
144 if (*s++ == ',') {
145 s[-1] = '\0';
146 store_client_iface_name:
147 iface_list[i++] = s;
148 }
144 } 149 }
145 return client_dev; 150
151 return iface_list;
146} 152}
147 153
148/* Creates listen sockets (in fds) bound to client and server ifaces, 154/* Creates listen sockets (in fds) bound to client and server ifaces,
149 * and returns numerically max fd. 155 * and returns numerically max fd.
150 */ 156 */
151static int init_sockets(char **client_ifaces, int num_clients, 157static int init_sockets(char **iface_list, int num_clients, int *fds)
152 char *server_iface, int *fds)
153{ 158{
154 int i, n; 159 int i, n;
155 160
156 /* talk to real server on bootps */ 161 n = 0;
157 fds[0] = udhcp_listen_socket(/*INADDR_ANY,*/ SERVER_PORT, server_iface); 162 for (i = 0; i < num_clients; i++) {
158 n = fds[0]; 163 fds[i] = udhcp_listen_socket(/*INADDR_ANY,*/ SERVER_PORT, iface_list[i]);
159 164 if (n < fds[i])
160 for (i = 1; i < num_clients; i++) {
161 /* listen for clients on bootps */
162 fds[i] = udhcp_listen_socket(/*INADDR_ANY,*/ SERVER_PORT, client_ifaces[i-1]);
163 if (fds[i] > n)
164 n = fds[i]; 165 n = fds[i];
165 } 166 }
166 return n; 167 return n;
167} 168}
168 169
170static int sendto_ip4(int sock, const void *msg, int msg_len, struct sockaddr_in *to)
171{
172 int err;
173
174 errno = 0;
175 err = sendto(sock, msg, msg_len, 0, (struct sockaddr*) to, sizeof(*to));
176 err -= msg_len;
177 if (err)
178 bb_perror_msg("sendto");
179 return err;
180}
181
169/** 182/**
170 * pass_to_server() - forwards dhcp packets from client to server 183 * pass_to_server() - forwards dhcp packets from client to server
171 * p - packet to send 184 * p - packet to send
@@ -174,7 +187,7 @@ static int init_sockets(char **client_ifaces, int num_clients,
174static void pass_to_server(struct dhcp_packet *p, int packet_len, int client, int *fds, 187static void pass_to_server(struct dhcp_packet *p, int packet_len, int client, int *fds,
175 struct sockaddr_in *client_addr, struct sockaddr_in *server_addr) 188 struct sockaddr_in *client_addr, struct sockaddr_in *server_addr)
176{ 189{
177 int res, type; 190 int type;
178 191
179 /* check packet_type */ 192 /* check packet_type */
180 type = get_dhcp_packet_type(p); 193 type = get_dhcp_packet_type(p);
@@ -188,13 +201,12 @@ static void pass_to_server(struct dhcp_packet *p, int packet_len, int client, in
188 /* create new xid entry */ 201 /* create new xid entry */
189 xid_add(p->xid, client_addr, client); 202 xid_add(p->xid, client_addr, client);
190 203
191 /* forward request to LAN (server) */ 204 /* forward request to server */
192 errno = 0; 205 /* note that we send from fds[0] which is bound to SERVER_PORT (67).
193 res = sendto(fds[0], p, packet_len, 0, (struct sockaddr*)server_addr, 206 * IOW: we send _from_ SERVER_PORT! Although this may look strange,
194 sizeof(struct sockaddr_in)); 207 * RFC 1542 not only allows, but prescribes this for BOOTP relays.
195 if (res != packet_len) { 208 */
196 bb_perror_msg("sendto"); 209 sendto_ip4(fds[0], p, packet_len, server_addr);
197 }
198} 210}
199 211
200/** 212/**
@@ -203,7 +215,7 @@ static void pass_to_server(struct dhcp_packet *p, int packet_len, int client, in
203 */ 215 */
204static void pass_to_client(struct dhcp_packet *p, int packet_len, int *fds) 216static void pass_to_client(struct dhcp_packet *p, int packet_len, int *fds)
205{ 217{
206 int res, type; 218 int type;
207 struct xid_item *item; 219 struct xid_item *item;
208 220
209 /* check xid */ 221 /* check xid */
@@ -218,14 +230,12 @@ static void pass_to_client(struct dhcp_packet *p, int packet_len, int *fds)
218 return; 230 return;
219 } 231 }
220 232
233//TODO: also do it if (p->flags & htons(BROADCAST_FLAG)) is set!
221 if (item->ip.sin_addr.s_addr == htonl(INADDR_ANY)) 234 if (item->ip.sin_addr.s_addr == htonl(INADDR_ANY))
222 item->ip.sin_addr.s_addr = htonl(INADDR_BROADCAST); 235 item->ip.sin_addr.s_addr = htonl(INADDR_BROADCAST);
223 errno = 0; 236
224 res = sendto(fds[item->client], p, packet_len, 0, (struct sockaddr*) &(item->ip), 237 if (sendto_ip4(fds[item->client], p, packet_len, &item->ip) != 0) {
225 sizeof(item->ip)); 238 return; /* send error occurred */
226 if (res != packet_len) {
227 bb_perror_msg("sendto");
228 return;
229 } 239 }
230 240
231 /* remove xid entry */ 241 /* remove xid entry */
@@ -235,36 +245,30 @@ static void pass_to_client(struct dhcp_packet *p, int packet_len, int *fds)
235int dhcprelay_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 245int dhcprelay_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
236int dhcprelay_main(int argc, char **argv) 246int dhcprelay_main(int argc, char **argv)
237{ 247{
238 struct dhcp_packet dhcp_msg;
239 struct sockaddr_in server_addr; 248 struct sockaddr_in server_addr;
240 struct sockaddr_in client_addr; 249 char **iface_list;
241 fd_set rfds;
242 char **client_ifaces;
243 int *fds; 250 int *fds;
244 int num_sockets, max_socket; 251 int num_sockets, max_socket;
245 uint32_t our_nip; 252 uint32_t our_nip;
246 253
247 server_addr.sin_family = AF_INET; 254 server_addr.sin_family = AF_INET;
255 server_addr.sin_addr.s_addr = htonl(INADDR_BROADCAST);
248 server_addr.sin_port = htons(SERVER_PORT); 256 server_addr.sin_port = htons(SERVER_PORT);
249 257
250 /* dhcprelay client_iface1,client_iface2,... server_iface [server_IP] */ 258 /* dhcprelay CLIENT_IFACE1[,CLIENT_IFACE2...] SERVER_IFACE [SERVER_IP] */
251 if (argc == 4) { 259 if (argc == 4) {
252 if (!inet_aton(argv[3], &server_addr.sin_addr)) 260 if (!inet_aton(argv[3], &server_addr.sin_addr))
253 bb_perror_msg_and_die("bad server IP"); 261 bb_perror_msg_and_die("bad server IP");
254 } else if (argc == 3) { 262 } else if (argc != 3) {
255 server_addr.sin_addr.s_addr = htonl(INADDR_BROADCAST);
256 } else {
257 bb_show_usage(); 263 bb_show_usage();
258 } 264 }
259 265
260 /* Produce list of client ifaces */ 266 iface_list = make_iface_list(argv + 1, &num_sockets);
261 client_ifaces = get_client_devices(argv[1], &num_sockets);
262 267
263 num_sockets++; /* for server socket at fds[0] */
264 fds = xmalloc(num_sockets * sizeof(fds[0])); 268 fds = xmalloc(num_sockets * sizeof(fds[0]));
265 269
266 /* Create sockets and bind one to every iface */ 270 /* Create sockets and bind one to every iface */
267 max_socket = init_sockets(client_ifaces, num_sockets, argv[2], fds); 271 max_socket = init_sockets(iface_list, num_sockets, fds);
268 272
269 /* Get our IP on server_iface */ 273 /* Get our IP on server_iface */
270 if (udhcp_read_interface(argv[2], NULL, &our_nip, NULL)) 274 if (udhcp_read_interface(argv[2], NULL, &our_nip, NULL))
@@ -272,11 +276,10 @@ int dhcprelay_main(int argc, char **argv)
272 276
273 /* Main loop */ 277 /* Main loop */
274 while (1) { 278 while (1) {
275//reinit stuff from time to time? go back to get_client_devices 279// reinit stuff from time to time? go back to make_iface_list
276//every N minutes? 280// every N minutes?
281 fd_set rfds;
277 struct timeval tv; 282 struct timeval tv;
278 size_t packlen;
279 socklen_t addr_size;
280 int i; 283 int i;
281 284
282 FD_ZERO(&rfds); 285 FD_ZERO(&rfds);
@@ -285,6 +288,9 @@ int dhcprelay_main(int argc, char **argv)
285 tv.tv_sec = SELECT_TIMEOUT; 288 tv.tv_sec = SELECT_TIMEOUT;
286 tv.tv_usec = 0; 289 tv.tv_usec = 0;
287 if (select(max_socket + 1, &rfds, NULL, NULL, &tv) > 0) { 290 if (select(max_socket + 1, &rfds, NULL, NULL, &tv) > 0) {
291 int packlen;
292 struct dhcp_packet dhcp_msg;
293
288 /* server */ 294 /* server */
289 if (FD_ISSET(fds[0], &rfds)) { 295 if (FD_ISSET(fds[0], &rfds)) {
290 packlen = udhcp_recv_kernel_packet(&dhcp_msg, fds[0]); 296 packlen = udhcp_recv_kernel_packet(&dhcp_msg, fds[0]);
@@ -292,24 +298,65 @@ int dhcprelay_main(int argc, char **argv)
292 pass_to_client(&dhcp_msg, packlen, fds); 298 pass_to_client(&dhcp_msg, packlen, fds);
293 } 299 }
294 } 300 }
301
295 /* clients */ 302 /* clients */
296 for (i = 1; i < num_sockets; i++) { 303 for (i = 1; i < num_sockets; i++) {
304 struct sockaddr_in client_addr;
305 socklen_t addr_size;
306
297 if (!FD_ISSET(fds[i], &rfds)) 307 if (!FD_ISSET(fds[i], &rfds))
298 continue; 308 continue;
299 addr_size = sizeof(struct sockaddr_in); 309
310 addr_size = sizeof(client_addr);
300 packlen = recvfrom(fds[i], &dhcp_msg, sizeof(dhcp_msg), 0, 311 packlen = recvfrom(fds[i], &dhcp_msg, sizeof(dhcp_msg), 0,
301 (struct sockaddr *)(&client_addr), &addr_size); 312 (struct sockaddr *)(&client_addr), &addr_size);
302 if (packlen <= 0) 313 if (packlen <= 0)
303 continue; 314 continue;
304 315
305 /* Get our IP on corresponding client_iface */ 316 /* Get our IP on corresponding client_iface */
306//why? what if server can't route such IP? 317// RFC 1542
307 if (udhcp_read_interface(client_ifaces[i-1], NULL, &dhcp_msg.gateway_nip, NULL)) { 318// 4.1 General BOOTP Processing for Relay Agents
308 /* Fall back to our server_iface's IP */ 319// 4.1.1 BOOTREQUEST Messages
309//this makes more sense! 320// If the relay agent does decide to relay the request, it MUST examine
321// the 'giaddr' ("gateway" IP address) field. If this field is zero,
322// the relay agent MUST fill this field with the IP address of the
323// interface on which the request was received. If the interface has
324// more than one IP address logically associated with it, the relay
325// agent SHOULD choose one IP address associated with that interface and
326// use it consistently for all BOOTP messages it relays. If the
327// 'giaddr' field contains some non-zero value, the 'giaddr' field MUST
328// NOT be modified. The relay agent MUST NOT, under any circumstances,
329// fill the 'giaddr' field with a broadcast address as is suggested in
330// [1] (Section 8, sixth paragraph).
331
332// but why? what if server can't route such IP? Client ifaces may be, say, NATed!
333
334// 4.1.2 BOOTREPLY Messages
335// BOOTP relay agents relay BOOTREPLY messages only to BOOTP clients.
336// It is the responsibility of BOOTP servers to send BOOTREPLY messages
337// directly to the relay agent identified in the 'giaddr' field.
338// (yeah right, unless it is impossible... see comment above)
339// Therefore, a relay agent may assume that all BOOTREPLY messages it
340// receives are intended for BOOTP clients on its directly-connected
341// networks.
342//
343// When a relay agent receives a BOOTREPLY message, it should examine
344// the BOOTP 'giaddr', 'yiaddr', 'chaddr', 'htype', and 'hlen' fields.
345// These fields should provide adequate information for the relay agent
346// to deliver the BOOTREPLY message to the client.
347//
348// The 'giaddr' field can be used to identify the logical interface from
349// which the reply must be sent (i.e., the host or router interface
350// connected to the same network as the BOOTP client). If the content
351// of the 'giaddr' field does not match one of the relay agent's
352// directly-connected logical interfaces, the BOOTREPLY messsage MUST be
353// silently discarded.
354 if (udhcp_read_interface(iface_list[i], NULL, &dhcp_msg.gateway_nip, NULL)) {
355 /* Fall back to our IP on server iface */
356// this makes more sense!
310 dhcp_msg.gateway_nip = our_nip; 357 dhcp_msg.gateway_nip = our_nip;
311 } 358 }
312//maybe set dhcp_msg.flags |= BROADCAST_FLAG too? 359// maybe dhcp_msg.hops++? drop packets with too many hops (RFC 1542 says 4 or 16)?
313 pass_to_server(&dhcp_msg, packlen, i, fds, &client_addr, &server_addr); 360 pass_to_server(&dhcp_msg, packlen, i, fds, &client_addr, &server_addr);
314 } 361 }
315 } 362 }