Extracted kernel structures for device addressing in ifconfig.
[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     (* In kernels < ~ 2.6.22, this is a simple linked list:
41      *   dev_base -> next -> next
42      * In kernels >= 2.6.23, this is a list_head:
43      *   dev_base_head -> list_head dev_list -> ...
44      *)
45     let map =
46       let { field_available = available } =
47         field_signature_of_net_device_next kernel_version in
48       if available then
49         Some map_next
50       else (
51         let { field_available = available } =
52           field_signature_of_net_device_dev_list'next kernel_version in
53         if available then
54           Some map_dev_list
55         else (
56           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;
57           None
58         )
59       ) in
60
61     match map with
62     | None -> image, None
63
64     | Some map ->
65         (* What is the starting point for iteration?  In older kernels
66          * it was the symbol 'dev_base'.  Then briefly (2.6.22-2.6.24)
67          * it became 'struct list_head dev_base_head'.  Then when net
68          * namespaces were introduced (>= 2.6.25) it became 'struct
69          * list_head init_net.dev_base_head'.
70          *)
71         let addr =
72           try Some (Ksymmap.find "dev_base" ksymmap)
73           with Not_found ->
74             try
75               let addr = Ksymmap.find "dev_base_head" ksymmap in
76               Some addr
77             with Not_found ->
78               try
79                 let addr = Ksymmap.find "init_net" ksymmap in
80                 if not (net_known kernel_version) then (
81                   eprintf (f_"%s: struct net not available in this kernel version.\n") image.domname;
82                   raise Not_found
83                 );
84                 let { field_offset = offset } =
85                   field_signature_of_net_dev_base_head'next kernel_version in
86                 let addr = addr +^ Int64.of_int offset in
87                 Some addr
88               with Not_found ->
89                 eprintf (f_"%s: cannot find dev_base, dev_base_head or init_net symbols in kernel image.\n") image.domname;
90                 None in
91
92         match addr with
93         | None -> image, None
94
95         | Some addr ->
96             (* Map over the structure using previously defined map function. *)
97             let image, netdevs =
98               map kernel_version image addr (
99                 fun netdev ->
100                   { netdev_name = truncate_c_string netdev.net_device_name;
101                     netdev_dev_addr = netdev.net_device_dev_addr }
102               ) in
103
104             image, Some netdevs
105   )
106
107 (* Map dev_base_head -> list_head dev_list -> ... *)
108 and map_dev_list kernel_version image lh_addr f =
109   let { field_offset = offset } =
110     field_signature_of_net_device_dev_list'next kernel_version in
111   let lh = Virt_mem_list_head.create image lh_addr offset in
112
113   let size = net_device_size kernel_version in
114   let image, lh = Virt_mem_list_head.load_all lh size in
115
116   let net_devices, _ =
117     Virt_mem_list_head.fold lh ([], 0) (
118       fun (net_devices, i) addr ->
119         if i > max_net_devices then
120           failwith (sprintf (f_"%s: too many network devices") image.domname);
121
122         let net_device = get_net_device kernel_version image.mem addr in
123         let net_devices = f net_device :: net_devices in
124         (net_devices, i+1)
125     ) in
126
127   image, net_devices
128
129 (* Iterate dev_base -> next -> next ... *)
130 and map_next kernel_version image addr f =
131   let size = net_device_size kernel_version in
132
133   let rec loop i image acc addr =
134     if i <= max_net_devices then (
135       if addr <> 0L then (
136         let mapped = Virt_mem_mmap.is_mapped_range image.mem addr size in
137         let image =
138           if not mapped then
139             Virt_mem_types.load_memory image addr size
140           else
141             image in
142         let dev = get_net_device kernel_version image.mem addr in
143         let acc = f dev :: acc in
144         let addr =
145           match dev.net_device_next with
146           | None -> assert false | Some addr -> addr in
147         loop (i+1) image acc addr
148       ) else
149         image, acc
150     ) else
151       failwith (sprintf (f_"%s: too many network devices") image.domname);
152   in
153   loop 0 image [] addr