diff --git a/package/system/ubus/Makefile b/package/system/ubus/Makefile
index 1ac12cf98316fab0105557a83d06d1233432915e..ba3d3a1b7791a840c0a72a6be0d316a429474e26 100644
--- a/package/system/ubus/Makefile
+++ b/package/system/ubus/Makefile
@@ -21,7 +21,7 @@ include $(INCLUDE_DIR)/cmake.mk
 define Package/ubus
   SECTION:=base
   CATEGORY:=Base system
-  DEPENDS:=+libubus +libblobmsg-json +ubusd
+  DEPENDS:=+libubus +libblobmsg-json +ubusd +libjson-validator +libjson-schema-validator
   TITLE:=OpenWrt RPC client utility
 endef
 
@@ -29,13 +29,13 @@ define Package/ubusd
   SECTION:=base
   CATEGORY:=Base system
   TITLE:=OpenWrt RPC daemon
-  DEPENDS:=+libubox +libblobmsg-json
+  DEPENDS:=+libubox +libblobmsg-json +libjson-validator +libjson-schema-validator
 endef
 
 define Package/libubus
   SECTION:=libs
   CATEGORY:=Libraries
-  DEPENDS:=+libubox
+  DEPENDS:=+libubox +libjson-validator +libjson-schema-validator
   ABI_VERSION:=$(PKG_VERSION)
   TITLE:=OpenWrt RPC client library
 endef
