From 87f49b845e763578b3cb7efe28ae678cb58b07c1 Mon Sep 17 00:00:00 2001
From: Jan Breuer <jan.breuer@jaybee.cz>
Date: Sat, 7 May 2016 11:48:13 +0200
Subject: [PATCH] dhcp: add support for vendor specific options 43 and 60

---
 src/core/ipv4/dhcp.c     | 56 ++++++++++++++++++++++++++++++++++++++++++++++++
 src/include/lwip/dhcp.h  |  5 +++++
 src/include/lwip/netif.h |  3 +++
 src/include/lwip/opt.h   | 19 ++++++++++++++++
 4 files changed, 83 insertions(+)

diff --git a/src/core/ipv4/dhcp.c b/src/core/ipv4/dhcp.c
index 3c14675..7ce6ecf 100644
--- a/src/core/ipv4/dhcp.c
+++ b/src/core/ipv4/dhcp.c
@@ -144,6 +144,9 @@ static u8_t dhcp_discover_request_options[] = {
 #if LWIP_DHCP_GET_NTP_SRV
   , DHCP_OPTION_NTP
 #endif /* LWIP_DHCP_GET_NTP_SRV */
+#if LWIP_DHCP_VENDOR_SPECIFIC
+  , DHCP_OPTION_VENDOR_SPECIFIC
+#endif /* LWIP_DHCP_VENDOR_SPECIFIC */
   };
 
 #ifdef DHCP_GLOBAL_XID
@@ -196,6 +199,9 @@ static void dhcp_option_hostname(struct dhcp *dhcp, struct netif *netif);
 #endif /* LWIP_NETIF_HOSTNAME */
 /* always add the DHCP options trailer to end and pad */
 static void dhcp_option_trailer(struct dhcp *dhcp);
+#if LWIP_DHCP_VENDOR_SPECIFIC
+static void dhcp_option_vendors_class_identifier(struct dhcp *dhcp, struct netif *netif);
+#endif
 
 /** Ensure DHCP PCB is allocated and bound */
 static err_t
@@ -366,6 +372,10 @@ dhcp_select(struct netif *netif)
     dhcp_option_hostname(dhcp, netif);
 #endif /* LWIP_NETIF_HOSTNAME */
 
+#if LWIP_DHCP_VENDOR_SPECIFIC
+    dhcp_option_vendors_class_identifier(dhcp, netif);
+#endif /* LWIP_DHCP_VENDOR_SPECIFIC */
+    
     dhcp_option_trailer(dhcp);
     /* shrink the pbuf to the actual content length */
     pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len);
@@ -951,6 +961,9 @@ dhcp_discover(struct netif *netif)
     for (i = 0; i < sizeof(dhcp_discover_request_options); i++) {
       dhcp_option_byte(dhcp, dhcp_discover_request_options[i]);
     }
+#if LWIP_DHCP_VENDOR_SPECIFIC
+    dhcp_option_vendors_class_identifier(dhcp, netif);
+#endif /* LWIP_DHCP_VENDOR_SPECIFIC */
     dhcp_option_trailer(dhcp);
 
     LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_discover: realloc()ing\n"));
@@ -1118,6 +1131,10 @@ dhcp_renew(struct netif *netif)
     dhcp_option_hostname(dhcp, netif);
 #endif /* LWIP_NETIF_HOSTNAME */
 
+#if LWIP_DHCP_VENDOR_SPECIFIC
+    dhcp_option_vendors_class_identifier(dhcp, netif);
+#endif /* LWIP_DHCP_VENDOR_SPECIFIC */
+
     /* append DHCP message trailer */
     dhcp_option_trailer(dhcp);
 
@@ -1170,6 +1187,10 @@ dhcp_rebind(struct netif *netif)
     dhcp_option_hostname(dhcp, netif);
 #endif /* LWIP_NETIF_HOSTNAME */
 
+#if LWIP_DHCP_VENDOR_SPECIFIC
+    dhcp_option_vendors_class_identifier(dhcp, netif);
+#endif /* LWIP_DHCP_VENDOR_SPECIFIC */
+
     dhcp_option_trailer(dhcp);
 
     pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len);
@@ -1413,6 +1434,20 @@ dhcp_option_hostname(struct dhcp *dhcp, struct netif *netif)
 }
 #endif /* LWIP_NETIF_HOSTNAME */
 
