]>
Commit | Line | Data |
---|---|---|
3e170ce0 A |
1 | /* |
2 | * Copyright (c) 2014 Apple Inc. All rights reserved. | |
3 | * | |
4 | * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ | |
5 | * | |
6 | * This file contains Original Code and/or Modifications of Original Code | |
7 | * as defined in and that are subject to the Apple Public Source License | |
8 | * Version 2.0 (the 'License'). You may not use this file except in | |
9 | * compliance with the License. The rights granted to you under the License | |
10 | * may not be used to create, or enable the creation or redistribution of, | |
11 | * unlawful or unlicensed copies of an Apple operating system, or to | |
12 | * circumvent, violate, or enable the circumvention or violation of, any | |
13 | * terms of an Apple operating system software license agreement. | |
14 | * | |
15 | * Please obtain a copy of the License at | |
16 | * http://www.opensource.apple.com/apsl/ and read it before using this file. | |
17 | * | |
18 | * The Original Code and all software distributed under the License are | |
19 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
20 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
21 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
22 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. | |
23 | * Please see the License for the specific language governing rights and | |
24 | * limitations under the License. | |
25 | * | |
26 | * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ | |
27 | */ | |
28 | ||
29 | #include <string.h> | |
30 | #include <sys/systm.h> | |
31 | #include <sys/types.h> | |
32 | #include <sys/syslog.h> | |
33 | #include <sys/queue.h> | |
34 | #include <sys/malloc.h> | |
35 | #include <libkern/OSMalloc.h> | |
36 | #include <sys/kernel.h> | |
37 | #include <sys/kern_control.h> | |
38 | #include <sys/mbuf.h> | |
39 | #include <sys/kpi_mbuf.h> | |
40 | #include <sys/sysctl.h> | |
41 | #include <sys/priv.h> | |
42 | #include <sys/kern_event.h> | |
43 | #include <sys/sysproto.h> | |
44 | #include <net/network_agent.h> | |
45 | #include <net/if_var.h> | |
46 | ||
47 | u_int32_t netagent_debug = LOG_NOTICE; // 0=None, 1=Basic | |
48 | ||
49 | SYSCTL_NODE(_net, OID_AUTO, netagent, CTLFLAG_RW | CTLFLAG_LOCKED, 0, "NetworkAgent"); | |
50 | SYSCTL_INT(_net_netagent, OID_AUTO, debug, CTLFLAG_LOCKED | CTLFLAG_RW, &netagent_debug, 0, ""); | |
51 | ||
52 | static int netagent_registered_count = 0; | |
53 | SYSCTL_INT(_net_netagent, OID_AUTO, registered_count , CTLFLAG_RD | CTLFLAG_LOCKED, | |
54 | &netagent_registered_count, 0, ""); | |
55 | ||
56 | static int netagent_active_count = 0; | |
57 | SYSCTL_INT(_net_netagent, OID_AUTO, active_count , CTLFLAG_RD | CTLFLAG_LOCKED, | |
58 | &netagent_active_count, 0, ""); | |
59 | ||
60 | #define NETAGENTLOG(level, format, ...) do { \ | |
61 | if (level <= netagent_debug) \ | |
62 | log((level > LOG_NOTICE ? LOG_NOTICE : level), "%s: " format "\n", __FUNCTION__, __VA_ARGS__); \ | |
63 | } while (0) | |
64 | ||
65 | #define NETAGENTLOG0(level, msg) do { \ | |
66 | if (level <= netagent_debug) \ | |
67 | log((level > LOG_NOTICE ? LOG_NOTICE : level), "%s: %s\n", __FUNCTION__, msg); \ | |
68 | } while (0) | |
69 | ||
70 | struct netagent_assertion { | |
71 | LIST_ENTRY(netagent_assertion) assertion_chain; | |
72 | uuid_t asserted_uuid; | |
73 | }; | |
74 | ||
75 | struct netagent_wrapper { | |
76 | LIST_ENTRY(netagent_wrapper) master_chain; | |
77 | u_int32_t control_unit; | |
78 | struct netagent netagent; | |
79 | }; | |
80 | ||
81 | struct netagent_session { | |
82 | u_int32_t control_unit; | |
83 | struct netagent_wrapper *wrapper; | |
84 | LIST_HEAD(_netagent_assertion_list, netagent_assertion) assertion_list; | |
85 | }; | |
86 | ||
87 | static LIST_HEAD(_netagent_list, netagent_wrapper) master_netagent_list; | |
88 | ||
89 | static kern_ctl_ref netagent_kctlref; | |
90 | static u_int32_t netagent_family; | |
91 | static OSMallocTag netagent_malloc_tag; | |
92 | static lck_grp_attr_t *netagent_grp_attr = NULL; | |
93 | static lck_attr_t *netagent_mtx_attr = NULL; | |
94 | static lck_grp_t *netagent_mtx_grp = NULL; | |
95 | decl_lck_rw_data(static, netagent_lock); | |
96 | ||
97 | static errno_t netagent_register_control(void); | |
98 | static errno_t netagent_ctl_connect(kern_ctl_ref kctlref, struct sockaddr_ctl *sac, | |
99 | void **unitinfo); | |
100 | static errno_t netagent_ctl_disconnect(kern_ctl_ref kctlref, u_int32_t unit, void *unitinfo); | |
101 | static errno_t netagent_ctl_send(kern_ctl_ref kctlref, u_int32_t unit, void *unitinfo, | |
102 | mbuf_t m, int flags); | |
103 | static void netagent_ctl_rcvd(kern_ctl_ref kctlref, u_int32_t unit, void *unitinfo, int flags); | |
104 | static errno_t netagent_ctl_getopt(kern_ctl_ref kctlref, u_int32_t unit, void *unitinfo, | |
105 | int opt, void *data, size_t *len); | |
106 | static errno_t netagent_ctl_setopt(kern_ctl_ref kctlref, u_int32_t unit, void *unitinfo, | |
107 | int opt, void *data, size_t len); | |
108 | ||
109 | static int netagent_send_ctl_data(u_int32_t control_unit, u_int8_t *buffer, size_t buffer_size); | |
110 | ||
111 | static struct netagent_session *netagent_create_session(u_int32_t control_unit); | |
112 | static void netagent_delete_session(struct netagent_session *session); | |
113 | ||
114 | static void netagent_handle_register(struct netagent_session *session, u_int32_t message_id, | |
115 | u_int32_t payload_length, mbuf_t packet, int offset); | |
116 | static void netagent_handle_unregister(struct netagent_session *session, u_int32_t message_id, | |
117 | u_int32_t payload_length, mbuf_t packet, int offset); | |
118 | static void netagent_handle_update(struct netagent_session *session, u_int32_t message_id, | |
119 | u_int32_t payload_length, mbuf_t packet, int offset); | |
120 | static void netagent_handle_get(struct netagent_session *session, u_int32_t message_id, | |
121 | u_int32_t payload_length, mbuf_t packet, int offset); | |
122 | static void netagent_handle_assert(struct netagent_session *session, u_int32_t message_id, | |
123 | u_int32_t payload_length, mbuf_t packet, int offset); | |
124 | static void netagent_handle_unassert(struct netagent_session *session, u_int32_t message_id, | |
125 | u_int32_t payload_length, mbuf_t packet, int offset); | |
126 | ||
127 | static struct netagent_wrapper *netagent_find_agent_with_uuid(uuid_t uuid); | |
128 | ||
129 | errno_t | |
130 | netagent_init(void) | |
131 | { | |
132 | errno_t result = 0; | |
133 | ||
134 | result = netagent_register_control(); | |
135 | if (result != 0) { | |
136 | goto done; | |
137 | } | |
138 | ||
139 | netagent_grp_attr = lck_grp_attr_alloc_init(); | |
140 | if (netagent_grp_attr == NULL) { | |
141 | NETAGENTLOG0(LOG_ERR, "lck_grp_attr_alloc_init failed"); | |
142 | result = ENOMEM; | |
143 | goto done; | |
144 | } | |
145 | ||
146 | netagent_mtx_grp = lck_grp_alloc_init(NETAGENT_CONTROL_NAME, netagent_grp_attr); | |
147 | if (netagent_mtx_grp == NULL) { | |
148 | NETAGENTLOG0(LOG_ERR, "lck_grp_alloc_init failed"); | |
149 | result = ENOMEM; | |
150 | goto done; | |
151 | } | |
152 | ||
153 | netagent_mtx_attr = lck_attr_alloc_init(); | |
154 | if (netagent_mtx_attr == NULL) { | |
155 | NETAGENTLOG0(LOG_ERR, "lck_attr_alloc_init failed"); | |
156 | result = ENOMEM; | |
157 | goto done; | |
158 | } | |
159 | ||
160 | lck_rw_init(&netagent_lock, netagent_mtx_grp, netagent_mtx_attr); | |
161 | ||
162 | LIST_INIT(&master_netagent_list); | |
163 | ||
164 | done: | |
165 | if (result != 0) { | |
166 | if (netagent_mtx_attr != NULL) { | |
167 | lck_attr_free(netagent_mtx_attr); | |
168 | netagent_mtx_attr = NULL; | |
169 | } | |
170 | if (netagent_mtx_grp != NULL) { | |
171 | lck_grp_free(netagent_mtx_grp); | |
172 | netagent_mtx_grp = NULL; | |
173 | } | |
174 | if (netagent_grp_attr != NULL) { | |
175 | lck_grp_attr_free(netagent_grp_attr); | |
176 | netagent_grp_attr = NULL; | |
177 | } | |
178 | if (netagent_kctlref != NULL) { | |
179 | ctl_deregister(netagent_kctlref); | |
180 | netagent_kctlref = NULL; | |
181 | } | |
182 | } | |
183 | return (result); | |
184 | } | |
185 | ||
186 | static errno_t | |
187 | netagent_register_control(void) | |
188 | { | |
189 | struct kern_ctl_reg kern_ctl; | |
190 | errno_t result = 0; | |
191 | ||
192 | // Create a tag to allocate memory | |
193 | netagent_malloc_tag = OSMalloc_Tagalloc(NETAGENT_CONTROL_NAME, OSMT_DEFAULT); | |
194 | ||
195 | // Find a unique value for our interface family | |
196 | result = mbuf_tag_id_find(NETAGENT_CONTROL_NAME, &netagent_family); | |
197 | if (result != 0) { | |
198 | NETAGENTLOG(LOG_ERR, "mbuf_tag_id_find_internal failed: %d", result); | |
199 | return (result); | |
200 | } | |
201 | ||
202 | bzero(&kern_ctl, sizeof(kern_ctl)); | |
203 | strlcpy(kern_ctl.ctl_name, NETAGENT_CONTROL_NAME, sizeof(kern_ctl.ctl_name)); | |
204 | kern_ctl.ctl_name[sizeof(kern_ctl.ctl_name) - 1] = 0; | |
205 | kern_ctl.ctl_flags = CTL_FLAG_PRIVILEGED; // Require root | |
206 | kern_ctl.ctl_sendsize = 64 * 1024; | |
207 | kern_ctl.ctl_recvsize = 64 * 1024; | |
208 | kern_ctl.ctl_connect = netagent_ctl_connect; | |
209 | kern_ctl.ctl_disconnect = netagent_ctl_disconnect; | |
210 | kern_ctl.ctl_send = netagent_ctl_send; | |
211 | kern_ctl.ctl_rcvd = netagent_ctl_rcvd; | |
212 | kern_ctl.ctl_setopt = netagent_ctl_setopt; | |
213 | kern_ctl.ctl_getopt = netagent_ctl_getopt; | |
214 | ||
215 | result = ctl_register(&kern_ctl, &netagent_kctlref); | |
216 | if (result != 0) { | |
217 | NETAGENTLOG(LOG_ERR, "ctl_register failed: %d", result); | |
218 | return (result); | |
219 | } | |
220 | ||
221 | return (0); | |
222 | } | |
223 | ||
224 | static errno_t | |
225 | netagent_ctl_connect(kern_ctl_ref kctlref, struct sockaddr_ctl *sac, void **unitinfo) | |
226 | { | |
227 | #pragma unused(kctlref) | |
228 | *unitinfo = netagent_create_session(sac->sc_unit); | |
229 | if (*unitinfo == NULL) { | |
230 | // Could not allocate session | |
231 | return (ENOBUFS); | |
232 | } | |
233 | ||
234 | return (0); | |
235 | } | |
236 | ||
237 | static errno_t | |
238 | netagent_ctl_disconnect(kern_ctl_ref kctlref, u_int32_t unit, void *unitinfo) | |
239 | { | |
240 | #pragma unused(kctlref, unit) | |
241 | struct netagent_session *session = (struct netagent_session *)unitinfo; | |
242 | if (session != NULL) { | |
243 | netagent_delete_session(session); | |
244 | } | |
245 | ||
246 | return (0); | |
247 | } | |
248 | ||
249 | // Kernel events | |
250 | static void | |
251 | netagent_post_event(uuid_t agent_uuid, u_int32_t event_code) | |
252 | { | |
253 | struct kev_msg ev_msg; | |
254 | memset(&ev_msg, 0, sizeof(ev_msg)); | |
255 | ||
256 | struct kev_netagent_data event_data; | |
257 | ||
258 | ev_msg.vendor_code = KEV_VENDOR_APPLE; | |
259 | ev_msg.kev_class = KEV_NETWORK_CLASS; | |
260 | ev_msg.kev_subclass = KEV_NETAGENT_SUBCLASS; | |
261 | ev_msg.event_code = event_code; | |
262 | ||
263 | uuid_copy(event_data.netagent_uuid, agent_uuid); | |
264 | ev_msg.dv[0].data_ptr = &event_data; | |
265 | ev_msg.dv[0].data_length = sizeof(event_data); | |
266 | ||
267 | kev_post_msg(&ev_msg); | |
268 | } | |
269 | ||
270 | // Message handling | |
271 | static u_int8_t * | |
272 | netagent_buffer_write_message_header(u_int8_t *buffer, u_int8_t message_type, u_int8_t flags, | |
273 | u_int32_t message_id, u_int32_t error, u_int32_t payload_length) | |
274 | { | |
275 | ((struct netagent_message_header *)(void *)buffer)->message_type = message_type; | |
276 | ((struct netagent_message_header *)(void *)buffer)->message_flags = flags; | |
277 | ((struct netagent_message_header *)(void *)buffer)->message_id = message_id; | |
278 | ((struct netagent_message_header *)(void *)buffer)->message_error = error; | |
279 | ((struct netagent_message_header *)(void *)buffer)->message_payload_length = payload_length; | |
280 | return (buffer + sizeof(struct netagent_message_header)); | |
281 | } | |
282 | ||
283 | static int | |
284 | netagent_send_ctl_data(u_int32_t control_unit, u_int8_t *buffer, size_t buffer_size) | |
285 | { | |
286 | if (netagent_kctlref == NULL || control_unit == 0 || buffer == NULL || buffer_size == 0) { | |
287 | return (EINVAL); | |
288 | } | |
289 | ||
290 | return ctl_enqueuedata(netagent_kctlref, control_unit, buffer, buffer_size, CTL_DATA_EOR); | |
291 | } | |
292 | ||
293 | static int | |
294 | netagent_send_trigger(struct netagent_wrapper *wrapper, struct proc *p, u_int32_t flags, u_int32_t trigger_type) | |
295 | { | |
296 | int error = 0; | |
297 | struct netagent_trigger_message *trigger_message = NULL; | |
298 | u_int8_t *trigger = NULL; | |
299 | size_t trigger_size = sizeof(struct netagent_message_header) + sizeof(struct netagent_trigger_message); | |
300 | ||
301 | MALLOC(trigger, u_int8_t *, trigger_size, M_NETAGENT, M_WAITOK); | |
302 | if (trigger == NULL) { | |
303 | return (ENOMEM); | |
304 | } | |
305 | ||
306 | (void)netagent_buffer_write_message_header(trigger, trigger_type, 0, 0, 0, sizeof(struct netagent_trigger_message)); | |
307 | ||
308 | trigger_message = (struct netagent_trigger_message *)(void *)(trigger + sizeof(struct netagent_message_header)); | |
309 | trigger_message->trigger_flags = flags; | |
310 | if (p != NULL) { | |
311 | trigger_message->trigger_pid = proc_pid(p); | |
312 | proc_getexecutableuuid(p, trigger_message->trigger_proc_uuid, sizeof(trigger_message->trigger_proc_uuid)); | |
313 | } else { | |
314 | trigger_message->trigger_pid = 0; | |
315 | uuid_clear(trigger_message->trigger_proc_uuid); | |
316 | } | |
317 | ||
318 | if ((error = netagent_send_ctl_data(wrapper->control_unit, (u_int8_t *)trigger, trigger_size))) { | |
319 | NETAGENTLOG(LOG_ERR, "Failed to send trigger message on control unit %d", wrapper->control_unit); | |
320 | } | |
321 | ||
322 | FREE(trigger, M_NETAGENT); | |
323 | return (error); | |
324 | } | |
325 | ||
326 | static int | |
327 | netagent_send_success_response(struct netagent_session *session, u_int8_t message_type, u_int32_t message_id) | |
328 | { | |
329 | int error = 0; | |
330 | u_int8_t *response = NULL; | |
331 | size_t response_size = sizeof(struct netagent_message_header); | |
332 | MALLOC(response, u_int8_t *, response_size, M_NETAGENT, M_WAITOK); | |
333 | if (response == NULL) { | |
334 | return (ENOMEM); | |
335 | } | |
336 | (void)netagent_buffer_write_message_header(response, message_type, NETAGENT_MESSAGE_FLAGS_RESPONSE, message_id, 0, 0); | |
337 | ||
338 | if ((error = netagent_send_ctl_data(session->control_unit, (u_int8_t *)response, response_size))) { | |
339 | NETAGENTLOG0(LOG_ERR, "Failed to send response"); | |
340 | } | |
341 | ||
342 | FREE(response, M_NETAGENT); | |
343 | return (error); | |
344 | } | |
345 | ||
346 | static int | |
347 | netagent_send_error_response(struct netagent_session *session, u_int8_t message_type, | |
348 | u_int32_t message_id, u_int32_t error_code) | |
349 | { | |
350 | int error = 0; | |
351 | u_int8_t *response = NULL; | |
352 | size_t response_size = sizeof(struct netagent_message_header); | |
353 | MALLOC(response, u_int8_t *, response_size, M_NETAGENT, M_WAITOK); | |
354 | if (response == NULL) { | |
355 | return (ENOMEM); | |
356 | } | |
357 | (void)netagent_buffer_write_message_header(response, message_type, NETAGENT_MESSAGE_FLAGS_RESPONSE, | |
358 | message_id, error_code, 0); | |
359 | ||
360 | if ((error = netagent_send_ctl_data(session->control_unit, (u_int8_t *)response, response_size))) { | |
361 | NETAGENTLOG0(LOG_ERR, "Failed to send response"); | |
362 | } | |
363 | ||
364 | FREE(response, M_NETAGENT); | |
365 | return (error); | |
366 | } | |
367 | ||
368 | static errno_t | |
369 | netagent_ctl_send(kern_ctl_ref kctlref, u_int32_t unit, void *unitinfo, mbuf_t packet, int flags) | |
370 | { | |
371 | #pragma unused(kctlref, unit, flags) | |
372 | struct netagent_session *session = (struct netagent_session *)unitinfo; | |
373 | struct netagent_message_header header; | |
374 | int error = 0; | |
375 | ||
376 | if (session == NULL) { | |
377 | NETAGENTLOG0(LOG_ERR, "Got a NULL session"); | |
378 | error = EINVAL; | |
379 | goto done; | |
380 | } | |
381 | ||
382 | if (mbuf_pkthdr_len(packet) < sizeof(header)) { | |
383 | NETAGENTLOG(LOG_ERR, "Got a bad packet, length (%lu) < sizeof header (%lu)", | |
384 | mbuf_pkthdr_len(packet), sizeof(header)); | |
385 | error = EINVAL; | |
386 | goto done; | |
387 | } | |
388 | ||
389 | error = mbuf_copydata(packet, 0, sizeof(header), &header); | |
390 | if (error) { | |
391 | NETAGENTLOG(LOG_ERR, "mbuf_copydata failed for the header: %d", error); | |
392 | error = ENOBUFS; | |
393 | goto done; | |
394 | } | |
395 | ||
396 | switch (header.message_type) { | |
397 | case NETAGENT_MESSAGE_TYPE_REGISTER: { | |
398 | netagent_handle_register(session, header.message_id, header.message_payload_length, | |
399 | packet, sizeof(header)); | |
400 | break; | |
401 | } | |
402 | case NETAGENT_MESSAGE_TYPE_UNREGISTER: { | |
403 | netagent_handle_unregister(session, header.message_id, header.message_payload_length, | |
404 | packet, sizeof(header)); | |
405 | break; | |
406 | } | |
407 | case NETAGENT_MESSAGE_TYPE_UPDATE: { | |
408 | netagent_handle_update(session, header.message_id, header.message_payload_length, | |
409 | packet, sizeof(header)); | |
410 | break; | |
411 | } | |
412 | case NETAGENT_MESSAGE_TYPE_GET: { | |
413 | netagent_handle_get(session, header.message_id, header.message_payload_length, | |
414 | packet, sizeof(header)); | |
415 | break; | |
416 | } | |
417 | case NETAGENT_MESSAGE_TYPE_ASSERT: { | |
418 | netagent_handle_assert(session, header.message_id, header.message_payload_length, | |
419 | packet, sizeof(header)); | |
420 | break; | |
421 | } | |
422 | case NETAGENT_MESSAGE_TYPE_UNASSERT: { | |
423 | netagent_handle_unassert(session, header.message_id, header.message_payload_length, | |
424 | packet, sizeof(header)); | |
425 | break; | |
426 | } | |
427 | default: { | |
428 | NETAGENTLOG(LOG_ERR, "Received unknown message type %d", header.message_type); | |
429 | netagent_send_error_response(session, header.message_type, header.message_id, | |
430 | NETAGENT_MESSAGE_ERROR_UNKNOWN_TYPE); | |
431 | break; | |
432 | } | |
433 | } | |
434 | ||
435 | done: | |
436 | mbuf_freem(packet); | |
437 | return (error); | |
438 | } | |
439 | ||
440 | static void | |
441 | netagent_ctl_rcvd(kern_ctl_ref kctlref, u_int32_t unit, void *unitinfo, int flags) | |
442 | { | |
443 | #pragma unused(kctlref, unit, unitinfo, flags) | |
444 | return; | |
445 | } | |
446 | ||
447 | static errno_t | |
448 | netagent_ctl_getopt(kern_ctl_ref kctlref, u_int32_t unit, void *unitinfo, int opt, | |
449 | void *data, size_t *len) | |
450 | { | |
451 | #pragma unused(kctlref, unit, unitinfo, opt, data, len) | |
452 | return (0); | |
453 | } | |
454 | ||
455 | static errno_t | |
456 | netagent_ctl_setopt(kern_ctl_ref kctlref, u_int32_t unit, void *unitinfo, int opt, | |
457 | void *data, size_t len) | |
458 | { | |
459 | #pragma unused(kctlref, unit, unitinfo, opt, data, len) | |
460 | return (0); | |
461 | } | |
462 | ||
463 | // Session Management | |
464 | static struct netagent_session * | |
465 | netagent_create_session(u_int32_t control_unit) | |
466 | { | |
467 | struct netagent_session *new_session = NULL; | |
468 | ||
469 | MALLOC(new_session, struct netagent_session *, sizeof(*new_session), M_NETAGENT, M_WAITOK); | |
470 | if (new_session == NULL) { | |
471 | goto done; | |
472 | } | |
473 | NETAGENTLOG(LOG_DEBUG, "Create agent session, control unit %d", control_unit); | |
474 | memset(new_session, 0, sizeof(*new_session)); | |
475 | new_session->control_unit = control_unit; | |
476 | LIST_INIT(&new_session->assertion_list); | |
477 | new_session->wrapper = NULL; | |
478 | done: | |
479 | return (new_session); | |
480 | } | |
481 | ||
482 | static void | |
483 | netagent_unregister_session_wrapper(struct netagent_session *session) | |
484 | { | |
485 | bool unregistered = FALSE; | |
486 | uuid_t unregistered_uuid; | |
487 | struct netagent_wrapper *wrapper = NULL; | |
488 | lck_rw_lock_exclusive(&netagent_lock); | |
489 | if (session != NULL) { | |
490 | wrapper = session->wrapper; | |
491 | if (wrapper != NULL) { | |
492 | if (netagent_registered_count > 0) { | |
493 | netagent_registered_count--; | |
494 | } | |
495 | if ((session->wrapper->netagent.netagent_flags & NETAGENT_FLAG_ACTIVE) && | |
496 | netagent_active_count > 0) { | |
497 | netagent_active_count--; | |
498 | } | |
499 | ||
500 | LIST_REMOVE(wrapper, master_chain); | |
501 | ||
502 | unregistered = TRUE; | |
503 | uuid_copy(unregistered_uuid, session->wrapper->netagent.netagent_uuid); | |
504 | ||
505 | FREE(wrapper, M_NETAGENT); | |
506 | session->wrapper = NULL; | |
507 | NETAGENTLOG0(LOG_DEBUG, "Unregistered agent"); | |
508 | } | |
509 | } | |
510 | lck_rw_done(&netagent_lock); | |
511 | ||
512 | if (unregistered) { | |
513 | netagent_post_event(unregistered_uuid, KEV_NETAGENT_UNREGISTERED); | |
514 | ifnet_clear_netagent(unregistered_uuid); | |
515 | } | |
516 | } | |
517 | ||
518 | static void | |
519 | netagent_delete_session(struct netagent_session *session) | |
520 | { | |
521 | if (session != NULL) { | |
522 | netagent_unregister_session_wrapper(session); | |
523 | ||
524 | // Unassert any pending assertions | |
525 | lck_rw_lock_shared(&netagent_lock); | |
526 | struct netagent_assertion *search_assertion = NULL; | |
527 | struct netagent_assertion *temp_assertion = NULL; | |
528 | LIST_FOREACH_SAFE(search_assertion, &session->assertion_list, assertion_chain, temp_assertion) { | |
529 | struct netagent_wrapper *wrapper = netagent_find_agent_with_uuid(search_assertion->asserted_uuid); | |
530 | if (wrapper != NULL) { | |
531 | netagent_send_trigger(wrapper, current_proc(), NETAGENT_TRIGGER_FLAG_USER, NETAGENT_MESSAGE_TYPE_TRIGGER_UNASSERT); | |
532 | } | |
533 | LIST_REMOVE(search_assertion, assertion_chain); | |
534 | FREE(search_assertion, M_NETAGENT); | |
535 | } | |
536 | lck_rw_done(&netagent_lock); | |
537 | ||
538 | FREE(session, M_NETAGENT); | |
539 | } | |
540 | } | |
541 | ||
542 | static int | |
543 | netagent_packet_get_netagent_data_size(mbuf_t packet, int offset, int *err) | |
544 | { | |
545 | int error = 0; | |
546 | ||
547 | struct netagent netagent_peek; | |
548 | memset(&netagent_peek, 0, sizeof(netagent_peek)); | |
549 | ||
550 | *err = 0; | |
551 | ||
552 | error = mbuf_copydata(packet, offset, sizeof(netagent_peek), &netagent_peek); | |
553 | if (error) { | |
554 | *err = ENOENT; | |
555 | return (-1); | |
556 | } | |
557 | ||
558 | return (netagent_peek.netagent_data_size); | |
559 | } | |
560 | ||
561 | static void | |
562 | netagent_handle_register(struct netagent_session *session, u_int32_t message_id, | |
563 | u_int32_t payload_length, mbuf_t packet, int offset) | |
564 | { | |
565 | int error; | |
566 | int data_size = 0; | |
567 | struct netagent_wrapper *new_wrapper = NULL; | |
568 | u_int32_t response_error = NETAGENT_MESSAGE_ERROR_INTERNAL; | |
569 | uuid_t netagent_uuid; | |
570 | uuid_clear(netagent_uuid); | |
571 | ||
572 | if (session == NULL) { | |
573 | NETAGENTLOG0(LOG_ERR, "Failed to find session"); | |
574 | response_error = NETAGENT_MESSAGE_ERROR_INTERNAL; | |
575 | goto fail; | |
576 | } | |
577 | ||
578 | if (session->wrapper != NULL) { | |
579 | NETAGENTLOG0(LOG_ERR, "Session already has a registered agent"); | |
580 | response_error = NETAGENT_MESSAGE_ERROR_ALREADY_REGISTERED; | |
581 | goto fail; | |
582 | } | |
583 | ||
584 | if (payload_length < sizeof(struct netagent)) { | |
585 | NETAGENTLOG(LOG_ERR, "Register message size too small for agent: (%d < %d)", | |
586 | payload_length, sizeof(struct netagent)); | |
587 | response_error = NETAGENT_MESSAGE_ERROR_INVALID_DATA; | |
588 | goto fail; | |
589 | } | |
590 | ||
591 | data_size = netagent_packet_get_netagent_data_size(packet, offset, &error); | |
592 | if (error || data_size < 0 || data_size > NETAGENT_MAX_DATA_SIZE) { | |
593 | NETAGENTLOG(LOG_ERR, "Register message size could not be read, error %d data_size %d", | |
594 | error, data_size); | |
595 | response_error = NETAGENT_MESSAGE_ERROR_INVALID_DATA; | |
596 | goto fail; | |
597 | } | |
598 | ||
599 | MALLOC(new_wrapper, struct netagent_wrapper *, sizeof(*new_wrapper) + data_size, M_NETAGENT, M_WAITOK); | |
600 | if (new_wrapper == NULL) { | |
601 | NETAGENTLOG0(LOG_ERR, "Failed to allocate agent"); | |
602 | response_error = NETAGENT_MESSAGE_ERROR_INTERNAL; | |
603 | goto fail; | |
604 | } | |
605 | ||
606 | memset(new_wrapper, 0, sizeof(*new_wrapper) + data_size); | |
607 | ||
608 | error = mbuf_copydata(packet, offset, sizeof(struct netagent) + data_size, | |
609 | &new_wrapper->netagent); | |
610 | if (error) { | |
611 | NETAGENTLOG(LOG_ERR, "Failed to read data into agent structure: %d", error); | |
612 | FREE(new_wrapper, M_NETAGENT); | |
613 | response_error = NETAGENT_MESSAGE_ERROR_INTERNAL; | |
614 | goto fail; | |
615 | } | |
616 | ||
617 | lck_rw_lock_exclusive(&netagent_lock); | |
618 | ||
619 | new_wrapper->control_unit = session->control_unit; | |
620 | ||
621 | session->wrapper = new_wrapper; | |
622 | LIST_INSERT_HEAD(&master_netagent_list, new_wrapper, master_chain); | |
623 | ||
624 | new_wrapper->netagent.netagent_flags |= NETAGENT_FLAG_REGISTERED; | |
625 | netagent_registered_count++; | |
626 | if (new_wrapper->netagent.netagent_flags & NETAGENT_FLAG_ACTIVE) { | |
627 | netagent_active_count++; | |
628 | } | |
629 | ||
630 | lck_rw_done(&netagent_lock); | |
631 | ||
632 | NETAGENTLOG0(LOG_DEBUG, "Registered new agent"); | |
633 | netagent_send_success_response(session, NETAGENT_MESSAGE_TYPE_REGISTER, message_id); | |
634 | netagent_post_event(new_wrapper->netagent.netagent_uuid, KEV_NETAGENT_REGISTERED); | |
635 | return; | |
636 | fail: | |
637 | netagent_send_error_response(session, NETAGENT_MESSAGE_TYPE_REGISTER, message_id, response_error); | |
638 | } | |
639 | ||
640 | static void | |
641 | netagent_handle_unregister(struct netagent_session *session, u_int32_t message_id, | |
642 | u_int32_t payload_length, mbuf_t packet, int offset) | |
643 | { | |
644 | #pragma unused(payload_length, packet, offset) | |
645 | u_int32_t response_error = NETAGENT_MESSAGE_ERROR_INTERNAL; | |
646 | ||
647 | if (session == NULL) { | |
648 | NETAGENTLOG0(LOG_ERR, "Failed to find session"); | |
649 | response_error = NETAGENT_MESSAGE_ERROR_INTERNAL; | |
650 | goto fail; | |
651 | } | |
652 | ||
653 | netagent_unregister_session_wrapper(session); | |
654 | ||
655 | netagent_send_success_response(session, NETAGENT_MESSAGE_TYPE_UNREGISTER, message_id); | |
656 | return; | |
657 | fail: | |
658 | netagent_send_error_response(session, NETAGENT_MESSAGE_TYPE_UNREGISTER, message_id, response_error); | |
659 | } | |
660 | ||
661 | static void | |
662 | netagent_handle_update(struct netagent_session *session, u_int32_t message_id, | |
663 | u_int32_t payload_length, mbuf_t packet, int offset) | |
664 | { | |
665 | int error; | |
666 | int data_size = 0; | |
667 | struct netagent_wrapper *new_wrapper = NULL; | |
668 | u_int32_t response_error = NETAGENT_MESSAGE_ERROR_INTERNAL; | |
669 | uuid_t netagent_uuid; | |
670 | uuid_clear(netagent_uuid); | |
671 | ||
672 | if (session == NULL) { | |
673 | NETAGENTLOG0(LOG_ERR, "Failed to find session"); | |
674 | response_error = NETAGENT_MESSAGE_ERROR_INTERNAL; | |
675 | goto fail; | |
676 | } | |
677 | ||
678 | if (session->wrapper == NULL) { | |
679 | NETAGENTLOG0(LOG_ERR, "Session has no agent to update"); | |
680 | response_error = NETAGENT_MESSAGE_ERROR_NOT_REGISTERED; | |
681 | goto fail; | |
682 | } | |
683 | ||
684 | if (payload_length < sizeof(struct netagent)) { | |
685 | NETAGENTLOG(LOG_ERR, "Update message size too small for agent: (%d < %d)", | |
686 | payload_length, sizeof(struct netagent)); | |
687 | response_error = NETAGENT_MESSAGE_ERROR_INVALID_DATA; | |
688 | goto fail; | |
689 | } | |
690 | ||
691 | data_size = netagent_packet_get_netagent_data_size(packet, offset, &error); | |
692 | if (error || data_size < 0 || data_size > NETAGENT_MAX_DATA_SIZE) { | |
693 | NETAGENTLOG(LOG_ERR, "Update message size could not be read, error %d data_size %d", | |
694 | error, data_size); | |
695 | response_error = NETAGENT_MESSAGE_ERROR_INVALID_DATA; | |
696 | goto fail; | |
697 | } | |
698 | ||
699 | MALLOC(new_wrapper, struct netagent_wrapper *, sizeof(*new_wrapper) + data_size, M_NETAGENT, M_WAITOK); | |
700 | if (new_wrapper == NULL) { | |
701 | NETAGENTLOG0(LOG_ERR, "Failed to allocate agent"); | |
702 | response_error = NETAGENT_MESSAGE_ERROR_INTERNAL; | |
703 | goto fail; | |
704 | } | |
705 | ||
706 | memset(new_wrapper, 0, sizeof(*new_wrapper) + data_size); | |
707 | ||
708 | error = mbuf_copydata(packet, offset, sizeof(struct netagent) + data_size, &new_wrapper->netagent); | |
709 | if (error) { | |
710 | NETAGENTLOG(LOG_ERR, "Failed to read data into agent structure: %d", error); | |
711 | FREE(new_wrapper, M_NETAGENT); | |
712 | response_error = NETAGENT_MESSAGE_ERROR_INTERNAL; | |
713 | goto fail; | |
714 | } | |
715 | ||
716 | lck_rw_lock_exclusive(&netagent_lock); | |
717 | ||
718 | if (uuid_compare(session->wrapper->netagent.netagent_uuid, new_wrapper->netagent.netagent_uuid) != 0 || | |
719 | memcmp(&session->wrapper->netagent.netagent_domain, &new_wrapper->netagent.netagent_domain, | |
720 | sizeof(new_wrapper->netagent.netagent_domain)) != 0 || | |
721 | memcmp(&session->wrapper->netagent.netagent_type, &new_wrapper->netagent.netagent_type, | |
722 | sizeof(new_wrapper->netagent.netagent_type)) != 0) { | |
723 | NETAGENTLOG0(LOG_ERR, "Basic agent parameters do not match, cannot update"); | |
724 | FREE(new_wrapper, M_NETAGENT); | |
725 | response_error = NETAGENT_MESSAGE_ERROR_CANNOT_UPDATE; | |
726 | lck_rw_done(&netagent_lock); | |
727 | goto fail; | |
728 | } | |
729 | ||
730 | new_wrapper->netagent.netagent_flags |= NETAGENT_FLAG_REGISTERED; | |
731 | if ((new_wrapper->netagent.netagent_flags & NETAGENT_FLAG_ACTIVE) && | |
732 | !(session->wrapper->netagent.netagent_flags & NETAGENT_FLAG_ACTIVE)) { | |
733 | netagent_active_count++; | |
734 | } else if (!(new_wrapper->netagent.netagent_flags & NETAGENT_FLAG_ACTIVE) && | |
735 | (session->wrapper->netagent.netagent_flags & NETAGENT_FLAG_ACTIVE) && | |
736 | netagent_active_count > 0) { | |
737 | netagent_active_count--; | |
738 | } | |
739 | ||
740 | LIST_REMOVE(session->wrapper, master_chain); | |
741 | FREE(session->wrapper, M_NETAGENT); | |
742 | session->wrapper = new_wrapper; | |
743 | new_wrapper->control_unit = session->control_unit; | |
744 | LIST_INSERT_HEAD(&master_netagent_list, new_wrapper, master_chain); | |
745 | ||
746 | lck_rw_done(&netagent_lock); | |
747 | ||
748 | NETAGENTLOG0(LOG_DEBUG, "Updated agent"); | |
749 | netagent_send_success_response(session, NETAGENT_MESSAGE_TYPE_UPDATE, message_id); | |
750 | netagent_post_event(new_wrapper->netagent.netagent_uuid, KEV_NETAGENT_UPDATED); | |
751 | return; | |
752 | fail: | |
753 | netagent_send_error_response(session, NETAGENT_MESSAGE_TYPE_UPDATE, message_id, response_error); | |
754 | } | |
755 | ||
756 | static void | |
757 | netagent_handle_get(struct netagent_session *session, u_int32_t message_id, | |
758 | u_int32_t payload_length, mbuf_t packet, int offset) | |
759 | { | |
760 | #pragma unused(payload_length, packet, offset) | |
761 | u_int8_t *response = NULL; | |
762 | u_int8_t *cursor = NULL; | |
763 | u_int32_t response_error = NETAGENT_MESSAGE_ERROR_INTERNAL; | |
764 | ||
765 | if (session == NULL) { | |
766 | NETAGENTLOG0(LOG_ERR, "Failed to find session"); | |
767 | response_error = NETAGENT_MESSAGE_ERROR_INTERNAL; | |
768 | goto fail; | |
769 | } | |
770 | ||
771 | if (session->wrapper == NULL) { | |
772 | NETAGENTLOG0(LOG_ERR, "Session has no agent to get"); | |
773 | response_error = NETAGENT_MESSAGE_ERROR_NOT_REGISTERED; | |
774 | goto fail; | |
775 | } | |
776 | ||
777 | lck_rw_lock_shared(&netagent_lock); | |
778 | ||
779 | size_t response_size = sizeof(struct netagent_message_header) + sizeof(session->wrapper->netagent) | |
780 | + session->wrapper->netagent.netagent_data_size; | |
781 | MALLOC(response, u_int8_t *, response_size, M_NETAGENT, M_WAITOK); | |
782 | if (response == NULL) { | |
783 | goto fail; | |
784 | } | |
785 | ||
786 | cursor = response; | |
787 | cursor = netagent_buffer_write_message_header(cursor, NETAGENT_MESSAGE_TYPE_GET, | |
788 | NETAGENT_MESSAGE_FLAGS_RESPONSE, message_id, 0, | |
789 | response_size - sizeof(struct netagent_message_header)); | |
790 | memcpy(cursor, &session->wrapper->netagent, sizeof(session->wrapper->netagent) + | |
791 | session->wrapper->netagent.netagent_data_size); | |
792 | ||
793 | lck_rw_done(&netagent_lock); | |
794 | ||
795 | if (!netagent_send_ctl_data(session->control_unit, (u_int8_t *)response, response_size)) { | |
796 | NETAGENTLOG0(LOG_ERR, "Failed to send response"); | |
797 | } | |
798 | FREE(response, M_NETAGENT); | |
799 | return; | |
800 | fail: | |
801 | netagent_send_error_response(session, NETAGENT_MESSAGE_TYPE_GET, message_id, response_error); | |
802 | } | |
803 | ||
804 | static void | |
805 | netagent_handle_assert(struct netagent_session *session, u_int32_t message_id, | |
806 | u_int32_t payload_length, mbuf_t packet, int offset) | |
807 | { | |
808 | int error; | |
809 | struct netagent_assertion *new_assertion = NULL; | |
810 | u_int32_t response_error = NETAGENT_MESSAGE_ERROR_INTERNAL; | |
811 | uuid_t netagent_uuid; | |
812 | uuid_clear(netagent_uuid); | |
813 | ||
814 | if (session == NULL) { | |
815 | NETAGENTLOG0(LOG_ERR, "Failed to find session"); | |
816 | response_error = NETAGENT_MESSAGE_ERROR_INTERNAL; | |
817 | goto fail; | |
818 | } | |
819 | ||
820 | if (payload_length < sizeof(uuid_t)) { | |
821 | NETAGENTLOG(LOG_ERR, "Assert message size too small for uuid: (%d < %d)", | |
822 | payload_length, sizeof(uuid_t)); | |
823 | response_error = NETAGENT_MESSAGE_ERROR_INVALID_DATA; | |
824 | goto fail; | |
825 | } | |
826 | ||
827 | error = mbuf_copydata(packet, offset, sizeof(uuid_t), &netagent_uuid); | |
828 | if (error) { | |
829 | NETAGENTLOG(LOG_ERR, "Failed to read uuid: %d", error); | |
830 | response_error = NETAGENT_MESSAGE_ERROR_INTERNAL; | |
831 | goto fail; | |
832 | } | |
833 | ||
834 | MALLOC(new_assertion, struct netagent_assertion *, sizeof(*new_assertion), M_NETAGENT, M_WAITOK); | |
835 | if (new_assertion == NULL) { | |
836 | NETAGENTLOG0(LOG_ERR, "Failed to allocate assertion"); | |
837 | response_error = NETAGENT_MESSAGE_ERROR_INTERNAL; | |
838 | goto fail; | |
839 | } | |
840 | ||
841 | uuid_copy(new_assertion->asserted_uuid, netagent_uuid); | |
842 | ||
843 | lck_rw_lock_shared(&netagent_lock); | |
844 | ||
845 | struct netagent_wrapper *wrapper = netagent_find_agent_with_uuid(netagent_uuid); | |
846 | if (wrapper == NULL) { | |
847 | lck_rw_done(&netagent_lock); | |
848 | response_error = NETAGENT_MESSAGE_ERROR_NOT_REGISTERED; | |
849 | FREE(new_assertion, M_NETAGENT); | |
850 | goto fail; | |
851 | } | |
852 | ||
853 | error = netagent_send_trigger(wrapper, current_proc(), NETAGENT_TRIGGER_FLAG_USER, NETAGENT_MESSAGE_TYPE_TRIGGER_ASSERT); | |
854 | if (error) { | |
855 | lck_rw_done(&netagent_lock); | |
856 | NETAGENTLOG(LOG_ERR, "Failed to trigger assert agent: %d", error); | |
857 | response_error = NETAGENT_MESSAGE_ERROR_INTERNAL; | |
858 | FREE(new_assertion, M_NETAGENT); | |
859 | goto fail; | |
860 | } | |
861 | ||
862 | LIST_INSERT_HEAD(&session->assertion_list, new_assertion, assertion_chain); | |
863 | ||
864 | lck_rw_done(&netagent_lock); | |
865 | ||
866 | NETAGENTLOG0(LOG_DEBUG, "Asserted agent"); | |
867 | netagent_send_success_response(session, NETAGENT_MESSAGE_TYPE_ASSERT, message_id); | |
868 | return; | |
869 | fail: | |
870 | netagent_send_error_response(session, NETAGENT_MESSAGE_TYPE_ASSERT, message_id, response_error); | |
871 | } | |
872 | ||
873 | static void | |
874 | netagent_handle_unassert(struct netagent_session *session, u_int32_t message_id, | |
875 | u_int32_t payload_length, mbuf_t packet, int offset) | |
876 | { | |
877 | int error; | |
878 | u_int32_t response_error = NETAGENT_MESSAGE_ERROR_INTERNAL; | |
879 | uuid_t netagent_uuid; | |
880 | uuid_clear(netagent_uuid); | |
881 | ||
882 | if (session == NULL) { | |
883 | NETAGENTLOG0(LOG_ERR, "Failed to find session"); | |
884 | response_error = NETAGENT_MESSAGE_ERROR_INTERNAL; | |
885 | goto fail; | |
886 | } | |
887 | ||
888 | if (payload_length < sizeof(uuid_t)) { | |
889 | NETAGENTLOG(LOG_ERR, "Unassert message size too small for uuid: (%d < %d)", | |
890 | payload_length, sizeof(uuid_t)); | |
891 | response_error = NETAGENT_MESSAGE_ERROR_INVALID_DATA; | |
892 | goto fail; | |
893 | } | |
894 | ||
895 | error = mbuf_copydata(packet, offset, sizeof(uuid_t), &netagent_uuid); | |
896 | if (error) { | |
897 | NETAGENTLOG(LOG_ERR, "Failed to read uuid: %d", error); | |
898 | response_error = NETAGENT_MESSAGE_ERROR_INTERNAL; | |
899 | goto fail; | |
900 | } | |
901 | ||
902 | struct netagent_assertion *found_assertion = NULL; | |
903 | struct netagent_assertion *search_assertion = NULL; | |
904 | LIST_FOREACH(search_assertion, &session->assertion_list, assertion_chain) { | |
905 | if (uuid_compare(search_assertion->asserted_uuid, netagent_uuid) == 0) { | |
906 | found_assertion = search_assertion; | |
907 | break; | |
908 | } | |
909 | } | |
910 | ||
911 | if (found_assertion == NULL) { | |
912 | NETAGENTLOG0(LOG_ERR, "Netagent uuid not previously asserted"); | |
913 | response_error = NETAGENT_MESSAGE_ERROR_INVALID_DATA; | |
914 | goto fail; | |
915 | } | |
916 | ||
917 | LIST_REMOVE(found_assertion, assertion_chain); | |
918 | FREE(found_assertion, M_NETAGENT); | |
919 | found_assertion = NULL; | |
920 | ||
921 | lck_rw_lock_shared(&netagent_lock); | |
922 | ||
923 | struct netagent_wrapper *wrapper = netagent_find_agent_with_uuid(netagent_uuid); | |
924 | if (wrapper == NULL) { | |
925 | lck_rw_done(&netagent_lock); | |
926 | response_error = NETAGENT_MESSAGE_ERROR_NOT_REGISTERED; | |
927 | goto fail; | |
928 | } | |
929 | ||
930 | error = netagent_send_trigger(wrapper, current_proc(), NETAGENT_TRIGGER_FLAG_USER, NETAGENT_MESSAGE_TYPE_TRIGGER_UNASSERT); | |
931 | if (error) { | |
932 | lck_rw_done(&netagent_lock); | |
933 | NETAGENTLOG(LOG_ERR, "Failed to trigger assert agent: %d", error); | |
934 | response_error = NETAGENT_MESSAGE_ERROR_INTERNAL; | |
935 | goto fail; | |
936 | } | |
937 | ||
938 | lck_rw_done(&netagent_lock); | |
939 | ||
940 | NETAGENTLOG0(LOG_DEBUG, "Unasserted agent"); | |
941 | netagent_send_success_response(session, NETAGENT_MESSAGE_TYPE_UNASSERT, message_id); | |
942 | return; | |
943 | fail: | |
944 | netagent_send_error_response(session, NETAGENT_MESSAGE_TYPE_UNASSERT, message_id, response_error); | |
945 | } | |
946 | ||
947 | static struct netagent_wrapper * | |
948 | netagent_find_agent_with_uuid(uuid_t uuid) | |
949 | { | |
950 | struct netagent_wrapper *search_netagent = NULL; | |
951 | ||
952 | LIST_FOREACH(search_netagent, &master_netagent_list, master_chain) { | |
953 | if (uuid_compare(search_netagent->netagent.netagent_uuid, uuid) == 0) { | |
954 | return (search_netagent); | |
955 | } | |
956 | } | |
957 | ||
958 | return (NULL); | |
959 | } | |
960 | ||
961 | void | |
962 | netagent_post_updated_interfaces(uuid_t uuid) | |
963 | { | |
964 | struct netagent_wrapper *wrapper = NULL; | |
965 | lck_rw_lock_shared(&netagent_lock); | |
966 | wrapper = netagent_find_agent_with_uuid(uuid); | |
967 | lck_rw_done(&netagent_lock); | |
968 | ||
969 | if (wrapper != NULL) { | |
970 | netagent_post_event(uuid, KEV_NETAGENT_UPDATED_INTERFACES); | |
971 | } else { | |
972 | NETAGENTLOG0(LOG_DEBUG, "Interface event with no associated agent"); | |
973 | } | |
974 | ||
975 | return; | |
976 | } | |
977 | ||
978 | int | |
979 | netagent_ioctl(u_long cmd, caddr_t data) | |
980 | { | |
981 | int error = 0; | |
982 | ||
983 | lck_rw_lock_shared(&netagent_lock); | |
984 | switch (cmd) { | |
985 | case SIOCGIFAGENTDATA32: { | |
986 | struct netagent_req32 *ifsir32 = (struct netagent_req32 *)(void *)data; | |
987 | struct netagent_wrapper *wrapper = netagent_find_agent_with_uuid(ifsir32->netagent_uuid); | |
988 | if (wrapper == NULL) { | |
989 | error = ENOENT; | |
990 | break; | |
991 | } | |
992 | uuid_copy(ifsir32->netagent_uuid, wrapper->netagent.netagent_uuid); | |
993 | memcpy(ifsir32->netagent_domain, wrapper->netagent.netagent_domain, sizeof(ifsir32->netagent_domain)); | |
994 | memcpy(ifsir32->netagent_type, wrapper->netagent.netagent_type, sizeof(ifsir32->netagent_type)); | |
995 | memcpy(ifsir32->netagent_desc, wrapper->netagent.netagent_desc, sizeof(ifsir32->netagent_desc)); | |
996 | ifsir32->netagent_flags = wrapper->netagent.netagent_flags; | |
997 | if (ifsir32->netagent_data_size == 0) { | |
998 | // First pass, client wants data size | |
999 | ifsir32->netagent_data_size = wrapper->netagent.netagent_data_size; | |
1000 | } else if (ifsir32->netagent_data != USER_ADDR_NULL && | |
1001 | ifsir32->netagent_data_size == wrapper->netagent.netagent_data_size) { | |
1002 | // Second pass, client wants data buffer filled out | |
1003 | error = copyout(wrapper->netagent.netagent_data, ifsir32->netagent_data, wrapper->netagent.netagent_data_size); | |
1004 | } else { | |
1005 | error = EINVAL; | |
1006 | } | |
1007 | break; | |
1008 | } | |
1009 | case SIOCGIFAGENTDATA64: { | |
1010 | struct netagent_req64 *ifsir64 = (struct netagent_req64 *)(void *)data; | |
1011 | struct netagent_wrapper *wrapper = netagent_find_agent_with_uuid(ifsir64->netagent_uuid); | |
1012 | if (wrapper == NULL) { | |
1013 | error = ENOENT; | |
1014 | break; | |
1015 | } | |
1016 | uuid_copy(ifsir64->netagent_uuid, wrapper->netagent.netagent_uuid); | |
1017 | memcpy(ifsir64->netagent_domain, wrapper->netagent.netagent_domain, sizeof(ifsir64->netagent_domain)); | |
1018 | memcpy(ifsir64->netagent_type, wrapper->netagent.netagent_type, sizeof(ifsir64->netagent_type)); | |
1019 | memcpy(ifsir64->netagent_desc, wrapper->netagent.netagent_desc, sizeof(ifsir64->netagent_desc)); | |
1020 | ifsir64->netagent_flags = wrapper->netagent.netagent_flags; | |
1021 | if (ifsir64->netagent_data_size == 0) { | |
1022 | // First pass, client wants data size | |
1023 | ifsir64->netagent_data_size = wrapper->netagent.netagent_data_size; | |
1024 | } else if (ifsir64->netagent_data != USER_ADDR_NULL && | |
1025 | ifsir64->netagent_data_size == wrapper->netagent.netagent_data_size) { | |
1026 | // Second pass, client wants data buffer filled out | |
1027 | error = copyout(wrapper->netagent.netagent_data, ifsir64->netagent_data, wrapper->netagent.netagent_data_size); | |
1028 | } else { | |
1029 | error = EINVAL; | |
1030 | } | |
1031 | break; | |
1032 | } | |
1033 | default: { | |
1034 | error = EINVAL; | |
1035 | break; | |
1036 | } | |
1037 | } | |
1038 | lck_rw_done(&netagent_lock); | |
1039 | return (error); | |
1040 | } | |
1041 | ||
1042 | u_int32_t | |
1043 | netagent_get_flags(uuid_t uuid) | |
1044 | { | |
1045 | u_int32_t flags = 0; | |
1046 | lck_rw_lock_shared(&netagent_lock); | |
1047 | struct netagent_wrapper *wrapper = netagent_find_agent_with_uuid(uuid); | |
1048 | if (wrapper != NULL) { | |
1049 | flags = wrapper->netagent.netagent_flags; | |
1050 | } else { | |
1051 | NETAGENTLOG0(LOG_DEBUG, "Flags requested for invalid netagent"); | |
1052 | } | |
1053 | lck_rw_done(&netagent_lock); | |
1054 | ||
1055 | return (flags); | |
1056 | } | |
1057 | ||
1058 | int | |
1059 | netagent_kernel_trigger(uuid_t uuid) | |
1060 | { | |
1061 | int error = 0; | |
1062 | ||
1063 | lck_rw_lock_shared(&netagent_lock); | |
1064 | struct netagent_wrapper *wrapper = netagent_find_agent_with_uuid(uuid); | |
1065 | if (wrapper == NULL) { | |
1066 | NETAGENTLOG0(LOG_ERR, "Requested netagent for kernel trigger could not be found"); | |
1067 | error = ENOENT; | |
1068 | goto done; | |
1069 | } | |
1070 | ||
1071 | if ((wrapper->netagent.netagent_flags & NETAGENT_FLAG_KERNEL_ACTIVATED) == 0) { | |
1072 | NETAGENTLOG0(LOG_ERR, "Requested netagent for kernel trigger is not kernel activated"); | |
1073 | // Agent does not accept kernel triggers | |
1074 | error = EINVAL; | |
1075 | goto done; | |
1076 | } | |
1077 | ||
1078 | if ((wrapper->netagent.netagent_flags & NETAGENT_FLAG_ACTIVE)) { | |
1079 | // Agent already active | |
1080 | NETAGENTLOG0(LOG_INFO, "Requested netagent for kernel trigger is already active"); | |
1081 | error = 0; | |
1082 | goto done; | |
1083 | } | |
1084 | ||
1085 | error = netagent_send_trigger(wrapper, current_proc(), NETAGENT_TRIGGER_FLAG_KERNEL, NETAGENT_MESSAGE_TYPE_TRIGGER); | |
1086 | NETAGENTLOG((error ? LOG_ERR : LOG_INFO), "Triggered netagent from kernel (error %d)", error); | |
1087 | done: | |
1088 | lck_rw_done(&netagent_lock); | |
1089 | return (error); | |
1090 | } | |
1091 | ||
1092 | int | |
1093 | netagent_trigger(struct proc *p, struct netagent_trigger_args *uap, int32_t *retval) | |
1094 | { | |
1095 | #pragma unused(p, retval) | |
1096 | uuid_t agent_uuid; | |
1097 | int error = 0; | |
1098 | ||
1099 | if (uap == NULL) { | |
1100 | NETAGENTLOG0(LOG_ERR, "uap == NULL"); | |
1101 | return (EINVAL); | |
1102 | } | |
1103 | ||
1104 | if (uap->agent_uuid) { | |
1105 | if (uap->agent_uuidlen != sizeof(uuid_t)) { | |
1106 | NETAGENTLOG(LOG_ERR, "Incorrect length (got %d, expected %d)", | |
1107 | uap->agent_uuidlen, sizeof(uuid_t)); | |
1108 | return (ERANGE); | |
1109 | } | |
1110 | ||
1111 | error = copyin(uap->agent_uuid, agent_uuid, sizeof(uuid_t)); | |
1112 | if (error) { | |
1113 | NETAGENTLOG(LOG_ERR, "copyin error (%d)", error); | |
1114 | return (error); | |
1115 | } | |
1116 | } | |
1117 | ||
1118 | if (uuid_is_null(agent_uuid)) { | |
1119 | NETAGENTLOG0(LOG_ERR, "Requested netagent UUID is empty"); | |
1120 | return (EINVAL); | |
1121 | } | |
1122 | ||
1123 | lck_rw_lock_shared(&netagent_lock); | |
1124 | struct netagent_wrapper *wrapper = netagent_find_agent_with_uuid(agent_uuid); | |
1125 | if (wrapper == NULL) { | |
1126 | NETAGENTLOG0(LOG_ERR, "Requested netagent UUID is not registered"); | |
1127 | error = ENOENT; | |
1128 | goto done; | |
1129 | } | |
1130 | ||
1131 | if ((wrapper->netagent.netagent_flags & NETAGENT_FLAG_USER_ACTIVATED) == 0) { | |
1132 | // Agent does not accept triggers | |
1133 | NETAGENTLOG0(LOG_ERR, "Requested netagent UUID is not eligible for triggering"); | |
1134 | error = EINVAL; | |
1135 | goto done; | |
1136 | } | |
1137 | ||
1138 | if ((wrapper->netagent.netagent_flags & NETAGENT_FLAG_ACTIVE)) { | |
1139 | // Agent already active | |
1140 | NETAGENTLOG0(LOG_INFO, "Requested netagent UUID is already active"); | |
1141 | error = 0; | |
1142 | goto done; | |
1143 | } | |
1144 | ||
1145 | error = netagent_send_trigger(wrapper, p, NETAGENT_TRIGGER_FLAG_USER, NETAGENT_MESSAGE_TYPE_TRIGGER); | |
1146 | NETAGENTLOG((error ? LOG_ERR : LOG_INFO), "Triggered netagent (error %d)", error); | |
1147 | done: | |
1148 | lck_rw_done(&netagent_lock); | |
1149 | return (error); | |
1150 | } |