13 |
#include "common.h" |
#include "common.h" |
14 |
#include "options.h" |
#include "options.h" |
15 |
|
|
|
/* constants */ |
|
16 |
#define SERVER_PORT 67 |
#define SERVER_PORT 67 |
17 |
#define SELECT_TIMEOUT 5 /* select timeout in sec. */ |
#define SELECT_TIMEOUT 5 /* select timeout in sec. */ |
18 |
#define MAX_LIFETIME 2*60 /* lifetime of an xid entry in sec. */ |
#define MAX_LIFETIME 2*60 /* lifetime of an xid entry in sec. */ |
97 |
* p - pointer to the dhcp packet |
* p - pointer to the dhcp packet |
98 |
* returns the message type on success, -1 otherwise |
* returns the message type on success, -1 otherwise |
99 |
*/ |
*/ |
100 |
static int get_dhcp_packet_type(struct dhcpMessage *p) |
static int get_dhcp_packet_type(struct dhcp_packet *p) |
101 |
{ |
{ |
102 |
uint8_t *op; |
uint8_t *op; |
103 |
|
|
148 |
} |
} |
149 |
|
|
150 |
|
|
151 |
/* Creates listen sockets (in fds) and returns numerically max fd. */ |
/* Creates listen sockets (in fds) bound to client and server ifaces, |
152 |
static int init_sockets(char **client, int num_clients, |
* and returns numerically max fd. |
153 |
char *server, int *fds) |
*/ |
154 |
|
static int init_sockets(char **client_ifaces, int num_clients, |
155 |
|
char *server_iface, int *fds) |
156 |
{ |
{ |
157 |
int i, n; |
int i, n; |
158 |
|
|
159 |
/* talk to real server on bootps */ |
/* talk to real server on bootps */ |
160 |
fds[0] = udhcp_listen_socket(/*INADDR_ANY,*/ SERVER_PORT, server); |
fds[0] = udhcp_listen_socket(/*INADDR_ANY,*/ SERVER_PORT, server_iface); |
161 |
n = fds[0]; |
n = fds[0]; |
162 |
|
|
163 |
for (i = 1; i < num_clients; i++) { |
for (i = 1; i < num_clients; i++) { |
164 |
/* listen for clients on bootps */ |
/* listen for clients on bootps */ |
165 |
fds[i] = udhcp_listen_socket(/*INADDR_ANY,*/ SERVER_PORT, client[i-1]); |
fds[i] = udhcp_listen_socket(/*INADDR_ANY,*/ SERVER_PORT, client_ifaces[i-1]); |
166 |
if (fds[i] > n) |
if (fds[i] > n) |
167 |
n = fds[i]; |
n = fds[i]; |
168 |
} |
} |
171 |
|
|
172 |
|
|
173 |
/** |
/** |
174 |
* pass_on() - forwards dhcp packets from client to server |
* pass_to_server() - forwards dhcp packets from client to server |
175 |
* p - packet to send |
* p - packet to send |
176 |
* client - number of the client |
* client - number of the client |
177 |
*/ |
*/ |
178 |
static void pass_on(struct dhcpMessage *p, int packet_len, int client, int *fds, |
static void pass_to_server(struct dhcp_packet *p, int packet_len, int client, int *fds, |
179 |
struct sockaddr_in *client_addr, struct sockaddr_in *server_addr) |
struct sockaddr_in *client_addr, struct sockaddr_in *server_addr) |
180 |
{ |
{ |
181 |
int res, type; |
int res, type; |
194 |
item = xid_add(p->xid, client_addr, client); |
item = xid_add(p->xid, client_addr, client); |
195 |
|
|
196 |
/* forward request to LAN (server) */ |
/* forward request to LAN (server) */ |
197 |
|
errno = 0; |
198 |
res = sendto(fds[0], p, packet_len, 0, (struct sockaddr*)server_addr, |
res = sendto(fds[0], p, packet_len, 0, (struct sockaddr*)server_addr, |
199 |
sizeof(struct sockaddr_in)); |
sizeof(struct sockaddr_in)); |
200 |
if (res != packet_len) { |
if (res != packet_len) { |
201 |
bb_perror_msg("pass_on"); |
bb_perror_msg("sendto"); |
|
return; |
|
202 |
} |
} |
203 |
} |
} |
204 |
|
|
205 |
/** |
/** |
206 |
* pass_back() - forwards dhcp packets from server to client |
* pass_to_client() - forwards dhcp packets from server to client |
207 |
* p - packet to send |
* p - packet to send |
208 |
*/ |
*/ |
209 |
static void pass_back(struct dhcpMessage *p, int packet_len, int *fds) |
static void pass_to_client(struct dhcp_packet *p, int packet_len, int *fds) |
210 |
{ |
{ |
211 |
int res, type; |
int res, type; |
212 |
struct xid_item *item; |
struct xid_item *item; |
225 |
|
|
226 |
if (item->ip.sin_addr.s_addr == htonl(INADDR_ANY)) |
if (item->ip.sin_addr.s_addr == htonl(INADDR_ANY)) |
227 |
item->ip.sin_addr.s_addr = htonl(INADDR_BROADCAST); |
item->ip.sin_addr.s_addr = htonl(INADDR_BROADCAST); |
228 |
res = sendto(fds[item->client], p, packet_len, 0, (struct sockaddr*)(&item->ip), |
errno = 0; |
229 |
sizeof(item->ip)); |
res = sendto(fds[item->client], p, packet_len, 0, (struct sockaddr*) &(item->ip), |
230 |
|
sizeof(item->ip)); |
231 |
if (res != packet_len) { |
if (res != packet_len) { |
232 |
bb_perror_msg("pass_back"); |
bb_perror_msg("sendto"); |
233 |
return; |
return; |
234 |
} |
} |
235 |
|
|
237 |
xid_del(p->xid); |
xid_del(p->xid); |
238 |
} |
} |
239 |
|
|
240 |
static void dhcprelay_loop(int *fds, int num_sockets, int max_socket, char **clients, |
int dhcprelay_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
241 |
struct sockaddr_in *server_addr, uint32_t gw_ip) NORETURN; |
int dhcprelay_main(int argc, char **argv) |
|
static void dhcprelay_loop(int *fds, int num_sockets, int max_socket, char **clients, |
|
|
struct sockaddr_in *server_addr, uint32_t gw_ip) |
|
242 |
{ |
{ |
243 |
struct dhcpMessage dhcp_msg; |
struct dhcp_packet dhcp_msg; |
244 |
fd_set rfds; |
struct sockaddr_in server_addr; |
|
size_t packlen; |
|
|
socklen_t addr_size; |
|
245 |
struct sockaddr_in client_addr; |
struct sockaddr_in client_addr; |
246 |
struct timeval tv; |
fd_set rfds; |
247 |
int i; |
char **client_ifaces; |
248 |
|
int *fds; |
249 |
|
int num_sockets, max_socket; |
250 |
|
uint32_t our_nip; |
251 |
|
|
252 |
|
server_addr.sin_family = AF_INET; |
253 |
|
server_addr.sin_port = htons(SERVER_PORT); |
254 |
|
|
255 |
|
/* dhcprelay client_iface1,client_iface2,... server_iface [server_IP] */ |
256 |
|
if (argc == 4) { |
257 |
|
if (!inet_aton(argv[3], &server_addr.sin_addr)) |
258 |
|
bb_perror_msg_and_die("bad server IP"); |
259 |
|
} else if (argc == 3) { |
260 |
|
server_addr.sin_addr.s_addr = htonl(INADDR_BROADCAST); |
261 |
|
} else { |
262 |
|
bb_show_usage(); |
263 |
|
} |
264 |
|
|
265 |
|
/* Produce list of client ifaces */ |
266 |
|
client_ifaces = get_client_devices(argv[1], &num_sockets); |
267 |
|
|
268 |
|
num_sockets++; /* for server socket at fds[0] */ |
269 |
|
fds = xmalloc(num_sockets * sizeof(fds[0])); |
270 |
|
|
271 |
|
/* Create sockets and bind one to every iface */ |
272 |
|
max_socket = init_sockets(client_ifaces, num_sockets, argv[2], fds); |
273 |
|
|
274 |
|
/* Get our IP on server_iface */ |
275 |
|
if (udhcp_read_interface(argv[2], NULL, &our_nip, NULL)) |
276 |
|
return 1; |
277 |
|
|
278 |
|
/* Main loop */ |
279 |
while (1) { |
while (1) { |
280 |
|
//reinit stuff from time to time? go back to get_client_devices |
281 |
|
//every N minutes? |
282 |
|
struct timeval tv; |
283 |
|
size_t packlen; |
284 |
|
socklen_t addr_size; |
285 |
|
int i; |
286 |
|
|
287 |
FD_ZERO(&rfds); |
FD_ZERO(&rfds); |
288 |
for (i = 0; i < num_sockets; i++) |
for (i = 0; i < num_sockets; i++) |
289 |
FD_SET(fds[i], &rfds); |
FD_SET(fds[i], &rfds); |
294 |
if (FD_ISSET(fds[0], &rfds)) { |
if (FD_ISSET(fds[0], &rfds)) { |
295 |
packlen = udhcp_recv_kernel_packet(&dhcp_msg, fds[0]); |
packlen = udhcp_recv_kernel_packet(&dhcp_msg, fds[0]); |
296 |
if (packlen > 0) { |
if (packlen > 0) { |
297 |
pass_back(&dhcp_msg, packlen, fds); |
pass_to_client(&dhcp_msg, packlen, fds); |
298 |
} |
} |
299 |
} |
} |
300 |
|
/* clients */ |
301 |
for (i = 1; i < num_sockets; i++) { |
for (i = 1; i < num_sockets; i++) { |
|
/* clients */ |
|
302 |
if (!FD_ISSET(fds[i], &rfds)) |
if (!FD_ISSET(fds[i], &rfds)) |
303 |
continue; |
continue; |
304 |
addr_size = sizeof(struct sockaddr_in); |
addr_size = sizeof(struct sockaddr_in); |
305 |
packlen = recvfrom(fds[i], &dhcp_msg, sizeof(dhcp_msg), 0, |
packlen = recvfrom(fds[i], &dhcp_msg, sizeof(dhcp_msg), 0, |
306 |
(struct sockaddr *)(&client_addr), &addr_size); |
(struct sockaddr *)(&client_addr), &addr_size); |
307 |
if (packlen <= 0) |
if (packlen <= 0) |
308 |
continue; |
continue; |
309 |
if (udhcp_read_interface(clients[i-1], NULL, &dhcp_msg.giaddr, NULL)) |
|
310 |
dhcp_msg.giaddr = gw_ip; |
/* Get our IP on corresponding client_iface */ |
311 |
pass_on(&dhcp_msg, packlen, i, fds, &client_addr, server_addr); |
//why? what if server can't route such IP? |
312 |
|
if (udhcp_read_interface(client_ifaces[i-1], NULL, &dhcp_msg.gateway_nip, NULL)) { |
313 |
|
/* Fall back to our server_iface's IP */ |
314 |
|
//this makes more sense! |
315 |
|
dhcp_msg.gateway_nip = our_nip; |
316 |
|
} |
317 |
|
//maybe set dhcp_msg.flags |= BROADCAST_FLAG too? |
318 |
|
pass_to_server(&dhcp_msg, packlen, i, fds, &client_addr, &server_addr); |
319 |
} |
} |
320 |
} |
} |
321 |
xid_expire(); |
xid_expire(); |
322 |
} |
} /* while (1) */ |
|
} |
|
|
|
|
|
int dhcprelay_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
|
|
int dhcprelay_main(int argc, char **argv) |
|
|
{ |
|
|
int num_sockets, max_socket; |
|
|
int *fds; |
|
|
uint32_t gw_ip; |
|
|
char **clients; |
|
|
struct sockaddr_in server_addr; |
|
|
|
|
|
server_addr.sin_family = AF_INET; |
|
|
server_addr.sin_port = htons(SERVER_PORT); |
|
|
if (argc == 4) { |
|
|
if (!inet_aton(argv[3], &server_addr.sin_addr)) |
|
|
bb_perror_msg_and_die("didn't grok server"); |
|
|
} else if (argc == 3) { |
|
|
server_addr.sin_addr.s_addr = htonl(INADDR_BROADCAST); |
|
|
} else { |
|
|
bb_show_usage(); |
|
|
} |
|
|
|
|
|
clients = get_client_devices(argv[1], &num_sockets); |
|
|
num_sockets++; /* for server socket at fds[0] */ |
|
|
fds = xmalloc(num_sockets * sizeof(fds[0])); |
|
|
max_socket = init_sockets(clients, num_sockets, argv[2], fds); |
|
|
|
|
|
if (udhcp_read_interface(argv[2], NULL, &gw_ip, NULL)) |
|
|
return 1; |
|
323 |
|
|
|
/* doesn't return */ |
|
|
dhcprelay_loop(fds, num_sockets, max_socket, clients, &server_addr, gw_ip); |
|
324 |
/* return 0; - not reached */ |
/* return 0; - not reached */ |
325 |
} |
} |