]>
Commit | Line | Data |
---|---|---|
9bccf70c | 1 | /* |
91447636 | 2 | * Copyright (c) 1999-2004 Apple Computer, Inc. All rights reserved. |
9bccf70c | 3 | * |
6601e61a | 4 | * @APPLE_LICENSE_HEADER_START@ |
9bccf70c | 5 | * |
6601e61a A |
6 | * The contents of this file constitute Original Code as defined in and |
7 | * are subject to the Apple Public Source License Version 1.1 (the | |
8 | * "License"). You may not use this file except in compliance with the | |
9 | * License. Please obtain a copy of the License at | |
10 | * http://www.apple.com/publicsource and read it before using this file. | |
8f6c56a5 | 11 | * |
6601e61a A |
12 | * This Original Code and all software distributed under the License are |
13 | * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
8f6c56a5 A |
14 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, |
15 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
6601e61a A |
16 | * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the |
17 | * License for the specific language governing rights and limitations | |
18 | * under the License. | |
8f6c56a5 | 19 | * |
6601e61a | 20 | * @APPLE_LICENSE_HEADER_END@ |
9bccf70c | 21 | */ |
9bccf70c A |
22 | |
23 | /* | |
91447636 A |
24 | * Kernel Control domain - allows control connections to |
25 | * and to read/write data. | |
9bccf70c | 26 | * |
91447636 | 27 | * Vincent Lubet, 040506 |
9bccf70c A |
28 | * Christophe Allie, 010928 |
29 | * Justin C. Walker, 990319 | |
30 | */ | |
31 | ||
32 | #include <sys/types.h> | |
33 | #include <sys/param.h> | |
34 | #include <sys/systm.h> | |
35 | #include <sys/syslog.h> | |
36 | #include <sys/socket.h> | |
37 | #include <sys/socketvar.h> | |
38 | #include <sys/protosw.h> | |
39 | #include <sys/domain.h> | |
40 | #include <sys/malloc.h> | |
41 | #include <sys/mbuf.h> | |
9bccf70c A |
42 | #include <sys/sys_domain.h> |
43 | #include <sys/kern_event.h> | |
44 | #include <sys/kern_control.h> | |
45 | #include <net/if_var.h> | |
46 | ||
47 | #include <mach/vm_types.h> | |
48 | #include <mach/kmod.h> | |
49 | ||
50 | #include <kern/thread.h> | |
51 | ||
9bccf70c A |
52 | /* |
53 | * Definitions and vars for we support | |
54 | */ | |
55 | ||
56 | #define CTL_SENDSIZE (2 * 1024) /* default buffer size */ | |
57 | #define CTL_RECVSIZE (8 * 1024) /* default buffer size */ | |
58 | ||
59 | /* | |
91447636 A |
60 | * Definitions and vars for we support |
61 | */ | |
9bccf70c | 62 | |
91447636 A |
63 | static u_int32_t ctl_last_id = 0; |
64 | static u_int32_t ctl_max = 256; | |
65 | static u_int32_t ctl_maxunit = 65536; | |
66 | static lck_grp_attr_t *ctl_lck_grp_attr = 0; | |
67 | static lck_attr_t *ctl_lck_attr = 0; | |
68 | static lck_grp_t *ctl_lck_grp = 0; | |
69 | static lck_mtx_t *ctl_mtx; | |
70 | ||
9bccf70c A |
71 | |
72 | /* all the controllers are chained */ | |
91447636 A |
73 | TAILQ_HEAD(, kctl) ctl_head; |
74 | ||
75 | static int ctl_attach(struct socket *, int, struct proc *); | |
76 | static int ctl_detach(struct socket *); | |
77 | static int ctl_sofreelastref(struct socket *so); | |
78 | static int ctl_connect(struct socket *, struct sockaddr *, struct proc *); | |
79 | static int ctl_disconnect(struct socket *); | |
80 | static int ctl_ioctl(struct socket *so, u_long cmd, caddr_t data, | |
9bccf70c | 81 | struct ifnet *ifp, struct proc *p); |
91447636 | 82 | static int ctl_send(struct socket *, int, struct mbuf *, |
9bccf70c | 83 | struct sockaddr *, struct mbuf *, struct proc *); |
91447636 A |
84 | static int ctl_ctloutput(struct socket *, struct sockopt *); |
85 | static int ctl_peeraddr(struct socket *so, struct sockaddr **nam); | |
86 | ||
87 | static struct kctl *ctl_find_by_id(u_int32_t); | |
88 | static struct kctl *ctl_find_by_name(const char *); | |
89 | static struct kctl *ctl_find_by_id_unit(u_int32_t id, u_int32_t unit); | |
9bccf70c | 90 | |
91447636 A |
91 | static struct ctl_cb *kcb_find(struct kctl *, u_int32_t unit); |
92 | static void ctl_post_msg(u_long event_code, u_int32_t id); | |
9bccf70c | 93 | |
91447636 A |
94 | static int ctl_lock(struct socket *, int, int); |
95 | static int ctl_unlock(struct socket *, int, int); | |
96 | static lck_mtx_t * ctl_getlock(struct socket *, int); | |
9bccf70c | 97 | |
91447636 | 98 | static struct pr_usrreqs ctl_usrreqs = |
9bccf70c A |
99 | { |
100 | pru_abort_notsupp, pru_accept_notsupp, ctl_attach, pru_bind_notsupp, | |
91447636 A |
101 | ctl_connect, pru_connect2_notsupp, ctl_ioctl, ctl_detach, |
102 | ctl_disconnect, pru_listen_notsupp, ctl_peeraddr, | |
9bccf70c A |
103 | pru_rcvd_notsupp, pru_rcvoob_notsupp, ctl_send, |
104 | pru_sense_null, pru_shutdown_notsupp, pru_sockaddr_notsupp, | |
91447636 A |
105 | sosend, soreceive, pru_sopoll_notsupp |
106 | }; | |
107 | ||
108 | static struct protosw kctlswk_dgram = | |
109 | { | |
110 | SOCK_DGRAM, &systemdomain, SYSPROTO_CONTROL, | |
111 | PR_ATOMIC|PR_CONNREQUIRED|PR_PCBLOCK, | |
112 | NULL, NULL, NULL, ctl_ctloutput, | |
113 | NULL, NULL, | |
114 | NULL, NULL, NULL, NULL, &ctl_usrreqs, | |
115 | ctl_lock, ctl_unlock, ctl_getlock, { 0, 0 } , 0, { 0 } | |
9bccf70c A |
116 | }; |
117 | ||
91447636 | 118 | static struct protosw kctlswk_stream = |
9bccf70c | 119 | { |
91447636 A |
120 | SOCK_STREAM, &systemdomain, SYSPROTO_CONTROL, |
121 | PR_CONNREQUIRED|PR_PCBLOCK, | |
9bccf70c A |
122 | NULL, NULL, NULL, ctl_ctloutput, |
123 | NULL, NULL, | |
91447636 A |
124 | NULL, NULL, NULL, NULL, &ctl_usrreqs, |
125 | ctl_lock, ctl_unlock, ctl_getlock, { 0, 0 } , 0, { 0 } | |
9bccf70c A |
126 | }; |
127 | ||
91447636 | 128 | |
9bccf70c | 129 | /* |
91447636 | 130 | * Install the protosw's for the Kernel Control manager. |
9bccf70c | 131 | */ |
91447636 | 132 | __private_extern__ int |
9bccf70c A |
133 | kern_control_init(void) |
134 | { | |
91447636 A |
135 | int error = 0; |
136 | ||
137 | ctl_lck_grp_attr = lck_grp_attr_alloc_init(); | |
138 | if (ctl_lck_grp_attr == 0) { | |
139 | printf(": lck_grp_attr_alloc_init failed\n"); | |
140 | error = ENOMEM; | |
141 | goto done; | |
142 | } | |
91447636 A |
143 | |
144 | ctl_lck_grp = lck_grp_alloc_init("Kernel Control Protocol", ctl_lck_grp_attr); | |
145 | if (ctl_lck_grp == 0) { | |
146 | printf("kern_control_init: lck_grp_alloc_init failed\n"); | |
147 | error = ENOMEM; | |
148 | goto done; | |
149 | } | |
150 | ||
151 | ctl_lck_attr = lck_attr_alloc_init(); | |
152 | if (ctl_lck_attr == 0) { | |
153 | printf("kern_control_init: lck_attr_alloc_init failed\n"); | |
154 | error = ENOMEM; | |
155 | goto done; | |
156 | } | |
91447636 A |
157 | |
158 | ctl_mtx = lck_mtx_alloc_init(ctl_lck_grp, ctl_lck_attr); | |
159 | if (ctl_mtx == 0) { | |
160 | printf("kern_control_init: lck_mtx_alloc_init failed\n"); | |
161 | error = ENOMEM; | |
162 | goto done; | |
163 | } | |
164 | TAILQ_INIT(&ctl_head); | |
165 | ||
166 | error = net_add_proto(&kctlswk_dgram, &systemdomain); | |
167 | if (error) { | |
168 | log(LOG_WARNING, "kern_control_init: net_add_proto dgram failed (%d)\n", error); | |
169 | } | |
170 | error = net_add_proto(&kctlswk_stream, &systemdomain); | |
171 | if (error) { | |
172 | log(LOG_WARNING, "kern_control_init: net_add_proto stream failed (%d)\n", error); | |
173 | } | |
174 | ||
175 | done: | |
176 | if (error != 0) { | |
177 | if (ctl_mtx) { | |
178 | lck_mtx_free(ctl_mtx, ctl_lck_grp); | |
179 | ctl_mtx = 0; | |
180 | } | |
181 | if (ctl_lck_grp) { | |
182 | lck_grp_free(ctl_lck_grp); | |
183 | ctl_lck_grp = 0; | |
184 | } | |
185 | if (ctl_lck_grp_attr) { | |
186 | lck_grp_attr_free(ctl_lck_grp_attr); | |
187 | ctl_lck_grp_attr = 0; | |
188 | } | |
189 | if (ctl_lck_attr) { | |
190 | lck_attr_free(ctl_lck_attr); | |
191 | ctl_lck_attr = 0; | |
192 | } | |
193 | } | |
194 | return error; | |
195 | } | |
9bccf70c | 196 | |
91447636 A |
197 | static void |
198 | kcb_delete(struct ctl_cb *kcb) | |
199 | { | |
200 | if (kcb != 0) { | |
201 | if (kcb->mtx != 0) | |
202 | lck_mtx_free(kcb->mtx, ctl_lck_grp); | |
203 | FREE(kcb, M_TEMP); | |
204 | } | |
9bccf70c A |
205 | } |
206 | ||
207 | ||
208 | /* | |
209 | * Kernel Controller user-request functions | |
91447636 A |
210 | * attach function must exist and succeed |
211 | * detach not necessary | |
212 | * we need a pcb for the per socket mutex | |
9bccf70c | 213 | */ |
91447636 A |
214 | static int |
215 | ctl_attach(__unused struct socket *so, __unused int proto, __unused struct proc *p) | |
9bccf70c | 216 | { |
91447636 A |
217 | int error = 0; |
218 | struct ctl_cb *kcb = 0; | |
219 | ||
220 | MALLOC(kcb, struct ctl_cb *, sizeof(struct ctl_cb), M_TEMP, M_WAITOK); | |
221 | if (kcb == NULL) { | |
222 | error = ENOMEM; | |
223 | goto quit; | |
224 | } | |
225 | bzero(kcb, sizeof(struct ctl_cb)); | |
226 | ||
227 | kcb->mtx = lck_mtx_alloc_init(ctl_lck_grp, ctl_lck_attr); | |
228 | if (kcb->mtx == NULL) { | |
229 | error = ENOMEM; | |
230 | goto quit; | |
231 | } | |
232 | kcb->so = so; | |
233 | so->so_pcb = (caddr_t)kcb; | |
234 | ||
235 | quit: | |
236 | if (error != 0) { | |
237 | kcb_delete(kcb); | |
238 | kcb = 0; | |
239 | } | |
240 | return error; | |
241 | } | |
242 | ||
243 | static int | |
244 | ctl_sofreelastref(struct socket *so) | |
245 | { | |
246 | struct ctl_cb *kcb = (struct ctl_cb *)so->so_pcb; | |
247 | ||
248 | so->so_pcb = 0; | |
249 | ||
250 | if (kcb != 0) { | |
251 | struct kctl *kctl; | |
252 | if ((kctl = kcb->kctl) != 0) { | |
253 | lck_mtx_lock(ctl_mtx); | |
254 | TAILQ_REMOVE(&kctl->kcb_head, kcb, next); | |
255 | lck_mtx_lock(ctl_mtx); | |
256 | } | |
257 | kcb_delete(kcb); | |
258 | } | |
0c530ab8 | 259 | sofreelastref(so, 1); |
91447636 A |
260 | return 0; |
261 | } | |
262 | ||
263 | static int | |
264 | ctl_detach(struct socket *so) | |
265 | { | |
266 | struct ctl_cb *kcb = (struct ctl_cb *)so->so_pcb; | |
9bccf70c | 267 | |
91447636 A |
268 | if (kcb == 0) |
269 | return 0; | |
270 | ||
271 | soisdisconnected(so); | |
272 | so->so_flags |= SOF_PCBCLEARING; | |
9bccf70c A |
273 | return 0; |
274 | } | |
275 | ||
91447636 A |
276 | |
277 | static int | |
278 | ctl_connect(struct socket *so, struct sockaddr *nam, __unused struct proc *p) | |
9bccf70c | 279 | { |
91447636 A |
280 | struct kctl *kctl; |
281 | int error = 0; | |
282 | struct sockaddr_ctl sa; | |
283 | struct ctl_cb *kcb = (struct ctl_cb *)so->so_pcb; | |
284 | ||
285 | if (kcb == 0) | |
286 | panic("ctl_connect so_pcb null\n"); | |
287 | ||
288 | if (nam->sa_len != sizeof(struct sockaddr_ctl)) | |
289 | return(EINVAL); | |
290 | ||
291 | bcopy(nam, &sa, sizeof(struct sockaddr_ctl)); | |
292 | ||
293 | lck_mtx_lock(ctl_mtx); | |
294 | kctl = ctl_find_by_id_unit(sa.sc_id, sa.sc_unit); | |
295 | if (kctl == NULL) { | |
296 | lck_mtx_unlock(ctl_mtx); | |
297 | return ENOENT; | |
298 | } | |
9bccf70c | 299 | |
91447636 A |
300 | if (((kctl->flags & CTL_FLAG_REG_SOCK_STREAM) && (so->so_type != SOCK_STREAM)) || |
301 | (!(kctl->flags & CTL_FLAG_REG_SOCK_STREAM) && (so->so_type != SOCK_DGRAM))) { | |
302 | lck_mtx_unlock(ctl_mtx); | |
303 | return EPROTOTYPE; | |
304 | } | |
9bccf70c | 305 | |
91447636 A |
306 | if (kctl->flags & CTL_FLAG_PRIVILEGED) { |
307 | if (p == 0) { | |
308 | lck_mtx_unlock(ctl_mtx); | |
55e303ae | 309 | return(EINVAL); |
91447636 A |
310 | } |
311 | if ((error = proc_suser(p))) { | |
312 | lck_mtx_unlock(ctl_mtx); | |
55e303ae | 313 | return error; |
91447636 A |
314 | } |
315 | } | |
316 | ||
317 | if ((kctl->flags & CTL_FLAG_REG_ID_UNIT) || sa.sc_unit != 0) { | |
318 | if (kcb_find(kctl, sa.sc_unit) != NULL) { | |
319 | lck_mtx_unlock(ctl_mtx); | |
320 | return EBUSY; | |
321 | } | |
322 | } else { | |
323 | u_int32_t unit = kctl->lastunit + 1; | |
324 | ||
325 | while (1) { | |
326 | if (unit == ctl_maxunit) | |
327 | unit = 1; | |
328 | if (kcb_find(kctl, unit) == NULL) { | |
329 | kctl->lastunit = sa.sc_unit = unit; | |
330 | break; | |
331 | } | |
332 | if (unit++ == kctl->lastunit) { | |
333 | lck_mtx_unlock(ctl_mtx); | |
334 | return EBUSY; | |
335 | } | |
336 | } | |
55e303ae A |
337 | } |
338 | ||
91447636 A |
339 | kcb->unit = sa.sc_unit; |
340 | kcb->kctl = kctl; | |
341 | TAILQ_INSERT_TAIL(&kctl->kcb_head, kcb, next); | |
342 | lck_mtx_unlock(ctl_mtx); | |
9bccf70c | 343 | |
91447636 | 344 | error = soreserve(so, kctl->sendbufsize, kctl->recvbufsize); |
9bccf70c | 345 | if (error) |
91447636 A |
346 | goto done; |
347 | soisconnecting(so); | |
9bccf70c | 348 | |
91447636 A |
349 | socket_unlock(so, 0); |
350 | error = (*kctl->connect)(kctl, &sa, &kcb->userdata); | |
351 | socket_lock(so, 0); | |
352 | if (error) | |
353 | goto done; | |
9bccf70c | 354 | |
91447636 A |
355 | soisconnected(so); |
356 | ||
357 | done: | |
9bccf70c | 358 | if (error) { |
91447636 A |
359 | soisdisconnected(so); |
360 | lck_mtx_lock(ctl_mtx); | |
361 | kcb->kctl = 0; | |
362 | kcb->unit = 0; | |
363 | TAILQ_REMOVE(&kctl->kcb_head, kcb, next); | |
364 | lck_mtx_unlock(ctl_mtx); | |
9bccf70c | 365 | } |
9bccf70c A |
366 | return error; |
367 | } | |
368 | ||
91447636 | 369 | static int |
9bccf70c A |
370 | ctl_disconnect(struct socket *so) |
371 | { | |
91447636 A |
372 | struct ctl_cb *kcb = (struct ctl_cb *)so->so_pcb; |
373 | ||
374 | if ((kcb = (struct ctl_cb *)so->so_pcb)) { | |
375 | struct kctl *kctl = kcb->kctl; | |
376 | ||
377 | if (kctl && kctl->disconnect) { | |
378 | socket_unlock(so, 0); | |
379 | (*kctl->disconnect)(kctl, kcb->unit, kcb->userdata); | |
380 | socket_lock(so, 0); | |
381 | } | |
382 | lck_mtx_lock(ctl_mtx); | |
383 | kcb->kctl = 0; | |
384 | kcb->unit = 0; | |
385 | TAILQ_REMOVE(&kctl->kcb_head, kcb, next); | |
9bccf70c | 386 | soisdisconnected(so); |
91447636 | 387 | lck_mtx_unlock(ctl_mtx); |
9bccf70c A |
388 | } |
389 | return 0; | |
390 | } | |
391 | ||
91447636 A |
392 | static int |
393 | ctl_peeraddr(struct socket *so, struct sockaddr **nam) | |
9bccf70c | 394 | { |
91447636 A |
395 | struct ctl_cb *kcb = (struct ctl_cb *)so->so_pcb; |
396 | struct kctl *kctl; | |
397 | struct sockaddr_ctl sc; | |
398 | ||
399 | if (kcb == NULL) /* sanity check */ | |
400 | return(ENOTCONN); | |
401 | ||
402 | if ((kctl = kcb->kctl) == NULL) | |
403 | return(EINVAL); | |
404 | ||
405 | bzero(&sc, sizeof(struct sockaddr_ctl)); | |
406 | sc.sc_len = sizeof(struct sockaddr_ctl); | |
407 | sc.sc_family = AF_SYSTEM; | |
408 | sc.ss_sysaddr = AF_SYS_CONTROL; | |
409 | sc.sc_id = kctl->id; | |
410 | sc.sc_unit = kcb->unit; | |
411 | ||
412 | *nam = dup_sockaddr((struct sockaddr *)&sc, 1); | |
413 | ||
414 | return 0; | |
9bccf70c A |
415 | } |
416 | ||
91447636 A |
417 | static int |
418 | ctl_send(struct socket *so, int flags, struct mbuf *m, | |
419 | __unused struct sockaddr *addr, __unused struct mbuf *control, | |
420 | __unused struct proc *p) | |
9bccf70c | 421 | { |
91447636 A |
422 | int error = 0; |
423 | struct ctl_cb *kcb = (struct ctl_cb *)so->so_pcb; | |
424 | struct kctl *kctl; | |
425 | ||
426 | if (kcb == NULL) /* sanity check */ | |
427 | return(ENOTCONN); | |
428 | ||
429 | if ((kctl = kcb->kctl) == NULL) | |
430 | return(EINVAL); | |
431 | ||
432 | if (kctl->send) { | |
433 | socket_unlock(so, 0); | |
434 | error = (*kctl->send)(kctl, kcb->unit, kcb->userdata, m, flags); | |
435 | socket_lock(so, 0); | |
436 | } | |
437 | return error; | |
9bccf70c A |
438 | } |
439 | ||
91447636 A |
440 | errno_t |
441 | ctl_enqueuembuf(void *kctlref, u_int32_t unit, struct mbuf *m, u_int32_t flags) | |
9bccf70c | 442 | { |
91447636 A |
443 | struct ctl_cb *kcb; |
444 | struct socket *so; | |
445 | errno_t error = 0; | |
446 | struct kctl *kctl = (struct kctl *)kctlref; | |
447 | ||
448 | if (kctl == NULL) | |
449 | return EINVAL; | |
450 | ||
451 | kcb = kcb_find(kctl, unit); | |
452 | if (kcb == NULL) | |
453 | return EINVAL; | |
454 | ||
455 | so = (struct socket *)kcb->so; | |
456 | if (so == NULL) | |
457 | return EINVAL; | |
458 | ||
459 | socket_lock(so, 1); | |
460 | if (sbspace(&so->so_rcv) < m->m_pkthdr.len) { | |
461 | error = ENOBUFS; | |
462 | goto bye; | |
463 | } | |
464 | if ((flags & CTL_DATA_EOR)) | |
465 | m->m_flags |= M_EOR; | |
466 | if (sbappend(&so->so_rcv, m) && (flags & CTL_DATA_NOWAKEUP) == 0) | |
467 | sorwakeup(so); | |
468 | bye: | |
469 | socket_unlock(so, 1); | |
470 | return error; | |
471 | } | |
9bccf70c | 472 | |
91447636 A |
473 | errno_t |
474 | ctl_enqueuedata(void *kctlref, u_int32_t unit, void *data, size_t len, u_int32_t flags) | |
475 | { | |
476 | struct ctl_cb *kcb; | |
477 | struct socket *so; | |
478 | struct mbuf *m; | |
479 | errno_t error = 0; | |
480 | struct kctl *kctl = (struct kctl *)kctlref; | |
481 | unsigned int num_needed; | |
482 | struct mbuf *n; | |
483 | size_t curlen = 0; | |
484 | ||
485 | if (kctlref == NULL) | |
486 | return EINVAL; | |
487 | ||
488 | kcb = kcb_find(kctl, unit); | |
489 | if (kcb == NULL) | |
490 | return EINVAL; | |
491 | ||
492 | so = (struct socket *)kcb->so; | |
493 | if (so == NULL) | |
494 | return EINVAL; | |
495 | ||
496 | socket_lock(so, 1); | |
497 | if ((size_t)sbspace(&so->so_rcv) < len) { | |
498 | error = ENOBUFS; | |
499 | goto bye; | |
500 | } | |
501 | ||
502 | num_needed = 1; | |
503 | m = m_allocpacket_internal(&num_needed, len, NULL, M_NOWAIT, 1, 0); | |
504 | if (m == NULL) { | |
505 | printf("ctl_enqueuedata: m_allocpacket_internal(%lu) failed\n", len); | |
506 | error = ENOBUFS; | |
507 | goto bye; | |
508 | } | |
509 | ||
510 | for (n = m; n != NULL; n = n->m_next) { | |
511 | size_t mlen = mbuf_maxlen(n); | |
512 | ||
513 | if (mlen + curlen > len) | |
514 | mlen = len - curlen; | |
515 | n->m_len = mlen; | |
516 | bcopy((char *)data + curlen, n->m_data, mlen); | |
517 | curlen += mlen; | |
518 | } | |
519 | mbuf_pkthdr_setlen(m, curlen); | |
520 | ||
521 | if ((flags & CTL_DATA_EOR)) | |
522 | m->m_flags |= M_EOR; | |
523 | if (sbappend(&so->so_rcv, m) && (flags & CTL_DATA_NOWAKEUP) == 0) | |
524 | sorwakeup(so); | |
525 | bye: | |
526 | socket_unlock(so, 1); | |
527 | return error; | |
528 | } | |
9bccf70c | 529 | |
55e303ae | 530 | |
91447636 A |
531 | errno_t |
532 | ctl_getenqueuespace(kern_ctl_ref kctlref, u_int32_t unit, size_t *space) | |
533 | { | |
534 | struct ctl_cb *kcb; | |
535 | struct kctl *kctl = (struct kctl *)kctlref; | |
536 | struct socket *so; | |
537 | ||
538 | if (kctlref == NULL || space == NULL) | |
539 | return EINVAL; | |
540 | ||
541 | kcb = kcb_find(kctl, unit); | |
542 | if (kcb == NULL) | |
543 | return EINVAL; | |
544 | ||
545 | so = (struct socket *)kcb->so; | |
546 | if (so == NULL) | |
547 | return EINVAL; | |
548 | ||
549 | socket_lock(so, 1); | |
550 | *space = sbspace(&so->so_rcv); | |
551 | socket_unlock(so, 1); | |
552 | ||
553 | return 0; | |
9bccf70c A |
554 | } |
555 | ||
91447636 | 556 | static int |
9bccf70c A |
557 | ctl_ctloutput(struct socket *so, struct sockopt *sopt) |
558 | { | |
91447636 A |
559 | struct ctl_cb *kcb = (struct ctl_cb *)so->so_pcb; |
560 | struct kctl *kctl; | |
561 | int error = 0; | |
562 | void *data; | |
563 | size_t len; | |
564 | ||
565 | if (sopt->sopt_level != SYSPROTO_CONTROL) { | |
566 | return(EINVAL); | |
567 | } | |
568 | ||
569 | if (kcb == NULL) /* sanity check */ | |
570 | return(ENOTCONN); | |
571 | ||
572 | if ((kctl = kcb->kctl) == NULL) | |
573 | return(EINVAL); | |
574 | ||
575 | switch (sopt->sopt_dir) { | |
576 | case SOPT_SET: | |
577 | if (kctl->setopt == NULL) | |
578 | return(ENOTSUP); | |
579 | MALLOC(data, void *, sopt->sopt_valsize, M_TEMP, M_WAITOK); | |
580 | if (data == NULL) | |
581 | return(ENOMEM); | |
582 | error = sooptcopyin(sopt, data, sopt->sopt_valsize, sopt->sopt_valsize); | |
583 | if (error == 0) { | |
584 | socket_unlock(so, 0); | |
585 | error = (*kctl->setopt)(kcb->kctl, kcb->unit, kcb->userdata, sopt->sopt_name, | |
586 | data, sopt->sopt_valsize); | |
587 | socket_lock(so, 0); | |
588 | } | |
589 | FREE(data, M_TEMP); | |
590 | break; | |
591 | ||
592 | case SOPT_GET: | |
593 | if (kctl->getopt == NULL) | |
594 | return(ENOTSUP); | |
595 | data = NULL; | |
596 | if (sopt->sopt_valsize && sopt->sopt_val) { | |
597 | MALLOC(data, void *, sopt->sopt_valsize, M_TEMP, M_WAITOK); | |
598 | if (data == NULL) | |
599 | return(ENOMEM); | |
cc9f6e38 A |
600 | /* 4108337 - copy in data for get socket option */ |
601 | error = sooptcopyin(sopt, data, sopt->sopt_valsize, sopt->sopt_valsize); | |
91447636 A |
602 | } |
603 | len = sopt->sopt_valsize; | |
604 | socket_unlock(so, 0); | |
605 | error = (*kctl->getopt)(kcb->kctl, kcb->unit, kcb->userdata, sopt->sopt_name, | |
606 | data, &len); | |
607 | socket_lock(so, 0); | |
608 | if (error == 0) { | |
609 | if (data != NULL) | |
610 | error = sooptcopyout(sopt, data, len); | |
611 | else | |
612 | sopt->sopt_valsize = len; | |
613 | } | |
614 | if (data != NULL) | |
615 | FREE(data, M_TEMP); | |
616 | break; | |
617 | } | |
618 | return error; | |
619 | } | |
9bccf70c | 620 | |
91447636 A |
621 | static int |
622 | ctl_ioctl(__unused struct socket *so, u_long cmd, caddr_t data, | |
623 | __unused struct ifnet *ifp, __unused struct proc *p) | |
624 | { | |
625 | int error = ENOTSUP; | |
626 | ||
627 | switch (cmd) { | |
628 | /* get the number of controllers */ | |
629 | case CTLIOCGCOUNT: { | |
630 | struct kctl *kctl; | |
631 | int n = 0; | |
632 | ||
633 | lck_mtx_lock(ctl_mtx); | |
634 | TAILQ_FOREACH(kctl, &ctl_head, next) | |
635 | n++; | |
636 | lck_mtx_unlock(ctl_mtx); | |
637 | ||
638 | *(u_int32_t *)data = n; | |
639 | error = 0; | |
640 | break; | |
641 | } | |
642 | case CTLIOCGINFO: { | |
643 | struct ctl_info *ctl_info = (struct ctl_info *)data; | |
644 | struct kctl *kctl = 0; | |
645 | size_t name_len = strlen(ctl_info->ctl_name); | |
646 | ||
647 | if (name_len == 0 || name_len + 1 > MAX_KCTL_NAME) { | |
648 | error = EINVAL; | |
649 | break; | |
650 | } | |
651 | lck_mtx_lock(ctl_mtx); | |
652 | kctl = ctl_find_by_name(ctl_info->ctl_name); | |
653 | lck_mtx_unlock(ctl_mtx); | |
654 | if (kctl == 0) { | |
655 | error = ENOENT; | |
656 | break; | |
657 | } | |
658 | ctl_info->ctl_id = kctl->id; | |
659 | error = 0; | |
660 | break; | |
661 | } | |
662 | ||
663 | /* add controls to get list of NKEs */ | |
664 | ||
665 | } | |
666 | ||
667 | return error; | |
668 | } | |
9bccf70c | 669 | |
91447636 A |
670 | /* |
671 | * Register/unregister a NKE | |
672 | */ | |
673 | errno_t | |
674 | ctl_register(struct kern_ctl_reg *userkctl, kern_ctl_ref *kctlref) | |
675 | { | |
676 | struct kctl *kctl = 0; | |
677 | u_int32_t id = -1; | |
678 | u_int32_t n; | |
679 | size_t name_len; | |
680 | ||
681 | if (userkctl == NULL) /* sanity check */ | |
682 | return(EINVAL); | |
683 | if (userkctl->ctl_connect == NULL) | |
684 | return(EINVAL); | |
685 | name_len = strlen(userkctl->ctl_name); | |
686 | if (name_len == 0 || name_len + 1 > MAX_KCTL_NAME) | |
687 | return(EINVAL); | |
688 | ||
689 | MALLOC(kctl, struct kctl *, sizeof(*kctl), M_TEMP, M_WAITOK); | |
690 | if (kctl == NULL) | |
691 | return(ENOMEM); | |
692 | bzero((char *)kctl, sizeof(*kctl)); | |
693 | ||
694 | lck_mtx_lock(ctl_mtx); | |
695 | ||
696 | if ((userkctl->ctl_flags & CTL_FLAG_REG_ID_UNIT) == 0) { | |
697 | if (ctl_find_by_name(userkctl->ctl_name) != NULL) { | |
698 | lck_mtx_unlock(ctl_mtx); | |
699 | FREE(kctl, M_TEMP); | |
700 | return(EEXIST); | |
701 | } | |
702 | for (n = 0, id = ctl_last_id + 1; n < ctl_max; id++, n++) { | |
703 | if (id == 0) { | |
704 | n--; | |
705 | continue; | |
706 | } | |
707 | if (ctl_find_by_id(id) == 0) | |
708 | break; | |
709 | } | |
710 | if (id == ctl_max) { | |
711 | lck_mtx_unlock(ctl_mtx); | |
712 | FREE(kctl, M_TEMP); | |
713 | return(ENOBUFS); | |
714 | } | |
715 | userkctl->ctl_id =id; | |
716 | kctl->id = id; | |
717 | kctl->reg_unit = -1; | |
718 | } else { | |
719 | if (ctl_find_by_id_unit(userkctl->ctl_id, userkctl->ctl_unit) != NULL) { | |
720 | lck_mtx_unlock(ctl_mtx); | |
721 | FREE(kctl, M_TEMP); | |
722 | return(EEXIST); | |
723 | } | |
724 | kctl->id = userkctl->ctl_id; | |
725 | kctl->reg_unit = userkctl->ctl_unit; | |
726 | } | |
727 | strcpy(kctl->name, userkctl->ctl_name); | |
728 | kctl->flags = userkctl->ctl_flags; | |
729 | ||
730 | /* Let the caller know the default send and receive sizes */ | |
731 | if (userkctl->ctl_sendsize == 0) | |
732 | userkctl->ctl_sendsize = CTL_SENDSIZE; | |
733 | kctl->sendbufsize = userkctl->ctl_sendsize; | |
734 | ||
3a60a9f5 | 735 | if (userkctl->ctl_recvsize == 0) |
91447636 A |
736 | userkctl->ctl_recvsize = CTL_RECVSIZE; |
737 | kctl->recvbufsize = userkctl->ctl_recvsize; | |
738 | ||
739 | kctl->connect = userkctl->ctl_connect; | |
740 | kctl->disconnect = userkctl->ctl_disconnect; | |
741 | kctl->send = userkctl->ctl_send; | |
742 | kctl->setopt = userkctl->ctl_setopt; | |
743 | kctl->getopt = userkctl->ctl_getopt; | |
744 | ||
745 | TAILQ_INIT(&kctl->kcb_head); | |
746 | ||
747 | TAILQ_INSERT_TAIL(&ctl_head, kctl, next); | |
748 | ctl_max++; | |
749 | ||
750 | lck_mtx_unlock(ctl_mtx); | |
751 | ||
752 | *kctlref = kctl; | |
753 | ||
754 | ctl_post_msg(KEV_CTL_REGISTERED, kctl->id); | |
755 | return(0); | |
9bccf70c A |
756 | } |
757 | ||
91447636 A |
758 | errno_t |
759 | ctl_deregister(void *kctlref) | |
760 | { | |
761 | struct kctl *kctl; | |
9bccf70c | 762 | |
91447636 A |
763 | if (kctlref == NULL) /* sanity check */ |
764 | return(EINVAL); | |
9bccf70c | 765 | |
91447636 A |
766 | lck_mtx_lock(ctl_mtx); |
767 | TAILQ_FOREACH(kctl, &ctl_head, next) { | |
768 | if (kctl == (struct kctl *)kctlref) | |
769 | break; | |
9bccf70c | 770 | } |
91447636 A |
771 | if (kctl != (struct kctl *)kctlref) { |
772 | lck_mtx_unlock(ctl_mtx); | |
773 | return EINVAL; | |
774 | } | |
775 | if (!TAILQ_EMPTY(&kctl->kcb_head)) { | |
776 | lck_mtx_unlock(ctl_mtx); | |
777 | return EBUSY; | |
778 | } | |
779 | ||
780 | TAILQ_REMOVE(&ctl_head, kctl, next); | |
781 | ctl_max--; | |
782 | ||
783 | lck_mtx_unlock(ctl_mtx); | |
9bccf70c | 784 | |
91447636 A |
785 | ctl_post_msg(KEV_CTL_DEREGISTERED, kctl->id); |
786 | FREE(kctl, M_TEMP); | |
787 | return(0); | |
9bccf70c A |
788 | } |
789 | ||
790 | /* | |
91447636 | 791 | * Must be called with global lock taked |
9bccf70c | 792 | */ |
91447636 A |
793 | static struct kctl * |
794 | ctl_find_by_id(u_int32_t id) | |
9bccf70c | 795 | { |
91447636 | 796 | struct kctl *kctl; |
9bccf70c | 797 | |
91447636 A |
798 | TAILQ_FOREACH(kctl, &ctl_head, next) |
799 | if (kctl->id == id) | |
800 | return kctl; | |
9bccf70c | 801 | |
91447636 | 802 | return NULL; |
9bccf70c A |
803 | } |
804 | ||
91447636 A |
805 | /* |
806 | * Must be called with global ctl_mtx lock taked | |
807 | */ | |
808 | static struct kctl * | |
809 | ctl_find_by_name(const char *name) | |
9bccf70c | 810 | { |
91447636 | 811 | struct kctl *kctl; |
9bccf70c | 812 | |
91447636 A |
813 | TAILQ_FOREACH(kctl, &ctl_head, next) |
814 | if (strcmp(kctl->name, name) == 0) | |
815 | return kctl; | |
9bccf70c | 816 | |
91447636 A |
817 | return NULL; |
818 | } | |
9bccf70c | 819 | |
91447636 A |
820 | /* |
821 | * Must be called with global ctl_mtx lock taked | |
822 | * | |
823 | */ | |
824 | static struct kctl * | |
825 | ctl_find_by_id_unit(u_int32_t id, u_int32_t unit) | |
826 | { | |
827 | struct kctl *kctl; | |
828 | ||
829 | TAILQ_FOREACH(kctl, &ctl_head, next) { | |
830 | if (kctl->id == id && (kctl->flags & CTL_FLAG_REG_ID_UNIT) == 0) | |
831 | return kctl; | |
832 | else if (kctl->id == id && kctl->reg_unit == unit) | |
833 | return kctl; | |
9bccf70c | 834 | } |
91447636 | 835 | return NULL; |
9bccf70c A |
836 | } |
837 | ||
838 | /* | |
91447636 | 839 | * Must be called with kernel controller lock taken |
9bccf70c | 840 | */ |
91447636 A |
841 | static struct ctl_cb * |
842 | kcb_find(struct kctl *kctl, u_int32_t unit) | |
9bccf70c | 843 | { |
91447636 | 844 | struct ctl_cb *kcb; |
9bccf70c | 845 | |
91447636 A |
846 | TAILQ_FOREACH(kcb, &kctl->kcb_head, next) |
847 | if ((kcb->unit == unit)) | |
848 | return kcb; | |
9bccf70c A |
849 | |
850 | return NULL; | |
851 | } | |
852 | ||
91447636 A |
853 | /* |
854 | * Must be called witout lock | |
855 | */ | |
856 | static void | |
857 | ctl_post_msg(u_long event_code, u_int32_t id) | |
9bccf70c A |
858 | { |
859 | struct ctl_event_data ctl_ev_data; | |
860 | struct kev_msg ev_msg; | |
861 | ||
862 | ev_msg.vendor_code = KEV_VENDOR_APPLE; | |
863 | ||
864 | ev_msg.kev_class = KEV_SYSTEM_CLASS; | |
865 | ev_msg.kev_subclass = KEV_CTL_SUBCLASS; | |
866 | ev_msg.event_code = event_code; | |
867 | ||
868 | /* common nke subclass data */ | |
869 | bzero(&ctl_ev_data, sizeof(ctl_ev_data)); | |
870 | ctl_ev_data.ctl_id = id; | |
9bccf70c A |
871 | ev_msg.dv[0].data_ptr = &ctl_ev_data; |
872 | ev_msg.dv[0].data_length = sizeof(ctl_ev_data); | |
873 | ||
874 | ev_msg.dv[1].data_length = 0; | |
875 | ||
876 | kev_post_msg(&ev_msg); | |
877 | } | |
878 | ||
91447636 A |
879 | static int |
880 | ctl_lock(struct socket *so, int refcount, int lr) | |
881 | { | |
882 | int lr_saved; | |
0c530ab8 A |
883 | if (lr == 0) |
884 | lr_saved = (unsigned int) __builtin_return_address(0); | |
91447636 | 885 | else lr_saved = lr; |
91447636 A |
886 | |
887 | if (so->so_pcb) { | |
888 | lck_mtx_lock(((struct ctl_cb *)so->so_pcb)->mtx); | |
889 | } else { | |
890 | panic("ctl_lock: so=%x NO PCB! lr=%x\n", so, lr_saved); | |
891 | lck_mtx_lock(so->so_proto->pr_domain->dom_mtx); | |
892 | } | |
893 | ||
894 | if (so->so_usecount < 0) | |
895 | panic("ctl_lock: so=%x so_pcb=%x lr=%x ref=%x\n", | |
896 | so, so->so_pcb, lr_saved, so->so_usecount); | |
897 | ||
898 | if (refcount) | |
899 | so->so_usecount++; | |
0c530ab8 A |
900 | |
901 | so->lock_lr[so->next_lock_lr] = (void *)lr_saved; | |
902 | so->next_lock_lr = (so->next_lock_lr+1) % SO_LCKDBG_MAX; | |
91447636 A |
903 | return (0); |
904 | } | |
905 | ||
906 | static int | |
907 | ctl_unlock(struct socket *so, int refcount, int lr) | |
908 | { | |
909 | int lr_saved; | |
910 | lck_mtx_t * mutex_held; | |
911 | ||
0c530ab8 A |
912 | if (lr == 0) |
913 | lr_saved = (unsigned int) __builtin_return_address(0); | |
91447636 | 914 | else lr_saved = lr; |
91447636 A |
915 | |
916 | #ifdef MORE_KCTLLOCK_DEBUG | |
917 | printf("ctl_unlock: so=%x sopcb=%x lock=%x ref=%x lr=%x\n", | |
918 | so, so->so_pcb, ((struct ctl_cb *)so->so_pcb)->mtx, so->so_usecount, lr_saved); | |
919 | #endif | |
920 | if (refcount) | |
921 | so->so_usecount--; | |
922 | ||
923 | if (so->so_usecount < 0) | |
924 | panic("ctl_unlock: so=%x usecount=%x\n", so, so->so_usecount); | |
925 | if (so->so_pcb == NULL) { | |
926 | panic("ctl_unlock: so=%x NO PCB usecount=%x lr=%x\n", so, so->so_usecount, lr_saved); | |
927 | mutex_held = so->so_proto->pr_domain->dom_mtx; | |
928 | } else { | |
929 | mutex_held = ((struct ctl_cb *)so->so_pcb)->mtx; | |
930 | } | |
931 | lck_mtx_assert(mutex_held, LCK_MTX_ASSERT_OWNED); | |
0c530ab8 A |
932 | so->unlock_lr[so->next_unlock_lr] = (void *)lr_saved; |
933 | so->next_unlock_lr = (so->next_unlock_lr+1) % SO_LCKDBG_MAX; | |
91447636 | 934 | lck_mtx_unlock(mutex_held); |
91447636 A |
935 | |
936 | if (so->so_usecount == 0) | |
937 | ctl_sofreelastref(so); | |
938 | ||
939 | return (0); | |
940 | } | |
941 | ||
942 | static lck_mtx_t * | |
943 | ctl_getlock(struct socket *so, __unused int locktype) | |
944 | { | |
945 | struct ctl_cb *kcb = (struct ctl_cb *)so->so_pcb; | |
946 | ||
947 | if (so->so_pcb) { | |
948 | if (so->so_usecount < 0) | |
949 | panic("ctl_getlock: so=%x usecount=%x\n", so, so->so_usecount); | |
950 | return(kcb->mtx); | |
951 | } else { | |
952 | panic("ctl_getlock: so=%x NULL so_pcb\n", so); | |
953 | return (so->so_proto->pr_domain->dom_mtx); | |
954 | } | |
955 | } |