1 /* 2 * The MIT License (MIT) 3 * 4 * Copyright (c) 2015 by Sergey Fetisov <[email protected]> 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 25 #include "dhserver.h" 26 27 /* DHCP message type */ 28 #define DHCP_DISCOVER 1 29 #define DHCP_OFFER 2 30 #define DHCP_REQUEST 3 31 #define DHCP_DECLINE 4 32 #define DHCP_ACK 5 33 #define DHCP_NAK 6 34 #define DHCP_RELEASE 7 35 #define DHCP_INFORM 8 36 37 /* DHCP options */ 38 enum DHCP_OPTIONS 39 { 40 DHCP_PAD = 0, 41 DHCP_SUBNETMASK = 1, 42 DHCP_ROUTER = 3, 43 DHCP_DNSSERVER = 6, 44 DHCP_HOSTNAME = 12, 45 DHCP_DNSDOMAIN = 15, 46 DHCP_MTU = 26, 47 DHCP_BROADCAST = 28, 48 DHCP_PERFORMROUTERDISC = 31, 49 DHCP_STATICROUTE = 33, 50 DHCP_NISDOMAIN = 40, 51 DHCP_NISSERVER = 41, 52 DHCP_NTPSERVER = 42, 53 DHCP_VENDOR = 43, 54 DHCP_IPADDRESS = 50, 55 DHCP_LEASETIME = 51, 56 DHCP_OPTIONSOVERLOADED = 52, 57 DHCP_MESSAGETYPE = 53, 58 DHCP_SERVERID = 54, 59 DHCP_PARAMETERREQUESTLIST = 55, 60 DHCP_MESSAGE = 56, 61 DHCP_MAXMESSAGESIZE = 57, 62 DHCP_RENEWALTIME = 58, 63 DHCP_REBINDTIME = 59, 64 DHCP_CLASSID = 60, 65 DHCP_CLIENTID = 61, 66 DHCP_USERCLASS = 77, /* RFC 3004 */ 67 DHCP_FQDN = 81, 68 DHCP_DNSSEARCH = 119, /* RFC 3397 */ 69 DHCP_CSR = 121, /* RFC 3442 */ 70 DHCP_MSCSR = 249, /* MS code for RFC 3442 */ 71 DHCP_END = 255 72 }; 73 74 typedef struct 75 { 76 uint8_t dp_op; /* packet opcode type */ 77 uint8_t dp_htype; /* hardware addr type */ 78 uint8_t dp_hlen; /* hardware addr length */ 79 uint8_t dp_hops; /* gateway hops */ 80 uint32_t dp_xid; /* transaction ID */ 81 uint16_t dp_secs; /* seconds since boot began */ 82 uint16_t dp_flags; 83 uint8_t dp_ciaddr[4]; /* client IP address */ 84 uint8_t dp_yiaddr[4]; /* 'your' IP address */ 85 uint8_t dp_siaddr[4]; /* server IP address */ 86 uint8_t dp_giaddr[4]; /* gateway IP address */ 87 uint8_t dp_chaddr[16]; /* client hardware address */ 88 uint8_t dp_legacy[192]; 89 uint8_t dp_magic[4]; 90 uint8_t dp_options[275]; /* options area */ 91 } DHCP_TYPE; 92 93 DHCP_TYPE dhcp_data; 94 static struct udp_pcb *pcb = NULL; 95 static dhcp_config_t *config = NULL; 96 97 char magic_cookie[] = {0x63,0x82,0x53,0x63}; 98 99 static uint32_t get_addr32(const uint8_t addr[4]) { 100 return PP_HTONL(LWIP_MAKEU32(addr[0],addr[1],addr[2],addr[3])); 101 } 102 103 static void set_addr32(uint8_t *dst, uint32_t src) { 104 memcpy(dst, &src, 4); 105 } 106 107 static dhcp_entry_t *entry_by_ip(uint32_t ip) 108 { 109 int i; 110 for (i = 0; i < config->num_entry; i++) 111 if (get_addr32(config->entries[i].addr) == ip) 112 return &config->entries[i]; 113 return NULL; 114 } 115 116 static dhcp_entry_t *entry_by_mac(uint8_t *mac) 117 { 118 int i; 119 for (i = 0; i < config->num_entry; i++) 120 if (memcmp(config->entries[i].mac, mac, 6) == 0) 121 return &config->entries[i]; 122 return NULL; 123 } 124 125 static __inline bool is_vacant(dhcp_entry_t *entry) 126 { 127 return memcmp("\0\0\0\0\0", entry->mac, 6) == 0; 128 } 129 130 static dhcp_entry_t *vacant_address(void) 131 { 132 int i; 133 for (i = 0; i < config->num_entry; i++) 134 if (is_vacant(config->entries + i)) 135 return config->entries + i; 136 return NULL; 137 } 138 139 static __inline void free_entry(dhcp_entry_t *entry) 140 { 141 memset(entry->mac, 0, 6); 142 } 143 144 static uint8_t *find_dhcp_option(uint8_t *attrs, int size, uint8_t attr) 145 { 146 int i = 0; 147 while ((i + 1) < size) 148 { 149 int next = i + attrs[i + 1] + 2; 150 if (next > size) return NULL; 151 if (attrs[i] == attr) 152 return attrs + i; 153 i = next; 154 } 155 return NULL; 156 } 157 158 static int fill_options(void *dest, 159 uint8_t msg_type, 160 const char *domain, 161 uint32_t dns, 162 int lease_time, 163 uint32_t serverid, 164 uint32_t router, 165 uint32_t subnet) 166 { 167 uint8_t *ptr = (uint8_t *)dest; 168 /* ACK message type */ 169 *ptr++ = 53; 170 *ptr++ = 1; 171 *ptr++ = msg_type; 172 173 /* dhcp server identifier */ 174 *ptr++ = DHCP_SERVERID; 175 *ptr++ = 4; 176 set_addr32(ptr, serverid); 177 ptr += 4; 178 179 /* lease time */ 180 *ptr++ = DHCP_LEASETIME; 181 *ptr++ = 4; 182 *ptr++ = (lease_time >> 24) & 0xFF; 183 *ptr++ = (lease_time >> 16) & 0xFF; 184 *ptr++ = (lease_time >> 8) & 0xFF; 185 *ptr++ = (lease_time >> 0) & 0xFF; 186 187 /* subnet mask */ 188 *ptr++ = DHCP_SUBNETMASK; 189 *ptr++ = 4; 190 set_addr32(ptr, subnet); 191 ptr += 4; 192 193 /* router */ 194 if (router != 0) 195 { 196 *ptr++ = DHCP_ROUTER; 197 *ptr++ = 4; 198 set_addr32(ptr, router); 199 ptr += 4; 200 } 201 202 /* domain name */ 203 if (domain != NULL) 204 { 205 int len = strlen(domain); 206 *ptr++ = DHCP_DNSDOMAIN; 207 *ptr++ = len; 208 memcpy(ptr, domain, len); 209 ptr += len; 210 } 211 212 /* domain name server (DNS) */ 213 if (dns != 0) 214 { 215 *ptr++ = DHCP_DNSSERVER; 216 *ptr++ = 4; 217 set_addr32(ptr, dns); 218 ptr += 4; 219 } 220 221 /* end */ 222 *ptr++ = DHCP_END; 223 return ptr - (uint8_t *)dest; 224 } 225 226 static void udp_recv_proc(void *arg, struct udp_pcb *upcb, struct pbuf *p, const ip_addr_t *addr, u16_t port) 227 { 228 (void) arg; 229 (void) addr; 230 231 uint8_t *ptr; 232 dhcp_entry_t *entry; 233 struct pbuf *pp; 234 struct netif *nif; 235 236 unsigned int n = p->len; 237 if (n > sizeof(dhcp_data)) n = sizeof(dhcp_data); 238 memcpy(&dhcp_data, p->payload, n); 239 switch (dhcp_data.dp_options[2]) 240 { 241 case DHCP_DISCOVER: 242 entry = entry_by_mac(dhcp_data.dp_chaddr); 243 if (entry == NULL) entry = vacant_address(); 244 if (entry == NULL) break; 245 246 dhcp_data.dp_op = 2; /* reply */ 247 dhcp_data.dp_secs = 0; 248 dhcp_data.dp_flags = 0; 249 set_addr32(dhcp_data.dp_yiaddr, get_addr32(entry->addr)); 250 memcpy(dhcp_data.dp_magic, magic_cookie, 4); 251 252 memset(dhcp_data.dp_options, 0, sizeof(dhcp_data.dp_options)); 253 254 fill_options(dhcp_data.dp_options, 255 DHCP_OFFER, 256 config->domain, 257 get_addr32(config->dns), 258 entry->lease, 259 get_addr32(config->addr), 260 get_addr32(config->addr), 261 get_addr32(entry->subnet)); 262 263 pp = pbuf_alloc(PBUF_TRANSPORT, sizeof(dhcp_data), PBUF_POOL); 264 if (pp == NULL) break; 265 memcpy(pp->payload, &dhcp_data, sizeof(dhcp_data)); 266 nif = ip_current_input_netif(); 267 if (nif) { 268 udp_sendto_if(upcb, pp, IP_ADDR_BROADCAST, port, nif); 269 } else { 270 udp_sendto(upcb, pp, IP_ADDR_BROADCAST, port); 271 } 272 pbuf_free(pp); 273 break; 274 275 case DHCP_REQUEST: 276 /* 1. find requested ipaddr in option list */ 277 ptr = find_dhcp_option(dhcp_data.dp_options, sizeof(dhcp_data.dp_options), DHCP_IPADDRESS); 278 if (ptr == NULL) break; 279 if (ptr[1] != 4) break; 280 ptr += 2; 281 282 /* 2. does hw-address registered? */ 283 entry = entry_by_mac(dhcp_data.dp_chaddr); 284 if (entry != NULL) free_entry(entry); 285 286 /* 3. find requested ipaddr */ 287 entry = entry_by_ip(get_addr32(ptr)); 288 if (entry == NULL) break; 289 if (!is_vacant(entry)) break; 290 291 /* 4. fill struct fields */ 292 memcpy(dhcp_data.dp_yiaddr, ptr, 4); 293 dhcp_data.dp_op = 2; /* reply */ 294 dhcp_data.dp_secs = 0; 295 dhcp_data.dp_flags = 0; 296 memcpy(dhcp_data.dp_magic, magic_cookie, 4); 297 298 /* 5. fill options */ 299 memset(dhcp_data.dp_options, 0, sizeof(dhcp_data.dp_options)); 300 301 fill_options(dhcp_data.dp_options, 302 DHCP_ACK, 303 config->domain, 304 get_addr32(config->dns), 305 entry->lease, 306 get_addr32(config->addr), 307 get_addr32(config->addr), 308 get_addr32(entry->subnet)); 309 310 /* 6. send ACK */ 311 pp = pbuf_alloc(PBUF_TRANSPORT, sizeof(dhcp_data), PBUF_POOL); 312 if (pp == NULL) break; 313 memcpy(entry->mac, dhcp_data.dp_chaddr, 6); 314 memcpy(pp->payload, &dhcp_data, sizeof(dhcp_data)); 315 nif = ip_current_input_netif(); 316 if (nif) { 317 udp_sendto_if(upcb, pp, IP_ADDR_BROADCAST, port, nif); 318 } else { 319 udp_sendto(upcb, pp, IP_ADDR_BROADCAST, port); 320 } 321 pbuf_free(pp); 322 break; 323 324 default: 325 break; 326 } 327 pbuf_free(p); 328 } 329 330 err_t dhserv_init(dhcp_config_t *c) 331 { 332 err_t err; 333 // udp_init(); already called from lwip_init 334 dhserv_free(); 335 pcb = udp_new(); 336 if (pcb == NULL) 337 return ERR_MEM; 338 err = udp_bind(pcb, IP_ADDR_ANY, c->port); 339 if (err != ERR_OK) 340 { 341 dhserv_free(); 342 return err; 343 } 344 udp_recv(pcb, udp_recv_proc, NULL); 345 config = c; 346 return ERR_OK; 347 } 348 349 void dhserv_free(void) 350 { 351 if (pcb == NULL) return; 352 udp_remove(pcb); 353 pcb = NULL; 354 } 355