a7078af0ea545d450811b927096a90093b2fd894
[virt-mem.git] / lib / virt_mem_net_devices.ml
1 (* Memory info command for virtual domains.
2    (C) Copyright 2008 Richard W.M. Jones, Red Hat Inc.
3    http://libvirt.org/
4
5    This program is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation; either version 2 of the License, or
8    (at your option) any later version.
9
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program; if not, write to the Free Software
17    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18  *)
19
20 open Printf
21
22 open Virt_mem_gettext.Gettext
23 open Virt_mem_utils
24 open Virt_mem_types
25
26 open Kernel_net_device
27 open Kernel_net
28
29 let max_net_devices = 10000
30
31 let rec find_net_devices debug image ksymmap kernel_version =
32   if not (net_device_known kernel_version) then (
33     eprintf (f_"%s: %s: unknown kernel version
34 Try a newer version of virt-mem, or if the guest is not from a
35 supported Linux distribution, see this page about adding support:
36   http://et.redhat.com/~rjones/virt-mem/faq.html\n")
37       image.domname kernel_version;
38     image, None
39   ) else (
40     let size = net_device_size kernel_version in
41
42     (* In kernels < ~ 2.6.22, this is a simple linked list:
43      *   dev_base -> next -> next
44      * In kernels >= 2.6.23, this is a list_head:
45      *   dev_base_head -> list_head dev_list -> ...
46      *)
47     let map =
48       let { field_available = available } =
49         field_signature_of_net_device_next kernel_version in
50       if available then
51         Some map_next
52       else (
53         let { field_available = available; field_offset = offset } =
54           field_signature_of_net_device_dev_list'next kernel_version in
55         if available then
56           Some (map_dev_list offset)
57         else (
58           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;
59           None
60         )
61       ) in
62
63     match map with
64     | None -> image, None
65
66     | Some map ->
67         (* What is the starting point for iteration?  In older kernels
68          * it was the symbol 'dev_base'.  Then briefly (2.6.22-2.6.24)
69          * it became 'sruct list_head dev_base_head'.  Then when net
70          * namespaces were introduced (>= 2.6.25) it became 'struct
71          * list_head init_net.dev_base_head'.
72          *)
73         let addr =
74           try Some (Ksymmap.find "dev_base" ksymmap)
75           with Not_found ->
76             try
77               let addr = Ksymmap.find "dev_base_head" ksymmap in
78               let addr = Virt_mem_mmap.follow_pointer image.mem addr in
79               Some addr
80             with Not_found ->
81               try
82                 let addr = Ksymmap.find "init_net" ksymmap in
83                 if not (net_known kernel_version) then (
84                   eprintf (f_"%s: struct net not available in this kernel version.\n") image.domname;
85                   raise Not_found
86                 );
87                 let init_net = get_net kernel_version image.mem addr in
88                 let addr = init_net.net_dev_base_head'next in
89                 Some addr
90               with Not_found ->
91                 eprintf (f_"%s: cannot find dev_base, dev_base_head or init_net symbols in kernel image.\n") image.domname;
92                 None in
93
94         match addr with
95         | None -> image, None
96
97         | Some addr ->
98             (* Map over the structure using previously defined map function. *)
99             let image, netdevs =
100               map image kernel_version addr size (
101                 fun netdev ->
102                   { netdev_name = truncate_c_string netdev.net_device_name;
103                     netdev_dev_addr = netdev.net_device_dev_addr }
104               ) in
105
106             image, Some netdevs
107   )
108
109 (* Map dev_base_head -> list_head dev_list -> ... *)
110 and map_dev_list offset image kernel_version first_addr size f =
111   eprintf "map_dev_list: first_addr is %Lx\n" first_addr;
112
113   (* The list_head points into the middle of the structure.
114    * Adjust this address to point to the start of the
115    * structure.
116    *)
117   let addr = Int64.sub first_addr (Int64.of_int offset) in
118   eprintf "map_dev_list: after subtracting, addr is %Lx\n" addr;
119
120   let rec loop i image acc addr =
121     if i <= max_net_devices then (
122       eprintf "map_dev_list: called at %Lx\n" addr;
123       let mapped = Virt_mem_mmap.is_mapped_range image.mem addr size in
124       let image =
125         if not mapped then
126           Virt_mem_types.load_memory image addr size
127         else
128           image in
129       let dev = get_net_device kernel_version image.mem addr in
130       eprintf "map_dev_list: %Lx %S\n" addr dev.net_device_name;
131       let acc = f dev :: acc in
132       let addr = Option.get dev.net_device_dev_list'next in
133       if addr <> first_addr then
134         loop (i+1) image acc addr
135       else
136         image, acc
137     ) else
138       failwith (sprintf (f_"%s: too many network devices") image.domname);
139   in
140   loop 0 image [] addr
141
142 (* Iterate dev_base -> next -> next ... *)
143 and map_next image kernel_version addr size f =
144   let rec loop i image acc addr =
145     if i <= max_net_devices then (
146       if addr <> 0L then (
147         let mapped = Virt_mem_mmap.is_mapped_range image.mem addr size in
148         let image =
149           if not mapped then
150             Virt_mem_types.load_memory image addr size
151           else
152             image in
153         let dev = get_net_device kernel_version image.mem addr in
154         eprintf "map_next: %S\n" dev.net_device_name;
155         let acc = f dev :: acc in
156         let addr =
157           match dev.net_device_next with
158           | None -> assert false | Some addr -> addr in
159         loop (i+1) image acc addr
160       ) else
161         image, acc
162     ) else
163       failwith (sprintf (f_"%s: too many network devices") image.domname);
164   in
165   loop 0 image [] addr