vnet: introduce vnet_error()

Decouples vnet return values from API return codes.

New vnet_error() creates vnet_error_t whicgh contains both vnet function
return value and return string.

vnet_api_error() converts vlib_error_t constructed with vnet_error() to
API return value.

Type: improvement
Change-Id: I17042954d48c010150fc1dfc5fce9330e8149e87
Signed-off-by: Damjan Marion <damarion@cisco.com>
diff --git a/src/vnet/CMakeLists.txt b/src/vnet/CMakeLists.txt
index 1b04db6..9e7e0aa 100644
--- a/src/vnet/CMakeLists.txt
+++ b/src/vnet/CMakeLists.txt
@@ -26,6 +26,7 @@
   config.c
   devices/devices.c
   devices/netlink.c
+  error.c
   flow/flow.c
   flow/flow_cli.c
   flow/flow_api.c
@@ -52,6 +53,7 @@
 
 list(APPEND VNET_HEADERS
   api_errno.h
+  error.h
   buffer.h
   config.h
   devices/devices.h
diff --git a/src/vnet/api_errno.h b/src/vnet/api_errno.h
index df3806a..4e91e13 100644
--- a/src/vnet/api_errno.h
+++ b/src/vnet/api_errno.h
@@ -18,146 +18,9 @@
 #include <stdarg.h>
 #include <vppinfra/types.h>
 #include <vppinfra/format.h>
+#include <vnet/error.h>
 
-#define foreach_vnet_api_error						\
-_(UNSPECIFIED, -1, "Unspecified Error")                                 \
-_(INVALID_SW_IF_INDEX, -2, "Invalid sw_if_index")                       \
-_(NO_SUCH_FIB, -3, "No such FIB / VRF")                                 \
-_(NO_SUCH_INNER_FIB, -4, "No such inner FIB / VRF")                     \
-_(NO_SUCH_LABEL, -5, "No such label")                                   \
-_(NO_SUCH_ENTRY, -6, "No such entry")                                   \
-_(INVALID_VALUE, -7, "Invalid value")                                   \
-_(INVALID_VALUE_2, -8, "Invalid value #2")                              \
-_(UNIMPLEMENTED, -9, "Unimplemented")                                   \
-_(INVALID_SW_IF_INDEX_2, -10, "Invalid sw_if_index #2")                 \
-_(SYSCALL_ERROR_1, -11, "System call error #1")                         \
-_(SYSCALL_ERROR_2, -12, "System call error #2")                         \
-_(SYSCALL_ERROR_3, -13, "System call error #3")                         \
-_(SYSCALL_ERROR_4, -14, "System call error #4")                         \
-_(SYSCALL_ERROR_5, -15, "System call error #5")                         \
-_(SYSCALL_ERROR_6, -16, "System call error #6")                         \
-_(SYSCALL_ERROR_7, -17, "System call error #7")                         \
-_(SYSCALL_ERROR_8, -18, "System call error #8")                         \
-_(SYSCALL_ERROR_9, -19, "System call error #9")                         \
-_(SYSCALL_ERROR_10, -20, "System call error #10")                       \
-_(FEATURE_DISABLED, -30, "Feature disabled by configuration")           \
-_(INVALID_REGISTRATION, -31, "Invalid registration")                    \
-_(NEXT_HOP_NOT_IN_FIB, -50, "Next hop not in FIB")                      \
-_(UNKNOWN_DESTINATION, -51, "Unknown destination")                      \
-_(NO_PATHS_IN_ROUTE, -52, "No paths specified in route")                \
-_(NEXT_HOP_NOT_FOUND_MP, -53, "Next hop not found (multipath)")         \
-_(NO_MATCHING_INTERFACE, -54, "No matching interface for probe")        \
-_(INVALID_VLAN, -55, "Invalid VLAN")                                    \
-_(VLAN_ALREADY_EXISTS, -56, "VLAN subif already exists")                \
-_(INVALID_SRC_ADDRESS, -57, "Invalid src address")                      \
-_(INVALID_DST_ADDRESS, -58, "Invalid dst address")                      \
-_(ADDRESS_LENGTH_MISMATCH, -59, "Address length mismatch")              \
-_(ADDRESS_NOT_FOUND_FOR_INTERFACE, -60, "Address not found for interface") \
-_(ADDRESS_NOT_DELETABLE, -61, "Address not deletable")                  \
-_(IP6_NOT_ENABLED, -62, "ip6 not enabled")				\
-_(NO_SUCH_NODE, -63, "No such graph node")				\
-_(NO_SUCH_NODE2, -64, "No such graph node #2")				\
-_(NO_SUCH_TABLE, -65, "No such table")                                  \
-_(NO_SUCH_TABLE2, -66, "No such table #2")                              \
-_(NO_SUCH_TABLE3, -67, "No such table #3")                              \
-_(SUBIF_ALREADY_EXISTS, -68, "Subinterface already exists")             \
-_(SUBIF_CREATE_FAILED, -69, "Subinterface creation failed")		\
-_(INVALID_MEMORY_SIZE, -70, "Invalid memory size requested")            \
-_(INVALID_INTERFACE, -71, "Invalid interface")                          \
-_(INVALID_VLAN_TAG_COUNT, -72, "Invalid number of tags for requested operation") \
-_(INVALID_ARGUMENT, -73, "Invalid argument")                            \
-_(UNEXPECTED_INTF_STATE, -74, "Unexpected interface state")             \
-_(TUNNEL_EXIST, -75, "Tunnel already exists")                           \
-_(INVALID_DECAP_NEXT, -76, "Invalid decap-next")			\
-_(RESPONSE_NOT_READY, -77, "Response not ready")			\
-_(NOT_CONNECTED, -78, "Not connected to the data plane")                \
-_(IF_ALREADY_EXISTS, -79, "Interface already exists")                   \
-_(BOND_SLAVE_NOT_ALLOWED, -80, "Operation not allowed on slave of BondEthernet") \
-_(VALUE_EXIST, -81, "Value already exists")                             \
-_(SAME_SRC_DST, -82, "Source and destination are the same")             \
-_(IP6_MULTICAST_ADDRESS_NOT_PRESENT, -83, "IP6 multicast address required") \
-_(SR_POLICY_NAME_NOT_PRESENT, -84, "Segment routing policy name required") \
-_(NOT_RUNNING_AS_ROOT, -85, "Not running as root") \
-_(ALREADY_CONNECTED, -86, "Connection to the data plane already exists") \
-_(UNSUPPORTED_JNI_VERSION, -87, "Unsupported JNI version") \
-_(IP_PREFIX_INVALID, -88, "IP prefix invalid (masked bits set in address") \
-_(INVALID_WORKER, -89, "Invalid worker thread")                         \
-_(LISP_DISABLED, -90, "LISP is disabled")                               \
-_(CLASSIFY_TABLE_NOT_FOUND, -91, "Classify table not found")            \
-_(INVALID_EID_TYPE, -92, "Unsupported LISP EID type")                   \
-_(CANNOT_CREATE_PCAP_FILE, -93, "Cannot create pcap file")              \
-_(INCORRECT_ADJACENCY_TYPE, -94, "Invalid adjacency type for this operation") \
-_(EXCEEDED_NUMBER_OF_RANGES_CAPACITY, -95, "Operation would exceed configured capacity of ranges") \
-_(EXCEEDED_NUMBER_OF_PORTS_CAPACITY, -96, "Operation would exceed capacity of number of ports") \
-_(INVALID_ADDRESS_FAMILY, -97, "Invalid address family")                \
-_(INVALID_SUB_SW_IF_INDEX, -98, "Invalid sub-interface sw_if_index")    \
-_(TABLE_TOO_BIG, -99, "Table too big")                                  \
-_(CANNOT_ENABLE_DISABLE_FEATURE, -100, "Cannot enable/disable feature") \
-_(BFD_EEXIST, -101, "Duplicate BFD object")                             \
-_(BFD_ENOENT, -102, "No such BFD object")                               \
-_(BFD_EINUSE, -103, "BFD object in use")                                \
-_(BFD_NOTSUPP, -104, "BFD feature not supported")                       \
-_(ADDRESS_IN_USE, -105, "Address in use")				\
-_(ADDRESS_NOT_IN_USE, -106, "Address not in use")			\
-_(QUEUE_FULL, -107, "Queue full")                                       \
-_(APP_UNSUPPORTED_CFG, -108, "Unsupported application config")		\
-_(URI_FIFO_CREATE_FAILED, -109, "URI FIFO segment create failed")       \
-_(LISP_RLOC_LOCAL, -110, "RLOC address is local")                       \
-_(BFD_EAGAIN, -111, "BFD object cannot be manipulated at this time")	\
-_(INVALID_GPE_MODE, -112, "Invalid GPE mode")                           \
-_(LISP_GPE_ENTRIES_PRESENT, -113, "LISP GPE entries are present")       \
-_(ADDRESS_FOUND_FOR_INTERFACE, -114, "Address found for interface")	\
-_(SESSION_CONNECT, -115, "Session failed to connect")              	\
-_(ENTRY_ALREADY_EXISTS, -116, "Entry already exists")			\
-_(SVM_SEGMENT_CREATE_FAIL, -117, "Svm segment create fail")		\
-_(APPLICATION_NOT_ATTACHED, -118, "Application not attached")           \
-_(BD_ALREADY_EXISTS, -119, "Bridge domain already exists")              \
-_(BD_IN_USE, -120, "Bridge domain has member interfaces")		\
-_(BD_NOT_MODIFIABLE, -121, "Bridge domain 0 can't be deleted/modified") \
-_(BD_ID_EXCEED_MAX, -122, "Bridge domain ID exceeds 16M limit")		\
-_(SUBIF_DOESNT_EXIST, -123, "Subinterface doesn't exist")               \
-_(L2_MACS_EVENT_CLINET_PRESENT, -124, "Client already exist for L2 MACs events") \
-_(INVALID_QUEUE, -125, "Invalid queue")                 		\
-_(UNSUPPORTED, -126, "Unsupported")					\
-_(DUPLICATE_IF_ADDRESS, -127, "Address already present on another interface")	\
-_(APP_INVALID_NS, -128, "Invalid application namespace")			\
-_(APP_WRONG_NS_SECRET, -129, "Wrong app namespace secret")		\
-_(APP_CONNECT_SCOPE, -130, "Connect scope")				\
-_(APP_ALREADY_ATTACHED, -131, "App already attached")			\
-_(SESSION_REDIRECT, -132, "Redirect failed")				\
-_(ILLEGAL_NAME, -133, "Illegal name")					\
-_(NO_NAME_SERVERS, -134, "No name servers configured")			\
-_(NAME_SERVER_NOT_FOUND, -135, "Name server not found")			\
-_(NAME_RESOLUTION_NOT_ENABLED, -136, "Name resolution not enabled")	\
-_(NAME_SERVER_FORMAT_ERROR, -137, "Server format error (bug!)")		\
-_(NAME_SERVER_NO_SUCH_NAME, -138, "No such name")                       \
-_(NAME_SERVER_NO_ADDRESSES, -139, "No addresses available")		\
-_(NAME_SERVER_NEXT_SERVER, -140, "Retry with new server")		\
-_(APP_CONNECT_FILTERED, -141, "Connect was filtered")			\
-_(ACL_IN_USE_INBOUND, -142, "Inbound ACL in use")			\
-_(ACL_IN_USE_OUTBOUND, -143, "Outbound ACL in use")			\
-_(INIT_FAILED, -144, "Initialization Failed")				\
-_(NETLINK_ERROR, -145, "Netlink error")                                 \
-_(BIER_BSL_UNSUP, -146, "BIER bit-string-length unsupported")		\
-_(INSTANCE_IN_USE, -147, "Instance in use")				\
-_(INVALID_SESSION_ID, -148, "Session ID out of range")			\
-_(ACL_IN_USE_BY_LOOKUP_CONTEXT, -149, "ACL in use by a lookup context")	\
-_(INVALID_VALUE_3, -150, "Invalid value #3")                            \
-_(NON_ETHERNET, -151, "Interface is not an Ethernet interface")         \
-_(BD_ALREADY_HAS_BVI, -152, "Bridge domain already has a BVI interface") \
-_(INVALID_PROTOCOL, -153, "Invalid Protocol")                           \
-_(INVALID_ALGORITHM, -154, "Invalid Algorithm")                         \
-_(RSRC_IN_USE, -155, "Resource In Use")                                 \
-_(KEY_LENGTH, -156, "invalid Key Length")                               \
-_(FIB_PATH_UNSUPPORTED_NH_PROTO, -157, "Unsupported FIB Path protocol") \
-_(API_ENDIAN_FAILED, -159, "Endian mismatch detected")			\
-_(NO_CHANGE, -160, "No change in table")				\
-_(MISSING_CERT_KEY, -161, "Missing certifcate or key")                  \
-_(LIMIT_EXCEEDED, -162, "limit exceeded")                               \
-_(IKE_NO_PORT, -163, "port not managed by IKE")                         \
-_(UDP_PORT_TAKEN, -164, "UDP port already taken")                       \
-_(EAGAIN, -165, "Retry stream call with cursor")                        \
-_(INVALID_VALUE_4, -166, "Invalid value #4")                           	\
+#define foreach_vnet_api_error foreach_vnet_error
 
 typedef enum
 {
@@ -167,29 +30,15 @@
     VNET_API_N_ERROR,
 } vnet_api_error_t;
 
-/* *INDENT-OFF* */
-static inline u8 *
-format_vnet_api_errno (u8 * s, va_list * args)
+format_function_t format_vnet_api_errno;
+
+static_always_inline vnet_api_error_t
+vnet_api_error (clib_error_t *err)
 {
-  vnet_api_error_t api_error = va_arg (*args, vnet_api_error_t);
-#ifdef _
-#undef _
-#endif
-#define _(a, b, c)           \
-  case b:                    \
-    s = format (s, "%s", c); \
-    break;
-  switch (api_error)
-    {
-      foreach_vnet_api_error
-      default:
-       	s = format (s, "UNKNOWN");
-        break;
-    }
-  return s;
-#undef _
+  if (err->code >= 0)
+    return VNET_API_ERROR_BUG;
+  return err->code;
 }
-/* *INDENT-ON* */
 
 #endif /* included_vnet_api_errno_h */
 
diff --git a/src/vnet/error.c b/src/vnet/error.c
new file mode 100644
index 0000000..473d111
--- /dev/null
+++ b/src/vnet/error.c
@@ -0,0 +1,54 @@
+/* SPDX-License-Identifier: Apache-2.0
+ * Copyright(c) 2022 Cisco Systems, Inc.
+ */
+
+#include <vppinfra/error.h>
+#include <vnet/api_errno.h>
+
+static char *error_strings[] = {
+#define _(a, b, c) [-(b)] = c,
+  foreach_vnet_error
+#undef _
+};
+
+clib_error_t *
+vnet_error (vnet_error_t rv, char *fmt, ...)
+{
+  clib_error_t *e, *err = 0;
+  va_list va;
+  vec_add2 (err, e, 1);
+  e->what = format (e->what, "%s", error_strings[-rv]);
+
+  if (fmt)
+    {
+      vec_add1 (e->what, ' ');
+      vec_add1 (e->what, '(');
+      va_start (va, fmt);
+      e->what = va_format (e->what, fmt, &va);
+      vec_add1 (e->what, ')');
+      va_end (va);
+    }
+
+  e->code = rv;
+  return err;
+}
+
+u8 *
+format_vnet_api_errno (u8 *s, va_list *args)
+{
+  vnet_api_error_t api_error = va_arg (*args, vnet_api_error_t);
+#ifdef _
+#undef _
+#endif
+#define _(a, b, c)                                                            \
+  case b:                                                                     \
+    s = format (s, "%s", c);                                                  \
+    break;
+  switch (api_error)
+    {
+      foreach_vnet_error default : s = format (s, "UNKNOWN");
+      break;
+    }
+  return s;
+#undef _
+}
diff --git a/src/vnet/error.h b/src/vnet/error.h
new file mode 100644
index 0000000..39a609b
--- /dev/null
+++ b/src/vnet/error.h
@@ -0,0 +1,174 @@
+/* SPDX-License-Identifier: Apache-2.0
+ * Copyright(c) 2022 Cisco Systems, Inc.
+ */
+#ifndef included_vnet_error_h
+#define included_vnet_error_h
+
+#include <stdarg.h>
+#include <vppinfra/types.h>
+#include <vppinfra/format.h>
+
+#define foreach_vnet_error                                                    \
+  _ (UNSPECIFIED, -1, "Unspecified Error")                                    \
+  _ (INVALID_SW_IF_INDEX, -2, "Invalid sw_if_index")                          \
+  _ (NO_SUCH_FIB, -3, "No such FIB / VRF")                                    \
+  _ (NO_SUCH_INNER_FIB, -4, "No such inner FIB / VRF")                        \
+  _ (NO_SUCH_LABEL, -5, "No such label")                                      \
+  _ (NO_SUCH_ENTRY, -6, "No such entry")                                      \
+  _ (INVALID_VALUE, -7, "Invalid value")                                      \
+  _ (INVALID_VALUE_2, -8, "Invalid value #2")                                 \
+  _ (UNIMPLEMENTED, -9, "Unimplemented")                                      \
+  _ (INVALID_SW_IF_INDEX_2, -10, "Invalid sw_if_index #2")                    \
+  _ (SYSCALL_ERROR_1, -11, "System call error #1")                            \
+  _ (SYSCALL_ERROR_2, -12, "System call error #2")                            \
+  _ (SYSCALL_ERROR_3, -13, "System call error #3")                            \
+  _ (SYSCALL_ERROR_4, -14, "System call error #4")                            \
+  _ (SYSCALL_ERROR_5, -15, "System call error #5")                            \
+  _ (SYSCALL_ERROR_6, -16, "System call error #6")                            \
+  _ (SYSCALL_ERROR_7, -17, "System call error #7")                            \
+  _ (SYSCALL_ERROR_8, -18, "System call error #8")                            \
+  _ (SYSCALL_ERROR_9, -19, "System call error #9")                            \
+  _ (SYSCALL_ERROR_10, -20, "System call error #10")                          \
+  _ (FEATURE_DISABLED, -30, "Feature disabled by configuration")              \
+  _ (INVALID_REGISTRATION, -31, "Invalid registration")                       \
+  _ (NEXT_HOP_NOT_IN_FIB, -50, "Next hop not in FIB")                         \
+  _ (UNKNOWN_DESTINATION, -51, "Unknown destination")                         \
+  _ (NO_PATHS_IN_ROUTE, -52, "No paths specified in route")                   \
+  _ (NEXT_HOP_NOT_FOUND_MP, -53, "Next hop not found (multipath)")            \
+  _ (NO_MATCHING_INTERFACE, -54, "No matching interface for probe")           \
+  _ (INVALID_VLAN, -55, "Invalid VLAN")                                       \
+  _ (VLAN_ALREADY_EXISTS, -56, "VLAN subif already exists")                   \
+  _ (INVALID_SRC_ADDRESS, -57, "Invalid src address")                         \
+  _ (INVALID_DST_ADDRESS, -58, "Invalid dst address")                         \
+  _ (ADDRESS_LENGTH_MISMATCH, -59, "Address length mismatch")                 \
+  _ (ADDRESS_NOT_FOUND_FOR_INTERFACE, -60, "Address not found for interface") \
+  _ (ADDRESS_NOT_DELETABLE, -61, "Address not deletable")                     \
+  _ (IP6_NOT_ENABLED, -62, "ip6 not enabled")                                 \
+  _ (NO_SUCH_NODE, -63, "No such graph node")                                 \
+  _ (NO_SUCH_NODE2, -64, "No such graph node #2")                             \
+  _ (NO_SUCH_TABLE, -65, "No such table")                                     \
+  _ (NO_SUCH_TABLE2, -66, "No such table #2")                                 \
+  _ (NO_SUCH_TABLE3, -67, "No such table #3")                                 \
+  _ (SUBIF_ALREADY_EXISTS, -68, "Subinterface already exists")                \
+  _ (SUBIF_CREATE_FAILED, -69, "Subinterface creation failed")                \
+  _ (INVALID_MEMORY_SIZE, -70, "Invalid memory size requested")               \
+  _ (INVALID_INTERFACE, -71, "Invalid interface")                             \
+  _ (INVALID_VLAN_TAG_COUNT, -72,                                             \
+     "Invalid number of tags for requested operation")                        \
+  _ (INVALID_ARGUMENT, -73, "Invalid argument")                               \
+  _ (UNEXPECTED_INTF_STATE, -74, "Unexpected interface state")                \
+  _ (TUNNEL_EXIST, -75, "Tunnel already exists")                              \
+  _ (INVALID_DECAP_NEXT, -76, "Invalid decap-next")                           \
+  _ (RESPONSE_NOT_READY, -77, "Response not ready")                           \
+  _ (NOT_CONNECTED, -78, "Not connected to the data plane")                   \
+  _ (IF_ALREADY_EXISTS, -79, "Interface already exists")                      \
+  _ (BOND_SLAVE_NOT_ALLOWED, -80,                                             \
+     "Operation not allowed on slave of BondEthernet")                        \
+  _ (VALUE_EXIST, -81, "Value already exists")                                \
+  _ (SAME_SRC_DST, -82, "Source and destination are the same")                \
+  _ (IP6_MULTICAST_ADDRESS_NOT_PRESENT, -83,                                  \
+     "IP6 multicast address required")                                        \
+  _ (SR_POLICY_NAME_NOT_PRESENT, -84, "Segment routing policy name required") \
+  _ (NOT_RUNNING_AS_ROOT, -85, "Not running as root")                         \
+  _ (ALREADY_CONNECTED, -86, "Connection to the data plane already exists")   \
+  _ (UNSUPPORTED_JNI_VERSION, -87, "Unsupported JNI version")                 \
+  _ (IP_PREFIX_INVALID, -88, "IP prefix invalid (masked bits set in address") \
+  _ (INVALID_WORKER, -89, "Invalid worker thread")                            \
+  _ (LISP_DISABLED, -90, "LISP is disabled")                                  \
+  _ (CLASSIFY_TABLE_NOT_FOUND, -91, "Classify table not found")               \
+  _ (INVALID_EID_TYPE, -92, "Unsupported LISP EID type")                      \
+  _ (CANNOT_CREATE_PCAP_FILE, -93, "Cannot create pcap file")                 \
+  _ (INCORRECT_ADJACENCY_TYPE, -94,                                           \
+     "Invalid adjacency type for this operation")                             \
+  _ (EXCEEDED_NUMBER_OF_RANGES_CAPACITY, -95,                                 \
+     "Operation would exceed configured capacity of ranges")                  \
+  _ (EXCEEDED_NUMBER_OF_PORTS_CAPACITY, -96,                                  \
+     "Operation would exceed capacity of number of ports")                    \
+  _ (INVALID_ADDRESS_FAMILY, -97, "Invalid address family")                   \
+  _ (INVALID_SUB_SW_IF_INDEX, -98, "Invalid sub-interface sw_if_index")       \
+  _ (TABLE_TOO_BIG, -99, "Table too big")                                     \
+  _ (CANNOT_ENABLE_DISABLE_FEATURE, -100, "Cannot enable/disable feature")    \
+  _ (BFD_EEXIST, -101, "Duplicate BFD object")                                \
+  _ (BFD_ENOENT, -102, "No such BFD object")                                  \
+  _ (BFD_EINUSE, -103, "BFD object in use")                                   \
+  _ (BFD_NOTSUPP, -104, "BFD feature not supported")                          \
+  _ (ADDRESS_IN_USE, -105, "Address in use")                                  \
+  _ (ADDRESS_NOT_IN_USE, -106, "Address not in use")                          \
+  _ (QUEUE_FULL, -107, "Queue full")                                          \
+  _ (APP_UNSUPPORTED_CFG, -108, "Unsupported application config")             \
+  _ (URI_FIFO_CREATE_FAILED, -109, "URI FIFO segment create failed")          \
+  _ (LISP_RLOC_LOCAL, -110, "RLOC address is local")                          \
+  _ (BFD_EAGAIN, -111, "BFD object cannot be manipulated at this time")       \
+  _ (INVALID_GPE_MODE, -112, "Invalid GPE mode")                              \
+  _ (LISP_GPE_ENTRIES_PRESENT, -113, "LISP GPE entries are present")          \
+  _ (ADDRESS_FOUND_FOR_INTERFACE, -114, "Address found for interface")        \
+  _ (SESSION_CONNECT, -115, "Session failed to connect")                      \
+  _ (ENTRY_ALREADY_EXISTS, -116, "Entry already exists")                      \
+  _ (SVM_SEGMENT_CREATE_FAIL, -117, "Svm segment create fail")                \
+  _ (APPLICATION_NOT_ATTACHED, -118, "Application not attached")              \
+  _ (BD_ALREADY_EXISTS, -119, "Bridge domain already exists")                 \
+  _ (BD_IN_USE, -120, "Bridge domain has member interfaces")                  \
+  _ (BD_NOT_MODIFIABLE, -121, "Bridge domain 0 can't be deleted/modified")    \
+  _ (BD_ID_EXCEED_MAX, -122, "Bridge domain ID exceeds 16M limit")            \
+  _ (SUBIF_DOESNT_EXIST, -123, "Subinterface doesn't exist")                  \
+  _ (L2_MACS_EVENT_CLINET_PRESENT, -124,                                      \
+     "Client already exist for L2 MACs events")                               \
+  _ (INVALID_QUEUE, -125, "Invalid queue")                                    \
+  _ (UNSUPPORTED, -126, "Unsupported")                                        \
+  _ (DUPLICATE_IF_ADDRESS, -127,                                              \
+     "Address already present on another interface")                          \
+  _ (APP_INVALID_NS, -128, "Invalid application namespace")                   \
+  _ (APP_WRONG_NS_SECRET, -129, "Wrong app namespace secret")                 \
+  _ (APP_CONNECT_SCOPE, -130, "Connect scope")                                \
+  _ (APP_ALREADY_ATTACHED, -131, "App already attached")                      \
+  _ (SESSION_REDIRECT, -132, "Redirect failed")                               \
+  _ (ILLEGAL_NAME, -133, "Illegal name")                                      \
+  _ (NO_NAME_SERVERS, -134, "No name servers configured")                     \
+  _ (NAME_SERVER_NOT_FOUND, -135, "Name server not found")                    \
+  _ (NAME_RESOLUTION_NOT_ENABLED, -136, "Name resolution not enabled")        \
+  _ (NAME_SERVER_FORMAT_ERROR, -137, "Server format error (bug!)")            \
+  _ (NAME_SERVER_NO_SUCH_NAME, -138, "No such name")                          \
+  _ (NAME_SERVER_NO_ADDRESSES, -139, "No addresses available")                \
+  _ (NAME_SERVER_NEXT_SERVER, -140, "Retry with new server")                  \
+  _ (APP_CONNECT_FILTERED, -141, "Connect was filtered")                      \
+  _ (ACL_IN_USE_INBOUND, -142, "Inbound ACL in use")                          \
+  _ (ACL_IN_USE_OUTBOUND, -143, "Outbound ACL in use")                        \
+  _ (INIT_FAILED, -144, "Initialization Failed")                              \
+  _ (NETLINK_ERROR, -145, "Netlink error")                                    \
+  _ (BIER_BSL_UNSUP, -146, "BIER bit-string-length unsupported")              \
+  _ (INSTANCE_IN_USE, -147, "Instance in use")                                \
+  _ (INVALID_SESSION_ID, -148, "Session ID out of range")                     \
+  _ (ACL_IN_USE_BY_LOOKUP_CONTEXT, -149, "ACL in use by a lookup context")    \
+  _ (INVALID_VALUE_3, -150, "Invalid value #3")                               \
+  _ (NON_ETHERNET, -151, "Interface is not an Ethernet interface")            \
+  _ (BD_ALREADY_HAS_BVI, -152, "Bridge domain already has a BVI interface")   \
+  _ (INVALID_PROTOCOL, -153, "Invalid Protocol")                              \
+  _ (INVALID_ALGORITHM, -154, "Invalid Algorithm")                            \
+  _ (RSRC_IN_USE, -155, "Resource In Use")                                    \
+  _ (KEY_LENGTH, -156, "invalid Key Length")                                  \
+  _ (FIB_PATH_UNSUPPORTED_NH_PROTO, -157, "Unsupported FIB Path protocol")    \
+  _ (API_ENDIAN_FAILED, -159, "Endian mismatch detected")                     \
+  _ (NO_CHANGE, -160, "No change in table")                                   \
+  _ (MISSING_CERT_KEY, -161, "Missing certifcate or key")                     \
+  _ (LIMIT_EXCEEDED, -162, "limit exceeded")                                  \
+  _ (IKE_NO_PORT, -163, "port not managed by IKE")                            \
+  _ (UDP_PORT_TAKEN, -164, "UDP port already taken")                          \
+  _ (EAGAIN, -165, "Retry stream call with cursor")                           \
+  _ (INVALID_VALUE_4, -166, "Invalid value #4")                               \
+  _ (BUSY, -167, "Busy")                                                      \
+  _ (BUG, -168, "Bug")
+
+typedef enum
+{
+#define _(a, b, c) VNET_ERR_##a = (b),
+  foreach_vnet_error
+#undef _
+    VNET_N_ERROR,
+} vnet_error_t;
+
+clib_error_t __clib_warn_unused_result *vnet_error (vnet_error_t code,
+						    char *fmt, ...);
+
+format_function_t format_vnet_api_errno;
+
+#endif
diff --git a/src/vnet/interface.c b/src/vnet/interface.c
index 982abbd..05a1c7c 100644
--- a/src/vnet/interface.c
+++ b/src/vnet/interface.c
@@ -768,18 +768,25 @@
   return WALK_CONTINUE;
 }
 
