Free (GPLv2) TCP/IP stack developed by TASS Belgium

Fork of PicoTCP by Daniele Lacamera

Committer:
daniele
Date:
Fri May 24 15:25:25 2013 +0000
Revision:
3:b4047e8a0123
Updated from main repo + fixed Mutexes;

Who changed what in which revision?

UserRevisionLine numberNew contents of line
daniele 3:b4047e8a0123 1 /*********************************************************************
daniele 3:b4047e8a0123 2 PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
daniele 3:b4047e8a0123 3 See LICENSE and COPYING for usage.
daniele 3:b4047e8a0123 4
daniele 3:b4047e8a0123 5
daniele 3:b4047e8a0123 6 Authors: Frederik Van Slycken, Kristof Roelants
daniele 3:b4047e8a0123 7 *********************************************************************/
daniele 3:b4047e8a0123 8
daniele 3:b4047e8a0123 9 #ifdef PICO_SUPPORT_DHCPD
daniele 3:b4047e8a0123 10
daniele 3:b4047e8a0123 11 #include "pico_dhcp_server.h"
daniele 3:b4047e8a0123 12 #include "pico_stack.h"
daniele 3:b4047e8a0123 13 #include "pico_config.h"
daniele 3:b4047e8a0123 14 #include "pico_addressing.h"
daniele 3:b4047e8a0123 15 #include "pico_socket.h"
daniele 3:b4047e8a0123 16 #include "pico_arp.h"
daniele 3:b4047e8a0123 17 #include <stdlib.h>
daniele 3:b4047e8a0123 18
daniele 3:b4047e8a0123 19 # define dhcpd_dbg(...) do{}while(0)
daniele 3:b4047e8a0123 20 //# define dhcpd_dbg dbg
daniele 3:b4047e8a0123 21
daniele 3:b4047e8a0123 22 #define dhcpd_make_offer(x) dhcpd_make_reply(x, PICO_DHCP_MSG_OFFER)
daniele 3:b4047e8a0123 23 #define dhcpd_make_ack(x) dhcpd_make_reply(x, PICO_DHCP_MSG_ACK)
daniele 3:b4047e8a0123 24 #define ip_inrange(x) ((long_be(x) >= long_be(dn->settings->pool_start)) && (long_be(x) <= long_be(dn->settings->pool_end)))
daniele 3:b4047e8a0123 25
daniele 3:b4047e8a0123 26 static int dhcp_settings_cmp(void *ka, void *kb)
daniele 3:b4047e8a0123 27 {
daniele 3:b4047e8a0123 28 struct pico_dhcpd_settings *a = ka, *b = kb;
daniele 3:b4047e8a0123 29 if (a->dev < b->dev)
daniele 3:b4047e8a0123 30 return -1;
daniele 3:b4047e8a0123 31 else if (a->dev > b->dev)
daniele 3:b4047e8a0123 32 return 1;
daniele 3:b4047e8a0123 33 else
daniele 3:b4047e8a0123 34 return 0;
daniele 3:b4047e8a0123 35 }
daniele 3:b4047e8a0123 36 PICO_TREE_DECLARE(DHCPSettings, dhcp_settings_cmp);
daniele 3:b4047e8a0123 37
daniele 3:b4047e8a0123 38 static int dhcp_negotiations_cmp(void *ka, void *kb)
daniele 3:b4047e8a0123 39 {
daniele 3:b4047e8a0123 40 struct pico_dhcp_negotiation *a = ka, *b = kb;
daniele 3:b4047e8a0123 41 if (a->xid < b->xid)
daniele 3:b4047e8a0123 42 return -1;
daniele 3:b4047e8a0123 43 else if (a->xid > b->xid)
daniele 3:b4047e8a0123 44 return 1;
daniele 3:b4047e8a0123 45 else
daniele 3:b4047e8a0123 46 return 0;
daniele 3:b4047e8a0123 47 }
daniele 3:b4047e8a0123 48 PICO_TREE_DECLARE(DHCPNegotiations, dhcp_negotiations_cmp);
daniele 3:b4047e8a0123 49
daniele 3:b4047e8a0123 50 static struct pico_dhcp_negotiation *get_negotiation_by_xid(uint32_t xid)
daniele 3:b4047e8a0123 51 {
daniele 3:b4047e8a0123 52 struct pico_dhcp_negotiation test = { }, *neg = NULL;
daniele 3:b4047e8a0123 53
daniele 3:b4047e8a0123 54 test.xid = xid;
daniele 3:b4047e8a0123 55 neg = pico_tree_findKey(&DHCPNegotiations, &test);
daniele 3:b4047e8a0123 56 if (!neg)
daniele 3:b4047e8a0123 57 return NULL;
daniele 3:b4047e8a0123 58 else
daniele 3:b4047e8a0123 59 return neg;
daniele 3:b4047e8a0123 60 }
daniele 3:b4047e8a0123 61
daniele 3:b4047e8a0123 62 static void dhcpd_make_reply(struct pico_dhcp_negotiation *dn, uint8_t reply_type)
daniele 3:b4047e8a0123 63 {
daniele 3:b4047e8a0123 64 uint8_t buf_out[DHCPD_DATAGRAM_SIZE] = {0};
daniele 3:b4047e8a0123 65 struct pico_dhcphdr *dh_out = (struct pico_dhcphdr *) buf_out;
daniele 3:b4047e8a0123 66 struct pico_ip4 destination = { };
daniele 3:b4047e8a0123 67 uint32_t bcast = dn->settings->my_ip.addr | ~(dn->settings->netmask.addr);
daniele 3:b4047e8a0123 68 uint32_t dns_server = OPENDNS;
daniele 3:b4047e8a0123 69 uint16_t port = PICO_DHCP_CLIENT_PORT;
daniele 3:b4047e8a0123 70 int sent = 0;
daniele 3:b4047e8a0123 71
daniele 3:b4047e8a0123 72 memcpy(dh_out->hwaddr, dn->eth.addr, PICO_HLEN_ETHER);
daniele 3:b4047e8a0123 73 dh_out->op = PICO_DHCP_OP_REPLY;
daniele 3:b4047e8a0123 74 dh_out->htype = PICO_HTYPE_ETHER;
daniele 3:b4047e8a0123 75 dh_out->hlen = PICO_HLEN_ETHER;
daniele 3:b4047e8a0123 76 dh_out->xid = dn->xid;
daniele 3:b4047e8a0123 77 dh_out->yiaddr = dn->ipv4.addr;
daniele 3:b4047e8a0123 78 dh_out->siaddr = dn->settings->my_ip.addr;
daniele 3:b4047e8a0123 79 dh_out->dhcp_magic = PICO_DHCPD_MAGIC_COOKIE;
daniele 3:b4047e8a0123 80
daniele 3:b4047e8a0123 81 /* Option: msg type, len 1 */
daniele 3:b4047e8a0123 82 dh_out->options[0] = PICO_DHCPOPT_MSGTYPE;
daniele 3:b4047e8a0123 83 dh_out->options[1] = 1;
daniele 3:b4047e8a0123 84 dh_out->options[2] = reply_type;
daniele 3:b4047e8a0123 85
daniele 3:b4047e8a0123 86 /* Option: server id, len 4 */
daniele 3:b4047e8a0123 87 dh_out->options[3] = PICO_DHCPOPT_SERVERID;
daniele 3:b4047e8a0123 88 dh_out->options[4] = 4;
daniele 3:b4047e8a0123 89 memcpy(dh_out->options + 5, &dn->settings->my_ip.addr, 4);
daniele 3:b4047e8a0123 90
daniele 3:b4047e8a0123 91 /* Option: Lease time, len 4 */
daniele 3:b4047e8a0123 92 dh_out->options[9] = PICO_DHCPOPT_LEASETIME;
daniele 3:b4047e8a0123 93 dh_out->options[10] = 4;
daniele 3:b4047e8a0123 94 memcpy(dh_out->options + 11, &dn->settings->lease_time, 4);
daniele 3:b4047e8a0123 95
daniele 3:b4047e8a0123 96 /* Option: Netmask, len 4 */
daniele 3:b4047e8a0123 97 dh_out->options[15] = PICO_DHCPOPT_NETMASK;
daniele 3:b4047e8a0123 98 dh_out->options[16] = 4;
daniele 3:b4047e8a0123 99 memcpy(dh_out->options + 17, &dn->settings->netmask.addr, 4);
daniele 3:b4047e8a0123 100
daniele 3:b4047e8a0123 101 /* Option: Router, len 4 */
daniele 3:b4047e8a0123 102 dh_out->options[21] = PICO_DHCPOPT_ROUTER;
daniele 3:b4047e8a0123 103 dh_out->options[22] = 4;
daniele 3:b4047e8a0123 104 memcpy(dh_out->options + 23, &dn->settings->my_ip.addr, 4);
daniele 3:b4047e8a0123 105
daniele 3:b4047e8a0123 106 /* Option: Broadcast, len 4 */
daniele 3:b4047e8a0123 107 dh_out->options[27] = PICO_DHCPOPT_BCAST;
daniele 3:b4047e8a0123 108 dh_out->options[28] = 4;
daniele 3:b4047e8a0123 109 memcpy(dh_out->options + 29, &bcast, 4);
daniele 3:b4047e8a0123 110
daniele 3:b4047e8a0123 111 /* Option: DNS, len 4 */
daniele 3:b4047e8a0123 112 dh_out->options[33] = PICO_DHCPOPT_DNS;
daniele 3:b4047e8a0123 113 dh_out->options[34] = 4;
daniele 3:b4047e8a0123 114 memcpy(dh_out->options + 35, &dns_server, 4);
daniele 3:b4047e8a0123 115
daniele 3:b4047e8a0123 116 dh_out->options[40] = PICO_DHCPOPT_END;
daniele 3:b4047e8a0123 117
daniele 3:b4047e8a0123 118 destination.addr = dh_out->yiaddr;
daniele 3:b4047e8a0123 119
daniele 3:b4047e8a0123 120 sent = pico_socket_sendto(dn->settings->s, buf_out, DHCPD_DATAGRAM_SIZE, &destination, port);
daniele 3:b4047e8a0123 121 if (sent < 0) {
daniele 3:b4047e8a0123 122 dhcpd_dbg("DHCPD: sendto failed with code %d!\n", pico_err);
daniele 3:b4047e8a0123 123 }
daniele 3:b4047e8a0123 124 }
daniele 3:b4047e8a0123 125
daniele 3:b4047e8a0123 126 static void dhcp_recv(struct pico_socket *s, uint8_t *buffer, int len)
daniele 3:b4047e8a0123 127 {
daniele 3:b4047e8a0123 128 struct pico_dhcphdr *dhdr = (struct pico_dhcphdr *) buffer;
daniele 3:b4047e8a0123 129 struct pico_dhcp_negotiation *dn = get_negotiation_by_xid(dhdr->xid);
daniele 3:b4047e8a0123 130 struct pico_ip4* ipv4 = NULL;
daniele 3:b4047e8a0123 131 struct pico_dhcpd_settings test, *settings = NULL;
daniele 3:b4047e8a0123 132 uint8_t *nextopt, opt_data[20], opt_type;
daniele 3:b4047e8a0123 133 int opt_len = 20;
daniele 3:b4047e8a0123 134
daniele 3:b4047e8a0123 135 if (!is_options_valid(dhdr->options, len - sizeof(struct pico_dhcphdr))) {
daniele 3:b4047e8a0123 136 dhcpd_dbg("DHCPD WARNING: invalid options in dhcp message\n");
daniele 3:b4047e8a0123 137 return;
daniele 3:b4047e8a0123 138 }
daniele 3:b4047e8a0123 139
daniele 3:b4047e8a0123 140 if (!dn) {
daniele 3:b4047e8a0123 141 dn = pico_zalloc(sizeof(struct pico_dhcp_negotiation));
daniele 3:b4047e8a0123 142 if (!dn) {
daniele 3:b4047e8a0123 143 pico_err = PICO_ERR_ENOMEM;
daniele 3:b4047e8a0123 144 return;
daniele 3:b4047e8a0123 145 }
daniele 3:b4047e8a0123 146 dn->xid = dhdr->xid;
daniele 3:b4047e8a0123 147 dn->state = DHCPSTATE_DISCOVER;
daniele 3:b4047e8a0123 148 memcpy(dn->eth.addr, dhdr->hwaddr, PICO_HLEN_ETHER);
daniele 3:b4047e8a0123 149
daniele 3:b4047e8a0123 150 test.dev = pico_ipv4_link_find(&s->local_addr.ip4);
daniele 3:b4047e8a0123 151 settings = pico_tree_findKey(&DHCPSettings, &test);
daniele 3:b4047e8a0123 152 if (settings) {
daniele 3:b4047e8a0123 153 dn->settings = settings;
daniele 3:b4047e8a0123 154 } else {
daniele 3:b4047e8a0123 155 dhcpd_dbg("DHCPD WARNING: received DHCP message on unconfigured link %s\n", test.dev->name);
daniele 3:b4047e8a0123 156 pico_free(dn);
daniele 3:b4047e8a0123 157 return;
daniele 3:b4047e8a0123 158 }
daniele 3:b4047e8a0123 159
daniele 3:b4047e8a0123 160 ipv4 = pico_arp_reverse_lookup(&dn->eth);
daniele 3:b4047e8a0123 161 if (!ipv4) {
daniele 3:b4047e8a0123 162 dn->ipv4.addr = settings->pool_next;
daniele 3:b4047e8a0123 163 pico_arp_create_entry(dn->eth.addr, dn->ipv4, settings->dev);
daniele 3:b4047e8a0123 164 settings->pool_next = long_be(long_be(settings->pool_next) + 1);
daniele 3:b4047e8a0123 165 } else {
daniele 3:b4047e8a0123 166 dn->ipv4.addr = ipv4->addr;
daniele 3:b4047e8a0123 167 }
daniele 3:b4047e8a0123 168
daniele 3:b4047e8a0123 169 if (pico_tree_insert(&DHCPNegotiations, dn)) {
daniele 3:b4047e8a0123 170 dhcpd_dbg("DHCPD WARNING: tried creating new negotation for existing xid %u\n", dn->xid);
daniele 3:b4047e8a0123 171 pico_free(dn);
daniele 3:b4047e8a0123 172 return; /* Element key already exists */
daniele 3:b4047e8a0123 173 }
daniele 3:b4047e8a0123 174 }
daniele 3:b4047e8a0123 175
daniele 3:b4047e8a0123 176 if (!ip_inrange(dn->ipv4.addr))
daniele 3:b4047e8a0123 177 return;
daniele 3:b4047e8a0123 178
daniele 3:b4047e8a0123 179 opt_type = dhcp_get_next_option(dhdr->options, opt_data, &opt_len, &nextopt);
daniele 3:b4047e8a0123 180 while (opt_type != PICO_DHCPOPT_END) {
daniele 3:b4047e8a0123 181 /* parse interesting options here */
daniele 3:b4047e8a0123 182 if (opt_type == PICO_DHCPOPT_MSGTYPE) {
daniele 3:b4047e8a0123 183 /* server simple state machine */
daniele 3:b4047e8a0123 184 uint8_t msg_type = opt_data[0];
daniele 3:b4047e8a0123 185 if (msg_type == PICO_DHCP_MSG_DISCOVER) {
daniele 3:b4047e8a0123 186 dhcpd_make_offer(dn);
daniele 3:b4047e8a0123 187 dn->state = DHCPSTATE_OFFER;
daniele 3:b4047e8a0123 188 return;
daniele 3:b4047e8a0123 189 } else if ((msg_type == PICO_DHCP_MSG_REQUEST)&&( dn->state == DHCPSTATE_OFFER)) {
daniele 3:b4047e8a0123 190 dhcpd_make_ack(dn);
daniele 3:b4047e8a0123 191 dn->state = DHCPSTATE_BOUND;
daniele 3:b4047e8a0123 192 return;
daniele 3:b4047e8a0123 193 }
daniele 3:b4047e8a0123 194 }
daniele 3:b4047e8a0123 195 opt_len = 20;
daniele 3:b4047e8a0123 196 opt_type = dhcp_get_next_option(NULL, opt_data, &opt_len, &nextopt);
daniele 3:b4047e8a0123 197 }
daniele 3:b4047e8a0123 198 }
daniele 3:b4047e8a0123 199
daniele 3:b4047e8a0123 200 static void pico_dhcpd_wakeup(uint16_t ev, struct pico_socket *s)
daniele 3:b4047e8a0123 201 {
daniele 3:b4047e8a0123 202 uint8_t buf[DHCPD_DATAGRAM_SIZE] = { };
daniele 3:b4047e8a0123 203 int r = 0;
daniele 3:b4047e8a0123 204 uint32_t peer = 0;
daniele 3:b4047e8a0123 205 uint16_t port = 0;
daniele 3:b4047e8a0123 206
daniele 3:b4047e8a0123 207 dhcpd_dbg("DHCPD: called dhcpd_wakeup\n");
daniele 3:b4047e8a0123 208 if (ev == PICO_SOCK_EV_RD) {
daniele 3:b4047e8a0123 209 do {
daniele 3:b4047e8a0123 210 r = pico_socket_recvfrom(s, buf, DHCPD_DATAGRAM_SIZE, &peer, &port);
daniele 3:b4047e8a0123 211 if (r > 0 && port == PICO_DHCP_CLIENT_PORT) {
daniele 3:b4047e8a0123 212 dhcp_recv(s, buf, r);
daniele 3:b4047e8a0123 213 }
daniele 3:b4047e8a0123 214 } while(r>0);
daniele 3:b4047e8a0123 215 }
daniele 3:b4047e8a0123 216 }
daniele 3:b4047e8a0123 217
daniele 3:b4047e8a0123 218 int pico_dhcp_server_initiate(struct pico_dhcpd_settings *setting)
daniele 3:b4047e8a0123 219 {
daniele 3:b4047e8a0123 220 struct pico_dhcpd_settings *settings = NULL;
daniele 3:b4047e8a0123 221 struct pico_ipv4_link *link = NULL;
daniele 3:b4047e8a0123 222 uint16_t port = PICO_DHCPD_PORT;
daniele 3:b4047e8a0123 223
daniele 3:b4047e8a0123 224 if (!setting) {
daniele 3:b4047e8a0123 225 pico_err = PICO_ERR_EINVAL;
daniele 3:b4047e8a0123 226 return -1;
daniele 3:b4047e8a0123 227 }
daniele 3:b4047e8a0123 228
daniele 3:b4047e8a0123 229 if (!setting->my_ip.addr) {
daniele 3:b4047e8a0123 230 pico_err = PICO_ERR_EINVAL;
daniele 3:b4047e8a0123 231 dhcpd_dbg("DHCPD: IP address of interface was not supplied\n");
daniele 3:b4047e8a0123 232 return -1;
daniele 3:b4047e8a0123 233 }
daniele 3:b4047e8a0123 234
daniele 3:b4047e8a0123 235 link = pico_ipv4_link_get(&setting->my_ip);
daniele 3:b4047e8a0123 236 if (!link) {
daniele 3:b4047e8a0123 237 pico_err = PICO_ERR_EINVAL;
daniele 3:b4047e8a0123 238 dhcpd_dbg("DHCPD: no link with IP %X found\n", setting->my_ip.addr);
daniele 3:b4047e8a0123 239 return -1;
daniele 3:b4047e8a0123 240 }
daniele 3:b4047e8a0123 241
daniele 3:b4047e8a0123 242 settings = pico_zalloc(sizeof(struct pico_dhcpd_settings));
daniele 3:b4047e8a0123 243 if (!settings) {
daniele 3:b4047e8a0123 244 pico_err = PICO_ERR_ENOMEM;
daniele 3:b4047e8a0123 245 return -1;
daniele 3:b4047e8a0123 246 }
daniele 3:b4047e8a0123 247 memcpy(settings, setting, sizeof(struct pico_dhcpd_settings));
daniele 3:b4047e8a0123 248
daniele 3:b4047e8a0123 249 settings->dev = link->dev;
daniele 3:b4047e8a0123 250 dhcpd_dbg("DHCPD: configuring DHCP server for link %s\n", link->dev->name);
daniele 3:b4047e8a0123 251 settings->my_ip.addr = link->address.addr;
daniele 3:b4047e8a0123 252 dhcpd_dbg("DHCPD: using server addr %X\n", long_be(settings->my_ip.addr));
daniele 3:b4047e8a0123 253 settings->netmask.addr = link->netmask.addr;
daniele 3:b4047e8a0123 254 dhcpd_dbg("DHCPD: using netmask %X\n", long_be(settings->netmask.addr));
daniele 3:b4047e8a0123 255
daniele 3:b4047e8a0123 256 /* default values if not provided */
daniele 3:b4047e8a0123 257 if (settings->pool_start == 0)
daniele 3:b4047e8a0123 258 settings->pool_start = (settings->my_ip.addr & settings->netmask.addr) | POOL_START;
daniele 3:b4047e8a0123 259 dhcpd_dbg("DHCPD: using pool_start %X\n", long_be(settings->pool_start));
daniele 3:b4047e8a0123 260 if (settings->pool_end == 0)
daniele 3:b4047e8a0123 261 settings->pool_end = (settings->my_ip.addr & settings->netmask.addr) | POOL_END;
daniele 3:b4047e8a0123 262 dhcpd_dbg("DHCPD: using pool_end %x\n", long_be(settings->pool_end));
daniele 3:b4047e8a0123 263 if (settings->lease_time == 0)
daniele 3:b4047e8a0123 264 settings->lease_time = LEASE_TIME;
daniele 3:b4047e8a0123 265 dhcpd_dbg("DHCPD: using lease time %x\n", long_be(settings->lease_time));
daniele 3:b4047e8a0123 266 settings->pool_next = settings->pool_start;
daniele 3:b4047e8a0123 267
daniele 3:b4047e8a0123 268 settings->s = pico_socket_open(PICO_PROTO_IPV4, PICO_PROTO_UDP, &pico_dhcpd_wakeup);
daniele 3:b4047e8a0123 269 if (!settings->s) {
daniele 3:b4047e8a0123 270 dhcpd_dbg("DHCP: could not open client socket\n");
daniele 3:b4047e8a0123 271 pico_free(settings);
daniele 3:b4047e8a0123 272 return -1;
daniele 3:b4047e8a0123 273 }
daniele 3:b4047e8a0123 274 if (pico_socket_bind(settings->s, &settings->my_ip, &port) != 0) {
daniele 3:b4047e8a0123 275 dhcpd_dbg("DHCP: could not bind server socket (%s)\n", strerror(pico_err));
daniele 3:b4047e8a0123 276 pico_free(settings);
daniele 3:b4047e8a0123 277 return -1;
daniele 3:b4047e8a0123 278 }
daniele 3:b4047e8a0123 279
daniele 3:b4047e8a0123 280 if (pico_tree_insert(&DHCPSettings, settings)) {
daniele 3:b4047e8a0123 281 dhcpd_dbg("DHCPD ERROR: link %s already configured\n", link->dev->name);
daniele 3:b4047e8a0123 282 pico_err = PICO_ERR_EINVAL;
daniele 3:b4047e8a0123 283 pico_free(settings);
daniele 3:b4047e8a0123 284 return -1; /* Element key already exists */
daniele 3:b4047e8a0123 285 }
daniele 3:b4047e8a0123 286 dhcpd_dbg("DHCPD: configured DHCP server for link %s\n", link->dev->name);
daniele 3:b4047e8a0123 287
daniele 3:b4047e8a0123 288 return 0;
daniele 3:b4047e8a0123 289 }
daniele 3:b4047e8a0123 290 #endif /* PICO_SUPPORT_DHCP */