diff options
author | Tom Gundersen <teg@jklm.no> | 2014-05-24 22:14:32 +0200 |
---|---|---|
committer | Tom Gundersen <teg@jklm.no> | 2014-06-13 16:53:13 +0200 |
commit | 816e2e7af96886e4a43194042ef61ba9fec2c77d (patch) | |
tree | 8255164a237559ec9c64c7f5437aed09c8e1452a | |
parent | be077570f779664ed87b50f60608df9fbe258821 (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.h | 15 | ||||
-rw-r--r-- | src/libsystemd-network/sd-dhcp-server.c | 88 |
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; |