dhcprelay.c 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314
  1. /* vi: set sw=4 ts=4: */
  2. /* Port to Busybox Copyright (C) 2006 Jesse Dutton <jessedutton@gmail.com>
  3. *
  4. * Licensed under GPL v2, see file LICENSE in this tarball for details.
  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. #include "common.h"
  13. #include "options.h"
  14. /* constants */
  15. #define SERVER_PORT 67
  16. #define SELECT_TIMEOUT 5 /* select timeout in sec. */
  17. #define MAX_LIFETIME 2*60 /* lifetime of an xid entry in sec. */
  18. /* This list holds information about clients. The xid_* functions manipulate this list. */
  19. struct xid_item {
  20. unsigned timestamp;
  21. int client;
  22. uint32_t xid;
  23. struct sockaddr_in ip;
  24. struct xid_item *next;
  25. };
  26. #define dhcprelay_xid_list (*(struct xid_item*)&bb_common_bufsiz1)
  27. static struct xid_item *xid_add(uint32_t xid, struct sockaddr_in *ip, int client)
  28. {
  29. struct xid_item *item;
  30. /* create new xid entry */
  31. item = xmalloc(sizeof(struct xid_item));
  32. /* add xid entry */
  33. item->ip = *ip;
  34. item->xid = xid;
  35. item->client = client;
  36. item->timestamp = monotonic_sec();
  37. item->next = dhcprelay_xid_list.next;
  38. dhcprelay_xid_list.next = item;
  39. return item;
  40. }
  41. static void xid_expire(void)
  42. {
  43. struct xid_item *item = dhcprelay_xid_list.next;
  44. struct xid_item *last = &dhcprelay_xid_list;
  45. unsigned current_time = monotonic_sec();
  46. while (item != NULL) {
  47. if ((current_time - item->timestamp) > MAX_LIFETIME) {
  48. last->next = item->next;
  49. free(item);
  50. item = last->next;
  51. } else {
  52. last = item;
  53. item = item->next;
  54. }
  55. }
  56. }
  57. static struct xid_item *xid_find(uint32_t xid)
  58. {
  59. struct xid_item *item = dhcprelay_xid_list.next;
  60. while (item != NULL) {
  61. if (item->xid == xid) {
  62. return item;
  63. }
  64. item = item->next;
  65. }
  66. return NULL;
  67. }
  68. static void xid_del(uint32_t xid)
  69. {
  70. struct xid_item *item = dhcprelay_xid_list.next;
  71. struct xid_item *last = &dhcprelay_xid_list;
  72. while (item != NULL) {
  73. if (item->xid == xid) {
  74. last->next = item->next;
  75. free(item);
  76. item = last->next;
  77. } else {
  78. last = item;
  79. item = item->next;
  80. }
  81. }
  82. }
  83. /**
  84. * get_dhcp_packet_type - gets the message type of a dhcp packet
  85. * p - pointer to the dhcp packet
  86. * returns the message type on success, -1 otherwise
  87. */
  88. static int get_dhcp_packet_type(struct dhcpMessage *p)
  89. {
  90. uint8_t *op;
  91. /* it must be either a BOOTREQUEST or a BOOTREPLY */
  92. if (p->op != BOOTREQUEST && p->op != BOOTREPLY)
  93. return -1;
  94. /* get message type option */
  95. op = get_option(p, DHCP_MESSAGE_TYPE);
  96. if (op != NULL)
  97. return op[0];
  98. return -1;
  99. }
  100. /**
  101. * get_client_devices - parses the devices list
  102. * dev_list - comma separated list of devices
  103. * returns array
  104. */
  105. static char **get_client_devices(char *dev_list, int *client_number)
  106. {
  107. char *s, **client_dev;
  108. int i, cn;
  109. /* copy list */
  110. dev_list = xstrdup(dev_list);
  111. /* get number of items, replace ',' with NULs */
  112. s = dev_list;
  113. cn = 1;
  114. while (*s) {
  115. if (*s == ',') {
  116. *s = '\0';
  117. cn++;
  118. }
  119. s++;
  120. }
  121. *client_number = cn;
  122. /* create vector of pointers */
  123. client_dev = xzalloc(cn * sizeof(*client_dev));
  124. client_dev[0] = dev_list;
  125. i = 1;
  126. while (i != cn) {
  127. client_dev[i] = client_dev[i - 1] + strlen(client_dev[i - 1]) + 1;
  128. i++;
  129. }
  130. return client_dev;
  131. }
  132. /* Creates listen sockets (in fds) and returns numerically max fd. */
  133. static int init_sockets(char **client, int num_clients,
  134. char *server, int *fds)
  135. {
  136. int i, n;
  137. /* talk to real server on bootps */
  138. fds[0] = udhcp_listen_socket(/*INADDR_ANY,*/ SERVER_PORT, server);
  139. n = fds[0];
  140. for (i = 1; i < num_clients; i++) {
  141. /* listen for clients on bootps */
  142. fds[i] = udhcp_listen_socket(/*INADDR_ANY,*/ SERVER_PORT, client[i-1]);
  143. if (fds[i] > n)
  144. n = fds[i];
  145. }
  146. return n;
  147. }
  148. /**
  149. * pass_on() - forwards dhcp packets from client to server
  150. * p - packet to send
  151. * client - number of the client
  152. */
  153. static void pass_on(struct dhcpMessage *p, int packet_len, int client, int *fds,
  154. struct sockaddr_in *client_addr, struct sockaddr_in *server_addr)
  155. {
  156. int res, type;
  157. struct xid_item *item;
  158. /* check packet_type */
  159. type = get_dhcp_packet_type(p);
  160. if (type != DHCPDISCOVER && type != DHCPREQUEST
  161. && type != DHCPDECLINE && type != DHCPRELEASE
  162. && type != DHCPINFORM
  163. ) {
  164. return;
  165. }
  166. /* create new xid entry */
  167. item = xid_add(p->xid, client_addr, client);
  168. /* forward request to LAN (server) */
  169. res = sendto(fds[0], p, packet_len, 0, (struct sockaddr*)server_addr,
  170. sizeof(struct sockaddr_in));
  171. if (res != packet_len) {
  172. bb_perror_msg("pass_on");
  173. return;
  174. }
  175. }
  176. /**
  177. * pass_back() - forwards dhcp packets from server to client
  178. * p - packet to send
  179. */
  180. static void pass_back(struct dhcpMessage *p, int packet_len, int *fds)
  181. {
  182. int res, type;
  183. struct xid_item *item;
  184. /* check xid */
  185. item = xid_find(p->xid);
  186. if (!item) {
  187. return;
  188. }
  189. /* check packet type */
  190. type = get_dhcp_packet_type(p);
  191. if (type != DHCPOFFER && type != DHCPACK && type != DHCPNAK) {
  192. return;
  193. }
  194. if (item->ip.sin_addr.s_addr == htonl(INADDR_ANY))
  195. item->ip.sin_addr.s_addr = htonl(INADDR_BROADCAST);
  196. res = sendto(fds[item->client], p, packet_len, 0, (struct sockaddr*)(&item->ip),
  197. sizeof(item->ip));
  198. if (res != packet_len) {
  199. bb_perror_msg("pass_back");
  200. return;
  201. }
  202. /* remove xid entry */
  203. xid_del(p->xid);
  204. }
  205. static void dhcprelay_loop(int *fds, int num_sockets, int max_socket, char **clients,
  206. struct sockaddr_in *server_addr, uint32_t gw_ip) NORETURN;
  207. static void dhcprelay_loop(int *fds, int num_sockets, int max_socket, char **clients,
  208. struct sockaddr_in *server_addr, uint32_t gw_ip)
  209. {
  210. struct dhcpMessage dhcp_msg;
  211. fd_set rfds;
  212. size_t packlen;
  213. socklen_t addr_size;
  214. struct sockaddr_in client_addr;
  215. struct timeval tv;
  216. int i;
  217. while (1) {
  218. FD_ZERO(&rfds);
  219. for (i = 0; i < num_sockets; i++)
  220. FD_SET(fds[i], &rfds);
  221. tv.tv_sec = SELECT_TIMEOUT;
  222. tv.tv_usec = 0;
  223. if (select(max_socket + 1, &rfds, NULL, NULL, &tv) > 0) {
  224. /* server */
  225. if (FD_ISSET(fds[0], &rfds)) {
  226. packlen = udhcp_recv_kernel_packet(&dhcp_msg, fds[0]);
  227. if (packlen > 0) {
  228. pass_back(&dhcp_msg, packlen, fds);
  229. }
  230. }
  231. for (i = 1; i < num_sockets; i++) {
  232. /* clients */
  233. if (!FD_ISSET(fds[i], &rfds))
  234. continue;
  235. addr_size = sizeof(struct sockaddr_in);
  236. packlen = recvfrom(fds[i], &dhcp_msg, sizeof(dhcp_msg), 0,
  237. (struct sockaddr *)(&client_addr), &addr_size);
  238. if (packlen <= 0)
  239. continue;
  240. if (udhcp_read_interface(clients[i-1], NULL, &dhcp_msg.giaddr, NULL))
  241. dhcp_msg.giaddr = gw_ip;
  242. pass_on(&dhcp_msg, packlen, i, fds, &client_addr, server_addr);
  243. }
  244. }
  245. xid_expire();
  246. }
  247. }
  248. int dhcprelay_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
  249. int dhcprelay_main(int argc, char **argv)
  250. {
  251. int num_sockets, max_socket;
  252. int *fds;
  253. uint32_t gw_ip;
  254. char **clients;
  255. struct sockaddr_in server_addr;
  256. server_addr.sin_family = AF_INET;
  257. server_addr.sin_port = htons(SERVER_PORT);
  258. if (argc == 4) {
  259. if (!inet_aton(argv[3], &server_addr.sin_addr))
  260. bb_perror_msg_and_die("didn't grok server");
  261. } else if (argc == 3) {
  262. server_addr.sin_addr.s_addr = htonl(INADDR_BROADCAST);
  263. } else {
  264. bb_show_usage();
  265. }
  266. clients = get_client_devices(argv[1], &num_sockets);
  267. num_sockets++; /* for server socket at fds[0] */
  268. fds = xmalloc(num_sockets * sizeof(fds[0]));
  269. max_socket = init_sockets(clients, num_sockets, argv[2], fds);
  270. if (udhcp_read_interface(argv[2], NULL, &gw_ip, NULL))
  271. return 1;
  272. /* doesn't return */
  273. dhcprelay_loop(fds, num_sockets, max_socket, clients, &server_addr, gw_ip);
  274. /* return 0; - not reached */
  275. }