qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Qemu-devel] [PATCH 4/7] slirp: Add translator of DHCP vendor option str


From: Fedor Lyakhov
Subject: [Qemu-devel] [PATCH 4/7] slirp: Add translator of DHCP vendor option string to DHCP TLV representation
Date: Sat, 26 Apr 2014 02:11:00 +0400

New module dhcp_vendopt.c provides translate_dhcp_vendopt() function to convert
command-line DHCP vendor option string into internal DHCP TLV (tag-length-
value) uint8_t buffer. The buffer is stored in global Slirp instance.

Signed-off-by: Fedor Lyakhov <address@hidden>
---
 slirp/Makefile.objs  |   1 +
 slirp/dhcp_vendopt.c | 163 +++++++++++++++++++++++++++++++++++++++++++++++++++
 slirp/slirp.c        |   5 ++
 slirp/slirp.h        |   5 ++
 4 files changed, 174 insertions(+)
 create mode 100644 slirp/dhcp_vendopt.c

diff --git a/slirp/Makefile.objs b/slirp/Makefile.objs
index 2daa9dc..b8c60c7 100644
--- a/slirp/Makefile.objs
+++ b/slirp/Makefile.objs
@@ -1,3 +1,4 @@
 common-obj-y = cksum.o if.o ip_icmp.o ip_input.o ip_output.o dnssearch.o
 common-obj-y += slirp.o mbuf.o misc.o sbuf.o socket.o tcp_input.o tcp_output.o
 common-obj-y += tcp_subr.o tcp_timer.o udp.o bootp.o tftp.o arp_table.o
+common-obj-y += dhcp_vendopt.o
diff --git a/slirp/dhcp_vendopt.c b/slirp/dhcp_vendopt.c
new file mode 100644
index 0000000..274a35f
--- /dev/null
+++ b/slirp/dhcp_vendopt.c
@@ -0,0 +1,163 @@
+/*
+ * dhcp_vendoropt.c: adds vendor-specific options (option 43) to DHCP Offer/Ack
+ *
+ * Copyright (c) 2014 Fedor Lyakhov <address@hidden>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>
+ */
+
+#include "bootp_defines.h"
+#include "slirp.h"
+#include <stdint.h>
+#include <stddef.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <glib.h>
+
+/* Delimiters for vendor-specific options specified from the command-line */
+#define VENDOR_OPTION_DELIMITER ";"
+#define VENDOR_TAG_DELIMITER ":"
+/* Maximum number of tag digits in null-terminated string form */
+#define TAG_MAX_DIGITS 3
+
+/**
+ * parse_option:
+ * @option: Single DHCP vendor option null-terminated string (MUST NOT be NULL)
+ * @ptag: [out] Tag of the option
+ * @plen: [out] Length of the option value
+ * @pvalue: [out] Value of the option
+ *
+ * Parses DHCP vendor option string in TLV format.
+ *
+ * Zero tag is returned in case of non-TLV format, value will be pointing
+ * to the original option string.
+ *
+ * If option string starts with zero tag "0:", it is treated like non-TLV,
+ * but the tag will be stripped from the value.
+ */
+static void parse_option(char *option, uint8_t *ptag, size_t *plen,
+                         char **pvalue);
+
+/**
+ * translate_dhcp_vendopt:
+ * @slirp: Pointer to Slirp struct to store the translated DHCP vendor option
+ *     in a binary TLV form. MUST NOT be NULL.
+ * @dhcp_vendopt_string: Null-terminated string of DHCP vendor options.
+ *
+ * Translates DHCP vendor option string to binary representation to be written
+ * to DHCP packet. This binary representation is uint8_t buffer in TLV format.
+ * The buffer and its length are stored in the @slirp struct.
+ *
+ * Returns: zero if translation succeeded, non-zero otherwise.
+ */
+int translate_dhcp_vendopt(Slirp *slirp, const char *dhpc_vendopt_string)
+{
+    uint8_t options_buf[OPT_HEADER_LEN + MAX_OPT_LEN];
+    uint8_t *buf = options_buf + OPT_HEADER_LEN;
+    /* Total length of vendor option field, excluding top TL header */
+    size_t total_length = 0;
+    char *options;
+    char *current_option;
+    char *option_value;
+    size_t option_length;
+    uint8_t option_tag;
+
+    /* Copying string because strtok will modify it */
+    options = g_strdup(dhpc_vendopt_string);
+    if (!options) {
+        return 1;
+    }
+
+    current_option = strtok(options, VENDOR_OPTION_DELIMITER);
+    if (!current_option) { /* Strtok returns only non-empty strings or NULL */
+        goto cleanup;
+    }
+
+    parse_option(current_option, &option_tag, &option_length, &option_value);
+
+    if (!option_tag) { /* Zero tag - single vendor option case */
+        total_length = option_length;
+        if (total_length > MAX_OPT_LEN) {
+            DEBUG_MISC((dfd, "Too long DHCP vendor option is specified, "
+                       "skipping it"));
+            goto cleanup;
+        }
+        memcpy(buf, option_value, total_length);
+    } else {
+        /* Multiple vendor options are specified, writing them in TLV format */
+        do {
+            size_t new_total_length = total_length + option_length
+                    + OPT_HEADER_LEN;
+            if (new_total_length > MAX_OPT_LEN) {
+                DEBUG_MISC((dfd, "Maximum DHCP options size is reached at "
+                           "option: %s, skipping it and the rest\n",
+                            current_option));
+                break;
+            }
+            total_length = (uint8_t) new_total_length;
+
+            *buf++ = option_tag;
+            *buf++ = (uint8_t) option_length;
+            memcpy(buf, option_value, option_length);
+            buf += option_length;
+
+            current_option = strtok(NULL, VENDOR_OPTION_DELIMITER);
+            if (current_option) {
+                parse_option(current_option, &option_tag, &option_length,
+                             &option_value);
+            }
+        } while (current_option && option_tag);
+    }
+
+    if (total_length > 0) {
+        /* Writing top TLV header */
+        options_buf[0] = RFC1533_VENDOR;
+        options_buf[1] = (uint8_t) total_length;
+        slirp->dhcp_vendopt_len = total_length + OPT_HEADER_LEN;
+        slirp->dhcp_vendopt = g_memdup(options_buf, slirp->dhcp_vendopt_len);
+    }
+
+cleanup:
+    g_free(options);
+    return 0;
+}
+
+static void parse_option(char *option, uint8_t *ptag, size_t *plen,
+                         char **pvalue)
+{
+    char tag_digits[TAG_MAX_DIGITS + 1] = {0, 0, 0, 0};
+    int i;
+
+    *pvalue = option;
+    *ptag = 0;
+
+    for (i = 0; (i < TAG_MAX_DIGITS) && (*option)
+        && (*option != VENDOR_TAG_DELIMITER[0]) && (isdigit(*option)); i++) {
+        tag_digits[i] = *option++; /* Collecting tag digits, if any */
+    }
+
+    if ((i > 0) && (i <= TAG_MAX_DIGITS)
+        && (*option == VENDOR_TAG_DELIMITER[0])) {
+        int res = atoi(tag_digits);
+        /* Only 1-254 tags are allowed by RFC2132, but we accept 0 tag below
+           as an indicator for tag-less value, i.e. we strip initial 0: */
+        if ((res >= 0) && (res < UINT8_MAX)) {
+            *ptag = (uint8_t) res;
+            ++option; /* removing delimiter from the value */
+            *pvalue = option;
+        }
+    }
+    *plen = strlen(*pvalue);
+}
diff --git a/slirp/slirp.c b/slirp/slirp.c
index 3dfce46..c0435d3 100644
--- a/slirp/slirp.c
+++ b/slirp/slirp.c
@@ -234,6 +234,10 @@ Slirp *slirp_init(int restricted, struct in_addr vnetwork,
         translate_dnssearch(slirp, vdnssearch);
     }
 
