summaryrefslogtreecommitdiff
path: root/src/libsystemd-network/sd-dhcp-server.c
diff options
context:
space:
mode:
authorTom Gundersen <teg@jklm.no>2014-05-24 22:14:32 +0200
committerTom Gundersen <teg@jklm.no>2014-06-13 16:53:13 +0200
commit816e2e7af96886e4a43194042ef61ba9fec2c77d (patch)
tree8255164a237559ec9c64c7f5437aed09c8e1452a /src/libsystemd-network/sd-dhcp-server.c
parentbe077570f779664ed87b50f60608df9fbe258821 (diff)
sd-dhcp-server: add basic message parsing
Parse the maximum message size the client can accept and the client id, falling back to sane defaults if they are not set.
Diffstat (limited to 'src/libsystemd-network/sd-dhcp-server.c')
-rw-r--r--src/libsystemd-network/sd-dhcp-server.c88
1 files changed, 86 insertions, 2 deletions
diff --git a/src/libsystemd-network/sd-dhcp-server.c b/src/libsystemd-network/sd-dhcp-server.c
index 57fb09aca0..b1f2fa063f 100644
--- a/src/libsystemd-network/sd-dhcp-server.c
+++ b/src/libsystemd-network/sd-dhcp-server.c
@@ -113,9 +113,84 @@ int sd_dhcp_server_stop(sd_dhcp_server *server) {
return 0;
}
+static int parse_request(uint8_t code, uint8_t len, const uint8_t *option,
+ void *user_data) {
+ DHCPRequest *req = user_data;
+
+ assert(req);
+
+ switch(code) {
+ case DHCP_OPTION_SERVER_IDENTIFIER:
+ if (len == 4)
+ req->server_id = *(be32_t*)option;
+
+ break;
+ case DHCP_OPTION_CLIENT_IDENTIFIER:
+ if (len >= 2) {
+ uint8_t *data;
+
+ data = memdup(option, len);
+ if (!data)
+ return -ENOMEM;
+
+ free(req->client_id.data);
+ req->client_id.data = data;
+ req->client_id.length = len;
+ }
+
+ break;
+ case DHCP_OPTION_MAXIMUM_MESSAGE_SIZE:
+ if (len == 2)
+ req->max_optlen = be16toh(*(be16_t*)option) -
+ - sizeof(DHCPPacket);
+
+ break;
+ }
+
+ return 0;
+}
+
+static void dhcp_request_free(DHCPRequest *req) {
+ if (!req)
+ return;
+
+ free(req->client_id.data);
+ free(req);
+}
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(DHCPRequest*, dhcp_request_free);
+#define _cleanup_dhcp_request_free_ _cleanup_(dhcp_request_freep)
+
+static int ensure_sane_request(DHCPRequest *req, DHCPMessage *message) {
+ assert(req);
+ assert(message);
+
+ req->message = message;
+
+ /* set client id based on mac address if client did not send an explicit one */
+ if (!req->client_id.data) {
+ uint8_t *data;
+
+ data = new0(uint8_t, ETH_ALEN + 1);
+ if (!data)
+ return -ENOMEM;
+
+ req->client_id.length = ETH_ALEN + 1;
+ req->client_id.data = data;
+ req->client_id.data[0] = 0x01;
+ memcpy(&req->client_id.data[1], &message->chaddr, ETH_ALEN);
+ }
+
+ if (req->max_optlen < DHCP_MIN_OPTIONS_SIZE)
+ req->max_optlen = DHCP_MIN_OPTIONS_SIZE;
+
+ return 0;
+}
+
int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message,
size_t length) {
- int type;
+ _cleanup_dhcp_request_free_ DHCPRequest *req = NULL;
+ int type, r;
assert(server);
assert(message);
@@ -125,10 +200,19 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message,
message->hlen != ETHER_ADDR_LEN)
return 0;
- type = dhcp_option_parse(message, length, NULL, NULL);
+ req = new0(DHCPRequest, 1);
+ if (!req)
+ return -ENOMEM;
+
+ type = dhcp_option_parse(message, length, parse_request, req);
if (type < 0)
return 0;
+ r = ensure_sane_request(req, message);
+ if (r < 0)
+ /* this only fails on critical errors */
+ return r;
+
log_dhcp_server(server, "received message of type %d", type);
return 1;