-void
-vnet_hw_interface_set_mtu (vnet_main_t * vnm, u32 hw_if_index, u32 mtu)
+clib_error_t *
+vnet_hw_interface_set_mtu (vnet_main_t *vnm, u32 hw_if_index, u32 mtu)
 {
   vnet_hw_interface_t *hi = vnet_get_hw_interface (vnm, hw_if_index);
 
   if (hi->max_packet_bytes != mtu)
     {
+      if (mtu > hi->max_supported_packet_bytes ||
+	  mtu < hi->min_supported_packet_bytes)
+	return vnet_error (VNET_ERR_INVALID_VALUE,
+			   "requested mtu must be in the %u to %u range",
+			   hi->min_supported_packet_bytes,
+			   hi->max_supported_packet_bytes);
       hi->max_packet_bytes = mtu;
       ethernet_set_flags (vnm, hw_if_index, ETHERNET_INTERFACE_FLAG_MTU);
       vnet_hw_interface_walk_sw (vnm, hw_if_index, sw_interface_walk_callback,
 				 &mtu);
     }
+  return 0;
 }
 
 static void
diff --git a/src/vnet/interface_api.c b/src/vnet/interface_api.c
index 5218f74..d70cd1e 100644
--- a/src/vnet/interface_api.c
+++ b/src/vnet/interface_api.c
@@ -146,6 +146,7 @@
   u32 sw_if_index = ntohl (mp->sw_if_index);
   u16 mtu = ntohs (mp->mtu);
   ethernet_main_t *em = &ethernet_main;
