]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - lib/libkvm/kvm_vnet.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.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 <nlist.h>
47 #include <kvm.h>
48 #include <limits.h>
49 #include <stdlib.h>
50 #include <unistd.h>
51
52 #include "kvm_private.h"
53
54 /*
55  * Set up libkvm to handle virtual network stack symbols by selecting a
56  * starting pid.
57  */
58 int
59 _kvm_vnet_selectpid(kvm_t *kd, pid_t pid)
60 {
61         struct proc proc;
62         struct ucred cred;
63         struct prison prison;
64         struct vnet vnet;
65         struct nlist nl[] = {
66                 /*
67                  * Note: kvm_nlist strips the first '_' so add an extra one
68                  * here to __{start,stop}_set_vnet.
69                  */
70 #define NLIST_START_VNET        0
71                 { .n_name = "___start_" VNET_SETNAME },
72 #define NLIST_STOP_VNET         1
73                 { .n_name = "___stop_" VNET_SETNAME },
74 #define NLIST_VNET_HEAD         2
75                 { .n_name = "vnet_head" },
76 #define NLIST_ALLPROC           3
77                 { .n_name = "allproc" },
78 #define NLIST_DUMPTID           4
79                 { .n_name = "dumptid" },
80 #define NLIST_PROC0             5
81                 { .n_name = "proc0" },
82                 { .n_name = NULL },
83         };
84         uintptr_t procp, credp;
85 #define VMCORE_VNET_OF_PROC0
86 #ifndef VMCORE_VNET_OF_PROC0
87         struct thread td;
88         uintptr_t tdp;
89 #endif
90         lwpid_t dumptid;
91
92         /*
93          * Locate and cache locations of important symbols
94          * using the internal version of _kvm_nlist, turning
95          * off initialization to avoid recursion in case of
96          * unresolveable symbols.
97          */
98         if (_kvm_nlist(kd, nl, 0) != 0) {
99                 /*
100                  * XXX-BZ: ___start_/___stop_VNET_SETNAME may fail.
101                  * For now do not report an error here as we are called
102                  * internally and in `void context' until we merge the
103                  * functionality to optionally activate this into programs.
104                  * By that time we can properly fail and let the callers
105                  * handle the error.
106                  */
107                 /* _kvm_err(kd, kd->program, "%s: no namelist", __func__); */
108                 return (-1);
109         }
110
111         /*
112          * Auto-detect if this is a crashdump by reading dumptid.
113          */
114         dumptid = 0;
115         if (nl[NLIST_DUMPTID].n_value) {
116                 if (kvm_read(kd, nl[NLIST_DUMPTID].n_value, &dumptid,
117                     sizeof(dumptid)) != sizeof(dumptid)) {
118                         _kvm_err(kd, kd->program, "%s: dumptid", __func__);
119                         return (-1);
120                 }
121         }
122
123         /*
124          * First, find the process for this pid.  If we are working on a
125          * dump, either locate the thread dumptid is refering to or proc0.
126          * Based on either, take the address of the ucred.
127          */
128         credp = 0;
129
130         procp = nl[NLIST_ALLPROC].n_value;
131 #ifdef VMCORE_VNET_OF_PROC0
132         if (dumptid > 0) {
133                 procp = nl[NLIST_PROC0].n_value;
134                 pid = 0;
135         }
136 #endif
137         while (procp != 0) {
138                 if (kvm_read(kd, procp, &proc, sizeof(proc)) != sizeof(proc)) {
139                         _kvm_err(kd, kd->program, "%s: proc", __func__);
140                         return (-1);
141                 }
142 #ifndef VMCORE_VNET_OF_PROC0
143                 if (dumptid > 0) {
144                         tdp = (uintptr_t)TAILQ_FIRST(&proc.p_threads);
145                         while (tdp != 0) {
146                                 if (kvm_read(kd, tdp, &td, sizeof(td)) !=
147                                     sizeof(td)) {
148                                         _kvm_err(kd, kd->program, "%s: thread",
149                                             __func__);
150                                         return (-1);
151                                 }
152                                 if (td.td_tid == dumptid) {
153                                         credp = (uintptr_t)td.td_ucred;
154                                         break;
155                                 }
156                                 tdp = (uintptr_t)TAILQ_NEXT(&td, td_plist);
157                         }
158                 } else
159 #endif
160                 if (proc.p_pid == pid)
161                         credp = (uintptr_t)proc.p_ucred;
162                 if (credp != 0)
163                         break;
164                 procp = (uintptr_t)LIST_NEXT(&proc, p_list);
165         }
166         if (credp == 0) {
167                 _kvm_err(kd, kd->program, "%s: pid/tid not found", __func__);
168                 return (-1);
169         }
170         if (kvm_read(kd, (uintptr_t)credp, &cred, sizeof(cred)) !=
171             sizeof(cred)) {
172                 _kvm_err(kd, kd->program, "%s: cred", __func__);
173                 return (-1);
174         }
175         if (cred.cr_prison == NULL) {
176                 _kvm_err(kd, kd->program, "%s: no jail", __func__);
177                 return (-1);
178         }
179         if (kvm_read(kd, (uintptr_t)cred.cr_prison, &prison, sizeof(prison)) !=
180             sizeof(prison)) {
181                 _kvm_err(kd, kd->program, "%s: prison", __func__);
182                 return (-1);
183         }
184         if (prison.pr_vnet == NULL) {
185                 _kvm_err(kd, kd->program, "%s: no vnet", __func__);
186                 return (-1);
187         }
188         if (kvm_read(kd, (uintptr_t)prison.pr_vnet, &vnet, sizeof(vnet)) !=
189             sizeof(vnet)) {
190                 _kvm_err(kd, kd->program, "%s: vnet", __func__);
191                 return (-1);
192         }
193         if (vnet.vnet_magic_n != VNET_MAGIC_N) {
194                 _kvm_err(kd, kd->program, "%s: invalid vnet magic#", __func__);
195                 return (-1);
196         }
197         kd->vnet_initialized = 1;
198         kd->vnet_start = nl[NLIST_START_VNET].n_value;
199         kd->vnet_stop = nl[NLIST_STOP_VNET].n_value;
200         kd->vnet_current = (uintptr_t)prison.pr_vnet;
201         kd->vnet_base = vnet.vnet_data_base;
202         return (0);
203 }
204
205 /*
206  * Check whether the vnet module has been initialized sucessfully
207  * or not, intialize it if permitted.
208  */
209 int
210 _kvm_vnet_initialized(kvm_t *kd, int intialize)
211 {
212
213         if (kd->vnet_initialized || !intialize)
214                 return (kd->vnet_initialized);
215
216         (void) _kvm_vnet_selectpid(kd, getpid());
217
218         return (kd->vnet_initialized);
219 }
220
221 /*
222  * Check whether the value is within the vnet symbol range and
223  * only if so adjust the offset relative to the current base.
224  */
225 uintptr_t
226 _kvm_vnet_validaddr(kvm_t *kd, uintptr_t value)
227 {
228
229         if (value == 0)
230                 return (value);
231
232         if (!kd->vnet_initialized)
233                 return (value);
234
235         if (value < kd->vnet_start || value >= kd->vnet_stop)
236                 return (value);
237
238         return (kd->vnet_base + value);
239 }