diff options
author | Denys Vlasenko <vda.linux@googlemail.com> | 2010-10-06 01:45:24 +0200 |
---|---|---|
committer | Denys Vlasenko <vda.linux@googlemail.com> | 2010-10-06 01:45:24 +0200 |
commit | c0f39b0fb288206036d2c442588cf597390c3622 (patch) | |
tree | 889f9bcfe5230ce7b2173dec73d99671c242dd98 | |
parent | 28458c64db779c27ecd63f400ea9af0e3e6b555a (diff) | |
download | busybox-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.c | 189 |
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. */ |
19 | struct xid_item { | 22 | struct 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 | ||
77 | static void xid_del(uint32_t xid) | 80 | static 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 | */ |
117 | static char **get_client_devices(char *dev_list, int *client_number) | 119 | static 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 | */ |
151 | static int init_sockets(char **client_ifaces, int num_clients, | 157 | static 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 | ||
170 | static 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, | |||
174 | static void pass_to_server(struct dhcp_packet *p, int packet_len, int client, int *fds, | 187 | static 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 | */ |
204 | static void pass_to_client(struct dhcp_packet *p, int packet_len, int *fds) | 216 | static 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) | |||
235 | int dhcprelay_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 245 | int dhcprelay_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
236 | int dhcprelay_main(int argc, char **argv) | 246 | int 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 | } |