+  clib_error_t *err;
   int rv = 0;
 
   VALIDATE_SW_IF_INDEX (mp);
@@ -157,7 +158,6 @@
       goto bad_sw_if_index;
     }
 
-  vnet_hw_interface_t *hi = vnet_get_hw_interface (vnm, si->hw_if_index);
   ethernet_interface_t *eif = ethernet_get_interface (em, si->hw_if_index);
 
   if (!eif)
@@ -166,20 +166,13 @@
       goto bad_sw_if_index;
     }
 
-  if (mtu < hi->min_supported_packet_bytes)
+  if ((err = vnet_hw_interface_set_mtu (vnm, si->hw_if_index, mtu)))
     {
-      rv = VNET_API_ERROR_INVALID_VALUE;
+      rv = vnet_api_error (err);
+      clib_error_free (err);
       goto bad_sw_if_index;
     }
 
-  if (mtu > hi->max_supported_packet_bytes)
-    {
-      rv = VNET_API_ERROR_INVALID_VALUE;
-      goto bad_sw_if_index;
-    }
-
-  vnet_hw_interface_set_mtu (vnm, si->hw_if_index, mtu);
-
   BAD_SW_IF_INDEX_LABEL;
   REPLY_MACRO (VL_API_HW_INTERFACE_SET_MTU_REPLY);
 }
