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

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers pico_dhcp_server.c Source File

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 */