dhserver.c 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352
  1. /*
  2. * The MIT License (MIT)
  3. *
  4. * Copyright (c) 2015 by Sergey Fetisov <fsenok@gmail.com>
  5. *
  6. * Permission is hereby granted, free of charge, to any person obtaining a copy
  7. * of this software and associated documentation files (the "Software"), to deal
  8. * in the Software without restriction, including without limitation the rights
  9. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10. * copies of the Software, and to permit persons to whom the Software is
  11. * furnished to do so, subject to the following conditions:
  12. *
  13. * The above copyright notice and this permission notice shall be included in all
  14. * copies or substantial portions of the Software.
  15. *
  16. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  19. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  22. * SOFTWARE.
  23. */
  24. #include "dhserver.h"
  25. /* DHCP message type */
  26. #define DHCP_DISCOVER 1
  27. #define DHCP_OFFER 2
  28. #define DHCP_REQUEST 3
  29. #define DHCP_DECLINE 4
  30. #define DHCP_ACK 5
  31. #define DHCP_NAK 6
  32. #define DHCP_RELEASE 7
  33. #define DHCP_INFORM 8
  34. /* DHCP options */
  35. enum DHCP_OPTIONS
  36. {
  37. DHCP_PAD = 0,
  38. DHCP_SUBNETMASK = 1,
  39. DHCP_ROUTER = 3,
  40. DHCP_DNSSERVER = 6,
  41. DHCP_HOSTNAME = 12,
  42. DHCP_DNSDOMAIN = 15,
  43. DHCP_MTU = 26,
  44. DHCP_BROADCAST = 28,
  45. DHCP_PERFORMROUTERDISC = 31,
  46. DHCP_STATICROUTE = 33,
  47. DHCP_NISDOMAIN = 40,
  48. DHCP_NISSERVER = 41,
  49. DHCP_NTPSERVER = 42,
  50. DHCP_VENDOR = 43,
  51. DHCP_IPADDRESS = 50,
  52. DHCP_LEASETIME = 51,
  53. DHCP_OPTIONSOVERLOADED = 52,
  54. DHCP_MESSAGETYPE = 53,
  55. DHCP_SERVERID = 54,
  56. DHCP_PARAMETERREQUESTLIST = 55,
  57. DHCP_MESSAGE = 56,
  58. DHCP_MAXMESSAGESIZE = 57,
  59. DHCP_RENEWALTIME = 58,
  60. DHCP_REBINDTIME = 59,
  61. DHCP_CLASSID = 60,
  62. DHCP_CLIENTID = 61,
  63. DHCP_USERCLASS = 77, /* RFC 3004 */
  64. DHCP_FQDN = 81,
  65. DHCP_DNSSEARCH = 119, /* RFC 3397 */
  66. DHCP_CSR = 121, /* RFC 3442 */
  67. DHCP_MSCSR = 249, /* MS code for RFC 3442 */
  68. DHCP_END = 255
  69. };
  70. typedef struct
  71. {
  72. uint8_t dp_op; /* packet opcode type */
  73. uint8_t dp_htype; /* hardware addr type */
  74. uint8_t dp_hlen; /* hardware addr length */
  75. uint8_t dp_hops; /* gateway hops */
  76. uint32_t dp_xid; /* transaction ID */
  77. uint16_t dp_secs; /* seconds since boot began */
  78. uint16_t dp_flags;
  79. uint8_t dp_ciaddr[4]; /* client IP address */
  80. uint8_t dp_yiaddr[4]; /* 'your' IP address */
  81. uint8_t dp_siaddr[4]; /* server IP address */
  82. uint8_t dp_giaddr[4]; /* gateway IP address */
  83. uint8_t dp_chaddr[16]; /* client hardware address */
  84. uint8_t dp_legacy[192];
  85. uint8_t dp_magic[4];
  86. uint8_t dp_options[275]; /* options area */
  87. } DHCP_TYPE;
  88. DHCP_TYPE dhcp_data;
  89. static struct udp_pcb *pcb = NULL;
  90. static const dhcp_config_t *config = NULL;
  91. char magic_cookie[] = {0x63,0x82,0x53,0x63};
  92. static ip4_addr_t get_ip(const uint8_t *pnt)
  93. {
  94. ip4_addr_t result;
  95. memcpy(&result, pnt, sizeof(result));
  96. return result;
  97. }
  98. static void set_ip(uint8_t *pnt, ip4_addr_t value)
  99. {
  100. memcpy(pnt, &value.addr, sizeof(value.addr));
  101. }
  102. static dhcp_entry_t *entry_by_ip(ip4_addr_t ip)
  103. {
  104. int i;
  105. for (i = 0; i < config->num_entry; i++)
  106. if (config->entries[i].addr.addr == ip.addr)
  107. return &config->entries[i];
  108. return NULL;
  109. }
  110. static dhcp_entry_t *entry_by_mac(uint8_t *mac)
  111. {
  112. int i;
  113. for (i = 0; i < config->num_entry; i++)
  114. if (memcmp(config->entries[i].mac, mac, 6) == 0)
  115. return &config->entries[i];
  116. return NULL;
  117. }
  118. static __inline bool is_vacant(dhcp_entry_t *entry)
  119. {
  120. return memcmp("\0\0\0\0\0", entry->mac, 6) == 0;
  121. }
  122. static dhcp_entry_t *vacant_address(void)
  123. {
  124. int i;
  125. for (i = 0; i < config->num_entry; i++)
  126. if (is_vacant(config->entries + i))
  127. return config->entries + i;
  128. return NULL;
  129. }
  130. static __inline void free_entry(dhcp_entry_t *entry)
  131. {
  132. memset(entry->mac, 0, 6);
  133. }
  134. uint8_t *find_dhcp_option(uint8_t *attrs, int size, uint8_t attr)
  135. {
  136. int i = 0;
  137. while ((i + 1) < size)
  138. {
  139. int next = i + attrs[i + 1] + 2;
  140. if (next > size) return NULL;
  141. if (attrs[i] == attr)
  142. return attrs + i;
  143. i = next;
  144. }
  145. return NULL;
  146. }
  147. int fill_options(void *dest,
  148. uint8_t msg_type,
  149. const char *domain,
  150. ip4_addr_t dns,
  151. int lease_time,
  152. ip4_addr_t serverid,
  153. ip4_addr_t router,
  154. ip4_addr_t subnet)
  155. {
  156. uint8_t *ptr = (uint8_t *)dest;
  157. /* ACK message type */
  158. *ptr++ = 53;
  159. *ptr++ = 1;
  160. *ptr++ = msg_type;
  161. /* dhcp server identifier */
  162. *ptr++ = DHCP_SERVERID;
  163. *ptr++ = 4;
  164. set_ip(ptr, serverid);
  165. ptr += 4;
  166. /* lease time */
  167. *ptr++ = DHCP_LEASETIME;
  168. *ptr++ = 4;
  169. *ptr++ = (lease_time >> 24) & 0xFF;
  170. *ptr++ = (lease_time >> 16) & 0xFF;
  171. *ptr++ = (lease_time >> 8) & 0xFF;
  172. *ptr++ = (lease_time >> 0) & 0xFF;
  173. /* subnet mask */
  174. *ptr++ = DHCP_SUBNETMASK;
  175. *ptr++ = 4;
  176. set_ip(ptr, subnet);
  177. ptr += 4;
  178. /* router */
  179. if (router.addr != 0)
  180. {
  181. *ptr++ = DHCP_ROUTER;
  182. *ptr++ = 4;
  183. set_ip(ptr, router);
  184. ptr += 4;
  185. }
  186. /* domain name */
  187. if (domain != NULL)
  188. {
  189. int len = strlen(domain);
  190. *ptr++ = DHCP_DNSDOMAIN;
  191. *ptr++ = len;
  192. memcpy(ptr, domain, len);
  193. ptr += len;
  194. }
  195. /* domain name server (DNS) */
  196. if (dns.addr != 0)
  197. {
  198. *ptr++ = DHCP_DNSSERVER;
  199. *ptr++ = 4;
  200. set_ip(ptr, dns);
  201. ptr += 4;
  202. }
  203. /* end */
  204. *ptr++ = DHCP_END;
  205. return ptr - (uint8_t *)dest;
  206. }
  207. static void udp_recv_proc(void *arg, struct udp_pcb *upcb, struct pbuf *p, const ip_addr_t *addr, u16_t port)
  208. {
  209. uint8_t *ptr;
  210. dhcp_entry_t *entry;
  211. struct pbuf *pp;
  212. struct netif *netif = netif_get_by_index(p->if_idx);
  213. (void)arg;
  214. (void)addr;
  215. unsigned n = p->len;
  216. if (n > sizeof(dhcp_data)) n = sizeof(dhcp_data);
  217. memcpy(&dhcp_data, p->payload, n);
  218. ptr = find_dhcp_option(dhcp_data.dp_options, sizeof(dhcp_data.dp_options), DHCP_MESSAGETYPE);
  219. if (ptr == NULL) return;
  220. switch (ptr[2])
  221. {
  222. case DHCP_DISCOVER:
  223. entry = entry_by_mac(dhcp_data.dp_chaddr);
  224. if (entry == NULL) entry = vacant_address();
  225. if (entry == NULL) break;
  226. dhcp_data.dp_op = 2; /* reply */
  227. dhcp_data.dp_secs = 0;
  228. dhcp_data.dp_flags = 0;
  229. set_ip(dhcp_data.dp_yiaddr, entry->addr);
  230. memcpy(dhcp_data.dp_magic, magic_cookie, 4);
  231. memset(dhcp_data.dp_options, 0, sizeof(dhcp_data.dp_options));
  232. fill_options(dhcp_data.dp_options,
  233. DHCP_OFFER,
  234. config->domain,
  235. config->dns,
  236. entry->lease,
  237. *netif_ip4_addr(netif),
  238. config->router,
  239. *netif_ip4_netmask(netif));
  240. pp = pbuf_alloc(PBUF_TRANSPORT, sizeof(dhcp_data), PBUF_POOL);
  241. if (pp == NULL) break;
  242. memcpy(pp->payload, &dhcp_data, sizeof(dhcp_data));
  243. udp_sendto(upcb, pp, IP_ADDR_BROADCAST, port);
  244. pbuf_free(pp);
  245. break;
  246. case DHCP_REQUEST:
  247. /* 1. find requested ipaddr in option list */
  248. ptr = find_dhcp_option(dhcp_data.dp_options, sizeof(dhcp_data.dp_options), DHCP_IPADDRESS);
  249. if (ptr == NULL) break;
  250. if (ptr[1] != 4) break;
  251. ptr += 2;
  252. /* 2. does hw-address registered? */
  253. entry = entry_by_mac(dhcp_data.dp_chaddr);
  254. if (entry != NULL) free_entry(entry);
  255. /* 3. find requested ipaddr */
  256. entry = entry_by_ip(get_ip(ptr));
  257. if (entry == NULL) break;
  258. if (!is_vacant(entry)) break;
  259. /* 4. fill struct fields */
  260. memcpy(dhcp_data.dp_yiaddr, ptr, 4);
  261. dhcp_data.dp_op = 2; /* reply */
  262. dhcp_data.dp_secs = 0;
  263. dhcp_data.dp_flags = 0;
  264. memcpy(dhcp_data.dp_magic, magic_cookie, 4);
  265. /* 5. fill options */
  266. memset(dhcp_data.dp_options, 0, sizeof(dhcp_data.dp_options));
  267. fill_options(dhcp_data.dp_options,
  268. DHCP_ACK,
  269. config->domain,
  270. config->dns,
  271. entry->lease,
  272. *netif_ip4_addr(netif),
  273. config->router,
  274. *netif_ip4_netmask(netif));
  275. /* 6. send ACK */
  276. pp = pbuf_alloc(PBUF_TRANSPORT, sizeof(dhcp_data), PBUF_POOL);
  277. if (pp == NULL) break;
  278. memcpy(entry->mac, dhcp_data.dp_chaddr, 6);
  279. memcpy(pp->payload, &dhcp_data, sizeof(dhcp_data));
  280. udp_sendto(upcb, pp, IP_ADDR_BROADCAST, port);
  281. pbuf_free(pp);
  282. break;
  283. default:
  284. break;
  285. }
  286. pbuf_free(p);
  287. }
  288. err_t dhserv_init(const dhcp_config_t *c)
  289. {
  290. err_t err;
  291. udp_init();
  292. dhserv_free();
  293. pcb = udp_new();
  294. if (pcb == NULL)
  295. return ERR_MEM;
  296. err = udp_bind(pcb, IP_ADDR_ANY, c->port);
  297. if (err != ERR_OK)
  298. {
  299. dhserv_free();
  300. return err;
  301. }
  302. udp_recv(pcb, udp_recv_proc, NULL);
  303. config = c;
  304. return ERR_OK;
  305. }
  306. void dhserv_free(void)
  307. {
  308. if (pcb == NULL) return;
  309. udp_remove(pcb);
  310. pcb = NULL;
  311. }