{"action":"created","comment":{"url":"https://api.github.com/repos/OpenLightingProject/ja-rule/pulls/comments/35488892","id":35488892,"diff_hunk":"@@ -0,0 +1,829 @@\n+/*\n+ * This library is free software; you can redistribute it and/or\n+ * modify it under the terms of the GNU Lesser General Public\n+ * License as published by the Free Software Foundation; either\n+ * version 2.1 of the License, or (at your option) any later version.\n+ *\n+ * This library is distributed in the hope that it will be useful,\n+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\n+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n+ * Lesser General Public License for more details.\n+ *\n+ * You should have received a copy of the GNU Lesser General Public\n+ * License along with this library; if not, write to the Free Software\n+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n+ *\n+ * network_model.c\n+ * Copyright (C) 2015 Simon Newton\n+ */\n+#include \"network_model.h\"\n+\n+#include <stdlib.h>\n+\n+#include \"constants.h\"\n+#include \"macros.h\"\n+#include \"random.h\"\n+#include \"rdm_buffer.h\"\n+#include \"rdm_frame.h\"\n+#include \"rdm_responder.h\"\n+#include \"rdm_util.h\"\n+#include \"utils.h\"\n+\n+// Various constants\n+enum { NUMBER_OF_NAMESERVERS = 3 };\n+enum { NUMBER_OF_INTERFACES = 3 };\n+enum { INTERFACE_ID_SIZE = 4 };\n+enum { SOFTWARE_VERSION = 0 };\n+\n+typedef enum {\n+  CONFIG_SOURCE_STATIC = 0x00,\n+  CONFIG_SOURCE_DHCP = 0x01,\n+  CONFIG_SOURCE_ZEROCONF = 0x02,\n+  CONFIG_SOURCE_NONE = 0x03\n+} ConfigSource;\n+\n+typedef enum {\n+  LAN_INTERFACE_ID = 1,\n+  IPSEC_INTERFACE_ID = 3,\n+  WLAN_INTERFACE_ID = 4\n+} InterfaceId;\n+\n+static const char DEVICE_MODEL_DESCRIPTION[] = \"Ja Rule Network Device\";\n+static const char SOFTWARE_LABEL[] = \"Alpha\";\n+static const char DEFAULT_DEVICE_LABEL[] = \"Ja Rule\";\n+static const char DEFAULT_HOSTNAME[] = \"responder\";\n+static const char DEFAULT_DOMAINNAME[] = \"local\";\n+static const uint32_t DHCP_FAILURE_RATIO = 3;  // Fail 1 / n DHCP requests.\n+\n+static const ResponderDefinition RESPONDER_DEFINITION;\n+\n+/*\n+ * @brief The read only state for a network Interface.\n+ */\n+typedef struct {\n+  const char *label;\n+  uint32_t id;\n+  uint16_t hardware_type;\n+  uint8_t hardware_address[MAC_ADDRESS_SIZE];\n+} InterfaceDefinition;\n+\n+/*\n+ * @brief The mutable state for an interface\n+ */\n+typedef struct {\n+  uint32_t configured_ip;\n+  uint32_t current_ip;\n+  uint8_t current_netmask;\n+  uint8_t configured_netmask;\n+  ConfigSource config_source;\n+  bool current_dhcp_mode;\n+  bool current_zeroconf_mode;\n+  bool configured_dhcp_mode;\n+  bool configured_zeroconf_mode;\n+} InterfaceState;\n+\n+/*\n+ * @brief The network model state.\n+ */\n+typedef struct {\n+  InterfaceState *interfaces;\n+  unsigned int interface_count;\n+\n+  uint16_t default_interface_route;\n+  uint32_t default_route;\n+  uint32_t nameservers[NUMBER_OF_NAMESERVERS];\n+  char hostname[DNS_HOST_NAME_SIZE];\n+  char domain_name[DNS_DOMAIN_NAME_SIZE];\n+} NetworkModel;\n+\n+// Data\n+static const char ETHERNET_INTERFACE_NAME[] = \"eth0\";\n+static const char IPSEC_INTERFACE_NAME[] = \"tun0\";\n+static const char WIFI_INTERFACE_NAME[] = \"wlan0\";\n+\n+static const InterfaceDefinition INTERFACE_DEFINITIONS[] = {\n+  {\n+    .label = ETHERNET_INTERFACE_NAME,\n+    .id = LAN_INTERFACE_ID,\n+    .hardware_type = ETHERNET_HARDWARE_TYPE,\n+    // Locally administered MAC address\n+    .hardware_address = {0x52, 0x12, 0x34, 0x56, 0x78, 0x9a},\n+  },\n+  {\n+    .label = IPSEC_INTERFACE_NAME,\n+    .id = IPSEC_INTERFACE_ID,\n+    .hardware_type = IPSEC_HARDWARE_TYPE,\n+    // No h/w address for ptp links\n+    .hardware_address = {0, 0, 0, 0, 0, 0},\n+  },\n+  {\n+    .label = WIFI_INTERFACE_NAME,\n+    .id = WLAN_INTERFACE_ID,\n+    .hardware_type = ETHERNET_HARDWARE_TYPE,\n+    // Local admin MAC address\n+    .hardware_address = {0x52, 0xab, 0xcd, 0xef, 0x01, 0x23},\n+  },\n+};\n+\n+static InterfaceState g_interfaces[NUMBER_OF_INTERFACES];\n+\n+static NetworkModel g_network_model;\n+\n+// Helper functions\n+// ----------------------------------------------------------------------------\n+\n+static int LookupIndex(unsigned int id) {\n+  unsigned int i = 0;\n+  for (; i < g_network_model.interface_count; i++) {\n+    if (INTERFACE_DEFINITIONS[i].id == id) {\n+      return i;\n+    }\n+  }\n+  return -1;\n+}\n+\n+/*\n+ * @brief Check a request that contains an interface id.\n+ * @returns -1 if the id was found and index was set, otherwise the size of the\n+ * RDM response that was created.\n+ */\n+static int CheckInterfaceRequest(const RDMHeader *header,\n+                                 const uint8_t *param_data,\n+                                 unsigned int *index) {\n+  if (header->param_data_length != sizeof(uint32_t)) {\n+    return RDMResponder_BuildNack(header, NR_FORMAT_ERROR);\n+  }\n+\n+  unsigned int id = JoinUInt32(param_data[0], param_data[1],\n+                               param_data[2], param_data[3]);\n+  int found_index = LookupIndex(id);\n+  if (found_index < 0) {\n+    return RDMResponder_BuildNack(header, NR_DATA_OUT_OF_RANGE);\n+  }\n+  *index = found_index;\n+  return -1;\n+}\n+\n+/*\n+ * @brief Simulate getting an DHCP address.\n+ *\n+ * This randomly fails and returns 0.0.0.0.\n+ */\n+static uint32_t GetDHCPAddress() {\n+  if (Random_PseudoGet() % DHCP_FAILURE_RATIO == 0) {\n+    // fail 1/3 of the time so we can test zeroconf\n+    return IPV4_UNCONFIGURED;\n+  }\n+  return (10 << 24) + (Random_PseudoGet() & 0xffffff);\n+}\n+\n+/*\n+ * @brief Use a zeroconf address or fallback to unassigned.\n+ */\n+static void UseZeroconfOrUnassign(InterfaceState *interface) {\n+  if (interface->current_zeroconf_mode) {\n+    interface->current_ip = 0xa9fe0000 + (Random_PseudoGet() % 0xfeff);\n+    interface->current_netmask = 16;\n+    interface->config_source = CONFIG_SOURCE_ZEROCONF;\n+  } else {\n+    interface->config_source = CONFIG_SOURCE_NONE;\n+    interface->current_ip = IPV4_UNCONFIGURED;\n+    interface->current_netmask = 0;\n+  }\n+}\n+\n+static void ConfigureInterface(InterfaceState *interface) {\n+  interface->current_dhcp_mode = interface->configured_dhcp_mode;\n+  interface->current_zeroconf_mode = interface->configured_zeroconf_mode;\n+\n+  uint32_t dhcp_address;\n+  if (interface->configured_ip) {\n+    // Static IP, use that\n+    interface->current_ip = interface->configured_ip;\n+    interface->current_netmask = interface->configured_netmask;\n+    interface->config_source = CONFIG_SOURCE_STATIC;\n+  } else if (interface->configured_dhcp_mode &&\n+             (dhcp_address = GetDHCPAddress())) {\n+    interface->current_ip = dhcp_address;\n+    interface->current_netmask = 8;\n+    interface->config_source = CONFIG_SOURCE_DHCP;\n+  } else {\n+    UseZeroconfOrUnassign(interface);\n+  }\n+}\n+\n+// PID Handlers\n+// ----------------------------------------------------------------------------\n+int NetworkModel_GetListInterfaces(const RDMHeader *header,\n+                                   UNUSED const uint8_t *param_data) {\n+  if (header->param_data_length) {\n+    return RDMResponder_BuildNack(header, NR_FORMAT_ERROR);\n+  }\n+\n+  ReturnUnlessUnicast(header);\n+\n+  unsigned int offset = sizeof(RDMHeader);\n+  unsigned int i = 0;\n+  for (; i < g_network_model.interface_count; i++) {\n+    g_rdm_buffer[offset++] = UInt32Byte0(INTERFACE_DEFINITIONS[i].id);\n+    g_rdm_buffer[offset++] = UInt32Byte1(INTERFACE_DEFINITIONS[i].id);\n+    g_rdm_buffer[offset++] = UInt32Byte2(INTERFACE_DEFINITIONS[i].id);\n+    g_rdm_buffer[offset++] = UInt32Byte3(INTERFACE_DEFINITIONS[i].id);\n+    g_rdm_buffer[offset++] = ShortMSB(INTERFACE_DEFINITIONS[i].hardware_type);\n+    g_rdm_buffer[offset++] = ShortLSB(INTERFACE_DEFINITIONS[i].hardware_type);\n+  }\n+  RDMResponder_BuildHeader(header, ACK, GET_COMMAND_RESPONSE,\n+                           ntohs(header->param_id),\n+                           offset - sizeof(RDMHeader));\n+  return RDMUtil_AppendChecksum(g_rdm_buffer);\n+}\n+\n+int NetworkModel_GetInterfaceLabel(const RDMHeader *header,\n+                                   const uint8_t *param_data) {\n+  unsigned int index = 0;\n+  int r = CheckInterfaceRequest(header, param_data, &index);\n+  if (r >= 0) {\n+    return r;\n+  }\n+\n+  ReturnUnlessUnicast(header);\n+\n+  unsigned int offset = sizeof(RDMHeader);\n+  memcpy(g_rdm_buffer + offset, param_data, INTERFACE_ID_SIZE);\n+  offset += INTERFACE_ID_SIZE;\n+  offset += RDMUtil_StringCopy((char*) (g_rdm_buffer + offset),\n+                               RDM_DEFAULT_STRING_SIZE,\n+                               INTERFACE_DEFINITIONS[index].label,\n+                               RDM_DEFAULT_STRING_SIZE);\n+  RDMResponder_BuildHeader(header, ACK, GET_COMMAND_RESPONSE,\n+                           ntohs(header->param_id),\n+                           offset - sizeof(RDMHeader));\n+  return RDMUtil_AppendChecksum(g_rdm_buffer);\n+}\n+\n+int NetworkModel_GetHardwareAddress(const RDMHeader *header,\n+                                    const uint8_t *param_data) {\n+  unsigned int index = 0;\n+  int r = CheckInterfaceRequest(header, param_data, &index);\n+  if (r >= 0) {\n+    return r;\n+  }\n+\n+  if (INTERFACE_DEFINITIONS[index].hardware_type != ETHERNET_HARDWARE_TYPE) {\n+    return RDMResponder_BuildNack(header, NR_DATA_OUT_OF_RANGE);\n+  }\n+\n+  ReturnUnlessUnicast(header);\n+\n+  unsigned int offset = sizeof(RDMHeader);\n+  memcpy(g_rdm_buffer + offset, param_data, INTERFACE_ID_SIZE);\n+  offset += INTERFACE_ID_SIZE;\n+  memcpy(g_rdm_buffer + offset, INTERFACE_DEFINITIONS[index].hardware_address,\n+         MAC_ADDRESS_SIZE);\n+  offset += MAC_ADDRESS_SIZE;\n+  RDMResponder_BuildHeader(header, ACK, GET_COMMAND_RESPONSE,\n+                           ntohs(header->param_id),\n+                           offset - sizeof(RDMHeader));\n+  return RDMUtil_AppendChecksum(g_rdm_buffer);\n+}\n+\n+int NetworkModel_GetDHCPMode(const RDMHeader *header,\n+                             const uint8_t *param_data) {\n+  unsigned int index = 0;\n+  int r = CheckInterfaceRequest(header, param_data, &index);\n+  if (r >= 0) {\n+    return r;\n+  }\n+\n+  ReturnUnlessUnicast(header);\n+\n+  unsigned int offset = sizeof(RDMHeader);\n+  memcpy(g_rdm_buffer + offset, param_data, INTERFACE_ID_SIZE);\n+  offset += INTERFACE_ID_SIZE;\n+  g_rdm_buffer[offset++] =\n+      g_network_model.interfaces[index].configured_dhcp_mode;\n+  RDMResponder_BuildHeader(header, ACK, GET_COMMAND_RESPONSE,\n+                           ntohs(header->param_id),\n+                           offset - sizeof(RDMHeader));\n+  return RDMUtil_AppendChecksum(g_rdm_buffer);\n+}\n+\n+int NetworkModel_SetDHCPMode(const RDMHeader *header,\n+                             const uint8_t *param_data) {\n+  if (header->param_data_length != sizeof(uint32_t) + sizeof(uint8_t)) {\n+    return RDMResponder_BuildNack(header, NR_FORMAT_ERROR);\n+  }\n+\n+  unsigned int id = JoinUInt32(param_data[0], param_data[1],\n+                               param_data[2], param_data[3]);\n+  int index = LookupIndex(id);\n+  if (index < 0) {\n+    return RDMResponder_BuildNack(header, NR_DATA_OUT_OF_RANGE);\n+  }\n+\n+  if (index == IPSEC_INTERFACE_ID) {\n+    return RDMResponder_BuildNack(header, NR_ACTION_NOT_SUPPORTED);\n+  }\n+\n+  g_network_model.interfaces[index].configured_dhcp_mode =\n+      param_data[INTERFACE_ID_SIZE];\n+\n+  return RDMResponder_BuildSetAck(header);\n+}\n+\n+int NetworkModel_GetZeroconfMode(const RDMHeader *header,\n+                                 const uint8_t *param_data) {\n+  unsigned int index = 0;\n+  int r = CheckInterfaceRequest(header, param_data, &index);\n+  if (r >= 0) {\n+    return r;\n+  }\n+\n+  ReturnUnlessUnicast(header);\n+\n+  unsigned int offset = sizeof(RDMHeader);\n+  memcpy(g_rdm_buffer + offset, param_data, INTERFACE_ID_SIZE);\n+  offset += INTERFACE_ID_SIZE;\n+  g_rdm_buffer[offset++] =\n+      g_network_model.interfaces[index].configured_zeroconf_mode;\n+  RDMResponder_BuildHeader(header, ACK, GET_COMMAND_RESPONSE,\n+                           ntohs(header->param_id),\n+                           offset - sizeof(RDMHeader));\n+  return RDMUtil_AppendChecksum(g_rdm_buffer);\n+}\n+\n+int NetworkModel_SetZeroconfMode(const RDMHeader *header,\n+                                 const uint8_t *param_data) {\n+  if (header->param_data_length != sizeof(uint32_t) + sizeof(uint8_t)) {\n+    return RDMResponder_BuildNack(header, NR_FORMAT_ERROR);\n+  }\n+\n+  unsigned int id = JoinUInt32(param_data[0], param_data[1],\n+                               param_data[2], param_data[3]);\n+  int index = LookupIndex(id);\n+  if (index < 0) {\n+    return RDMResponder_BuildNack(header, NR_DATA_OUT_OF_RANGE);\n+  }\n+\n+  if (index == IPSEC_INTERFACE_ID) {\n+    return RDMResponder_BuildNack(header, NR_ACTION_NOT_SUPPORTED);\n+  }\n+\n+  g_network_model.interfaces[index].configured_zeroconf_mode =\n+      param_data[INTERFACE_ID_SIZE];\n+\n+  return RDMResponder_BuildSetAck(header);\n+}\n+\n+int NetworkModel_GetCurrentAddress(const RDMHeader *header,\n+                                   const uint8_t *param_data) {\n+  unsigned int index = 0;\n+  int r = CheckInterfaceRequest(header, param_data, &index);\n+  if (r >= 0) {\n+    return r;\n+  }\n+\n+  ReturnUnlessUnicast(header);\n+  InterfaceState *interface = &g_network_model.interfaces[index];\n+\n+  unsigned int offset = sizeof(RDMHeader);\n+  memcpy(g_rdm_buffer + offset, param_data, INTERFACE_ID_SIZE);\n+  offset += INTERFACE_ID_SIZE;\n+  g_rdm_buffer[offset++] = UInt32Byte0(interface->current_ip);\n+  g_rdm_buffer[offset++] = UInt32Byte1(interface->current_ip);\n+  g_rdm_buffer[offset++] = UInt32Byte2(interface->current_ip);\n+  g_rdm_buffer[offset++] = UInt32Byte3(interface->current_ip);\n+  g_rdm_buffer[offset++] = interface->current_netmask;\n+  if (index == IPSEC_INTERFACE_ID) {\n+    g_rdm_buffer[offset++] = DHCP_MODE_UNKNOWN;\n+  } else {\n+    g_rdm_buffer[offset++] = (interface->config_source == CONFIG_SOURCE_DHCP ?\n+                              DHCP_MODE_ACTIVE : DHCP_MODE_INACTIVE);\n+  }\n+\n+  RDMResponder_BuildHeader(header, ACK, GET_COMMAND_RESPONSE,\n+                           ntohs(header->param_id),\n+                           offset - sizeof(RDMHeader));\n+  return RDMUtil_AppendChecksum(g_rdm_buffer);\n+}\n+\n+int NetworkModel_GetStaticAddress(const RDMHeader *header,\n+                                  const uint8_t *param_data) {\n+  unsigned int index = 0;\n+  int r = CheckInterfaceRequest(header, param_data, &index);\n+  if (r >= 0) {\n+    return r;\n+  }\n+\n+  ReturnUnlessUnicast(header);\n+\n+  InterfaceState *interface = &g_network_model.interfaces[index];\n+  unsigned int offset = sizeof(RDMHeader);\n+  memcpy(g_rdm_buffer + offset, param_data, INTERFACE_ID_SIZE);\n+  offset += INTERFACE_ID_SIZE;\n+  g_rdm_buffer[offset++] = UInt32Byte0(interface->configured_ip);\n+  g_rdm_buffer[offset++] = UInt32Byte1(interface->configured_ip);\n+  g_rdm_buffer[offset++] = UInt32Byte2(interface->configured_ip);\n+  g_rdm_buffer[offset++] = UInt32Byte3(interface->configured_ip);\n+  g_rdm_buffer[offset++] = interface->configured_netmask;\n+\n+  RDMResponder_BuildHeader(header, ACK, GET_COMMAND_RESPONSE,\n+                           ntohs(header->param_id),\n+                           offset - sizeof(RDMHeader));\n+  return RDMUtil_AppendChecksum(g_rdm_buffer);\n+}\n+\n+int NetworkModel_SetStaticAddress(const RDMHeader *header,\n+                                  const uint8_t *param_data) {\n+  if (header->param_data_length != 2 * sizeof(uint32_t) + sizeof(uint8_t)) {\n+    return RDMResponder_BuildNack(header, NR_FORMAT_ERROR);\n+  }\n+\n+  unsigned int id = JoinUInt32(param_data[0], param_data[1],\n+                               param_data[2], param_data[3]);\n+  int index = LookupIndex(id);\n+  if (index < 0) {\n+    return RDMResponder_BuildNack(header, NR_DATA_OUT_OF_RANGE);\n+  }\n+\n+  uint32_t ip = JoinUInt32(param_data[4], param_data[5],\n+                           param_data[6], param_data[7]);\n+  uint8_t netmask = param_data[8];\n+\n+  if (netmask > MAX_NETMASK) {\n+    return RDMResponder_BuildNack(header, NR_DATA_OUT_OF_RANGE);\n+  }\n+\n+  g_network_model.interfaces[index].configured_ip = ip;\n+  g_network_model.interfaces[index].configured_netmask = netmask;\n+\n+  return RDMResponder_BuildSetAck(header);\n+}\n+\n+\n+int NetworkModel_RenewDHCP(const RDMHeader *header,\n+                           const uint8_t *param_data) {\n+  if (header->param_data_length != sizeof(uint32_t)) {\n+    return RDMResponder_BuildNack(header, NR_FORMAT_ERROR);\n+  }\n+\n+  unsigned int id = JoinUInt32(param_data[0], param_data[1],\n+                               param_data[2], param_data[3]);\n+  int index = LookupIndex(id);\n+  if (index < 0) {\n+    return RDMResponder_BuildNack(header, NR_DATA_OUT_OF_RANGE);\n+  }\n+\n+  InterfaceState *interface = &g_network_model.interfaces[index];\n+  if (interface->config_source == CONFIG_SOURCE_STATIC ||\n+      interface->current_dhcp_mode == false) {\n+    return RDMResponder_BuildNack(header, NR_ACTION_NOT_SUPPORTED);\n+  }\n+\n+  if (interface->config_source == CONFIG_SOURCE_DHCP) {\n+    if (Random_PseudoGet() % DHCP_FAILURE_RATIO == 0) {\n+      UseZeroconfOrUnassign(interface);\n+    }\n+  } else {\n+    uint32_t dhcp_address = 0;\n+    if (dhcp_address = GetDHCPAddress()) {\n+      interface->current_ip = dhcp_address;\n+      interface->current_netmask = 8;\n+      interface->config_source = CONFIG_SOURCE_DHCP;\n+    } else {\n+      UseZeroconfOrUnassign(interface);\n+    }\n+  }\n+\n+  return RDMResponder_BuildSetAck(header);\n+}\n+\n+int NetworkModel_ReleaseDHCP(const RDMHeader *header,\n+                             const uint8_t *param_data) {\n+  if (header->param_data_length != sizeof(uint32_t)) {\n+    return RDMResponder_BuildNack(header, NR_FORMAT_ERROR);\n+  }\n+\n+  unsigned int id = JoinUInt32(param_data[0], param_data[1],\n+                               param_data[2], param_data[3]);\n+  int index = LookupIndex(id);\n+  if (index < 0) {\n+    return RDMResponder_BuildNack(header, NR_DATA_OUT_OF_RANGE);\n+  }\n+\n+  InterfaceState *interface = &g_network_model.interfaces[index];\n+  if (interface->config_source != CONFIG_SOURCE_DHCP) {\n+    return RDMResponder_BuildNack(header, NR_ACTION_NOT_SUPPORTED);\n+  }\n+\n+  UseZeroconfOrUnassign(interface);\n+  return RDMResponder_BuildSetAck(header);\n+}\n+\n+int NetworkModel_ApplyInterfaceConfiguration(const RDMHeader *header,\n+                                             const uint8_t *param_data) {\n+  if (header->param_data_length != sizeof(uint32_t)) {\n+    return RDMResponder_BuildNack(header, NR_FORMAT_ERROR);\n+  }\n+\n+  unsigned int id = JoinUInt32(param_data[0], param_data[1],\n+                               param_data[2], param_data[3]);\n+  int index = LookupIndex(id);\n+  if (index < 0) {\n+    return RDMResponder_BuildNack(header, NR_DATA_OUT_OF_RANGE);\n+  }\n+\n+  ConfigureInterface(&g_network_model.interfaces[index]);\n+  return RDMResponder_BuildSetAck(header);\n+}\n+\n+int NetworkModel_GetDefaultRoute(const RDMHeader *header,\n+                                 UNUSED const uint8_t *param_data) {\n+  if (header->param_data_length) {\n+    return RDMResponder_BuildNack(header, NR_FORMAT_ERROR);\n+  }\n+  ReturnUnlessUnicast(header);\n+\n+  unsigned int offset = sizeof(RDMHeader);\n+  g_rdm_buffer[offset++] = UInt32Byte0(g_network_model.default_interface_route);\n+  g_rdm_buffer[offset++] = UInt32Byte1(g_network_model.default_interface_route);\n+  g_rdm_buffer[offset++] = UInt32Byte2(g_network_model.default_interface_route);\n+  g_rdm_buffer[offset++] = UInt32Byte3(g_network_model.default_interface_route);\n+  g_rdm_buffer[offset++] = UInt32Byte0(g_network_model.default_route);\n+  g_rdm_buffer[offset++] = UInt32Byte1(g_network_model.default_route);\n+  g_rdm_buffer[offset++] = UInt32Byte2(g_network_model.default_route);\n+  g_rdm_buffer[offset++] = UInt32Byte3(g_network_model.default_route);\n+  RDMResponder_BuildHeader(header, ACK, GET_COMMAND_RESPONSE,\n+                           ntohs(header->param_id),\n+                           offset - sizeof(RDMHeader));\n+  return RDMUtil_AppendChecksum(g_rdm_buffer);\n+}\n+\n+int NetworkModel_SetDefaultRoute(const RDMHeader *header,\n+                                 const uint8_t *param_data) {\n+  if (header->param_data_length != 2 * sizeof(uint32_t)) {\n+    return RDMResponder_BuildNack(header, NR_FORMAT_ERROR);\n+  }\n+\n+  uint32_t interface_id = JoinUInt32(param_data[0], param_data[1],\n+                                     param_data[2], param_data[3]);\n+  uint32_t ip = JoinUInt32(param_data[4], param_data[5], param_data[6],\n+                           param_data[7]);\n+\n+  if (interface_id != NO_DEFAULT_ROUTE && ip != NO_DEFAULT_ROUTE) {\n+    return RDMResponder_BuildNack(header, NR_DATA_OUT_OF_RANGE);\n+  }\n+\n+  if (interface_id != NO_DEFAULT_ROUTE &&\n+      (LookupIndex(interface_id) < 0 || interface_id != IPSEC_INTERFACE_ID)) {\n+    // Only the ptp IPsec interface can be used as the default route\n+    return RDMResponder_BuildNack(header, NR_DATA_OUT_OF_RANGE);\n+  }\n+  g_network_model.default_interface_route = interface_id;\n+  g_network_model.default_route = ip;\n+  return RDMResponder_BuildSetAck(header);\n+}\n+\n+int NetworkModel_GetNameServer(const RDMHeader *header,\n+                               const uint8_t *param_data) {\n+  if (header->param_data_length != sizeof(uint8_t)) {\n+    return RDMResponder_BuildNack(header, NR_FORMAT_ERROR);\n+  }\n+\n+  uint8_t index = param_data[0];\n+  if (index >= NUMBER_OF_NAMESERVERS) {\n+    return RDMResponder_BuildNack(header, NR_DATA_OUT_OF_RANGE);\n+  }\n+\n+  ReturnUnlessUnicast(header);\n+  uint32_t ip = g_network_model.nameservers[index];\n+\n+  unsigned int offset = sizeof(RDMHeader);\n+  g_rdm_buffer[offset++] = index;\n+  g_rdm_buffer[offset++] = UInt32Byte0(ip);\n+  g_rdm_buffer[offset++] = UInt32Byte1(ip);\n+  g_rdm_buffer[offset++] = UInt32Byte2(ip);\n+  g_rdm_buffer[offset++] = UInt32Byte3(ip);\n+  RDMResponder_BuildHeader(header, ACK, GET_COMMAND_RESPONSE,\n+                           ntohs(header->param_id),\n+                           offset - sizeof(RDMHeader));\n+  return RDMUtil_AppendChecksum(g_rdm_buffer);\n+}\n+\n+int NetworkModel_SetNameServer(const RDMHeader *header,\n+                               const uint8_t *param_data) {\n+  if (header->param_data_length != sizeof(uint8_t) + sizeof(uint32_t)) {\n+    return RDMResponder_BuildNack(header, NR_FORMAT_ERROR);\n+  }\n+\n+  uint8_t index = param_data[0];\n+  if (index >= NUMBER_OF_NAMESERVERS) {\n+    return RDMResponder_BuildNack(header, NR_DATA_OUT_OF_RANGE);\n+  }\n+\n+  uint32_t ip = JoinUInt32(param_data[1], param_data[2],\n+                           param_data[3], param_data[4]);\n+\n+  g_network_model.nameservers[index] = ip;\n+  return RDMResponder_BuildSetAck(header);\n+}\n+\n+int NetworkModel_GetHostname(const RDMHeader *header,\n+                             UNUSED const uint8_t *param_data) {\n+  return RDMResponder_GenericReturnString(header, g_network_model.hostname,\n+                                          DNS_HOST_NAME_SIZE);\n+}\n+\n+int NetworkModel_SetHostName(const RDMHeader *header,\n+                             const uint8_t *param_data) {\n+  if (header->param_data_length == 0 ||\n+      header->param_data_length > DNS_HOST_NAME_SIZE) {\n+    return RDMResponder_BuildNack(header, NR_DATA_OUT_OF_RANGE);\n+  }\n+\n+  RDMUtil_StringCopy(g_network_model.hostname, DNS_HOST_NAME_SIZE,\n+                     (const char*) param_data,\n+                     header->param_data_length);\n+  return RDMResponder_BuildSetAck(header);\n+}\n+\n+int NetworkModel_GetDomainName(const RDMHeader *header,\n+                               UNUSED const uint8_t *param_data) {\n+  return RDMResponder_GenericReturnString(header, g_network_model.domain_name,","path":"firmware/src/network_model.c","position":652,"original_position":652,"commit_id":"4fea600301fd6b2de7d5a36d54e09614366c1c8e","original_commit_id":"4fea600301fd6b2de7d5a36d54e09614366c1c8e","user":{"login":"peternewman","id":6216436,"avatar_url":"https://avatars.githubusercontent.com/u/6216436?v=3","gravatar_id":"","url":"https://api.github.com/users/peternewman","html_url":"https://github.com/peternewman","followers_url":"https://api.github.com/users/peternewman/followers","following_url":"https://api.github.com/users/peternewman/following{/other_user}","gists_url":"https://api.github.com/users/peternewman/gists{/gist_id}","starred_url":"https://api.github.com/users/peternewman/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/peternewman/subscriptions","organizations_url":"https://api.github.com/users/peternewman/orgs","repos_url":"https://api.github.com/users/peternewman/repos","events_url":"https://api.github.com/users/peternewman/events{/privacy}","received_events_url":"https://api.github.com/users/peternewman/received_events","type":"User","site_admin":false},"body":"If we add DHCP support in future, we could get a domain name via that which breaks this.","created_at":"2015-07-26T03:11:13Z","updated_at":"2015-07-26T03:11:13Z","html_url":"https://github.com/OpenLightingProject/ja-rule/pull/90#discussion_r35488892","pull_request_url":"https://api.github.com/repos/OpenLightingProject/ja-rule/pulls/90","_links":{"self":{"href":"https://api.github.com/repos/OpenLightingProject/ja-rule/pulls/comments/35488892"},"html":{"href":"https://github.com/OpenLightingProject/ja-rule/pull/90#discussion_r35488892"},"pull_request":{"href":"https://api.github.com/repos/OpenLightingProject/ja-rule/pulls/90"}}},"pull_request":{"url":"https://api.github.com/repos/OpenLightingProject/ja-rule/pulls/90","id":40629772,"html_url":"https://github.com/OpenLightingProject/ja-rule/pull/90","diff_url":"https://github.com/OpenLightingProject/ja-rule/pull/90.diff","patch_url":"https://github.com/OpenLightingProject/ja-rule/pull/90.patch","issue_url":"https://api.github.com/repos/OpenLightingProject/ja-rule/issues/90","number":90,"state":"closed","locked":false,"title":"Add the network responder","user":{"login":"nomis52","id":4355332,"avatar_url":"https://avatars.githubusercontent.com/u/4355332?v=3","gravatar_id":"","url":"https://api.github.com/users/nomis52","html_url":"https://github.com/nomis52","followers_url":"https://api.github.com/users/nomis52/followers","following_url":"https://api.github.com/users/nomis52/following{/other_user}","gists_url":"https://api.github.com/users/nomis52/gists{/gist_id}","starred_url":"https://api.github.com/users/nomis52/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/nomis52/subscriptions","organizations_url":"https://api.github.com/users/nomis52/orgs","repos_url":"https://api.github.com/users/nomis52/repos","events_url":"https://api.github.com/users/nomis52/events{/privacy}","received_events_url":"https://api.github.com/users/nomis52/received_events","type":"User","site_admin":false},"body":"","created_at":"2015-07-23T01:41:22Z","updated_at":"2015-07-26T03:11:13Z","closed_at":"2015-07-23T14:30:39Z","merged_at":"2015-07-23T14:30:39Z","merge_commit_sha":"b33d33e1d323ba036d7fe36b2151437d942a32aa","assignee":{"login":"peternewman","id":6216436,"avatar_url":"https://avatars.githubusercontent.com/u/6216436?v=3","gravatar_id":"","url":"https://api.github.com/users/peternewman","html_url":"https://github.com/peternewman","followers_url":"https://api.github.com/users/peternewman/followers","following_url":"https://api.github.com/users/peternewman/following{/other_user}","gists_url":"https://api.github.com/users/peternewman/gists{/gist_id}","starred_url":"https://api.github.com/users/peternewman/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/peternewman/subscriptions","organizations_url":"https://api.github.com/users/peternewman/orgs","repos_url":"https://api.github.com/users/peternewman/repos","events_url":"https://api.github.com/users/peternewman/events{/privacy}","received_events_url":"https://api.github.com/users/peternewman/received_events","type":"User","site_admin":false},"milestone":null,"commits_url":"https://api.github.com/repos/OpenLightingProject/ja-rule/pulls/90/commits","review_comments_url":"https://api.github.com/repos/OpenLightingProject/ja-rule/pulls/90/comments","review_comment_url":"https://api.github.com/repos/OpenLightingProject/ja-rule/pulls/comments{/number}","comments_url":"https://api.github.com/repos/OpenLightingProject/ja-rule/issues/90/comments","statuses_url":"https://api.github.com/repos/OpenLightingProject/ja-rule/statuses/4fea600301fd6b2de7d5a36d54e09614366c1c8e","head":{"label":"nomis52:master","ref":"master","sha":"4fea600301fd6b2de7d5a36d54e09614366c1c8e","user":{"login":"nomis52","id":4355332,"avatar_url":"https://avatars.githubusercontent.com/u/4355332?v=3","gravatar_id":"","url":"https://api.github.com/users/nomis52","html_url":"https://github.com/nomis52","followers_url":"https://api.github.com/users/nomis52/followers","following_url":"https://api.github.com/users/nomis52/following{/other_user}","gists_url":"https://api.github.com/users/nomis52/gists{/gist_id}","starred_url":"https://api.github.com/users/nomis52/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/nomis52/subscriptions","organizations_url":"https://api.github.com/users/nomis52/orgs","repos_url":"https://api.github.com/users/nomis52/repos","events_url":"https://api.github.com/users/nomis52/events{/privacy}","received_events_url":"https://api.github.com/users/nomis52/received_events","type":"User","site_admin":false},"repo":{"id":29171822,"name":"ja-rule","full_name":"nomis52/ja-rule","owner":{"login":"nomis52","id":4355332,"avatar_url":"https://avatars.githubusercontent.com/u/4355332?v=3","gravatar_id":"","url":"https://api.github.com/users/nomis52","html_url":"https://github.com/nomis52","followers_url":"https://api.github.com/users/nomis52/followers","following_url":"https://api.github.com/users/nomis52/following{/other_user}","gists_url":"https://api.github.com/users/nomis52/gists{/gist_id}","starred_url":"https://api.github.com/users/nomis52/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/nomis52/subscriptions","organizations_url":"https://api.github.com/users/nomis52/orgs","repos_url":"https://api.github.com/users/nomis52/repos","events_url":"https://api.github.com/users/nomis52/events{/privacy}","received_events_url":"https://api.github.com/users/nomis52/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/nomis52/ja-rule","description":"Ja Rule","fork":true,"url":"https://api.github.com/repos/nomis52/ja-rule","forks_url":"https://api.github.com/repos/nomis52/ja-rule/forks","keys_url":"https://api.github.com/repos/nomis52/ja-rule/keys{/key_id}","collaborators_url":"https://api.github.com/repos/nomis52/ja-rule/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/nomis52/ja-rule/teams","hooks_url":"https://api.github.com/repos/nomis52/ja-rule/hooks","issue_events_url":"https://api.github.com/repos/nomis52/ja-rule/issues/events{/number}","events_url":"https://api.github.com/repos/nomis52/ja-rule/events","assignees_url":"https://api.github.com/repos/nomis52/ja-rule/assignees{/user}","branches_url":"https://api.github.com/repos/nomis52/ja-rule/branches{/branch}","tags_url":"https://api.github.com/repos/nomis52/ja-rule/tags","blobs_url":"https://api.github.com/repos/nomis52/ja-rule/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/nomis52/ja-rule/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/nomis52/ja-rule/git/refs{/sha}","trees_url":"https://api.github.com/repos/nomis52/ja-rule/git/trees{/sha}","statuses_url":"https://api.github.com/repos/nomis52/ja-rule/statuses/{sha}","languages_url":"https://api.github.com/repos/nomis52/ja-rule/languages","stargazers_url":"https://api.github.com/repos/nomis52/ja-rule/stargazers","contributors_url":"https://api.github.com/repos/nomis52/ja-rule/contributors","subscribers_url":"https://api.github.com/repos/nomis52/ja-rule/subscribers","subscription_url":"https://api.github.com/repos/nomis52/ja-rule/subscription","commits_url":"https://api.github.com/repos/nomis52/ja-rule/commits{/sha}","git_commits_url":"https://api.github.com/repos/nomis52/ja-rule/git/commits{/sha}","comments_url":"https://api.github.com/repos/nomis52/ja-rule/comments{/number}","issue_comment_url":"https://api.github.com/repos/nomis52/ja-rule/issues/comments{/number}","contents_url":"https://api.github.com/repos/nomis52/ja-rule/contents/{+path}","compare_url":"https://api.github.com/repos/nomis52/ja-rule/compare/{base}...{head}","merges_url":"https://api.github.com/repos/nomis52/ja-rule/merges","archive_url":"https://api.github.com/repos/nomis52/ja-rule/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/nomis52/ja-rule/downloads","issues_url":"https://api.github.com/repos/nomis52/ja-rule/issues{/number}","pulls_url":"https://api.github.com/repos/nomis52/ja-rule/pulls{/number}","milestones_url":"https://api.github.com/repos/nomis52/ja-rule/milestones{/number}","notifications_url":"https://api.github.com/repos/nomis52/ja-rule/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/nomis52/ja-rule/labels{/name}","releases_url":"https://api.github.com/repos/nomis52/ja-rule/releases{/id}","created_at":"2015-01-13T03:58:08Z","updated_at":"2015-04-19T22:29:48Z","pushed_at":"2015-07-26T02:21:19Z","git_url":"git://github.com/nomis52/ja-rule.git","ssh_url":"git@github.com:nomis52/ja-rule.git","clone_url":"https://github.com/nomis52/ja-rule.git","svn_url":"https://github.com/nomis52/ja-rule","homepage":null,"size":1763,"stargazers_count":0,"watchers_count":0,"language":"C","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"OpenLightingProject:master","ref":"master","sha":"c0253ee340baf0f196819b939a5236ac22d9485d","user":{"login":"OpenLightingProject","id":6199314,"avatar_url":"https://avatars.githubusercontent.com/u/6199314?v=3","gravatar_id":"","url":"https://api.github.com/users/OpenLightingProject","html_url":"https://github.com/OpenLightingProject","followers_url":"https://api.github.com/users/OpenLightingProject/followers","following_url":"https://api.github.com/users/OpenLightingProject/following{/other_user}","gists_url":"https://api.github.com/users/OpenLightingProject/gists{/gist_id}","starred_url":"https://api.github.com/users/OpenLightingProject/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/OpenLightingProject/subscriptions","organizations_url":"https://api.github.com/users/OpenLightingProject/orgs","repos_url":"https://api.github.com/users/OpenLightingProject/repos","events_url":"https://api.github.com/users/OpenLightingProject/events{/privacy}","received_events_url":"https://api.github.com/users/OpenLightingProject/received_events","type":"Organization","site_admin":false},"repo":{"id":27968183,"name":"ja-rule","full_name":"OpenLightingProject/ja-rule","owner":{"login":"OpenLightingProject","id":6199314,"avatar_url":"https://avatars.githubusercontent.com/u/6199314?v=3","gravatar_id":"","url":"https://api.github.com/users/OpenLightingProject","html_url":"https://github.com/OpenLightingProject","followers_url":"https://api.github.com/users/OpenLightingProject/followers","following_url":"https://api.github.com/users/OpenLightingProject/following{/other_user}","gists_url":"https://api.github.com/users/OpenLightingProject/gists{/gist_id}","starred_url":"https://api.github.com/users/OpenLightingProject/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/OpenLightingProject/subscriptions","organizations_url":"https://api.github.com/users/OpenLightingProject/orgs","repos_url":"https://api.github.com/users/OpenLightingProject/repos","events_url":"https://api.github.com/users/OpenLightingProject/events{/privacy}","received_events_url":"https://api.github.com/users/OpenLightingProject/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/OpenLightingProject/ja-rule","description":"Ja Rule","fork":false,"url":"https://api.github.com/repos/OpenLightingProject/ja-rule","forks_url":"https://api.github.com/repos/OpenLightingProject/ja-rule/forks","keys_url":"https://api.github.com/repos/OpenLightingProject/ja-rule/keys{/key_id}","collaborators_url":"https://api.github.com/repos/OpenLightingProject/ja-rule/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/OpenLightingProject/ja-rule/teams","hooks_url":"https://api.github.com/repos/OpenLightingProject/ja-rule/hooks","issue_events_url":"https://api.github.com/repos/OpenLightingProject/ja-rule/issues/events{/number}","events_url":"https://api.github.com/repos/OpenLightingProject/ja-rule/events","assignees_url":"https://api.github.com/repos/OpenLightingProject/ja-rule/assignees{/user}","branches_url":"https://api.github.com/repos/OpenLightingProject/ja-rule/branches{/branch}","tags_url":"https://api.github.com/repos/OpenLightingProject/ja-rule/tags","blobs_url":"https://api.github.com/repos/OpenLightingProject/ja-rule/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/OpenLightingProject/ja-rule/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/OpenLightingProject/ja-rule/git/refs{/sha}","trees_url":"https://api.github.com/repos/OpenLightingProject/ja-rule/git/trees{/sha}","statuses_url":"https://api.github.com/repos/OpenLightingProject/ja-rule/statuses/{sha}","languages_url":"https://api.github.com/repos/OpenLightingProject/ja-rule/languages","stargazers_url":"https://api.github.com/repos/OpenLightingProject/ja-rule/stargazers","contributors_url":"https://api.github.com/repos/OpenLightingProject/ja-rule/contributors","subscribers_url":"https://api.github.com/repos/OpenLightingProject/ja-rule/subscribers","subscription_url":"https://api.github.com/repos/OpenLightingProject/ja-rule/subscription","commits_url":"https://api.github.com/repos/OpenLightingProject/ja-rule/commits{/sha}","git_commits_url":"https://api.github.com/repos/OpenLightingProject/ja-rule/git/commits{/sha}","comments_url":"https://api.github.com/repos/OpenLightingProject/ja-rule/comments{/number}","issue_comment_url":"https://api.github.com/repos/OpenLightingProject/ja-rule/issues/comments{/number}","contents_url":"https://api.github.com/repos/OpenLightingProject/ja-rule/contents/{+path}","compare_url":"https://api.github.com/repos/OpenLightingProject/ja-rule/compare/{base}...{head}","merges_url":"https://api.github.com/repos/OpenLightingProject/ja-rule/merges","archive_url":"https://api.github.com/repos/OpenLightingProject/ja-rule/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/OpenLightingProject/ja-rule/downloads","issues_url":"https://api.github.com/repos/OpenLightingProject/ja-rule/issues{/number}","pulls_url":"https://api.github.com/repos/OpenLightingProject/ja-rule/pulls{/number}","milestones_url":"https://api.github.com/repos/OpenLightingProject/ja-rule/milestones{/number}","notifications_url":"https://api.github.com/repos/OpenLightingProject/ja-rule/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/OpenLightingProject/ja-rule/labels{/name}","releases_url":"https://api.github.com/repos/OpenLightingProject/ja-rule/releases{/id}","created_at":"2014-12-13T17:36:05Z","updated_at":"2015-04-19T22:43:43Z","pushed_at":"2015-07-26T02:53:05Z","git_url":"git://github.com/OpenLightingProject/ja-rule.git","ssh_url":"git@github.com:OpenLightingProject/ja-rule.git","clone_url":"https://github.com/OpenLightingProject/ja-rule.git","svn_url":"https://github.com/OpenLightingProject/ja-rule","homepage":null,"size":1977,"stargazers_count":1,"watchers_count":1,"language":"C","has_issues":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":5,"mirror_url":null,"open_issues_count":5,"forks":5,"open_issues":5,"watchers":1,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/OpenLightingProject/ja-rule/pulls/90"},"html":{"href":"https://github.com/OpenLightingProject/ja-rule/pull/90"},"issue":{"href":"https://api.github.com/repos/OpenLightingProject/ja-rule/issues/90"},"comments":{"href":"https://api.github.com/repos/OpenLightingProject/ja-rule/issues/90/comments"},"review_comments":{"href":"https://api.github.com/repos/OpenLightingProject/ja-rule/pulls/90/comments"},"review_comment":{"href":"https://api.github.com/repos/OpenLightingProject/ja-rule/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/OpenLightingProject/ja-rule/pulls/90/commits"},"statuses":{"href":"https://api.github.com/repos/OpenLightingProject/ja-rule/statuses/4fea600301fd6b2de7d5a36d54e09614366c1c8e"}}}}