+#if LWIP_DHCP_VENDOR_SPECIFIC
+static void
+dhcp_option_vendors_class_identifier(struct dhcp *dhcp, struct netif *netif)
+{
+  size_t available = DHCP_OPTIONS_LEN - dhcp->options_out_len - 3;
+  const char *p = netif->vendor_class_identifier;
+  u8_t len = LWIP_MIN(strlen(p), available);
+  dhcp_option(dhcp, DHCP_OPTION_VENDOR_CLASS_IDENT, len);
+  while (len--) {
+    dhcp_option_byte(dhcp, *p++);
+  }
+}
+#endif /* LWIP_DHCP_VENDOR_SPECIFIC */
+
 /**
  * Extract the DHCP message and the DHCP options.
  *
@@ -1540,6 +1575,27 @@ again:
         LWIP_ERROR("len == 4", len == 4, return ERR_VAL;);
         decode_idx = DHCP_OPTION_IDX_T2;
         break;
+#ifdef LWIP_DHCP_VENDOR_SPECIFIC
+      case(DHCP_OPTION_VENDOR_SPECIFIC):
+      {
+        if (val_offset + len > p->len) {
+            char * vendor_specific;
+            /* limit the length of field to reasonable number */
+            decode_len = LWIP_MIN(len, p->tot_len - val_offset);
+            vendor_specific = mem_calloc(1, decode_len);
+            if (vendor_specific != NULL) {
+                pbuf_copy_partial(p, vendor_specific, decode_len, val_offset);
+                dhcp_handle_vendor_specific(vendor_specific, decode_len);
+                mem_free(vendor_specific);
+            }
+        } else {
+            dhcp_handle_vendor_specific((char*)(p->payload) + val_offset, len);
+        }
+        /* informs the rest of lwip code to skip this option */
+        decode_len = 0;
+        break;
+      }
+#endif /* LWIP_DHCP_VENDOR_SPECIFIC */
       default:
         decode_len = 0;
         LWIP_DEBUGF(DHCP_DEBUG, ("skipping option %"U16_F" in options\n", op));
diff --git a/src/include/lwip/dhcp.h b/src/include/lwip/dhcp.h
index 579bc98..fa0268b 100644
--- a/src/include/lwip/dhcp.h
+++ b/src/include/lwip/dhcp.h
@@ -239,6 +239,8 @@ void dhcp_fine_tmr(void);
 #define DHCP_OPTION_BROADCAST 28
 #define DHCP_OPTION_TCP_TTL 37
 #define DHCP_OPTION_NTP 42
+#define DHCP_OPTION_VENDOR_SPECIFIC 43
+#define DHCP_OPTION_VENDOR_CLASS_IDENT 60
 #define DHCP_OPTION_END 255
 
 /** DHCP options */
@@ -275,6 +277,9 @@ void dhcp_fine_tmr(void);
 extern void dhcp_set_ntp_servers(u8_t num_ntp_servers, const ip4_addr_t* ntp_server_addrs);
 #endif /* LWIP_DHCP_GET_NTP_SRV */
 
+#if LWIP_DHCP_VENDOR_SPECIFIC
+extern void dhcp_handle_vendor_specific(const char * data, size_t len);
+#endif /* LWIP_DHCP_VENDOR_SPECIFIC */
 #ifdef __cplusplus
 }
 #endif
diff --git a/src/include/lwip/netif.h b/src/include/lwip/netif.h
index 7435221..06437b7 100644
--- a/src/include/lwip/netif.h
+++ b/src/include/lwip/netif.h
@@ -294,6 +294,9 @@ struct netif {
 #if LWIP_NETIF_HWADDRHINT
   u8_t *addr_hint;
 #endif /* LWIP_NETIF_HWADDRHINT */
+#if LWIP_DHCP_VENDOR_SPECIFIC
+  char vendor_class_identifier[LWIP_DHCP_VENDOR_CLASS_IDENTIFIER_LEN];
+#endif /* #if LWIP_DHCP_VENDOR_SPECIFIC */
 #if ENABLE_LOOPBACK
   /* List of packets to be queued for ourselves. */
   struct pbuf *loop_first;
diff --git a/src/include/lwip/opt.h b/src/include/lwip/opt.h
index 79f6965..0dcb6a0 100644
--- a/src/include/lwip/opt.h
+++ b/src/include/lwip/opt.h
@@ -755,6 +755,25 @@
 #define LWIP_DHCP_MAX_NTP_SERVERS       1
 #endif
 
+/**
+ * LWIP_DHCP_VENDOR_SPECIFIC==1: Request vendor specific information by identifer
+ * netif->vendor_class_identifier. For each response packet, an callback is
+ * called, which has to be provided by the port:
+ * void dhcp_handle_vendor_specific(const char * data, size_t len);
+ * Data must be handled immediately or copied, because they are freed after return
+ * from this callback.
+ */
+#ifndef LWIP_DHCP_VENDOR_SPECIFIC
+#define LWIP_DHCP_VENDOR_SPECIFIC       0
+#endif
+
+/**
+ * The maximum vendor class identifer length
+ */
+#ifndef LWIP_DHCP_VENDOR_CLASS_IDENTIFIER_LEN
+#define LWIP_DHCP_VENDOR_CLASS_IDENTIFIER_LEN       20
+#endif
+
 /*
    ------------------------------------
    ---------- AUTOIP options ----------
-- 
2.7.3