+    if (dhpc_vendopt_string) {
+        translate_dhcp_vendopt(slirp, dhpc_vendopt_string);
+    }
+
     slirp->opaque = opaque;
 
     register_savevm(NULL, "slirp", 0, 3,
@@ -254,6 +258,7 @@ void slirp_cleanup(Slirp *slirp)
     m_cleanup(slirp);
 
     g_free(slirp->vdnssearch);
+    g_free(slirp->dhcp_vendopt);
     g_free(slirp->tftp_prefix);
     g_free(slirp->bootp_filename);
     g_free(slirp);
diff --git a/slirp/slirp.h b/slirp/slirp.h
index e4a1bd4..f5882bc 100644
--- a/slirp/slirp.h
+++ b/slirp/slirp.h
@@ -239,6 +239,8 @@ struct Slirp {
     char *bootp_filename;
     size_t vdnssearch_len;
     uint8_t *vdnssearch;
+    size_t dhcp_vendopt_len;
+    uint8_t *dhcp_vendopt;
 
     /* tcp states */
     struct socket tcb;
@@ -301,6 +303,9 @@ void lprint(const char *, ...) GCC_FMT_ATTR(1, 2);
 /* dnssearch.c */
 int translate_dnssearch(Slirp *s, const char ** names);
 
+/* dhcp_vendopt.c */
+int translate_dhcp_vendopt(Slirp *s, const char *dhpc_vendopt_string);
+
 /* cksum.c */
 int cksum(struct mbuf *m, int len);
 
-- 
1.8.4.5




reply via email to

[Prev in Thread] Current Thread [Next in Thread]