summaryrefslogtreecommitdiffstats
path: root/tinyusb/lib/networking/dhserver.c
diff options
context:
space:
mode:
Diffstat (limited to 'tinyusb/lib/networking/dhserver.c')
-rwxr-xr-xtinyusb/lib/networking/dhserver.c348
1 files changed, 348 insertions, 0 deletions
diff --git a/tinyusb/lib/networking/dhserver.c b/tinyusb/lib/networking/dhserver.c
new file mode 100755
index 00000000..9287858e
--- /dev/null
+++ b/tinyusb/lib/networking/dhserver.c
@@ -0,0 +1,348 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2015 by Sergey Fetisov <fsenok@gmail.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "dhserver.h"
+
+/* DHCP message type */
+#define DHCP_DISCOVER 1
+#define DHCP_OFFER 2
+#define DHCP_REQUEST 3
+#define DHCP_DECLINE 4
+#define DHCP_ACK 5
+#define DHCP_NAK 6
+#define DHCP_RELEASE 7
+#define DHCP_INFORM 8
+
+/* DHCP options */
+enum DHCP_OPTIONS
+{
+ DHCP_PAD = 0,
+ DHCP_SUBNETMASK = 1,
+ DHCP_ROUTER = 3,
+ DHCP_DNSSERVER = 6,
+ DHCP_HOSTNAME = 12,
+ DHCP_DNSDOMAIN = 15,
+ DHCP_MTU = 26,
+ DHCP_BROADCAST = 28,
+ DHCP_PERFORMROUTERDISC = 31,
+ DHCP_STATICROUTE = 33,
+ DHCP_NISDOMAIN = 40,
+ DHCP_NISSERVER = 41,
+ DHCP_NTPSERVER = 42,
+ DHCP_VENDOR = 43,
+ DHCP_IPADDRESS = 50,
+ DHCP_LEASETIME = 51,
+ DHCP_OPTIONSOVERLOADED = 52,
+ DHCP_MESSAGETYPE = 53,
+ DHCP_SERVERID = 54,
+ DHCP_PARAMETERREQUESTLIST = 55,
+ DHCP_MESSAGE = 56,
+ DHCP_MAXMESSAGESIZE = 57,
+ DHCP_RENEWALTIME = 58,
+ DHCP_REBINDTIME = 59,
+ DHCP_CLASSID = 60,
+ DHCP_CLIENTID = 61,
+ DHCP_USERCLASS = 77, /* RFC 3004 */
+ DHCP_FQDN = 81,
+ DHCP_DNSSEARCH = 119, /* RFC 3397 */
+ DHCP_CSR = 121, /* RFC 3442 */
+ DHCP_MSCSR = 249, /* MS code for RFC 3442 */
+ DHCP_END = 255
+};
+
+typedef struct
+{
+ uint8_t dp_op; /* packet opcode type */
+ uint8_t dp_htype; /* hardware addr type */
+ uint8_t dp_hlen; /* hardware addr length */
+ uint8_t dp_hops; /* gateway hops */
+ uint32_t dp_xid; /* transaction ID */
+ uint16_t dp_secs; /* seconds since boot began */
+ uint16_t dp_flags;
+ uint8_t dp_ciaddr[4]; /* client IP address */
+ uint8_t dp_yiaddr[4]; /* 'your' IP address */
+ uint8_t dp_siaddr[4]; /* server IP address */
+ uint8_t dp_giaddr[4]; /* gateway IP address */
+ uint8_t dp_chaddr[16]; /* client hardware address */
+ uint8_t dp_legacy[192];
+ uint8_t dp_magic[4];
+ uint8_t dp_options[275]; /* options area */
+} DHCP_TYPE;
+
+DHCP_TYPE dhcp_data;
+static struct udp_pcb *pcb = NULL;
+static const dhcp_config_t *config = NULL;
+
+char magic_cookie[] = {0x63,0x82,0x53,0x63};
+
+static ip_addr_t get_ip(const uint8_t *pnt)
+{
+ ip_addr_t result;
+ memcpy(&result, pnt, sizeof(result));
+ return result;
+}
+
+static void set_ip(uint8_t *pnt, ip_addr_t value)
+{
+ memcpy(pnt, &value.addr, sizeof(value.addr));
+}
+
+static dhcp_entry_t *entry_by_ip(ip_addr_t ip)
+{
+ int i;
+ for (i = 0; i < config->num_entry; i++)
+ if (config->entries[i].addr.addr == ip.addr)
+ return &config->entries[i];
+ return NULL;
+}
+
+static dhcp_entry_t *entry_by_mac(uint8_t *mac)
+{
+ int i;
+ for (i = 0; i < config->num_entry; i++)
+ if (memcmp(config->entries[i].mac, mac, 6) == 0)
+ return &config->entries[i];
+ return NULL;
+}
+
+static __inline bool is_vacant(dhcp_entry_t *entry)
+{
+ return memcmp("\0\0\0\0\0", entry->mac, 6) == 0;
+}
+
+static dhcp_entry_t *vacant_address(void)
+{
+ int i;
+ for (i = 0; i < config->num_entry; i++)
+ if (is_vacant(config->entries + i))
+ return config->entries + i;
+ return NULL;
+}
+
+static __inline void free_entry(dhcp_entry_t *entry)
+{
+ memset(entry->mac, 0, 6);
+}
+
+uint8_t *find_dhcp_option(uint8_t *attrs, int size, uint8_t attr)
+{
+ int i = 0;
+ while ((i + 1) < size)
+ {
+ int next = i + attrs[i + 1] + 2;
+ if (next > size) return NULL;
+ if (attrs[i] == attr)
+ return attrs + i;
+ i = next;
+ }
+ return NULL;
+}
+
+int fill_options(void *dest,
+ uint8_t msg_type,
+ const char *domain,
+ ip_addr_t dns,
+ int lease_time,
+ ip_addr_t serverid,
+ ip_addr_t router,
+ ip_addr_t subnet)
+{
+ uint8_t *ptr = (uint8_t *)dest;
+ /* ACK message type */
+ *ptr++ = 53;
+ *ptr++ = 1;
+ *ptr++ = msg_type;
+
+ /* dhcp server identifier */
+ *ptr++ = DHCP_SERVERID;
+ *ptr++ = 4;
+ set_ip(ptr, serverid);
+ ptr += 4;
+
+ /* lease time */
+ *ptr++ = DHCP_LEASETIME;
+ *ptr++ = 4;
+ *ptr++ = (lease_time >> 24) & 0xFF;
+ *ptr++ = (lease_time >> 16) & 0xFF;
+ *ptr++ = (lease_time >> 8) & 0xFF;
+ *ptr++ = (lease_time >> 0) & 0xFF;
+
+ /* subnet mask */
+ *ptr++ = DHCP_SUBNETMASK;
+ *ptr++ = 4;
+ set_ip(ptr, subnet);
+ ptr += 4;
+
+ /* router */
+ if (router.addr != 0)
+ {
+ *ptr++ = DHCP_ROUTER;
+ *ptr++ = 4;
+ set_ip(ptr, router);
+ ptr += 4;
+ }
+
+ /* domain name */
+ if (domain != NULL)
+ {
+ int len = strlen(domain);
+ *ptr++ = DHCP_DNSDOMAIN;
+ *ptr++ = len;
+ memcpy(ptr, domain, len);
+ ptr += len;
+ }
+
+ /* domain name server (DNS) */
+ if (dns.addr != 0)
+ {
+ *ptr++ = DHCP_DNSSERVER;
+ *ptr++ = 4;
+ set_ip(ptr, dns);
+ ptr += 4;
+ }
+
+ /* end */
+ *ptr++ = DHCP_END;
+ return ptr - (uint8_t *)dest;
+}
+
+static void udp_recv_proc(void *arg, struct udp_pcb *upcb, struct pbuf *p, const ip_addr_t *addr, u16_t port)
+{
+ uint8_t *ptr;
+ dhcp_entry_t *entry;
+ struct pbuf *pp;
+ struct netif *netif = netif_get_by_index(p->if_idx);
+
+ (void)arg;
+ (void)addr;
+
+ unsigned n = p->len;
+ if (n > sizeof(dhcp_data)) n = sizeof(dhcp_data);
+ memcpy(&dhcp_data, p->payload, n);
+ switch (dhcp_data.dp_options[2])
+ {
+ case DHCP_DISCOVER:
+ entry = entry_by_mac(dhcp_data.dp_chaddr);
+ if (entry == NULL) entry = vacant_address();
+ if (entry == NULL) break;
+
+ dhcp_data.dp_op = 2; /* reply */
+ dhcp_data.dp_secs = 0;
+ dhcp_data.dp_flags = 0;
+ set_ip(dhcp_data.dp_yiaddr, entry->addr);
+ memcpy(dhcp_data.dp_magic, magic_cookie, 4);
+
+ memset(dhcp_data.dp_options, 0, sizeof(dhcp_data.dp_options));
+
+ fill_options(dhcp_data.dp_options,
+ DHCP_OFFER,
+ config->domain,
+ config->dns,
+ entry->lease,
+ *netif_ip4_addr(netif),
+ config->router,
+ *netif_ip4_netmask(netif));
+
+ pp = pbuf_alloc(PBUF_TRANSPORT, sizeof(dhcp_data), PBUF_POOL);
+ if (pp == NULL) break;
+ memcpy(pp->payload, &dhcp_data, sizeof(dhcp_data));
+ udp_sendto(upcb, pp, IP_ADDR_BROADCAST, port);
+ pbuf_free(pp);
+ break;
+
+ case DHCP_REQUEST:
+ /* 1. find requested ipaddr in option list */
+ ptr = find_dhcp_option(dhcp_data.dp_options, sizeof(dhcp_data.dp_options), DHCP_IPADDRESS);
+ if (ptr == NULL) break;
+ if (ptr[1] != 4) break;
+ ptr += 2;
+
+ /* 2. does hw-address registered? */
+ entry = entry_by_mac(dhcp_data.dp_chaddr);
+ if (entry != NULL) free_entry(entry);
+
+ /* 3. find requested ipaddr */
+ entry = entry_by_ip(get_ip(ptr));
+ if (entry == NULL) break;
+ if (!is_vacant(entry)) break;
+
+ /* 4. fill struct fields */
+ memcpy(dhcp_data.dp_yiaddr, ptr, 4);
+ dhcp_data.dp_op = 2; /* reply */
+ dhcp_data.dp_secs = 0;
+ dhcp_data.dp_flags = 0;
+ memcpy(dhcp_data.dp_magic, magic_cookie, 4);
+
+ /* 5. fill options */
+ memset(dhcp_data.dp_options, 0, sizeof(dhcp_data.dp_options));
+
+ fill_options(dhcp_data.dp_options,
+ DHCP_ACK,
+ config->domain,
+ config->dns,
+ entry->lease,
+ *netif_ip4_addr(netif),
+ config->router,
+ *netif_ip4_netmask(netif));
+
+ /* 6. send ACK */
+ pp = pbuf_alloc(PBUF_TRANSPORT, sizeof(dhcp_data), PBUF_POOL);
+ if (pp == NULL) break;
+ memcpy(entry->mac, dhcp_data.dp_chaddr, 6);
+ memcpy(pp->payload, &dhcp_data, sizeof(dhcp_data));
+ udp_sendto(upcb, pp, IP_ADDR_BROADCAST, port);
+ pbuf_free(pp);
+ break;
+
+ default:
+ break;
+ }
+ pbuf_free(p);
+}
+
+err_t dhserv_init(const dhcp_config_t *c)
+{
+ err_t err;
+ udp_init();
+ dhserv_free();
+ pcb = udp_new();
+ if (pcb == NULL)
+ return ERR_MEM;
+ err = udp_bind(pcb, IP_ADDR_ANY, c->port);
+ if (err != ERR_OK)
+ {
+ dhserv_free();
+ return err;
+ }
+ udp_recv(pcb, udp_recv_proc, NULL);
+ config = c;
+ return ERR_OK;
+}
+
+void dhserv_free(void)
+{
+ if (pcb == NULL) return;
+ udp_remove(pcb);
+ pcb = NULL;
+}