]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/libkvm/kvm_vnet.c
Add UPDATING entries and bump version.
[FreeBSD/FreeBSD.git] / lib / libkvm / kvm_vnet.c
1 /*-
2  * Copyright (c) 2009 Robert N. M. Watson
3  * Copyright (c) 2009 Bjoern A. Zeeb <bz@FreeBSD.org>
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  */
27
28 #include <sys/cdefs.h>
29 __FBSDID("$FreeBSD$");
30
31 #include <sys/param.h>
32
33 #define _WANT_PRISON
34 #define _WANT_UCRED
35 #define _WANT_VNET
36
37 #include <sys/_lock.h>
38 #include <sys/_mutex.h>
39 #include <sys/_task.h>
40 #include <sys/jail.h>
41 #include <sys/proc.h>
42 #include <sys/types.h>
43
44 #include <net/vnet.h>
45
46 #include <kvm.h>
47 #include <limits.h>
48 #include <stdlib.h>
49 #include <unistd.h>
50
51 #include "kvm_private.h"
52
53 /*
54  * Set up libkvm to handle virtual network stack symbols by selecting a
55  * starting pid.
56  */
57 int
58 _kvm_vnet_selectpid(kvm_t *kd, pid_t pid)
59 {
60         struct proc proc;
61         struct ucred cred;
62         struct prison prison;
63         struct vnet vnet;
64         struct kvm_nlist nl[] = {
65                 /*
66                  * Note: kvm_nlist strips the first '_' so add an extra one
67                  * here to __{start,stop}_set_vnet.
68                  */
69 #define NLIST_START_VNET        0
70                 { .n_name = "___start_" VNET_SETNAME },
71 #define NLIST_STOP_VNET         1
72                 { .n_name = "___stop_" VNET_SETNAME },
73 #define NLIST_VNET_HEAD         2
74                 { .n_name = "vnet_head" },
75 #define NLIST_ALLPROC           3
76                 { .n_name = "allproc" },
77 #define NLIST_DUMPTID           4
78                 { .n_name = "dumptid" },
79 #define NLIST_PROC0             5
80                 { .n_name = "proc0" },
81                 { .n_name = NULL },
82         };
83         uintptr_t procp, credp;
84 #define VMCORE_VNET_OF_PROC0
85 #ifndef VMCORE_VNET_OF_PROC0
86         struct thread td;
87         uintptr_t tdp;
88 #endif
89         lwpid_t dumptid;
90
91         /*
92          * XXX: This only works for native kernels for now.
93          */
94         if (!kvm_native(kd))
95                 return (-1);
96
97         /*
98          * Locate and cache locations of important symbols
99          * using the internal version of _kvm_nlist, turning
100          * off initialization to avoid recursion in case of
101          * unresolveable symbols.
102          */
103         if (_kvm_nlist(kd, nl, 0) != 0) {
104                 /*
105                  * XXX-BZ: ___start_/___stop_VNET_SETNAME may fail.
106                  * For now do not report an error here as we are called
107                  * internally and in `void context' until we merge the
108                  * functionality to optionally activate this into programs.
109                  * By that time we can properly fail and let the callers
110                  * handle the error.
111                  */
112                 /* _kvm_err(kd, kd->program, "%s: no namelist", __func__); */
113                 return (-1);
114         }
115
116         /*
117          * Auto-detect if this is a crashdump by reading dumptid.
118          */
119         dumptid = 0;
120         if (nl[NLIST_DUMPTID].n_value) {
121                 if (kvm_read(kd, nl[NLIST_DUMPTID].n_value, &dumptid,
122                     sizeof(dumptid)) != sizeof(dumptid)) {
123                         _kvm_err(kd, kd->program, "%s: dumptid", __func__);
124                         return (-1);
125                 }
126         }
127
128         /*
129          * First, find the process for this pid.  If we are working on a
130          * dump, either locate the thread dumptid is referring to or proc0.
131          * Based on either, take the address of the ucred.
132          */
133         credp = 0;
134
135         procp = nl[NLIST_ALLPROC].n_value;
136 #ifdef VMCORE_VNET_OF_PROC0
137         if (dumptid > 0) {
138                 procp = nl[NLIST_PROC0].n_value;
139                 pid = 0;
140         }
141 #endif
142         while (procp != 0) {
143                 if (kvm_read(kd, procp, &proc, sizeof(proc)) != sizeof(proc)) {
144                         _kvm_err(kd, kd->program, "%s: proc", __func__);
145                         return (-1);
146                 }
147 #ifndef VMCORE_VNET_OF_PROC0
148                 if (dumptid > 0) {
149                         tdp = (uintptr_t)TAILQ_FIRST(&proc.p_threads);
150                         while (tdp != 0) {
151                                 if (kvm_read(kd, tdp, &td, sizeof(td)) !=
152                                     sizeof(td)) {
153                                         _kvm_err(kd, kd->program, "%s: thread",
154                                             __func__);
155                                         return (-1);
156                                 }
157                                 if (td.td_tid == dumptid) {
158                                         credp = (uintptr_t)td.td_ucred;
159                                         break;
160                                 }
161                                 tdp = (uintptr_t)TAILQ_NEXT(&td, td_plist);
162                         }
163                 } else
164 #endif
165                 if (proc.p_pid == pid)
166                         credp = (uintptr_t)proc.p_ucred;
167                 if (credp != 0)
168                         break;
169                 procp = (uintptr_t)LIST_NEXT(&proc, p_list);
170         }
171         if (credp == 0) {
172                 _kvm_err(kd, kd->program, "%s: pid/tid not found", __func__);
173                 return (-1);
174         }
175         if (kvm_read(kd, (uintptr_t)credp, &cred, sizeof(cred)) !=
176             sizeof(cred)) {
177                 _kvm_err(kd, kd->program, "%s: cred", __func__);
178                 return (-1);
179         }
180         if (cred.cr_prison == NULL) {
181                 _kvm_err(kd, kd->program, "%s: no jail", __func__);
182                 return (-1);
183         }
184         if (kvm_read(kd, (uintptr_t)cred.cr_prison, &prison, sizeof(prison)) !=
185             sizeof(prison)) {
186                 _kvm_err(kd, kd->program, "%s: prison", __func__);
187                 return (-1);
188         }
189         if (prison.pr_vnet == NULL) {
190                 _kvm_err(kd, kd->program, "%s: no vnet", __func__);
191                 return (-1);
192         }
193         if (kvm_read(kd, (uintptr_t)prison.pr_vnet, &vnet, sizeof(vnet)) !=
194             sizeof(vnet)) {
195                 _kvm_err(kd, kd->program, "%s: vnet", __func__);
196                 return (-1);
197         }
198         if (vnet.vnet_magic_n != VNET_MAGIC_N) {
199                 _kvm_err(kd, kd->program, "%s: invalid vnet magic#", __func__);
200                 return (-1);
201         }
202         kd->vnet_initialized = 1;
203         kd->vnet_start = nl[NLIST_START_VNET].n_value;
204         kd->vnet_stop = nl[NLIST_STOP_VNET].n_value;
205         kd->vnet_current = (uintptr_t)prison.pr_vnet;
206         kd->vnet_base = vnet.vnet_data_base;
207         return (0);
208 }
209
210 /*
211  * Check whether the vnet module has been initialized successfully
212  * or not, initialize it if permitted.
213  */
214 int
215 _kvm_vnet_initialized(kvm_t *kd, int intialize)
216 {
217
218         if (kd->vnet_initialized || !intialize)
219                 return (kd->vnet_initialized);
220
221         (void) _kvm_vnet_selectpid(kd, getpid());
222
223         return (kd->vnet_initialized);
224 }
225
226 /*
227  * Check whether the value is within the vnet symbol range and
228  * only if so adjust the offset relative to the current base.
229  */
230 kvaddr_t
231 _kvm_vnet_validaddr(kvm_t *kd, kvaddr_t value)
232 {
233
234         if (value == 0)
235                 return (value);
236
237         if (!kd->vnet_initialized)
238                 return (value);
239
240         if (value < kd->vnet_start || value >= kd->vnet_stop)
241                 return (value);
242
243         return (kd->vnet_base + value);
244 }