summaryrefslogtreecommitdiff
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
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.
-rw-r--r--src/libsystemd-network/dhcp-server-internal.h15
-rw-r--r--src/libsystemd-network/sd-dhcp-server.c88
2 files changed, 101 insertions, 2 deletions
diff --git a/src/libsystemd-network/dhcp-server-internal.h b/src/libsystemd-network/dhcp-server-internal.h
index 63883fab99..3d49cba917 100644
--- a/src/libsystemd-network/dhcp-server-internal.h
+++ b/src/libsystemd-network/dhcp-server-internal.h
@@ -40,6 +40,21 @@ struct sd_dhcp_server {
int index;
};
+typedef struct DHCPClientId {
+ size_t length;
+ uint8_t *data;
+} DHCPClientId;
+
+typedef struct DHCPRequest {
+ /* received message */
+ DHCPMessage *message;
+
+ /* options */
+ DHCPClientId client_id;
+ size_t max_optlen;
+ be32_t server_id;
+} DHCPRequest;
+
DEFINE_TRIVIAL_CLEANUP_FUNC(sd_dhcp_server*, sd_dhcp_server_unref);
#define _cleanup_dhcp_server_unref_ _cleanup_(sd_dhcp_server_unrefp)
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;