Extracted kernel structures for device addressing in ifconfig.
[virt-mem.git] / lib / virt_mem_net_devices.ml
index d792d0d..8ce8eb3 100644 (file)
@@ -24,8 +24,9 @@ open Virt_mem_utils
 open Virt_mem_types
 
 open Kernel_net_device
 open Virt_mem_types
 
 open Kernel_net_device
+open Kernel_net
 
 
-type linkage = Next | DevList of int
+let max_net_devices = 10000
 
 let rec find_net_devices debug image ksymmap kernel_version =
   if not (net_device_known kernel_version) then (
 
 let rec find_net_devices debug image ksymmap kernel_version =
   if not (net_device_known kernel_version) then (
@@ -36,100 +37,101 @@ supported Linux distribution, see this page about adding support:
       image.domname kernel_version;
     image, None
   ) else (
       image.domname kernel_version;
     image, None
   ) else (
-    let size = net_device_size kernel_version in
-
-    (* In kernels < around 2.6.22, this is a simple linked list:
+    (* In kernels < ~ 2.6.22, this is a simple linked list:
      *   dev_base -> next -> next
      * In kernels >= 2.6.23, this is a list_head:
      *   dev_base_head -> list_head dev_list -> ...
      *)
      *   dev_base -> next -> next
      * In kernels >= 2.6.23, this is a list_head:
      *   dev_base_head -> list_head dev_list -> ...
      *)
-    let linkage =
+    let map =
       let { field_available = available } =
        field_signature_of_net_device_next kernel_version in
       if available then
       let { field_available = available } =
        field_signature_of_net_device_next kernel_version in
       if available then
-       Some Next
+       Some map_next
       else (
       else (
-       let { field_available = available; field_offset = offset } =
+       let { field_available = available } =
          field_signature_of_net_device_dev_list'next kernel_version in
        if available then
          field_signature_of_net_device_dev_list'next kernel_version in
        if available then
-         Some (DevList offset)
+         Some map_dev_list
        else (
          eprintf (f_"%s: kernel net_device table is not linked through either next pointer or dev_list list_head.  Cannot read net devices.\n") image.domname;
          None
        )
       ) in
        else (
          eprintf (f_"%s: kernel net_device table is not linked through either next pointer or dev_list list_head.  Cannot read net devices.\n") image.domname;
          None
        )
       ) in
-    match linkage with
-    | None -> image, None
 
 
-    | Some Next ->
-       printf "linkage = Next\n%!";
+    match map with
+    | None -> image, None
 
 
-       (* Linkage through old-style chain of next pointers. *)
-       let dev_base =
+    | Some map ->
+       (* What is the starting point for iteration?  In older kernels
+        * it was the symbol 'dev_base'.  Then briefly (2.6.22-2.6.24)
+        * it became 'struct list_head dev_base_head'.  Then when net
+        * namespaces were introduced (>= 2.6.25) it became 'struct
+        * list_head init_net.dev_base_head'.
+        *)
+       let addr =
          try Some (Ksymmap.find "dev_base" ksymmap)
          with Not_found ->
          try Some (Ksymmap.find "dev_base" ksymmap)
          with Not_found ->
-           eprintf (f_"%s: could not find dev_base symbol in kernel image\n") image.domname;
-           None in
-       (match dev_base with
-        | None -> image, None
-        | Some dev_base ->
-            do_next image kernel_version dev_base size
-       );
-
-    | Some (DevList offset) ->
-       printf "linkage = DevList %d\n%!" offset;
-
-       (* Linkage through new-style list_head dev_list. *)
-       let dev_base_head =
-         try Some (Ksymmap.find "dev_base_head" ksymmap)
-         with Not_found ->
-           eprintf (f_"%s: could not find dev_base_head symbol in kernel image\n") image.domname;
-           None in
-       (match dev_base_head with
-        | None -> image, None
-        | Some dev_base_head ->
-            let dev_base_head =
-              Virt_mem_mmap.follow_pointer image.mem dev_base_head in
-
-            do_dev_list image kernel_version dev_base_head offset size
-       );
+           try
+             let addr = Ksymmap.find "dev_base_head" ksymmap in
+             Some addr
+           with Not_found ->
+             try
+               let addr = Ksymmap.find "init_net" ksymmap in
+               if not (net_known kernel_version) then (
+                 eprintf (f_"%s: struct net not available in this kernel version.\n") image.domname;
+                 raise Not_found
+               );
+               let { field_offset = offset } =
+                 field_signature_of_net_dev_base_head'next kernel_version in
+               let addr = addr +^ Int64.of_int offset in
+               Some addr
+             with Not_found ->
+               eprintf (f_"%s: cannot find dev_base, dev_base_head or init_net symbols in kernel image.\n") image.domname;
+               None in
+
+       match addr with
+       | None -> image, None
+
+       | Some addr ->
+           (* Map over the structure using previously defined map function. *)
+           let image, netdevs =
+             map kernel_version image addr (
+               fun netdev ->
+                 { netdev_name = truncate_c_string netdev.net_device_name;
+                   netdev_dev_addr = netdev.net_device_dev_addr }
+             ) in
+
+           image, Some netdevs
   )
 
   )
 
