blob: 13a180f0976edb701ae676b4abd06ada32dc8050 [file] [edit]
/*
* Copyright 2017, Data61, CSIRO (ABN 41 687 119 230)
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <assert.h>
#include <camkes.h>
#include <stdint.h>
#include <stdio.h>
#include "util.h"
/* Our own IP address */
static uint32_t my_ip = 0xc0a80101; /* 192.168.1.1 */
/* We're going to assign IP addresses from our own subnet (192.168.1.*). The
* next one we'll assign will be 192.168.1.next_offer_octet, modulo some IP
* addresses that are off limits. This variable is used to track which IP
* addresses we've offered previously. Note that it is not a big deal if we
* offer an IP address that is unavailable/illegal as we can just reject the
* client's later request for it.
*/
static uint8_t next_offer_octet;
/* Table mapping clients to IP. rounting_table[client_id] = IP address of that
* client. 0 means unassigned.
*/
static uint32_t routing_table[4] = { 0 };
#define routing_table_sz (sizeof(routing_table) / sizeof(routing_table[0]))
/* Handle a DHCPDISCOVER message. */
static uint32_t discover(uint64_t hwaddr, uint32_t *siaddr)
{
lock_lock();
/* Figure out a suitable IP address to offer them */
uint32_t offer;
do {
offer = (my_ip & ~0xff) | (uint32_t)next_offer_octet;
next_offer_octet++;
} while ((offer & 0xff) == 0 || offer == my_ip ||
offer == routing_table[0] || offer == routing_table[1] ||
offer == routing_table[2] || offer == routing_table[3]);
lock_unlock();
/* Pass them our IP address so they can pass it back to us when requested
* IP assignment. This thing is in the DHCP spec to allow for multiple DHCP
* servers on the same network.
*/
*siaddr = my_ip;
char pretty_ip[STRLEN_IP];
ip_to_string(offer, pretty_ip);
dprintf("%s: Sending DHCPOFFER of IP %s\n", get_instance_name(), pretty_ip);
return offer;
}
/* Handle a DHCPREQ message */
static uint32_t request(unsigned int client, uint32_t ip, uint32_t siaddr)
{
if (siaddr != my_ip) {
/* This message was intended for a different DHCP server. In a real
* system we wouldn't send a DHCPNAK here, but would just ignore this
* message. In this simulated setup we are the only server and we
* *must* send a reply, so we just NAK it.
*/
dprintf("%s: Sending DHCPNAK due to server IP mismatch\n",
get_instance_name());
return 0;
}
assert(client < routing_table_sz && "DHCPREQ from non-existent client");
lock_lock();
/* IP address the client gets. This will remain 0 after the logic below if
* we're denying the client's request.
*/
uint32_t assigned = 0;
if (routing_table[client] == ip) {
/* They requested their existing IP. OK, whatever. */
dprintf("%s: Sending DHCPACK to client %u for its existing IP\n",
get_instance_name(), client);
assigned = ip;
} else if (ip != my_ip && ip != 0 && ip != routing_table[0] &&
ip != routing_table[1] && ip != routing_table[2] &&
ip != routing_table[3]) {
/* They requested an IP that was not ours, 0 or in the routing table
* already. XXX: we should probably block the broadcast IP here too.
*/
char pretty_ip[STRLEN_IP];
ip_to_string(ip, pretty_ip);
dprintf("%s: Sending DHCPACK to client %u of IP %s\n",
get_instance_name(), client, pretty_ip);
routing_table[client] = ip;
assigned = ip;
}
lock_unlock();
return assigned;
}
uint32_t client1_discover(uint64_t hwaddr, uint32_t *siaddr)
{
return discover(hwaddr, siaddr);
}
uint32_t client2_discover(uint64_t hwaddr, uint32_t *siaddr)
{
return discover(hwaddr, siaddr);
}
uint32_t client3_discover(uint64_t hwaddr, uint32_t *siaddr)
{
return discover(hwaddr, siaddr);
}
uint32_t client4_discover(uint64_t hwaddr, uint32_t *siaddr)
{
return discover(hwaddr, siaddr);
}
uint32_t client1_request(uint32_t ip, uint32_t siaddr)
{
return request(0, ip, siaddr);
}
uint32_t client2_request(uint32_t ip, uint32_t siaddr)
{
return request(1, ip, siaddr);
}
uint32_t client3_request(uint32_t ip, uint32_t siaddr)
{
return request(2, ip, siaddr);
}
uint32_t client4_request(uint32_t ip, uint32_t siaddr)
{
return request(3, ip, siaddr);
}