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