-(* Iterate dev_base_head -> list_head dev_list -> ... *)
-and do_dev_list image kernel_version dev_base_head offset size =
-  (* The list_head points into the middle of the structure.
-   * Adjust this address to point to the start of the
-   * structure.
-   *)
-  let addr = Int64.sub dev_base_head (Int64.of_int offset) in
-
-  printf "do_dev_list, size = %d\n" size;
-
-  let image =
-    let rec loop image addr =
-      let mapped = Virt_mem_mmap.is_mapped_range image.mem addr size in
-      let image =
-       if not mapped then
-         Virt_mem_types.load_memory image addr size
-       else
-         image in
-      let dev = get_net_device kernel_version image.mem addr in
-      printf "net_device_name = %S\n" dev.net_device_name;
-      let addr = Option.get dev.net_device_dev_list'next in
-      if addr <> dev_base_head then
-       loop image addr
-      else
-       image
-    in
-    loop image addr in
-  image, Some dev_base_head
+(* Map dev_base_head -> list_head dev_list -> ... *)
+and map_dev_list kernel_version image lh_addr f =
+  let { field_offset = offset } =
+    field_signature_of_net_device_dev_list'next kernel_version in
+  let lh = Virt_mem_list_head.create image lh_addr offset in
+
+  let size = net_device_size kernel_version in
+  let image, lh = Virt_mem_list_head.load_all lh size in
+
+  let net_devices, _ =
+    Virt_mem_list_head.fold lh ([], 0) (
+      fun (net_devices, i) addr ->
+       if i > max_net_devices then
+         failwith (sprintf (f_"%s: too many network devices") image.domname);
+
+       let net_device = get_net_device kernel_version image.mem addr in
+       let net_devices = f net_device :: net_devices in
+       (net_devices, i+1)
+    ) in
+
+  image, net_devices
 
 (* Iterate dev_base -> next -> next ... *)
 
 (* Iterate dev_base -> next -> next ... *)
-and do_next image kernel_version addr size =
-  printf "do_next, size = %d\n" size;
+and map_next kernel_version image addr f =
+  let size = net_device_size kernel_version in
 
 
-  let image =
-    let rec loop image addr =
+  let rec loop i image acc addr =
+    if i <= max_net_devices then (
       if addr <> 0L then (
        let mapped = Virt_mem_mmap.is_mapped_range image.mem addr size in
        let image =
       if addr <> 0L then (
        let mapped = Virt_mem_mmap.is_mapped_range image.mem addr size in
        let image =
@@ -138,13 +140,14 @@ and do_next image kernel_version addr size =
          else
            image in
        let dev = get_net_device kernel_version image.mem addr in
          else
            image in
        let dev = get_net_device kernel_version image.mem addr in
-      printf "net_device_name = %S\n" dev.net_device_name;
+       let acc = f dev :: acc in
        let addr =
          match dev.net_device_next with
          | None -> assert false | Some addr -> addr in
        let addr =
          match dev.net_device_next with
          | None -> assert false | Some addr -> addr in
-       loop image addr
+       loop (i+1) image acc addr
       ) else
       ) else
-       image
-    in
-    loop image addr in
-  image, Some addr
+       image, acc
+    ) else
+      failwith (sprintf (f_"%s: too many network devices") image.domname);
+  in
+  loop 0 image [] addr