/*
- * Copyright (c) 2014, 2016, 2017 Apple Inc. All rights reserved.
+ * Copyright (c) 2014-2018 Apple Inc. All rights reserved.
*
* @APPLE_OSREFERENCE_LICENSE_HEADER_START@
*
struct netagent_wrapper {
LIST_ENTRY(netagent_wrapper) master_chain;
u_int32_t control_unit;
+ netagent_event_f event_handler;
+ void *event_context;
u_int32_t generation;
u_int64_t use_count;
struct netagent_client_list_s pending_triggers_list;
};
struct netagent_session {
- u_int32_t control_unit;
+ u_int32_t control_unit; // A control unit of 0 indicates an agent owned by the kernel
struct netagent_wrapper *wrapper;
+ netagent_event_f event_handler;
+ void *event_context;
};
typedef enum {
return (new_session);
}
+netagent_session_t netagent_create(netagent_event_f event_handler, void *context)
+{
+ struct netagent_session *session = netagent_create_session(0);
+ if (session == NULL) {
+ return NULL;
+ }
+
+ session->event_handler = event_handler;
+ session->event_context = context;
+ return session;
+}
+
static void
netagent_free_wrapper(struct netagent_wrapper *wrapper)
{
}
}
+void netagent_destroy(netagent_session_t session)
+{
+ return netagent_delete_session((struct netagent_session *)session);
+}
+
static int
netagent_packet_get_netagent_data_size(mbuf_t packet, int offset, int *err)
{
lck_rw_lock_exclusive(&netagent_lock);
new_wrapper->control_unit = session->control_unit;
+ new_wrapper->event_handler = session->event_handler;
+ new_wrapper->event_context = session->event_context;
new_wrapper->generation = g_next_generation++;
session->wrapper = new_wrapper;
return 0;
}
+errno_t
+netagent_register(netagent_session_t _session, struct netagent *agent)
+{
+ int data_size = 0;
+ struct netagent_wrapper *new_wrapper = NULL;
+
+ struct netagent_session *session = (struct netagent_session *)_session;
+ if (session == NULL) {
+ NETAGENTLOG0(LOG_ERR, "Cannot register agent on NULL session");
+ return EINVAL;
+ }
+
+ if (agent == NULL) {
+ NETAGENTLOG0(LOG_ERR, "Cannot register NULL agent");
+ return EINVAL;
+ }
+
+ if (session->wrapper != NULL) {
+ NETAGENTLOG0(LOG_ERR, "Session already has a registered agent");
+ return EINVAL;
+ }
+
+ data_size = agent->netagent_data_size;
+ if (data_size < 0 || data_size > NETAGENT_MAX_DATA_SIZE) {
+ NETAGENTLOG(LOG_ERR, "Register message size could not be read, data_size %d",
+ data_size);
+ return EINVAL;
+ }
+
+ MALLOC(new_wrapper, struct netagent_wrapper *, sizeof(*new_wrapper) + data_size, M_NETAGENT, M_WAITOK);
+ if (new_wrapper == NULL) {
+ NETAGENTLOG0(LOG_ERR, "Failed to allocate agent");
+ return ENOMEM;
+ }
+
+ memset(new_wrapper, 0, sizeof(*new_wrapper) + data_size);
+ memcpy(&new_wrapper->netagent, agent, sizeof(struct netagent) + data_size);
+
+ int error = netagent_handle_register_inner(session, new_wrapper);
+ if (error != 0) {
+ FREE(new_wrapper, M_NETAGENT);
+ return error;
+ }
+
+ NETAGENTLOG0(LOG_DEBUG, "Registered new agent");
+ netagent_post_event(new_wrapper->netagent.netagent_uuid, KEV_NETAGENT_REGISTERED, TRUE);
+
+ return 0;
+}
+
static errno_t
netagent_handle_register_setopt(struct netagent_session *session, u_int8_t *payload,
u_int32_t payload_length)
}
if (payload_length < sizeof(struct netagent)) {
- NETAGENTLOG(LOG_ERR, "Register message size too small for agent: (%d < %d)",
+ NETAGENTLOG(LOG_ERR, "Register message size too small for agent: (%u < %lu)",
payload_length, sizeof(struct netagent));
response_error = EINVAL;
goto done;
}
if (payload_length != (sizeof(struct netagent) + data_size)) {
- NETAGENTLOG(LOG_ERR, "Mismatch between data size and payload length (%u != %u)", (sizeof(struct netagent) + data_size), payload_length);
+ NETAGENTLOG(LOG_ERR, "Mismatch between data size and payload length (%lu != %u)", (sizeof(struct netagent) + data_size), payload_length);
response_error = EINVAL;
goto done;
}
}
if (payload_length < sizeof(struct netagent)) {
- NETAGENTLOG(LOG_ERR, "Register message size too small for agent: (%d < %d)",
+ NETAGENTLOG(LOG_ERR, "Register message size too small for agent: (%u < %lu)",
payload_length, sizeof(struct netagent));
response_error = NETAGENT_MESSAGE_ERROR_INVALID_DATA;
goto fail;
netagent_send_error_response(session, NETAGENT_MESSAGE_TYPE_REGISTER, message_id, response_error);
}
+errno_t
+netagent_unregister(netagent_session_t _session)
+{
+ struct netagent_session *session = (struct netagent_session *)_session;
+ if (session == NULL) {
+ NETAGENTLOG0(LOG_ERR, "Cannot unregister NULL session");
+ return EINVAL;
+ }
+
+ netagent_unregister_session_wrapper(session);
+ return 0;
+}
+
static errno_t
netagent_handle_unregister_setopt(struct netagent_session *session, u_int8_t *payload,
u_int32_t payload_length)
netagent_free_wrapper(session->wrapper);
session->wrapper = new_wrapper;
new_wrapper->control_unit = session->control_unit;
+ new_wrapper->event_handler = session->event_handler;
+ new_wrapper->event_context = session->event_context;
LIST_INSERT_HEAD(&master_netagent_list, new_wrapper, master_chain);
LIST_INIT(&new_wrapper->pending_triggers_list);
return response_error;
}
+errno_t
+netagent_update(netagent_session_t _session, struct netagent *agent)
+{
+ u_int8_t agent_changed;
+ int data_size = 0;
+ struct netagent_wrapper *new_wrapper = NULL;
+
+ struct netagent_session *session = (struct netagent_session *)_session;
+ if (session == NULL) {
+ NETAGENTLOG0(LOG_ERR, "Cannot update agent on NULL session");
+ return EINVAL;
+ }
+
+ if (agent == NULL) {
+ NETAGENTLOG0(LOG_ERR, "Cannot register NULL agent");
+ return EINVAL;
+ }
+
+ if (session->wrapper == NULL) {
+ NETAGENTLOG0(LOG_ERR, "Session has no agent to update");
+ return EINVAL;
+ }
+
+ data_size = agent->netagent_data_size;
+ if (data_size > NETAGENT_MAX_DATA_SIZE) {
+ NETAGENTLOG(LOG_ERR, "Update message size (%u > %u) too large", data_size, NETAGENT_MAX_DATA_SIZE);
+ return EINVAL;
+ }
+
+ MALLOC(new_wrapper, struct netagent_wrapper *, sizeof(*new_wrapper) + data_size, M_NETAGENT, M_WAITOK);
+ if (new_wrapper == NULL) {
+ NETAGENTLOG0(LOG_ERR, "Failed to allocate agent");
+ return ENOMEM;
+ }
+
+ memset(new_wrapper, 0, sizeof(*new_wrapper) + data_size);
+ memcpy(&new_wrapper->netagent, agent, sizeof(struct netagent) + data_size);
+
+ int error = netagent_handle_update_inner(session, new_wrapper, data_size, &agent_changed, kNetagentErrorDomainPOSIX);
+ if (error == 0) {
+ netagent_post_event(session->wrapper->netagent.netagent_uuid, KEV_NETAGENT_UPDATED, agent_changed);
+ if (agent_changed == FALSE) {
+ // The session wrapper does not need the "new_wrapper" as nothing changed
+ FREE(new_wrapper, M_NETAGENT);
+ }
+ } else {
+ FREE(new_wrapper, M_NETAGENT);
+ return error;
+ }
+
+ return 0;
+}
+
static errno_t
netagent_handle_update_setopt(struct netagent_session *session, u_int8_t *payload, u_int32_t payload_length)
{
}
if (payload_length < sizeof(struct netagent)) {
- NETAGENTLOG(LOG_ERR, "Update message size too small for agent: (%d < %d)",
+ NETAGENTLOG(LOG_ERR, "Update message size too small for agent: (%u < %lu)",
payload_length, sizeof(struct netagent));
response_error = EINVAL;
goto done;
}
if (payload_length != (sizeof(struct netagent) + data_size)) {
- NETAGENTLOG(LOG_ERR, "Mismatch between data size and payload length (%u != %u)", (sizeof(struct netagent) + data_size), payload_length);
+ NETAGENTLOG(LOG_ERR, "Mismatch between data size and payload length (%lu != %u)", (sizeof(struct netagent) + data_size), payload_length);
response_error = EINVAL;
goto done;
}
}
if (payload_length < sizeof(struct netagent)) {
- NETAGENTLOG(LOG_ERR, "Update message size too small for agent: (%d < %d)",
+ NETAGENTLOG(LOG_ERR, "Update message size too small for agent: (%u < %lu)",
payload_length, sizeof(struct netagent));
response_error = NETAGENT_MESSAGE_ERROR_INVALID_DATA;
goto fail;
netagent_send_error_response(session, NETAGENT_MESSAGE_TYPE_GET, message_id, response_error);
}
+errno_t
+netagent_assign_nexus(netagent_session_t _session, uuid_t necp_client_uuid,
+ void *assign_message, size_t assigned_results_length)
+{
+ struct netagent_session *session = (struct netagent_session *)_session;
+ if (session == NULL) {
+ NETAGENTLOG0(LOG_ERR, "Cannot assign nexus from NULL session");
+ return EINVAL;
+ }
+
+ if (session->wrapper == NULL) {
+ NETAGENTLOG0(LOG_ERR, "Session has no agent");
+ return ENOENT;
+ }
+
+ // Note that if the error is 0, NECP has taken over our malloc'ed buffer
+ int error = necp_assign_client_result(session->wrapper->netagent.netagent_uuid, necp_client_uuid, assign_message, assigned_results_length);
+ if (error) {
+ // necp_assign_client_result returns POSIX errors; don't error for ENOENT
+ NETAGENTLOG((error == ENOENT ? LOG_DEBUG : LOG_ERR), "Client assignment failed: %d", error);
+ return error;
+ }
+
+ NETAGENTLOG0(LOG_DEBUG, "Agent assigned nexus properties to client");
+ return 0;
+}
+
+errno_t
+netagent_update_flow_protoctl_event(netagent_session_t _session,
+ uuid_t client_id, uint32_t protoctl_event_code,
+ uint32_t protoctl_event_val, uint32_t protoctl_event_tcp_seq_number)
+{
+ struct netagent_session *session = (struct netagent_session *)_session;
+ int error = 0;
+
+ if (session == NULL) {
+ NETAGENTLOG0(LOG_ERR, "Cannot assign nexus from NULL session");
+ return (EINVAL);
+ }
+
+ if (session->wrapper == NULL) {
+ NETAGENTLOG0(LOG_ERR, "Session has no agent");
+ return (ENOENT);
+ }
+
+ error = necp_update_flow_protoctl_event(session->wrapper->netagent.netagent_uuid,
+ client_id, protoctl_event_code, protoctl_event_val, protoctl_event_tcp_seq_number);
+
+ return (error);
+}
+
static errno_t
netagent_handle_assign_nexus_setopt(struct netagent_session *session, u_int8_t *payload,
u_int32_t payload_length)
}
if (payload_length != sizeof(use_count)) {
- NETAGENTLOG(LOG_ERR, "Payload length is invalid (%u)", payload_length);
+ NETAGENTLOG(LOG_ERR, "Payload length is invalid (%lu)", payload_length);
response_error = EINVAL;
goto done;
}
}
if (*buffer_length != sizeof(use_count)) {
- NETAGENTLOG(LOG_ERR, "Buffer length is invalid (%u)", buffer_length);
+ NETAGENTLOG(LOG_ERR, "Buffer length is invalid (%lu)", *buffer_length);
response_error = EINVAL;
goto done;
}
}
int
-netagent_client_message(uuid_t agent_uuid, uuid_t necp_client_uuid, u_int8_t message_type)
+netagent_client_message_with_params(uuid_t agent_uuid,
+ uuid_t necp_client_uuid,
+ pid_t pid,
+ u_int8_t message_type,
+ struct necp_client_nexus_parameters *parameters,
+ void **assigned_results,
+ size_t *assigned_results_length)
{
int error = 0;
message_type != NETAGENT_MESSAGE_TYPE_CLIENT_ASSERT &&
message_type != NETAGENT_MESSAGE_TYPE_CLIENT_UNASSERT &&
message_type != NETAGENT_MESSAGE_TYPE_REQUEST_NEXUS &&
- message_type != NETAGENT_MESSAGE_TYPE_CLOSE_NEXUS) {
+ message_type != NETAGENT_MESSAGE_TYPE_CLOSE_NEXUS &&
+ message_type != NETAGENT_MESSAGE_TYPE_ABORT_NEXUS) {
NETAGENTLOG(LOG_ERR, "Client netagent message type (%d) is invalid", message_type);
return(EINVAL);
}
bool should_unlock = TRUE;
struct netagent_wrapper *wrapper = netagent_find_agent_with_uuid(agent_uuid);
if (wrapper == NULL) {
- NETAGENTLOG0(LOG_ERR, "Requested netagent for nexus instance could not be found");
+ NETAGENTLOG0(LOG_DEBUG, "Requested netagent for nexus instance could not be found");
error = ENOENT;
goto done;
}
// Don't log, since this is a common case used to trigger events that cellular data is blocked, etc.
error = ENOTSUP;
- struct proc *p = current_proc();
- pid_t current_pid = 0;
- uuid_t current_proc_uuid;
- uuid_clear(current_proc_uuid);
- if (p != NULL) {
- current_pid = proc_pid(p);
- proc_getexecutableuuid(p, current_proc_uuid, sizeof(current_proc_uuid));
+
+ pid_t report_pid = 0;
+ uuid_t report_proc_uuid = {};
+ if (parameters != NULL) {
+ report_pid = parameters->epid;
+ uuid_copy(report_proc_uuid, parameters->euuid);
+ } else {
+ struct proc *p = current_proc();
+ if (p != NULL) {
+ report_pid = proc_pid(p);
+ proc_getexecutableuuid(p, report_proc_uuid, sizeof(report_proc_uuid));
+ }
}
- netagent_send_cellular_failed_event(wrapper, current_pid, current_proc_uuid);
+ netagent_send_cellular_failed_event(wrapper, report_pid, report_proc_uuid);
goto done;
}
} else if (message_type == NETAGENT_MESSAGE_TYPE_REQUEST_NEXUS ||
- message_type == NETAGENT_MESSAGE_TYPE_CLOSE_NEXUS) {
+ message_type == NETAGENT_MESSAGE_TYPE_CLOSE_NEXUS ||
+ message_type == NETAGENT_MESSAGE_TYPE_ABORT_NEXUS) {
if ((wrapper->netagent.netagent_flags & NETAGENT_FLAG_NEXUS_PROVIDER) == 0) {
NETAGENTLOG0(LOG_ERR, "Requested netagent for nexus instance is not a nexus provider");
// Agent is not a nexus provider
}
}
- error = netagent_send_client_message(wrapper, necp_client_uuid, message_type);
- if (error == 0 && message_type == NETAGENT_MESSAGE_TYPE_CLIENT_TRIGGER) {
- if (lck_rw_lock_shared_to_exclusive(&netagent_lock)) {
- // Grab the lock exclusively to add a pending client to the list
- struct netagent_client *new_pending_client = NULL;
- MALLOC(new_pending_client, struct netagent_client *, sizeof(*new_pending_client), M_NETAGENT, M_WAITOK);
- if (new_pending_client == NULL) {
- NETAGENTLOG0(LOG_ERR, "Failed to allocate client for trigger");
- } else {
- uuid_copy(new_pending_client->client_id, necp_client_uuid);
- struct proc *p = current_proc();
- if (p != NULL) {
- new_pending_client->client_pid = proc_pid(p);
- proc_getexecutableuuid(p, new_pending_client->client_proc_uuid, sizeof(new_pending_client->client_proc_uuid));
+ if (wrapper->control_unit == 0) {
+ should_unlock = FALSE;
+ lck_rw_done(&netagent_lock);
+ if (wrapper->event_handler == NULL) {
+ // No event handler registered for kernel agent
+ error = EINVAL;
+ } else {
+ error = wrapper->event_handler(message_type, necp_client_uuid, pid, wrapper->event_context, parameters, assigned_results, assigned_results_length);
+ if (error != 0) {
+ VERIFY(assigned_results == NULL || *assigned_results == NULL);
+ VERIFY(assigned_results_length == NULL || *assigned_results_length == 0);
+ }
+ }
+ } else {
+ // ABORT_NEXUS is kernel-private, so translate it for userspace nexus
+ if (message_type == NETAGENT_MESSAGE_TYPE_ABORT_NEXUS) {
+ message_type = NETAGENT_MESSAGE_TYPE_CLOSE_NEXUS;
+ }
+
+ error = netagent_send_client_message(wrapper, necp_client_uuid, message_type);
+ if (error == 0 && message_type == NETAGENT_MESSAGE_TYPE_CLIENT_TRIGGER) {
+ if (lck_rw_lock_shared_to_exclusive(&netagent_lock)) {
+ // Grab the lock exclusively to add a pending client to the list
+ struct netagent_client *new_pending_client = NULL;
+ MALLOC(new_pending_client, struct netagent_client *, sizeof(*new_pending_client), M_NETAGENT, M_WAITOK);
+ if (new_pending_client == NULL) {
+ NETAGENTLOG0(LOG_ERR, "Failed to allocate client for trigger");
+ } else {
+ uuid_copy(new_pending_client->client_id, necp_client_uuid);
+ if (parameters != NULL) {
+ new_pending_client->client_pid = parameters->epid;
+ uuid_copy(new_pending_client->client_proc_uuid, parameters->euuid);
+ } else {
+ struct proc *p = current_proc();
+ if (p != NULL) {
+ new_pending_client->client_pid = proc_pid(p);
+ proc_getexecutableuuid(p, new_pending_client->client_proc_uuid, sizeof(new_pending_client->client_proc_uuid));
+ }
+ }
+ LIST_INSERT_HEAD(&wrapper->pending_triggers_list, new_pending_client, client_chain);
}
- LIST_INSERT_HEAD(&wrapper->pending_triggers_list, new_pending_client, client_chain);
+ } else {
+ // If lck_rw_lock_shared_to_exclusive fails, it unlocks automatically
+ should_unlock = FALSE;
}
- } else {
- // If lck_rw_lock_shared_to_exclusive fails, it unlocks automatically
- should_unlock = FALSE;
}
}
- NETAGENTLOG((error ? LOG_ERR : LOG_INFO), "Send message %d for client (error %d)", message_type, error);
+ NETAGENTLOG(((error && error != ENOENT) ? LOG_ERR : LOG_INFO), "Send message %d for client (error %d)", message_type, error);
+ if (message_type == NETAGENT_MESSAGE_TYPE_CLIENT_TRIGGER) {
+ uuid_string_t uuid_str;
+ uuid_unparse(agent_uuid, uuid_str);
+ NETAGENTLOG(LOG_NOTICE, "Triggered network agent %s, error = %d", uuid_str, error);
+ }
done:
if (should_unlock) {
lck_rw_done(&netagent_lock);
return (error);
}
+int
+netagent_client_message(uuid_t agent_uuid, uuid_t necp_client_uuid, pid_t pid, u_int8_t message_type)
+{
+ return (netagent_client_message_with_params(agent_uuid, necp_client_uuid, pid, message_type, NULL, NULL, NULL));
+}
+
int
netagent_use(uuid_t agent_uuid, uint64_t *out_use_count)
{
lck_rw_lock_shared(&netagent_lock);
struct netagent_wrapper *wrapper = netagent_find_agent_with_uuid(agent_uuid);
if (wrapper == NULL) {
- NETAGENTLOG0(LOG_ERR, "Requested netagent for nexus instance could not be found");
+ NETAGENTLOG0(LOG_DEBUG, "Requested netagent for nexus instance could not be found");
error = ENOENT;
goto done;
}
if (uap->agent_uuid) {
if (uap->agent_uuidlen != sizeof(uuid_t)) {
- NETAGENTLOG(LOG_ERR, "Incorrect length (got %d, expected %d)",
+ NETAGENTLOG(LOG_ERR, "Incorrect length (got %llu, expected %lu)",
uap->agent_uuidlen, sizeof(uuid_t));
return (ERANGE);
}