diff options
Diffstat (limited to 'networking/udhcp/dhcprelay.c')
-rw-r--r-- | networking/udhcp/dhcprelay.c | 63 |
1 files changed, 40 insertions, 23 deletions
diff --git a/networking/udhcp/dhcprelay.c b/networking/udhcp/dhcprelay.c index ef9447b4b..2352c34a2 100644 --- a/networking/udhcp/dhcprelay.c +++ b/networking/udhcp/dhcprelay.c | |||
@@ -17,7 +17,8 @@ | |||
17 | //usage:#define dhcprelay_trivial_usage | 17 | //usage:#define dhcprelay_trivial_usage |
18 | //usage: "CLIENT_IFACE[,CLIENT_IFACE2]... SERVER_IFACE [SERVER_IP]" | 18 | //usage: "CLIENT_IFACE[,CLIENT_IFACE2]... SERVER_IFACE [SERVER_IP]" |
19 | //usage:#define dhcprelay_full_usage "\n\n" | 19 | //usage:#define dhcprelay_full_usage "\n\n" |
20 | //usage: "Relay DHCP requests between clients and server" | 20 | //usage: "Relay DHCP requests between clients and server.\n" |
21 | //usage: "Without SERVER_IP, requests are broadcast on SERVER_IFACE." | ||
21 | 22 | ||
22 | #include "common.h" | 23 | #include "common.h" |
23 | 24 | ||
@@ -31,7 +32,7 @@ | |||
31 | /* This list holds information about clients. The xid_* functions manipulate this list. */ | 32 | /* This list holds information about clients. The xid_* functions manipulate this list. */ |
32 | struct xid_item { | 33 | struct xid_item { |
33 | unsigned timestamp; | 34 | unsigned timestamp; |
34 | int client; | 35 | unsigned iface_no; |
35 | uint32_t xid; | 36 | uint32_t xid; |
36 | struct sockaddr_in ip; | 37 | struct sockaddr_in ip; |
37 | struct xid_item *next; | 38 | struct xid_item *next; |
@@ -40,7 +41,7 @@ struct xid_item { | |||
40 | #define dhcprelay_xid_list (*(struct xid_item*)bb_common_bufsiz1) | 41 | #define dhcprelay_xid_list (*(struct xid_item*)bb_common_bufsiz1) |
41 | #define INIT_G() do { setup_common_bufsiz(); } while (0) | 42 | #define INIT_G() do { setup_common_bufsiz(); } while (0) |
42 | 43 | ||
43 | static struct xid_item *xid_add(uint32_t xid, struct sockaddr_in *ip, int client) | 44 | static struct xid_item *xid_add(uint32_t xid, struct sockaddr_in *ip, unsigned iface_no) |
44 | { | 45 | { |
45 | struct xid_item *item; | 46 | struct xid_item *item; |
46 | 47 | ||
@@ -50,7 +51,7 @@ static struct xid_item *xid_add(uint32_t xid, struct sockaddr_in *ip, int client | |||
50 | /* add xid entry */ | 51 | /* add xid entry */ |
51 | item->ip = *ip; | 52 | item->ip = *ip; |
52 | item->xid = xid; | 53 | item->xid = xid; |
53 | item->client = client; | 54 | item->iface_no = iface_no; |
54 | item->timestamp = monotonic_sec(); | 55 | item->timestamp = monotonic_sec(); |
55 | item->next = dhcprelay_xid_list.next; | 56 | item->next = dhcprelay_xid_list.next; |
56 | dhcprelay_xid_list.next = item; | 57 | dhcprelay_xid_list.next = item; |
@@ -127,10 +128,10 @@ static int get_dhcp_packet_type(struct dhcp_packet *p) | |||
127 | * make_iface_list - parses client/server interface names | 128 | * make_iface_list - parses client/server interface names |
128 | * returns array | 129 | * returns array |
129 | */ | 130 | */ |
130 | static char **make_iface_list(char **client_and_server_ifaces, int *client_number) | 131 | static char **make_iface_list(char **client_and_server_ifaces, unsigned *client_number) |
131 | { | 132 | { |
132 | char *s, **iface_list; | 133 | char *s, **iface_list; |
133 | int i, cn; | 134 | unsigned i, cn; |
134 | 135 | ||
135 | /* get number of items */ | 136 | /* get number of items */ |
136 | cn = 2; /* 1 server iface + at least 1 client one */ | 137 | cn = 2; /* 1 server iface + at least 1 client one */ |
@@ -165,9 +166,9 @@ static char **make_iface_list(char **client_and_server_ifaces, int *client_numbe | |||
165 | /* Creates listen sockets (in fds) bound to client and server ifaces, | 166 | /* Creates listen sockets (in fds) bound to client and server ifaces, |
166 | * and returns numerically max fd. | 167 | * and returns numerically max fd. |
167 | */ | 168 | */ |
168 | static int init_sockets(char **iface_list, int num_clients, int *fds) | 169 | static unsigned init_sockets(char **iface_list, unsigned num_clients, int *fds) |
169 | { | 170 | { |
170 | int i, n; | 171 | unsigned i, n; |
171 | 172 | ||
172 | n = 0; | 173 | n = 0; |
173 | for (i = 0; i < num_clients; i++) { | 174 | for (i = 0; i < num_clients; i++) { |
@@ -195,13 +196,14 @@ static int sendto_ip4(int sock, const void *msg, int msg_len, struct sockaddr_in | |||
195 | * p - packet to send | 196 | * p - packet to send |
196 | * client - number of the client | 197 | * client - number of the client |
197 | */ | 198 | */ |
198 | static void pass_to_server(struct dhcp_packet *p, int packet_len, int client, int *fds, | 199 | static void pass_to_server(struct dhcp_packet *p, int packet_len, unsigned from_iface_no, int *fds, |
199 | struct sockaddr_in *client_addr, struct sockaddr_in *server_addr) | 200 | struct sockaddr_in *client_addr, struct sockaddr_in *server_addr) |
200 | { | 201 | { |
201 | int type; | 202 | int type; |
202 | 203 | ||
203 | /* check packet_type */ | 204 | /* check packet_type */ |
204 | type = get_dhcp_packet_type(p); | 205 | type = get_dhcp_packet_type(p); |
206 | //FIXME: the above does not consider packet_len! | ||
205 | if (type != DHCPDISCOVER && type != DHCPREQUEST | 207 | if (type != DHCPDISCOVER && type != DHCPREQUEST |
206 | && type != DHCPDECLINE && type != DHCPRELEASE | 208 | && type != DHCPDECLINE && type != DHCPRELEASE |
207 | && type != DHCPINFORM | 209 | && type != DHCPINFORM |
@@ -210,7 +212,10 @@ static void pass_to_server(struct dhcp_packet *p, int packet_len, int client, in | |||
210 | } | 212 | } |
211 | 213 | ||
212 | /* create new xid entry */ | 214 | /* create new xid entry */ |
213 | xid_add(p->xid, client_addr, client); | 215 | xid_add(p->xid, client_addr, from_iface_no); |
216 | //TODO: since we key request/reply pairs on xid values, shouldn't we drop new requests | ||
217 | //with xid accidentally matching a xid of one of requests we currently hold | ||
218 | //waiting for their replies? | ||
214 | 219 | ||
215 | /* forward request to server */ | 220 | /* forward request to server */ |
216 | /* note that we send from fds[0] which is bound to SERVER_PORT (67). | 221 | /* note that we send from fds[0] which is bound to SERVER_PORT (67). |
@@ -229,25 +234,30 @@ static void pass_to_client(struct dhcp_packet *p, int packet_len, int *fds) | |||
229 | int type; | 234 | int type; |
230 | struct xid_item *item; | 235 | struct xid_item *item; |
231 | 236 | ||
232 | /* check xid */ | ||
233 | item = xid_find(p->xid); | ||
234 | if (!item) { | ||
235 | return; | ||
236 | } | ||
237 | |||
238 | /* check packet type */ | 237 | /* check packet type */ |
239 | type = get_dhcp_packet_type(p); | 238 | type = get_dhcp_packet_type(p); |
239 | //FIXME: the above does not consider packet_len! | ||
240 | if (type != DHCPOFFER && type != DHCPACK && type != DHCPNAK) { | 240 | if (type != DHCPOFFER && type != DHCPACK && type != DHCPNAK) { |
241 | return; | 241 | return; |
242 | } | 242 | } |
243 | 243 | ||
244 | /* check xid */ | ||
245 | item = xid_find(p->xid); | ||
246 | if (!item) { | ||
247 | return; | ||
248 | } | ||
249 | //NB: RFC 1542 section 4.1 seems to envision the logic that | ||
250 | //relay agents use giaddr (dhcp_msg.gateway_nip in our code) | ||
251 | //to find out on which interface to reply. | ||
252 | //(server is meant to copy giaddr from our request packet to its reply). | ||
253 | //Above, we don't use that logic, instead we use xid as a key. | ||
254 | |||
244 | //TODO: also do it if (p->flags & htons(BROADCAST_FLAG)) is set! | 255 | //TODO: also do it if (p->flags & htons(BROADCAST_FLAG)) is set! |
245 | if (item->ip.sin_addr.s_addr == htonl(INADDR_ANY)) | 256 | if (item->ip.sin_addr.s_addr == htonl(INADDR_ANY)) |
246 | item->ip.sin_addr.s_addr = htonl(INADDR_BROADCAST); | 257 | item->ip.sin_addr.s_addr = htonl(INADDR_BROADCAST); |
247 | 258 | ||
248 | if (sendto_ip4(fds[item->client], p, packet_len, &item->ip) != 0) { | 259 | sendto_ip4(fds[item->iface_no], p, packet_len, &item->ip); |
249 | return; /* send error occurred */ | 260 | /* ^^^ if send error occurred, we can't do much, hence no check */ |
250 | } | ||
251 | 261 | ||
252 | /* remove xid entry */ | 262 | /* remove xid entry */ |
253 | xid_del(p->xid); | 263 | xid_del(p->xid); |
@@ -259,7 +269,7 @@ int dhcprelay_main(int argc UNUSED_PARAM, char **argv) | |||
259 | struct sockaddr_in server_addr; | 269 | struct sockaddr_in server_addr; |
260 | char **iface_list; | 270 | char **iface_list; |
261 | int *fds; | 271 | int *fds; |
262 | int num_sockets, max_socket; | 272 | unsigned num_sockets, max_socket; |
263 | uint32_t our_nip; | 273 | uint32_t our_nip; |
264 | 274 | ||
265 | INIT_G(); | 275 | INIT_G(); |
@@ -293,7 +303,7 @@ int dhcprelay_main(int argc UNUSED_PARAM, char **argv) | |||
293 | // every N minutes? | 303 | // every N minutes? |
294 | fd_set rfds; | 304 | fd_set rfds; |
295 | struct timeval tv; | 305 | struct timeval tv; |
296 | int i; | 306 | unsigned i; |
297 | 307 | ||
298 | FD_ZERO(&rfds); | 308 | FD_ZERO(&rfds); |
299 | for (i = 0; i < num_sockets; i++) | 309 | for (i = 0; i < num_sockets; i++) |
@@ -304,15 +314,17 @@ int dhcprelay_main(int argc UNUSED_PARAM, char **argv) | |||
304 | int packlen; | 314 | int packlen; |
305 | struct dhcp_packet dhcp_msg; | 315 | struct dhcp_packet dhcp_msg; |
306 | 316 | ||
307 | /* server */ | 317 | /* from server */ |
308 | if (FD_ISSET(fds[0], &rfds)) { | 318 | if (FD_ISSET(fds[0], &rfds)) { |
309 | packlen = udhcp_recv_kernel_packet(&dhcp_msg, fds[0]); | 319 | packlen = udhcp_recv_kernel_packet(&dhcp_msg, fds[0]); |
320 | //NB: we do not check source port here. Should we? | ||
321 | //It should be SERVER_PORT. | ||
310 | if (packlen > 0) { | 322 | if (packlen > 0) { |
311 | pass_to_client(&dhcp_msg, packlen, fds); | 323 | pass_to_client(&dhcp_msg, packlen, fds); |
312 | } | 324 | } |
313 | } | 325 | } |
314 | 326 | ||
315 | /* clients */ | 327 | /* from clients */ |
316 | for (i = 1; i < num_sockets; i++) { | 328 | for (i = 1; i < num_sockets; i++) { |
317 | struct sockaddr_in client_addr; | 329 | struct sockaddr_in client_addr; |
318 | socklen_t addr_size; | 330 | socklen_t addr_size; |
@@ -325,6 +337,11 @@ int dhcprelay_main(int argc UNUSED_PARAM, char **argv) | |||
325 | (struct sockaddr *)(&client_addr), &addr_size); | 337 | (struct sockaddr *)(&client_addr), &addr_size); |
326 | if (packlen <= 0) | 338 | if (packlen <= 0) |
327 | continue; | 339 | continue; |
340 | //NB: we do not check source port here. Should we? | ||
341 | //It should be CLIENT_PORT for clients. | ||
342 | //It can be SERVER_PORT for relay agents (in which case giaddr must be != 0.0.0.0), | ||
343 | //but is it even supported to chain relay agents like this? | ||
344 | //(we still copy client_addr.port and use it to reply to the port we got request from) | ||
328 | 345 | ||
329 | /* Get our IP on corresponding client_iface */ | 346 | /* Get our IP on corresponding client_iface */ |
330 | // RFC 1542 | 347 | // RFC 1542 |