diff --git a/package/system/ubus/patches/0001-json-schema-validate-input_output b/package/system/ubus/patches/0001-json-schema-validate-input_output
new file mode 100644
index 0000000000000000000000000000000000000000..f13da433a67a05843c931482657055461be78f7b
--- /dev/null
+++ b/package/system/ubus/patches/0001-json-schema-validate-input_output
@@ -0,0 +1,231 @@
+diff --git a/CMakeLists.txt b/CMakeLists.txt
+index 471b38e..a6027ac 100644
+--- a/CMakeLists.txt
++++ b/CMakeLists.txt
+@@ -21,11 +21,14 @@ ELSE(BUILD_STATIC)
+   FIND_LIBRARY(blob_library NAMES blobmsg_json)
+ ENDIF(BUILD_STATIC)
+ 
++FIND_LIBRARY(validator_library NAMES json-validator)
++FIND_LIBRARY(schema_validator_library NAMES json-schema-validator)
++
+ FIND_PATH(ubox_include_dir libubox/usock.h)
+ INCLUDE_DIRECTORIES(${ubox_include_dir})
+ 
+ ADD_LIBRARY(ubus SHARED libubus.c libubus-io.c libubus-obj.c libubus-sub.c libubus-req.c libubus-acl.c)
+-TARGET_LINK_LIBRARIES(ubus ${ubox_library})
++TARGET_LINK_LIBRARIES(ubus ${ubox_library} ${blob_library} ${validator_library} ${schema_validator_library})
+ 
+ find_library(json NAMES json-c json)
+ 
+diff --git a/cli.c b/cli.c
+index 19ccbb5..550367b 100644
+--- a/cli.c
++++ b/cli.c
+@@ -87,8 +87,13 @@ static void receive_list_result(struct ubus_context *ctx, struct ubus_object_dat
+ static void receive_call_result_data(struct ubus_request *req, int type, struct blob_attr *msg)
+ {
+ 	char *str;
+-	if (!msg)
++
++	if (!msg) {
++		/* hacky way to parse output format error */
++		if (type == 12)
++			fprintf(stderr, "Command failed: %s\n", ubus_strerror(type));
+ 		return;
++	}
+ 
+ 	str = blobmsg_format_json_indent(msg, true, simple_output ? -1 : 0);
+ 	printf("%s\n", str);
+diff --git a/libubus-req.c b/libubus-req.c
+index 92f80fa..f606f4c 100644
+--- a/libubus-req.c
++++ b/libubus-req.c
+@@ -12,6 +12,11 @@
+  */
+ 
+ #include <unistd.h>
++#include <libubox/avl.h>
++#include <libubox/list.h>
++#include <libubox/blobmsg.h>
++#include <libubox/blobmsg_json.h>
++#include <json-validator.h>
+ #include "libubus.h"
+ #include "libubus-internal.h"
+ 
+@@ -21,9 +26,40 @@ struct ubus_pending_data {
+ 	struct blob_attr data[];
+ };
+ 
++static void parse_id(struct ubus_context *ctx, struct ubus_object_data *obj,
++				void *priv)
++{
++	struct {
++		const char *method;
++		struct blob_attr *msg;
++		uint32_t id;
++		bool valid;
++		enum schema_call_t type;
++	} *priv_data = priv;
++
++	if (obj->id == priv_data->id)
++		priv_data->valid = schema_validator_validate_blob(priv_data->msg,
++				obj->path, priv_data->method, priv_data->type);
++
++}
++
+ static void req_data_cb(struct ubus_request *req, int type, struct blob_attr *data)
+ {
+ 	struct blob_attr **attr;
++	struct blob_attr *attr_data;
++	int len;
++	struct {
++		const char *method;
++		struct blob_attr *msg;
++		uint32_t id;
++		bool valid;
++		enum schema_call_t type;
++	} priv_data = {
++		.method = req->method,
++		.id = req->peer,
++		.valid = true,
++		.type = SCHEMA_OUTPUT_CALL
++	};
+ 
+ 	if (req->raw_data_cb)
+ 		req->raw_data_cb(req, type, data);
+@@ -32,7 +68,26 @@ static void req_data_cb(struct ubus_request *req, int type, struct blob_attr *da
+ 		return;
+ 
+ 	attr = ubus_parse_msg(data);
+-	req->data_cb(req, type, attr[UBUS_ATTR_DATA]);
++	len = blob_raw_len(attr[UBUS_ATTR_DATA]);
++
++	attr_data = calloc(1, sizeof(struct blob_attr) + len);
++	if (!attr_data)
++		return;
++
++	memcpy(attr_data, attr[UBUS_ATTR_DATA], len);
++
++	priv_data.msg = attr_data;
++	ubus_lookup(req->ctx, NULL, parse_id, &priv_data);
++
++	if (!priv_data.valid) {
++		/* hacky way to send error code 12 */
++		req->data_cb(req, 12, NULL);
++		goto out;
++	}
++	req->data_cb(req, type, attr_data);
++
++out:
++	free(attr_data);
+ }
+ 
+ static void __ubus_process_req_data(struct ubus_request *req)
+@@ -230,12 +285,31 @@ int ubus_invoke_fd(struct ubus_context *ctx, uint32_t obj, const char *method,
+ 		   int timeout, int fd)
+ {
+ 	struct ubus_request req;
++	struct {
++		const char *method;
++		struct blob_attr *msg;
++		uint32_t id;
++		bool valid;
++		enum schema_call_t type;
++	} priv_data = {
++		.method = method,
++		.msg = msg,
++		.id = obj,
++		.valid = true,
++		.type = SCHEMA_INPUT_CALL
++	};
+ 	int rc;
+ 
++	schema_validator_init();
++	ubus_lookup(ctx, NULL, parse_id, &priv_data);
++	if (!priv_data.valid)
++		return 11;
++
+ 	rc = ubus_invoke_async_fd(ctx, obj, method, msg, &req, fd);
+ 	if (rc)
+ 		return rc;
+ 
++	req.method = method;
+ 	req.data_cb = cb;
+ 	req.priv = priv;
+ 	return ubus_complete_request(ctx, &req, timeout);
+diff --git a/libubus.c b/libubus.c
+index 9463522..1179f12 100644
+--- a/libubus.c
++++ b/libubus.c
+@@ -18,6 +18,8 @@
+ #include <libubox/blob.h>
+ #include <libubox/blobmsg.h>
+ 
++#include <json-validator.h>
++
+ #include "libubus.h"
+ #include "libubus-internal.h"
+ #include "ubusmsg.h"
+@@ -34,6 +36,8 @@ const char *__ubus_strerror[__UBUS_STATUS_LAST] = {
+ 	[UBUS_STATUS_NOT_SUPPORTED] = "Operation not supported",
+ 	[UBUS_STATUS_UNKNOWN_ERROR] = "Unknown error",
+ 	[UBUS_STATUS_CONNECTION_FAILED] = "Connection failed",
++	[UBUS_STATUS_INVALID_INPUT_FORMAT] = "Invalid input format",
++	[UBUS_STATUS_INVALID_OUTPUT_FORMAT] = "Invalid output format",
+ };
+ 
+ struct blob_buf b __hidden = {};
+@@ -354,6 +358,8 @@ struct ubus_context *ubus_connect(const char *path)
+ 		ctx = NULL;
+ 	}
+ 
++	schema_validator_init();
++
+ 	return ctx;
+ }
+ 
+@@ -365,6 +371,8 @@ void ubus_shutdown(struct ubus_context *ctx)
+ 	close(ctx->sock.fd);
+ 	uloop_timeout_cancel(&ctx->pending_timer);
+ 	free(ctx->msgbuf.data);
++
++	schema_validator_destroy();
+ }
+ 
+ void ubus_free(struct ubus_context *ctx)
+diff --git a/libubus.h b/libubus.h
+index dc42ea7..2248dad 100644
+--- a/libubus.h
++++ b/libubus.h
+@@ -204,6 +204,8 @@ struct ubus_request {
+ 	bool cancelled;
+ 	bool notify;
+ 
++	const char *method;
++
+ 	uint32_t peer;
+ 	uint16_t seq;
+ 
+@@ -284,6 +286,8 @@ void ubus_abort_request(struct ubus_context *ctx, struct ubus_request *req);
+ int ubus_lookup(struct ubus_context *ctx, const char *path,
+ 		ubus_lookup_handler_t cb, void *priv);
+ 
++int ubus_lookup_method(struct ubus_context *ctx, char **path, uint32_t id);
++
+ int ubus_lookup_id(struct ubus_context *ctx, const char *path, uint32_t *id);
+ 
+ /* make an object visible to remote connections */
+diff --git a/ubusmsg.h b/ubusmsg.h
+index 398b126..a74678d 100644
+--- a/ubusmsg.h
++++ b/ubusmsg.h
+@@ -127,6 +127,8 @@ enum ubus_msg_status {
+ 	UBUS_STATUS_NOT_SUPPORTED,
+ 	UBUS_STATUS_UNKNOWN_ERROR,
+ 	UBUS_STATUS_CONNECTION_FAILED,
++	UBUS_STATUS_INVALID_INPUT_FORMAT,
++	UBUS_STATUS_INVALID_OUTPUT_FORMAT,
+ 	__UBUS_STATUS_LAST
+ };
+