diff --git a/src/vnet/interface_cli.c b/src/vnet/interface_cli.c
index d2e748a..740a8af 100644
--- a/src/vnet/interface_cli.c
+++ b/src/vnet/interface_cli.c
@@ -1158,6 +1158,7 @@
   u32 hw_if_index, sw_if_index, mtu;
   ethernet_main_t *em = &ethernet_main;
   u32 mtus[VNET_N_MTU] = { 0, 0, 0, 0 };
+  clib_error_t *err;
 
   if (unformat (input, "%d %U", &mtu,
 		unformat_vnet_hw_interface, vnm, &hw_if_index))
@@ -1181,7 +1182,9 @@
 	return clib_error_return (0, "Invalid mtu (%d): must be <= (%d)", mtu,
 				  hi->max_supported_packet_bytes);
 
-      vnet_hw_interface_set_mtu (vnm, hw_if_index, mtu);
+      err = vnet_hw_interface_set_mtu (vnm, hw_if_index, mtu);
+      if (err)
+	return err;
       goto done;
     }
   else if (unformat (input, "packet %d %U", &mtu,
diff --git a/src/vnet/interface_funcs.h b/src/vnet/interface_funcs.h
index 7dfa617..57e0d33 100644
--- a/src/vnet/interface_funcs.h
+++ b/src/vnet/interface_funcs.h
@@ -431,7 +431,8 @@
 int set_hw_interface_tx_queue (u32 hw_if_index, u32 queue_id, uword *bitmap);
 
 /* Set the MTU on the HW interface */
-void vnet_hw_interface_set_mtu (vnet_main_t * vnm, u32 hw_if_index, u32 mtu);
+clib_error_t *vnet_hw_interface_set_mtu (vnet_main_t *vnm, u32 hw_if_index,
+					 u32 mtu);
 
 /* Set the MTU on the SW interface */
 void vnet_sw_interface_set_mtu (vnet_main_t * vnm, u32 sw_if_index, u32 mtu);
diff --git a/src/vnet/vnet.h b/src/vnet/vnet.h
index 24afe63..227fa5b 100644
--- a/src/vnet/vnet.h
+++ b/src/vnet/vnet.h
@@ -45,6 +45,7 @@
 #include <vppinfra/types.h>
 
 #include <vppinfra/pcap.h>
+#include <vnet/error.h>
 #include <vnet/buffer.h>
 #include <vnet/config.h>
 #include <vnet/interface.h>