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 235 unsigned int n = p->len; 236 if (n > sizeof(dhcp_data)) n = sizeof(dhcp_data); 237 memcpy(&dhcp_data, p->payload, n); 238 switch (dhcp_data.dp_options[2]) 239 { 240 case DHCP_DISCOVER: 241 entry = entry_by_mac(dhcp_data.dp_chaddr); 242 if (entry == NULL) entry = vacant_address(); 243 if (entry == NULL) break; 244 245 dhcp_data.dp_op = 2; /* reply */ 246 dhcp_data.dp_secs = 0; 247 dhcp_data.dp_flags = 0; 248 set_addr32(dhcp_data.dp_yiaddr, get_addr32(entry->addr)); 249 memcpy(dhcp_data.dp_magic, magic_cookie, 4); 250 251 memset(dhcp_data.dp_options, 0, sizeof(dhcp_data.dp_options)); 252 253 fill_options(dhcp_data.dp_options, 254 DHCP_OFFER, 255 config->domain, 256 get_addr32(config->dns), 257 entry->lease, 258 get_addr32(config->addr), 259 get_addr32(config->addr), 260 get_addr32(entry->subnet)); 261 262 pp = pbuf_alloc(PBUF_TRANSPORT, sizeof(dhcp_data), PBUF_POOL); 263 if (pp == NULL) break; 264 memcpy(pp->payload, &dhcp_data, sizeof(dhcp_data)); 265 udp_sendto(upcb, pp, IP_ADDR_BROADCAST, port); 266 pbuf_free(pp); 267 break; 268 269 case DHCP_REQUEST: 270 /* 1. find requested ipaddr in option list */ 271 ptr = find_dhcp_option(dhcp_data.dp_options, sizeof(dhcp_data.dp_options), DHCP_IPADDRESS); 272 if (ptr == NULL) break; 273 if (ptr[1] != 4) break; 274 ptr += 2; 275 276 /* 2. does hw-address registered? */ 277 entry = entry_by_mac(dhcp_data.dp_chaddr); 278 if (entry != NULL) free_entry(entry); 279 280 /* 3. find requested ipaddr */ 281 entry = entry_by_ip(get_addr32(ptr)); 282 if (entry == NULL) break; 283 if (!is_vacant(entry)) break; 284 285 /* 4. fill struct fields */ 286 memcpy(dhcp_data.dp_yiaddr, ptr, 4); 287 dhcp_data.dp_op = 2; /* reply */ 288 dhcp_data.dp_secs = 0; 289 dhcp_data.dp_flags = 0; 290 memcpy(dhcp_data.dp_magic, magic_cookie, 4); 291 292 /* 5. fill options */ 293 memset(dhcp_data.dp_options, 0, sizeof(dhcp_data.dp_options)); 294 295 fill_options(dhcp_data.dp_options, 296 DHCP_ACK, 297 config->domain, 298 get_addr32(config->dns), 299 entry->lease, 300 get_addr32(config->addr), 301 get_addr32(config->addr), 302 get_addr32(entry->subnet)); 303 304 /* 6. send ACK */ 305 pp = pbuf_alloc(PBUF_TRANSPORT, sizeof(dhcp_data), PBUF_POOL); 306 if (pp == NULL) break; 307 memcpy(entry->mac, dhcp_data.dp_chaddr, 6); 308 memcpy(pp->payload, &dhcp_data, sizeof(dhcp_data)); 309 udp_sendto(upcb, pp, IP_ADDR_BROADCAST, port); 310 pbuf_free(pp); 311 break; 312 313 default: 314 break; 315 } 316 pbuf_free(p); 317 } 318 319 err_t dhserv_init(dhcp_config_t *c) 320 { 321 err_t err; 322 // udp_init(); already called from lwip_init 323 dhserv_free(); 324 pcb = udp_new(); 325 if (pcb == NULL) 326 return ERR_MEM; 327 err = udp_bind(pcb, IP_ADDR_ANY, c->port); 328 if (err != ERR_OK) 329 { 330 dhserv_free(); 331 return err; 332 } 333 udp_recv(pcb, udp_recv_proc, NULL); 334 config = c; 335 return ERR_OK; 336 } 337 338 void dhserv_free(void) 339 { 340 if (pcb == NULL) return; 341 udp_remove(pcb); 342 pcb = NULL; 343 } 344