dhcp.c 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. /*
  2. * Copyright (C) 2010 Felix Fietkau <nbd@openwrt.org>
  3. *
  4. * This program is free software; you can redistribute it and/or modify
  5. * it under the terms of the GNU General Public License v2 as published by
  6. * the Free Software Foundation.
  7. *
  8. * This program is distributed in the hope that it will be useful,
  9. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. * GNU General Public License for more details.
  12. *
  13. * You should have received a copy of the GNU General Public License
  14. * along with this program; if not, write to the Free Software
  15. * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
  16. */
  17. #define _GNU_SOURCE
  18. #include <sys/socket.h>
  19. #include <stdio.h>
  20. #include <string.h>
  21. #include <errno.h>
  22. #include <unistd.h>
  23. #include <fcntl.h>
  24. #include "relayd.h"
  25. struct ip_packet {
  26. struct ether_header eth;
  27. struct iphdr iph;
  28. } __packed;
  29. enum {
  30. DHCP_OPTION_ROUTER = 0x03,
  31. DHCP_OPTION_ROUTES = 0x79,
  32. DHCP_OPTION_END = 0xff,
  33. };
  34. struct dhcp_option {
  35. uint8_t code;
  36. uint8_t len;
  37. uint8_t data[];
  38. };
  39. struct dhcp_header {
  40. uint8_t op, htype, hlen, hops;
  41. uint32_t xit;
  42. uint16_t secs, flags;
  43. struct in_addr ciaddr, yiaddr, siaddr, giaddr;
  44. unsigned char chaddr[16];
  45. unsigned char sname[64];
  46. unsigned char file[128];
  47. uint32_t cookie;
  48. uint8_t option_data[];
  49. } __packed;
  50. static uint16_t
  51. chksum(uint16_t sum, const uint8_t *data, uint16_t len)
  52. {
  53. const uint8_t *last;
  54. uint16_t t;
  55. last = data + len - 1;
  56. while(data < last) {
  57. t = (data[0] << 8) + data[1];
  58. sum += t;
  59. if(sum < t)
  60. sum++;
  61. data += 2;
  62. }
  63. if(data == last) {
  64. t = (data[0] << 8) + 0;
  65. sum += t;
  66. if(sum < t)
  67. sum++;
  68. }
  69. return sum;
  70. }
  71. static void
  72. parse_dhcp_options(struct relayd_host *host, struct dhcp_header *dhcp, int len)
  73. {
  74. uint8_t *end = (uint8_t *) dhcp + len;
  75. struct dhcp_option *opt = (void *)dhcp->option_data;
  76. static const uint8_t dest[4] = { 0, 0, 0, 0 };
  77. while((uint8_t *) opt + sizeof(*opt) < end) {
  78. if ((uint8_t *) opt + opt->len > end ||
  79. (uint8_t *) opt + sizeof(*opt) > end )
  80. break;
  81. opt = (void *) &opt->data[opt->len];
  82. if ((uint8_t *) opt + sizeof(*opt) > end )
  83. break;
  84. switch(opt->code) {
  85. case DHCP_OPTION_ROUTER:
  86. DPRINTF(2, "Found a DHCP router option, len=%d\n", opt->len);
  87. if (!memcmp(opt->data, host->ipaddr, 4))
  88. relayd_add_host_route(host, dest, 0);
  89. else
  90. relayd_add_pending_route(opt->data, dest, 0, 10000);
  91. break;
  92. case DHCP_OPTION_ROUTES:
  93. DPRINTF(2, "Found a DHCP static routes option, len=%d\n", opt->len);
  94. break;
  95. case DHCP_OPTION_END:
  96. opt = (void *) end;
  97. continue;
  98. default:
  99. DPRINTF(3, "Skipping unknown DHCP option %02x\n", opt->code);
  100. continue;
  101. }
  102. }
  103. }
  104. bool relayd_handle_dhcp_packet(struct relayd_interface *rif, void *data, int len, bool forward, bool parse)
  105. {
  106. struct ip_packet *pkt = data;
  107. struct udphdr *udp;
  108. struct dhcp_header *dhcp;
  109. struct relayd_host *host;
  110. int udplen;
  111. uint16_t sum;
  112. if (pkt->eth.ether_type != htons(ETH_P_IP))
  113. return false;
  114. if (pkt->iph.version != 4)
  115. return false;
  116. if (pkt->iph.protocol != IPPROTO_UDP)
  117. return false;
  118. udp = (void *) ((char *) &pkt->iph + (pkt->iph.ihl << 2));
  119. dhcp = (void *) (udp + 1);
  120. if ((uint8_t *)udp + sizeof(*udp) > (uint8_t *)data + len ||
  121. (uint8_t *)dhcp + sizeof(*dhcp) > (uint8_t *)data + len)
  122. return false;
  123. udplen = ntohs(udp->len);
  124. if (udplen > len - ((char *) udp - (char *) data))
  125. return false;
  126. if (udp->dest != htons(67) && udp->source != htons(67))
  127. return false;
  128. if (dhcp->op != 1 && dhcp->op != 2)
  129. return false;
  130. if (!forward)
  131. return true;
  132. if (dhcp->op == 2) {
  133. host = relayd_refresh_host(rif, pkt->eth.ether_shost, (void *) &pkt->iph.saddr);
  134. if (host && parse)
  135. parse_dhcp_options(host, dhcp, udplen - sizeof(struct udphdr));
  136. }
  137. DPRINTF(2, "%s: handling DHCP %s\n", rif->ifname, (dhcp->op == 1 ? "request" : "response"));
  138. dhcp->flags |= htons(DHCP_FLAG_BROADCAST);
  139. udp->check = 0;
  140. sum = udplen + IPPROTO_UDP;
  141. sum = chksum(sum, (void *) &pkt->iph.saddr, 8);
  142. sum = chksum(sum, (void *) udp, udplen);
  143. if (sum == 0)
  144. sum = 0xffff;
  145. udp->check = htons(~sum);
  146. relayd_forward_bcast_packet(rif, data, len);
  147. return true;
  148. }