Free (GPLv2) TCP/IP stack developed by TASS Belgium
Dependents: lpc1768-picotcp-demo ZeroMQ_PicoTCP_Publisher_demo TCPSocket_HelloWorld_PicoTCP Pico_TCP_UDP_Test ... more
pico_dhcp_server.c
00001 /********************************************************************* 00002 PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved. 00003 See LICENSE and COPYING for usage. 00004 00005 00006 Authors: Frederik Van Slycken, Kristof Roelants 00007 *********************************************************************/ 00008 00009 #include "pico_dhcp_server.h" 00010 #include "pico_config.h" 00011 #include "pico_addressing.h" 00012 #include "pico_socket.h" 00013 #include "pico_udp.h" 00014 #include "pico_stack.h" 00015 #include "pico_arp.h" 00016 00017 #if (defined PICO_SUPPORT_DHCPD && defined PICO_SUPPORT_UDP) 00018 00019 #define dhcps_dbg(...) do {} while(0) 00020 /* #define dhcps_dbg dbg */ 00021 00022 /* default configurations */ 00023 #define DHCP_SERVER_OPENDNS long_be(0xd043dede) /* OpenDNS DNS server 208.67.222.222 */ 00024 #define DHCP_SERVER_POOL_START long_be(0x00000064) 00025 #define DHCP_SERVER_POOL_END long_be(0x000000fe) 00026 #define DHCP_SERVER_LEASE_TIME long_be(0x00000078) 00027 00028 /* maximum size of a DHCP message */ 00029 #define DHCP_SERVER_MAXMSGSIZE (PICO_IP_MRU - sizeof(struct pico_ipv4_hdr) - sizeof(struct pico_udp_hdr)) 00030 00031 enum dhcp_server_state { 00032 PICO_DHCP_STATE_DISCOVER = 0, 00033 PICO_DHCP_STATE_OFFER, 00034 PICO_DHCP_STATE_REQUEST, 00035 PICO_DHCP_STATE_BOUND, 00036 PICO_DHCP_STATE_RENEWING 00037 }; 00038 00039 struct pico_dhcp_server_negotiation { 00040 uint32_t xid; 00041 enum dhcp_server_state state; 00042 struct pico_dhcp_server_setting *dhcps; 00043 struct pico_ip4 ciaddr; 00044 struct pico_eth hwaddr; 00045 uint8_t bcast; 00046 }; 00047 00048 static inline int ip_address_is_in_dhcp_range(struct pico_dhcp_server_negotiation *n, uint32_t x) 00049 { 00050 uint32_t ip_hostendian = long_be(x); 00051 if (ip_hostendian < long_be(n->dhcps->pool_start)) 00052 return 0; 00053 00054 if (ip_hostendian > long_be(n->dhcps->pool_end)) 00055 return 0; 00056 00057 return 1; 00058 } 00059 00060 00061 static void pico_dhcpd_wakeup(uint16_t ev, struct pico_socket *s); 00062 00063 static int dhcp_settings_cmp(void *ka, void *kb) 00064 { 00065 struct pico_dhcp_server_setting *a = ka, *b = kb; 00066 if (a->dev == b->dev) 00067 return 0; 00068 00069 return (a->dev < b->dev) ? (-1) : (1); 00070 } 00071 PICO_TREE_DECLARE(DHCPSettings, dhcp_settings_cmp); 00072 00073 static int dhcp_negotiations_cmp(void *ka, void *kb) 00074 { 00075 struct pico_dhcp_server_negotiation *a = ka, *b = kb; 00076 if (a->xid == b->xid) 00077 return 0; 00078 00079 return (a->xid < b->xid) ? (-1) : (1); 00080 } 00081 PICO_TREE_DECLARE(DHCPNegotiations, dhcp_negotiations_cmp); 00082 00083 00084 static inline void dhcps_set_default_pool_start_if_not_provided(struct pico_dhcp_server_setting *dhcps) 00085 { 00086 if (!dhcps->pool_start) 00087 dhcps->pool_start = (dhcps->server_ip.addr & dhcps->netmask.addr) | DHCP_SERVER_POOL_START; 00088 } 00089 00090 static inline void dhcps_set_default_pool_end_if_not_provided(struct pico_dhcp_server_setting *dhcps) 00091 { 00092 if (!dhcps->pool_end) 00093 dhcps->pool_end = (dhcps->server_ip.addr & dhcps->netmask.addr) | DHCP_SERVER_POOL_END; 00094 } 00095 static inline void dhcps_set_default_lease_time_if_not_provided(struct pico_dhcp_server_setting *dhcps) 00096 { 00097 if (!dhcps->lease_time) 00098 dhcps->lease_time = DHCP_SERVER_LEASE_TIME; 00099 } 00100 00101 static inline struct pico_dhcp_server_setting *dhcps_try_open_socket(struct pico_dhcp_server_setting *dhcps) 00102 { 00103 uint16_t port = PICO_DHCPD_PORT; 00104 dhcps->s = pico_socket_open(PICO_PROTO_IPV4, PICO_PROTO_UDP, &pico_dhcpd_wakeup); 00105 if (!dhcps->s) { 00106 dhcps_dbg("DHCP server ERROR: failure opening socket (%s)\n", strerror(pico_err)); 00107 PICO_FREE(dhcps); 00108 return NULL; 00109 } 00110 00111 if (pico_socket_bind(dhcps->s, &dhcps->server_ip, &port) < 0) { 00112 dhcps_dbg("DHCP server ERROR: failure binding socket (%s)\n", strerror(pico_err)); 00113 PICO_FREE(dhcps); 00114 return NULL; 00115 } 00116 00117 pico_tree_insert(&DHCPSettings, dhcps); 00118 return dhcps; 00119 } 00120 00121 static struct pico_dhcp_server_setting *pico_dhcp_server_add_setting(struct pico_dhcp_server_setting *setting) 00122 { 00123 struct pico_dhcp_server_setting *dhcps = NULL, *found = NULL, test = { 00124 0 00125 }; 00126 struct pico_ipv4_link *link = NULL; 00127 00128 link = pico_ipv4_link_get(&setting->server_ip); 00129 if (!link) { 00130 pico_err = PICO_ERR_EINVAL; 00131 return NULL; 00132 } 00133 00134 test.dev = setting->dev; 00135 found = pico_tree_findKey(&DHCPSettings, &test); 00136 if (found) { 00137 pico_err = PICO_ERR_EINVAL; 00138 return NULL; 00139 } 00140 00141 dhcps = PICO_ZALLOC(sizeof(struct pico_dhcp_server_setting)); 00142 if (!dhcps) { 00143 pico_err = PICO_ERR_ENOMEM; 00144 return NULL; 00145 } 00146 00147 dhcps->lease_time = setting->lease_time; 00148 dhcps->pool_start = setting->pool_start; 00149 dhcps->pool_next = setting->pool_next; 00150 dhcps->pool_end = setting->pool_end; 00151 dhcps->dev = link->dev; 00152 dhcps->server_ip = link->address; 00153 dhcps->netmask = link->netmask; 00154 00155 /* default values if not provided */ 00156 dhcps_set_default_lease_time_if_not_provided(dhcps); 00157 dhcps_set_default_pool_end_if_not_provided(dhcps); 00158 dhcps_set_default_pool_start_if_not_provided(dhcps); 00159 00160 dhcps->pool_next = dhcps->pool_start; 00161 00162 return dhcps_try_open_socket(dhcps); 00163 00164 } 00165 00166 static struct pico_dhcp_server_negotiation *pico_dhcp_server_find_negotiation(uint32_t xid) 00167 { 00168 struct pico_dhcp_server_negotiation test = { 00169 0 00170 }, *found = NULL; 00171 00172 test.xid = xid; 00173 found = pico_tree_findKey(&DHCPNegotiations, &test); 00174 if (found) 00175 return found; 00176 else 00177 return NULL; 00178 } 00179 00180 static inline void dhcp_negotiation_set_ciaddr(struct pico_dhcp_server_negotiation *dhcpn) 00181 { 00182 struct pico_ip4 *ciaddr = NULL; 00183 ciaddr = pico_arp_reverse_lookup(&dhcpn->hwaddr); 00184 if (!ciaddr) { 00185 dhcpn->ciaddr.addr = dhcpn->dhcps->pool_next; 00186 dhcpn->dhcps->pool_next = long_be(long_be(dhcpn->dhcps->pool_next) + 1); 00187 pico_arp_create_entry(dhcpn->hwaddr.addr, dhcpn->ciaddr, dhcpn->dhcps->dev); 00188 } else { 00189 dhcpn->ciaddr = *ciaddr; 00190 } 00191 } 00192 00193 static struct pico_dhcp_server_negotiation *pico_dhcp_server_add_negotiation(struct pico_device *dev, struct pico_dhcp_hdr *hdr) 00194 { 00195 struct pico_dhcp_server_negotiation *dhcpn = NULL; 00196 struct pico_dhcp_server_setting test = { 00197 0 00198 }; 00199 00200 if (pico_dhcp_server_find_negotiation(hdr->xid)) 00201 return NULL; 00202 00203 dhcpn = PICO_ZALLOC(sizeof(struct pico_dhcp_server_negotiation)); 00204 if (!dhcpn) { 00205 pico_err = PICO_ERR_ENOMEM; 00206 return NULL; 00207 } 00208 00209 dhcpn->xid = hdr->xid; 00210 dhcpn->state = PICO_DHCP_STATE_DISCOVER; 00211 dhcpn->bcast = ((short_be(hdr->flags) & PICO_DHCP_FLAG_BROADCAST) != 0) ? (1) : (0); 00212 memcpy(dhcpn->hwaddr.addr, hdr->hwaddr, PICO_SIZE_ETH); 00213 00214 test.dev = dev; 00215 dhcpn->dhcps = pico_tree_findKey(&DHCPSettings, &test); 00216 if (!dhcpn->dhcps) { 00217 dhcps_dbg("DHCP server WARNING: received DHCP message on unconfigured link %s\n", dev->name); 00218 PICO_FREE(dhcpn); 00219 return NULL; 00220 } 00221 00222 dhcp_negotiation_set_ciaddr(dhcpn); 00223 pico_tree_insert(&DHCPNegotiations, dhcpn); 00224 return dhcpn; 00225 } 00226 00227 static void dhcpd_make_reply(struct pico_dhcp_server_negotiation *dhcpn, uint8_t msg_type) 00228 { 00229 int r = 0, optlen = 0, offset = 0; 00230 struct pico_ip4 broadcast = { 00231 0 00232 }, dns = { 00233 0 00234 }, destination = { 00235 .addr = 0xFFFFFFFF 00236 }; 00237 struct pico_dhcp_hdr *hdr = NULL; 00238 00239 dns.addr = DHCP_SERVER_OPENDNS; 00240 broadcast.addr = dhcpn->dhcps->server_ip.addr | ~(dhcpn->dhcps->netmask.addr); 00241 00242 optlen = PICO_DHCP_OPTLEN_MSGTYPE + PICO_DHCP_OPTLEN_SERVERID + PICO_DHCP_OPTLEN_LEASETIME + PICO_DHCP_OPTLEN_NETMASK + PICO_DHCP_OPTLEN_ROUTER 00243 + PICO_DHCP_OPTLEN_BROADCAST + PICO_DHCP_OPTLEN_DNS + PICO_DHCP_OPTLEN_END; 00244 hdr = PICO_ZALLOC(sizeof(struct pico_dhcp_hdr) + (uint32_t)optlen); 00245 if (!hdr) { 00246 return; 00247 } 00248 00249 hdr->op = PICO_DHCP_OP_REPLY; 00250 hdr->htype = PICO_DHCP_HTYPE_ETH; 00251 hdr->hlen = PICO_SIZE_ETH; 00252 hdr->xid = dhcpn->xid; 00253 hdr->yiaddr = dhcpn->ciaddr.addr; 00254 hdr->siaddr = dhcpn->dhcps->server_ip.addr; 00255 hdr->dhcp_magic = PICO_DHCPD_MAGIC_COOKIE; 00256 memcpy(hdr->hwaddr, dhcpn->hwaddr.addr, PICO_SIZE_ETH); 00257 00258 /* options */ 00259 offset += pico_dhcp_opt_msgtype(DHCP_OPT(hdr,offset), msg_type); 00260 offset += pico_dhcp_opt_serverid(DHCP_OPT(hdr,offset), &dhcpn->dhcps->server_ip); 00261 offset += pico_dhcp_opt_leasetime(DHCP_OPT(hdr,offset), dhcpn->dhcps->lease_time); 00262 offset += pico_dhcp_opt_netmask(DHCP_OPT(hdr,offset), &dhcpn->dhcps->netmask); 00263 offset += pico_dhcp_opt_router(DHCP_OPT(hdr,offset), &dhcpn->dhcps->server_ip); 00264 offset += pico_dhcp_opt_broadcast(DHCP_OPT(hdr,offset), &broadcast); 00265 offset += pico_dhcp_opt_dns(DHCP_OPT(hdr,offset), &dns); 00266 offset += pico_dhcp_opt_end(DHCP_OPT(hdr,offset)); 00267 00268 if (dhcpn->bcast == 0) 00269 destination.addr = hdr->yiaddr; 00270 else { 00271 hdr->flags |= short_be(PICO_DHCP_FLAG_BROADCAST); 00272 destination.addr = broadcast.addr; 00273 } 00274 00275 r = pico_socket_sendto(dhcpn->dhcps->s, hdr, (int)(sizeof(struct pico_dhcp_hdr) + (uint32_t)optlen), &destination, PICO_DHCP_CLIENT_PORT); 00276 if (r < 0) 00277 dhcps_dbg("DHCP server WARNING: failure sending: %s!\n", strerror(pico_err)); 00278 00279 PICO_FREE(hdr); 00280 00281 return; 00282 } 00283 00284 static inline void parse_opt_msgtype(struct pico_dhcp_opt *opt, uint8_t *msgtype) 00285 { 00286 if (opt->code == PICO_DHCP_OPT_MSGTYPE) { 00287 *msgtype = opt->ext.msg_type.type; 00288 dhcps_dbg("DHCP server: message type %u\n", msgtype); 00289 } 00290 } 00291 00292 static inline void parse_opt_reqip(struct pico_dhcp_opt *opt, struct pico_ip4 *reqip) 00293 { 00294 if (opt->code == PICO_DHCP_OPT_REQIP) 00295 reqip->addr = opt->ext.req_ip.ip.addr; 00296 } 00297 00298 static inline void parse_opt_serverid(struct pico_dhcp_opt *opt, struct pico_ip4 *serverid) 00299 { 00300 if (opt->code == PICO_DHCP_OPT_SERVERID) 00301 *serverid = opt->ext.server_id.ip; 00302 } 00303 00304 static inline void dhcps_make_reply_to_request_msg(struct pico_dhcp_server_negotiation *dhcpn, int bound_valid_flag) 00305 { 00306 if ((dhcpn->state == PICO_DHCP_STATE_BOUND) && bound_valid_flag) 00307 dhcpd_make_reply(dhcpn, PICO_DHCP_MSG_ACK); 00308 00309 if (dhcpn->state == PICO_DHCP_STATE_OFFER) { 00310 dhcpn->state = PICO_DHCP_STATE_BOUND; 00311 dhcpd_make_reply(dhcpn, PICO_DHCP_MSG_ACK); 00312 } 00313 } 00314 00315 static inline void dhcps_make_reply_to_discover_or_request(struct pico_dhcp_server_negotiation *dhcpn, uint8_t msgtype, int bound_valid_flag) 00316 { 00317 if (PICO_DHCP_MSG_DISCOVER == msgtype) { 00318 dhcpd_make_reply(dhcpn, PICO_DHCP_MSG_OFFER); 00319 dhcpn->state = PICO_DHCP_STATE_OFFER; 00320 } else if (PICO_DHCP_MSG_REQUEST == msgtype) { 00321 dhcps_make_reply_to_request_msg(dhcpn, bound_valid_flag); 00322 } 00323 } 00324 00325 static inline void dhcps_parse_options_loop(struct pico_dhcp_server_negotiation *dhcpn, struct pico_dhcp_hdr *hdr) 00326 { 00327 struct pico_dhcp_opt *opt = DHCP_OPT(hdr,0); 00328 uint8_t msgtype = 0; 00329 struct pico_ip4 reqip = { 00330 0 00331 }, server_id = { 00332 0 00333 }; 00334 00335 do { 00336 parse_opt_msgtype(opt, &msgtype); 00337 parse_opt_reqip(opt, &reqip); 00338 parse_opt_serverid(opt, &server_id); 00339 } while (pico_dhcp_next_option(&opt)); 00340 dhcps_make_reply_to_discover_or_request(dhcpn, msgtype, (!reqip.addr) && (!server_id.addr) && (hdr->ciaddr == dhcpn->ciaddr.addr)); 00341 } 00342 00343 static void pico_dhcp_server_recv(struct pico_socket *s, uint8_t *buf, uint32_t len) 00344 { 00345 int32_t optlen = (int32_t)(len - sizeof(struct pico_dhcp_hdr)); 00346 struct pico_dhcp_hdr *hdr = (struct pico_dhcp_hdr *)buf; 00347 struct pico_dhcp_server_negotiation *dhcpn = NULL; 00348 struct pico_device *dev = NULL; 00349 00350 if (!pico_dhcp_are_options_valid(DHCP_OPT(hdr,0), optlen)) 00351 return; 00352 00353 dev = pico_ipv4_link_find(&s->local_addr.ip4); 00354 dhcpn = pico_dhcp_server_find_negotiation(hdr->xid); 00355 if (!dhcpn) 00356 dhcpn = pico_dhcp_server_add_negotiation(dev, hdr); 00357 00358 if (!ip_address_is_in_dhcp_range(dhcpn, dhcpn->ciaddr.addr)) 00359 return; 00360 00361 dhcps_parse_options_loop(dhcpn, hdr); 00362 00363 } 00364 00365 static void pico_dhcpd_wakeup(uint16_t ev, struct pico_socket *s) 00366 { 00367 uint8_t buf[DHCP_SERVER_MAXMSGSIZE] = { 00368 0 00369 }; 00370 int r = 0; 00371 00372 if (ev != PICO_SOCK_EV_RD) 00373 return; 00374 00375 r = pico_socket_recvfrom(s, buf, DHCP_SERVER_MAXMSGSIZE, NULL, NULL); 00376 if (r < 0) 00377 return; 00378 00379 pico_dhcp_server_recv(s, buf, (uint32_t)r); 00380 return; 00381 } 00382 00383 int pico_dhcp_server_initiate(struct pico_dhcp_server_setting *setting) 00384 { 00385 if (!setting || !setting->server_ip.addr) { 00386 pico_err = PICO_ERR_EINVAL; 00387 return -1; 00388 } 00389 00390 if (pico_dhcp_server_add_setting(setting) == NULL) 00391 return -1; 00392 00393 return 0; 00394 } 00395 00396 int pico_dhcp_server_destroy(struct pico_device *dev) 00397 { 00398 struct pico_dhcp_server_setting *found, test = { 0 }; 00399 test.dev = dev; 00400 found = pico_tree_findKey(&DHCPSettings, &test); 00401 if (!found) { 00402 pico_err = PICO_ERR_ENOENT; 00403 return -1; 00404 } 00405 00406 pico_tree_delete(&DHCPSettings, found); 00407 PICO_FREE(found); 00408 return 0; 00409 } 00410 00411 #endif /* PICO_SUPPORT_DHCP */
Generated on Tue Jul 12 2022 15:59:21 by 1.7.2