|      **bpftool** **map** { **show** | **list** }   [*MAP*]
 |      **bpftool** **map create**     *FILE* **type** *TYPE* **key** *KEY_SIZE* **value** *VALUE_SIZE* \
-|              **entries** *MAX_ENTRIES* **name** *NAME* [**flags** *FLAGS*] [**dev** *NAME*]
+|              **entries** *MAX_ENTRIES* **name** *NAME* [**flags** *FLAGS*] [**inner_map** *MAP*] \
+|              [**dev** *NAME*]
 |      **bpftool** **map dump**       *MAP*
 |      **bpftool** **map update**     *MAP* [**key** *DATA*] [**value** *VALUE*] [*UPDATE_FLAGS*]
 |      **bpftool** **map lookup**     *MAP* [**key** *DATA*]
                  maps. On such kernels bpftool will automatically emit this
                  information as well.
 
-       **bpftool map create** *FILE* **type** *TYPE* **key** *KEY_SIZE* **value** *VALUE_SIZE*  **entries** *MAX_ENTRIES* **name** *NAME* [**flags** *FLAGS*] [**dev** *NAME*]
+       **bpftool map create** *FILE* **type** *TYPE* **key** *KEY_SIZE* **value** *VALUE_SIZE*  **entries** *MAX_ENTRIES* **name** *NAME* [**flags** *FLAGS*] [**inner_map** *MAP*] [**dev** *NAME*]
                  Create a new map with given parameters and pin it to *bpffs*
                  as *FILE*.
 
                  desired flags, e.g. 1024 for **BPF_F_MMAPABLE** (see bpf.h
                  UAPI header for existing flags).
 
+                 To create maps of type array-of-maps or hash-of-maps, the
+                 **inner_map** keyword must be used to pass an inner map. The
+                 kernel needs it to collect metadata related to the inner maps
+                 that the new map will work with.
+
                  Keyword **dev** expects a network interface name, and is used
                  to request hardware offload for the map.
 
 
 {
        struct bpf_create_map_attr attr = { NULL, };
        const char *pinfile;
-       int err, fd;
+       int err = -1, fd;
 
        if (!REQ_ARGS(7))
                return -1;
 
                        if (attr.map_type) {
                                p_err("map type already specified");
-                               return -1;
+                               goto exit;
                        }
 
                        attr.map_type = map_type_from_str(*argv);
                        if ((int)attr.map_type < 0) {
                                p_err("unrecognized map type: %s", *argv);
-                               return -1;
+                               goto exit;
                        }
                        NEXT_ARG();
                } else if (is_prefix(*argv, "name")) {
                } else if (is_prefix(*argv, "key")) {
                        if (parse_u32_arg(&argc, &argv, &attr.key_size,
                                          "key size"))
-                               return -1;
+                               goto exit;
                } else if (is_prefix(*argv, "value")) {
                        if (parse_u32_arg(&argc, &argv, &attr.value_size,
                                          "value size"))
-                               return -1;
+                               goto exit;
                } else if (is_prefix(*argv, "entries")) {
                        if (parse_u32_arg(&argc, &argv, &attr.max_entries,
                                          "max entries"))
-                               return -1;
+                               goto exit;
                } else if (is_prefix(*argv, "flags")) {
                        if (parse_u32_arg(&argc, &argv, &attr.map_flags,
                                          "flags"))
-                               return -1;
+                               goto exit;
                } else if (is_prefix(*argv, "dev")) {
                        NEXT_ARG();
 
                        if (attr.map_ifindex) {
                                p_err("offload device already specified");
-                               return -1;
+                               goto exit;
                        }
 
                        attr.map_ifindex = if_nametoindex(*argv);
                        if (!attr.map_ifindex) {
                                p_err("unrecognized netdevice '%s': %s",
                                      *argv, strerror(errno));
-                               return -1;
+                               goto exit;
                        }
                        NEXT_ARG();
+               } else if (is_prefix(*argv, "inner_map")) {
+                       struct bpf_map_info info = {};
+                       __u32 len = sizeof(info);
+                       int inner_map_fd;
+
+                       NEXT_ARG();
+                       if (!REQ_ARGS(2))
+                               usage();
+                       inner_map_fd = map_parse_fd_and_info(&argc, &argv,
+                                                            &info, &len);
+                       if (inner_map_fd < 0)
+                               return -1;
+                       attr.inner_map_fd = inner_map_fd;
                } else {
                        p_err("unknown arg %s", *argv);
-                       return -1;
+                       goto exit;
                }
        }
 
        if (!attr.name) {
                p_err("map name not specified");
-               return -1;
+               goto exit;
        }
 
        set_max_rlimit();
        fd = bpf_create_map_xattr(&attr);
        if (fd < 0) {
                p_err("map create failed: %s", strerror(errno));
-               return -1;
+               goto exit;
        }
 
        err = do_pin_fd(fd, pinfile);
        close(fd);
        if (err)
-               return err;
+               goto exit;
 
        if (json_output)
                jsonw_null(json_wtr);
-       return 0;
+
+exit:
+       if (attr.inner_map_fd > 0)
+               close(attr.inner_map_fd);
+
+       return err;
 }
 
 static int do_pop_dequeue(int argc, char **argv)
                "Usage: %1$s %2$s { show | list }   [MAP]\n"
                "       %1$s %2$s create     FILE type TYPE key KEY_SIZE value VALUE_SIZE \\\n"
                "                                  entries MAX_ENTRIES name NAME [flags FLAGS] \\\n"
-               "                                  [dev NAME]\n"
+               "                                  [inner_map MAP] [dev NAME]\n"
                "       %1$s %2$s dump       MAP\n"
                "       %1$s %2$s update     MAP [key DATA] [value VALUE] [UPDATE_FLAGS]\n"
                "       %1$s %2$s lookup     MAP [key DATA]\n"