]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/net/necp.c
xnu-4570.31.3.tar.gz
[apple/xnu.git] / bsd / net / necp.c
index e40268c30a510fec642b7ee5ea67bf4a51208e28..22f5afbd54c1df59704e11c1ec6b04e71cdadd3a 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013-2015 Apple Inc. All rights reserved.
+ * Copyright (c) 2013-2017 Apple Inc. All rights reserved.
  *
  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
  *
@@ -29,7 +29,6 @@
 #include <string.h>
 #include <sys/systm.h>
 #include <sys/types.h>
-#include <sys/syslog.h>
 #include <sys/queue.h>
 #include <sys/malloc.h>
 #include <libkern/OSMalloc.h>
 #include <netinet/ip6.h>
 #include <netinet/tcp.h>
 #include <netinet/tcp_var.h>
+#include <netinet/tcp_cache.h>
 #include <netinet/udp.h>
 #include <netinet/in_pcb.h>
+#include <netinet/in_tclass.h>
 #include <netinet6/esp.h>
 #include <net/flowhash.h>
 #include <net/if_var.h>
@@ -57,6 +58,8 @@
 #include <sys/sysproto.h>
 #include <sys/priv.h>
 #include <sys/kern_event.h>
+#include <sys/file_internal.h>
+#include <IOKit/IOBSD.h>
 #include <net/network_agent.h>
 #include <net/necp.h>
 
  *    list is traversed to create optimized sub-lists ("Maps") which are used during
  *    data-path evaluation. IP policies are sent into necp_kernel_ip_output_policies_map,
  *    which hashes incoming packets based on marked socket-layer policies, and removes
- *    duplicate or overlapping polcies. Socket policies are sent into two maps,
+ *    duplicate or overlapping policies. Socket policies are sent into two maps,
  *    necp_kernel_socket_policies_map and necp_kernel_socket_policies_app_layer_map.
  *    The app layer map is used for policy checks coming in from user space, and is one
  *    list with duplicate and overlapping policies removed. The socket map hashes based
@@ -137,14 +140,6 @@ u_int32_t necp_debug = 0; // 0=None, 1=Basic, 2=EveryMatch
 
 u_int32_t necp_session_count = 0;
 
-#define        NECPLOG(level, format, ...) do {                                                                                        \
-       log((level > LOG_NOTICE ? LOG_NOTICE : level), "%s: " format "\n", __FUNCTION__, __VA_ARGS__);  \
-} while (0)
-
-#define        NECPLOG0(level, msg) do {                                                                                       \
-       log((level > LOG_NOTICE ? LOG_NOTICE : level), "%s: %s\n", __FUNCTION__, msg);  \
-} while (0)
-
 #define        LIST_INSERT_SORTED_ASCENDING(head, elm, field, sortfield, tmpelm) do {          \
        if (LIST_EMPTY((head)) || (LIST_FIRST(head)->sortfield >= (elm)->sortfield)) {  \
                LIST_INSERT_HEAD((head), elm, field);                                                                           \
@@ -184,25 +179,36 @@ u_int32_t necp_session_count = 0;
        }                                                                                                                                                               \
 } while (0)
 
-#define        NECP_KERNEL_CONDITION_ALL_INTERFACES    0x00001
-#define        NECP_KERNEL_CONDITION_BOUND_INTERFACE   0x00002
-#define        NECP_KERNEL_CONDITION_PROTOCOL                  0x00004
-#define        NECP_KERNEL_CONDITION_LOCAL_START               0x00008
-#define        NECP_KERNEL_CONDITION_LOCAL_END                 0x00010
-#define        NECP_KERNEL_CONDITION_LOCAL_PREFIX              0x00020
-#define        NECP_KERNEL_CONDITION_REMOTE_START              0x00040
-#define        NECP_KERNEL_CONDITION_REMOTE_END                0x00080
-#define        NECP_KERNEL_CONDITION_REMOTE_PREFIX             0x00100
-#define        NECP_KERNEL_CONDITION_APP_ID                    0x00200
-#define        NECP_KERNEL_CONDITION_REAL_APP_ID               0x00400
-#define        NECP_KERNEL_CONDITION_DOMAIN                    0x00800
-#define        NECP_KERNEL_CONDITION_ACCOUNT_ID                0x01000
-#define        NECP_KERNEL_CONDITION_POLICY_ID                 0x02000
-#define        NECP_KERNEL_CONDITION_PID                               0x04000
-#define        NECP_KERNEL_CONDITION_UID                               0x08000
-#define        NECP_KERNEL_CONDITION_LAST_INTERFACE    0x10000                 // Only set from packets looping between interfaces
-#define NECP_KERNEL_CONDITION_TRAFFIC_CLASS            0x20000
-#define NECP_KERNEL_CONDITION_ENTITLEMENT              0x40000
+#define        IS_NECP_ROUTE_RULE_ALLOW_OR_DENY(x)     ((x) == NECP_ROUTE_RULE_DENY_INTERFACE || (x) == NECP_ROUTE_RULE_ALLOW_INTERFACE)
+
+#define        NECP_KERNEL_CONDITION_ALL_INTERFACES            0x00001
+#define        NECP_KERNEL_CONDITION_BOUND_INTERFACE           0x00002
+#define        NECP_KERNEL_CONDITION_PROTOCOL                          0x00004
+#define        NECP_KERNEL_CONDITION_LOCAL_START                       0x00008
+#define        NECP_KERNEL_CONDITION_LOCAL_END                         0x00010
+#define        NECP_KERNEL_CONDITION_LOCAL_PREFIX                      0x00020
+#define        NECP_KERNEL_CONDITION_REMOTE_START                      0x00040
+#define        NECP_KERNEL_CONDITION_REMOTE_END                        0x00080
+#define        NECP_KERNEL_CONDITION_REMOTE_PREFIX                     0x00100
+#define        NECP_KERNEL_CONDITION_APP_ID                            0x00200
+#define        NECP_KERNEL_CONDITION_REAL_APP_ID                       0x00400
+#define        NECP_KERNEL_CONDITION_DOMAIN                            0x00800
+#define        NECP_KERNEL_CONDITION_ACCOUNT_ID                        0x01000
+#define        NECP_KERNEL_CONDITION_POLICY_ID                         0x02000
+#define        NECP_KERNEL_CONDITION_PID                                       0x04000
+#define        NECP_KERNEL_CONDITION_UID                                       0x08000
+#define        NECP_KERNEL_CONDITION_LAST_INTERFACE            0x10000                 // Only set from packets looping between interfaces
+#define        NECP_KERNEL_CONDITION_TRAFFIC_CLASS                     0x20000
+#define        NECP_KERNEL_CONDITION_ENTITLEMENT                       0x40000
+#define        NECP_KERNEL_CONDITION_CUSTOM_ENTITLEMENT        0x80000
+
+#define NECP_MAX_POLICY_RESULT_SIZE                                    512
+#define NECP_MAX_ROUTE_RULES_ARRAY_SIZE                                1024
+#define NECP_MAX_CONDITIONS_ARRAY_SIZE                         4096
+#define NECP_MAX_POLICY_LIST_COUNT                                     1024
+
+// Cap the policy size at the max result + conditions size, with room for extra TLVs
+#define NECP_MAX_POLICY_SIZE                                           (1024 + NECP_MAX_POLICY_RESULT_SIZE + NECP_MAX_CONDITIONS_ARRAY_SIZE)
 
 struct necp_service_registration {
        LIST_ENTRY(necp_service_registration)   session_chain;
@@ -211,10 +217,13 @@ struct necp_service_registration {
 };
 
 struct necp_session {
+       u_int8_t                                        necp_fd_type;
        u_int32_t                                       control_unit;
        u_int32_t                                       session_priority; // Descriptive priority rating
        u_int32_t                                       session_order;
 
+       decl_lck_mtx_data(, lock);
+
        bool                                            proc_locked; // Messages must come from proc_uuid
        uuid_t                                          proc_uuid;
        int                                                     proc_pid;
@@ -223,8 +232,15 @@ struct necp_session {
        LIST_HEAD(_policies, necp_session_policy) policies;
 
        LIST_HEAD(_services, necp_service_registration) services;
+
+       TAILQ_ENTRY(necp_session) chain;
 };
 
+#define NECP_SESSION_LOCK(_s) lck_mtx_lock(&_s->lock)
+#define NECP_SESSION_UNLOCK(_s) lck_mtx_unlock(&_s->lock)
+
+static TAILQ_HEAD(_necp_session_list, necp_session) necp_session_list;
+
 struct necp_socket_info {
        pid_t pid;
        uid_t uid;
@@ -298,20 +314,26 @@ static LIST_HEAD(_necpkernelipoutputpolicies, necp_kernel_ip_output_policy) necp
 #define        NECP_IP_OUTPUT_MAP_ID_TO_BUCKET(id) (id ? (id%(NECP_KERNEL_IP_OUTPUT_POLICIES_MAP_NUM_ID_BUCKETS - 1) + 1) : 0)
 static struct necp_kernel_ip_output_policy **necp_kernel_ip_output_policies_map[NECP_KERNEL_IP_OUTPUT_POLICIES_MAP_NUM_ID_BUCKETS];
 
-static struct necp_session *necp_create_session(u_int32_t control_unit);
+static struct necp_session *necp_create_session(void);
 static void necp_delete_session(struct necp_session *session);
 
-static void necp_handle_policy_add(struct necp_session *session, u_int32_t message_id, mbuf_t packet, int offset);
+static necp_policy_id necp_handle_policy_add(struct necp_session *session, u_int32_t message_id, mbuf_t packet,
+                                                                                        u_int8_t *tlv_buffer, size_t tlv_buffer_length, int offset, int *error);
 static void necp_handle_policy_get(struct necp_session *session, u_int32_t message_id, mbuf_t packet, int offset);
 static void necp_handle_policy_delete(struct necp_session *session, u_int32_t message_id, mbuf_t packet, int offset);
 static void necp_handle_policy_apply_all(struct necp_session *session, u_int32_t message_id, mbuf_t packet, int offset);
 static void necp_handle_policy_list_all(struct necp_session *session, u_int32_t message_id, mbuf_t packet, int offset);
 static void necp_handle_policy_delete_all(struct necp_session *session, u_int32_t message_id, mbuf_t packet, int offset);
+static int necp_handle_policy_dump_all(struct necp_session *session, u_int32_t message_id, mbuf_t packet,
+                                                                          user_addr_t out_buffer, size_t out_buffer_length, int offset);
 static void necp_handle_set_session_priority(struct necp_session *session, u_int32_t message_id, mbuf_t packet, int offset);
 static void necp_handle_lock_session_to_proc(struct necp_session *session, u_int32_t message_id, mbuf_t packet, int offset);
 static void necp_handle_register_service(struct necp_session *session, u_int32_t message_id, mbuf_t packet, int offset);
 static void necp_handle_unregister_service(struct necp_session *session, u_int32_t message_id, mbuf_t packet, int offset);
 
+#define MAX_RESULT_STRING_LEN 64
+static inline const char * necp_get_result_description(char *result_string, necp_kernel_policy_result result, necp_kernel_policy_result_parameter result_parameter);
+
 static struct necp_session_policy *necp_policy_create(struct necp_session *session, necp_policy_order order, u_int8_t *conditions_array, u_int32_t conditions_array_size, u_int8_t *route_rules_array, u_int32_t route_rules_array_size, u_int8_t *result, u_int32_t result_size);
 static struct necp_session_policy *necp_policy_find(struct necp_session *session, necp_policy_id policy_id);
 static bool necp_policy_mark_for_deletion(struct necp_session *session, struct necp_session_policy *policy);
@@ -319,11 +341,11 @@ static bool necp_policy_mark_all_for_deletion(struct necp_session *session);
 static bool necp_policy_delete(struct necp_session *session, struct necp_session_policy *policy);
 static void necp_policy_apply_all(struct necp_session *session);
 
-static necp_kernel_policy_id necp_kernel_socket_policy_add(necp_policy_id parent_policy_id, necp_policy_order order, u_int32_t session_order, int session_pid, u_int32_t condition_mask, u_int32_t condition_negated_mask, necp_app_id cond_app_id, necp_app_id cond_real_app_id, u_int32_t cond_account_id, char *domain, pid_t cond_pid, uid_t cond_uid, ifnet_t cond_bound_interface, struct necp_policy_condition_tc_range cond_traffic_class, u_int16_t cond_protocol, union necp_sockaddr_union *cond_local_start, union necp_sockaddr_union *cond_local_end, u_int8_t cond_local_prefix, union necp_sockaddr_union *cond_remote_start, union necp_sockaddr_union *cond_remote_end, u_int8_t cond_remote_prefix, necp_kernel_policy_result result, necp_kernel_policy_result_parameter result_parameter);
+static necp_kernel_policy_id necp_kernel_socket_policy_add(necp_policy_id parent_policy_id, necp_policy_order order, u_int32_t session_order, int session_pid, u_int32_t condition_mask, u_int32_t condition_negated_mask, necp_app_id cond_app_id, necp_app_id cond_real_app_id, char *cond_custom_entitlement, u_int32_t cond_account_id, char *domain, pid_t cond_pid, uid_t cond_uid, ifnet_t cond_bound_interface, struct necp_policy_condition_tc_range cond_traffic_class, u_int16_t cond_protocol, union necp_sockaddr_union *cond_local_start, union necp_sockaddr_union *cond_local_end, u_int8_t cond_local_prefix, union necp_sockaddr_union *cond_remote_start, union necp_sockaddr_union *cond_remote_end, u_int8_t cond_remote_prefix, necp_kernel_policy_result result, necp_kernel_policy_result_parameter result_parameter);
 static bool necp_kernel_socket_policy_delete(necp_kernel_policy_id policy_id);
 static bool necp_kernel_socket_policies_reprocess(void);
 static bool necp_kernel_socket_policies_update_uuid_table(void);
-static inline struct necp_kernel_socket_policy *necp_socket_find_policy_match_with_info_locked(struct necp_kernel_socket_policy **policy_search_array, struct necp_socket_info *info, necp_kernel_policy_filter *return_filter, u_int32_t *return_route_rule_id, necp_kernel_policy_result *return_service_action, necp_kernel_policy_service *return_service, u_int32_t *return_netagent_array, size_t netagent_array_count);
+static inline struct necp_kernel_socket_policy *necp_socket_find_policy_match_with_info_locked(struct necp_kernel_socket_policy **policy_search_array, struct necp_socket_info *info, necp_kernel_policy_filter *return_filter, u_int32_t *return_route_rule_id, necp_kernel_policy_result *return_service_action, necp_kernel_policy_service *return_service, u_int32_t *return_netagent_array, size_t netagent_array_count, proc_t proc);
 
 static necp_kernel_policy_id necp_kernel_ip_output_policy_add(necp_policy_id parent_policy_id, necp_policy_order order, necp_policy_order suborder, u_int32_t session_order, int session_pid, u_int32_t condition_mask, u_int32_t condition_negated_mask, necp_kernel_policy_id cond_policy_id, ifnet_t cond_bound_interface, u_int32_t cond_last_interface_index, u_int16_t cond_protocol, union necp_sockaddr_union *cond_local_start, union necp_sockaddr_union *cond_local_end, u_int8_t cond_local_prefix, union necp_sockaddr_union *cond_remote_start, union necp_sockaddr_union *cond_remote_end, u_int8_t cond_remote_prefix, necp_kernel_policy_result result, necp_kernel_policy_result_parameter result_parameter);
 static bool necp_kernel_ip_output_policy_delete(necp_kernel_policy_id policy_id);
@@ -335,6 +357,7 @@ static bool necp_is_addr_in_subnet(struct sockaddr *addr, struct sockaddr *subne
 static int necp_addr_compare(struct sockaddr *sa1, struct sockaddr *sa2, int check_port);
 static bool necp_buffer_compare_with_bit_prefix(u_int8_t *p1, u_int8_t *p2, u_int32_t bits);
 static bool necp_is_loopback(struct sockaddr *local_addr, struct sockaddr *remote_addr, struct inpcb *inp, struct mbuf *packet);
+static bool necp_is_intcoproc(struct inpcb *inp, struct mbuf *packet);
 
 struct necp_uuid_id_mapping {
        LIST_ENTRY(necp_uuid_id_mapping) chain;
@@ -352,6 +375,7 @@ static LIST_HEAD(necp_uuid_id_mapping_head, necp_uuid_id_mapping) *necp_uuid_app
 #define        APPUUIDHASH(uuid) (&necp_uuid_app_id_hashtbl[uuid[0] & necp_uuid_app_id_hash_mask]) // Assume first byte of UUIDs are evenly distributed
 static u_int32_t necp_create_uuid_app_id_mapping(uuid_t uuid, bool *allocated_mapping, bool uuid_policy_table);
 static bool necp_remove_uuid_app_id_mapping(uuid_t uuid, bool *removed_mapping, bool uuid_policy_table);
+static struct necp_uuid_id_mapping *necp_uuid_lookup_uuid_with_app_id_locked(u_int32_t local_id);
 
 static struct necp_uuid_id_mapping *necp_uuid_lookup_service_id_locked(uuid_t uuid);
 static struct necp_uuid_id_mapping *necp_uuid_lookup_uuid_with_service_id_locked(u_int32_t local_id);
@@ -367,12 +391,16 @@ struct necp_string_id_mapping {
 static LIST_HEAD(necp_string_id_mapping_list, necp_string_id_mapping) necp_account_id_list;
 static u_int32_t necp_create_string_to_id_mapping(struct necp_string_id_mapping_list *list, char *domain);
 static bool necp_remove_string_to_id_mapping(struct necp_string_id_mapping_list *list, char *domain);
+static struct necp_string_id_mapping *necp_lookup_string_with_id_locked(struct necp_string_id_mapping_list *list, u_int32_t local_id);
 
 static LIST_HEAD(_necp_kernel_service_list, necp_service_registration) necp_registered_service_list;
 
 static char *necp_create_trimmed_domain(char *string, size_t length);
 static inline int necp_count_dots(char *string, size_t length);
 
+static char *necp_copy_string(char *string, size_t length);
+static bool necp_update_qos_marking(struct ifnet *ifp, u_int32_t route_rule_id);
+
 #define ROUTE_RULE_IS_AGGREGATE(ruleid) (ruleid > UINT16_MAX)
 
 #define MAX_ROUTE_RULE_INTERFACES 10
@@ -391,7 +419,7 @@ struct necp_route_rule {
 static LIST_HEAD(necp_route_rule_list, necp_route_rule) necp_route_rules;
 static u_int32_t necp_create_route_rule(struct necp_route_rule_list *list, u_int8_t *route_rules_array, u_int32_t route_rules_array_size);
 static bool necp_remove_route_rule(struct necp_route_rule_list *list, u_int32_t route_rule_id);
-static bool necp_route_is_allowed(struct rtentry *route, ifnet_t interface, u_int32_t route_rule_id, bool *cellular_denied);
+static bool necp_route_is_allowed(struct rtentry *route, ifnet_t interface, u_int32_t route_rule_id, u_int32_t *interface_type_denied);
 static struct necp_route_rule *necp_lookup_route_rule_locked(struct necp_route_rule_list *list, u_int32_t route_rule_id);
 
 #define MAX_AGGREGATE_ROUTE_RULES 16
@@ -453,6 +481,629 @@ sysctl_handle_necp_level SYSCTL_HANDLER_ARGS
        return (error);
 }
 
+// Session fd
+
+static int noop_read(struct fileproc *, struct uio *, int, vfs_context_t);
+static int noop_write(struct fileproc *, struct uio *, int, vfs_context_t);
+static int noop_ioctl(struct fileproc *, unsigned long, caddr_t,
+                                         vfs_context_t);
+static int noop_select(struct fileproc *, int, void *, vfs_context_t);
+static int necp_session_op_close(struct fileglob *, vfs_context_t);
+static int noop_kqfilter(struct fileproc *, struct knote *,
+               struct kevent_internal_s *, vfs_context_t);
+
+static const struct fileops necp_session_fd_ops = {
+       .fo_type = DTYPE_NETPOLICY,
+       .fo_read = noop_read,
+       .fo_write = noop_write,
+       .fo_ioctl = noop_ioctl,
+       .fo_select = noop_select,
+       .fo_close = necp_session_op_close,
+       .fo_kqfilter = noop_kqfilter,
+       .fo_drain = NULL,
+};
+
+static int
+noop_read(struct fileproc *fp, struct uio *uio, int flags, vfs_context_t ctx)
+{
+#pragma unused(fp, uio, flags, ctx)
+       return (ENXIO);
+}
+
+static int
+noop_write(struct fileproc *fp, struct uio *uio, int flags,
+                  vfs_context_t ctx)
+{
+#pragma unused(fp, uio, flags, ctx)
+       return (ENXIO);
+}
+
+static int
+noop_ioctl(struct fileproc *fp, unsigned long com, caddr_t data,
+                  vfs_context_t ctx)
+{
+#pragma unused(fp, com, data, ctx)
+       return (ENOTTY);
+}
+
+static int
+noop_select(struct fileproc *fp, int which, void *wql, vfs_context_t ctx)
+{
+#pragma unused(fp, which, wql, ctx)
+       return (ENXIO);
+}
+
+static int
+noop_kqfilter(struct fileproc *fp, struct knote *kn,
+               struct kevent_internal_s *kev, vfs_context_t ctx)
+{
+#pragma unused(fp, kn, kev, ctx)
+       return (ENXIO);
+}
+
+int
+necp_session_open(struct proc *p, struct necp_session_open_args *uap, int *retval)
+{
+#pragma unused(uap)
+       int error = 0;
+       struct necp_session *session = NULL;
+       struct fileproc *fp = NULL;
+       int fd = -1;
+
+       uid_t uid = kauth_cred_getuid(proc_ucred(p));
+       if (uid != 0 && priv_check_cred(kauth_cred_get(), PRIV_NET_PRIVILEGED_NECP_POLICIES, 0) != 0) {
+               NECPLOG0(LOG_ERR, "Process does not hold necessary entitlement to open NECP session");
+               error = EACCES;
+               goto done;
+       }
+
+       error = falloc(p, &fp, &fd, vfs_context_current());
+       if (error != 0) {
+               goto done;
+       }
+
+       session = necp_create_session();
+       if (session == NULL) {
+               error = ENOMEM;
+               goto done;
+       }
+
+       fp->f_fglob->fg_flag = 0;
+       fp->f_fglob->fg_ops = &necp_session_fd_ops;
+       fp->f_fglob->fg_data = session;
+
+       proc_fdlock(p);
+       FDFLAGS_SET(p, fd, (UF_EXCLOSE | UF_FORKCLOSE));
+       procfdtbl_releasefd(p, fd, NULL);
+       fp_drop(p, fd, fp, 1);
+       proc_fdunlock(p);
+
+       *retval = fd;
+done:
+       if (error != 0) {
+               if (fp != NULL) {
+                       fp_free(p, fd, fp);
+                       fp = NULL;
+               }
+       }
+
+       return (error);
+}
+
+static int
+necp_session_op_close(struct fileglob *fg, vfs_context_t ctx)
+{
+#pragma unused(ctx)
+       struct necp_session *session = (struct necp_session *)fg->fg_data;
+       fg->fg_data = NULL;
+
+       if (session != NULL) {
+               necp_policy_mark_all_for_deletion(session);
+               necp_policy_apply_all(session);
+               necp_delete_session(session);
+               return (0);
+       } else {
+               return (ENOENT);
+       }
+}
+
+static int
+necp_session_find_from_fd(int fd, struct necp_session **session)
+{
+       proc_t p = current_proc();
+       struct fileproc *fp = NULL;
+       int error = 0;
+
+       proc_fdlock_spin(p);
+       if ((error = fp_lookup(p, fd, &fp, 1)) != 0) {
+               goto done;
+       }
+       if (fp->f_fglob->fg_ops->fo_type != DTYPE_NETPOLICY) {
+               fp_drop(p, fd, fp, 1);
+               error = ENODEV;
+               goto done;
+       }
+       *session = (struct necp_session *)fp->f_fglob->fg_data;
+
+done:
+       proc_fdunlock(p);
+       return (error);
+}
+
+static int
+necp_session_add_policy(struct necp_session *session, struct necp_session_action_args *uap, int *retval)
+{
+       int error = 0;
+       u_int8_t *tlv_buffer = NULL;
+
+       if (uap->in_buffer_length == 0 || uap->in_buffer_length > NECP_MAX_POLICY_SIZE || uap->in_buffer == 0) {
+               NECPLOG(LOG_ERR, "necp_session_add_policy invalid input (%zu)", uap->in_buffer_length);
+               error = EINVAL;
+               goto done;
+       }
+
+       if (uap->out_buffer_length < sizeof(necp_policy_id) || uap->out_buffer == 0) {
+               NECPLOG(LOG_ERR, "necp_session_add_policy invalid output buffer (%zu)", uap->out_buffer_length);
+               error = EINVAL;
+               goto done;
+       }
+
+       if ((tlv_buffer = _MALLOC(uap->in_buffer_length, M_NECP, M_WAITOK | M_ZERO)) == NULL) {
+               error = ENOMEM;
+               goto done;
+       }
+
+       error = copyin(uap->in_buffer, tlv_buffer, uap->in_buffer_length);
+       if (error != 0) {
+               NECPLOG(LOG_ERR, "necp_session_add_policy tlv copyin error (%d)", error);
+               goto done;
+       }
+
+       necp_policy_id new_policy_id = necp_handle_policy_add(session, 0, NULL, tlv_buffer, uap->in_buffer_length, 0, &error);
+       if (error != 0) {
+               NECPLOG(LOG_ERR, "necp_session_add_policy failed to add policy (%d)", error);
+               goto done;
+       }
+
+       error = copyout(&new_policy_id, uap->out_buffer, sizeof(new_policy_id));
+       if (error != 0) {
+               NECPLOG(LOG_ERR, "necp_session_add_policy policy_id copyout error (%d)", error);
+               goto done;
+       }
+
+done:
+       if (tlv_buffer != NULL) {
+               FREE(tlv_buffer, M_NECP);
+               tlv_buffer = NULL;
+       }
+       *retval = error;
+
+       return (error);
+}
+
+static int
+necp_session_get_policy(struct necp_session *session, struct necp_session_action_args *uap, int *retval)
+{
+       int error = 0;
+       u_int8_t *response = NULL;
+
+       if (uap->in_buffer_length < sizeof(necp_policy_id) || uap->in_buffer == 0) {
+               NECPLOG(LOG_ERR, "necp_session_get_policy invalid input (%zu)", uap->in_buffer_length);
+               error = EINVAL;
+               goto done;
+       }
+
+       necp_policy_id policy_id = 0;
+       error = copyin(uap->in_buffer, &policy_id, sizeof(policy_id));
+       if (error != 0) {
+               NECPLOG(LOG_ERR, "necp_session_get_policy policy_id copyin error (%d)", error);
+               goto done;
+       }
+
+       struct necp_session_policy *policy = necp_policy_find(session, policy_id);
+       if (policy == NULL || policy->pending_deletion) {
+               NECPLOG(LOG_ERR, "Failed to find policy with id %d", policy_id);
+               error = ENOENT;
+               goto done;
+       }
+
+       u_int32_t order_tlv_size = sizeof(u_int8_t) + sizeof(u_int32_t) + sizeof(necp_policy_order);
+       u_int32_t result_tlv_size = (policy->result_size ? (sizeof(u_int8_t) + sizeof(u_int32_t) + policy->result_size) : 0);
+       u_int32_t response_size = order_tlv_size + result_tlv_size + policy->conditions_size;
+
+       if (uap->out_buffer_length < response_size || uap->out_buffer == 0) {
+               NECPLOG(LOG_ERR, "necp_session_get_policy buffer not large enough (%u < %u)", uap->out_buffer_length, response_size);
+               error = EINVAL;
+               goto done;
+       }
+
+       if (response_size > NECP_MAX_POLICY_SIZE) {
+               NECPLOG(LOG_ERR, "necp_session_get_policy size too large to copy (%u)", response_size);
+               error = EINVAL;
+               goto done;
+       }
+
+       MALLOC(response, u_int8_t *, response_size, M_NECP, M_WAITOK | M_ZERO);
+       if (response == NULL) {
+               error = ENOMEM;
+               goto done;
+       }
+
+       u_int8_t *cursor = response;
+       cursor = necp_buffer_write_tlv(cursor, NECP_TLV_POLICY_ORDER, sizeof(necp_policy_order), &policy->order, response, response_size);
+       if (result_tlv_size) {
+               cursor = necp_buffer_write_tlv(cursor, NECP_TLV_POLICY_RESULT, policy->result_size, &policy->result, response, response_size);
+       }
+       if (policy->conditions_size) {
+               memcpy(((u_int8_t *)(void *)(cursor)), policy->conditions, policy->conditions_size);
+       }
+
+       error = copyout(response, uap->out_buffer, response_size);
+       if (error != 0) {
+               NECPLOG(LOG_ERR, "necp_session_get_policy TLV copyout error (%d)", error);
+               goto done;
+       }
+
+done:
+       if (response != NULL) {
+               FREE(response, M_NECP);
+               response = NULL;
+       }
+       *retval = error;
+
+       return (error);
+}
+
+static int
+necp_session_delete_policy(struct necp_session *session, struct necp_session_action_args *uap, int *retval)
+{
+       int error = 0;
+
+       if (uap->in_buffer_length < sizeof(necp_policy_id) || uap->in_buffer == 0) {
+               NECPLOG(LOG_ERR, "necp_session_delete_policy invalid input (%zu)", uap->in_buffer_length);
+               error = EINVAL;
+               goto done;
+       }
+
+       necp_policy_id delete_policy_id = 0;
+       error = copyin(uap->in_buffer, &delete_policy_id, sizeof(delete_policy_id));
+       if (error != 0) {
+               NECPLOG(LOG_ERR, "necp_session_delete_policy policy_id copyin error (%d)", error);
+               goto done;
+       }
+
+       struct necp_session_policy *policy = necp_policy_find(session, delete_policy_id);
+       if (policy == NULL || policy->pending_deletion) {
+               NECPLOG(LOG_ERR, "necp_session_delete_policy failed to find policy with id %u", delete_policy_id);
+               error = ENOENT;
+               goto done;
+       }
+
+       necp_policy_mark_for_deletion(session, policy);
+done:
+       *retval = error;
+       return (error);
+}
+
+static int
+necp_session_apply_all(struct necp_session *session, struct necp_session_action_args *uap, int *retval)
+{
+#pragma unused(uap)
+       necp_policy_apply_all(session);
+       *retval = 0;
+       return (0);
+}
+
+static int
+necp_session_list_all(struct necp_session *session, struct necp_session_action_args *uap, int *retval)
+{
+       u_int32_t tlv_size = (sizeof(u_int8_t) + sizeof(u_int32_t) + sizeof(necp_policy_id));
+       u_int32_t response_size = 0;
+       u_int8_t *response = NULL;
+       int num_policies = 0;
+       int cur_policy_index = 0;
+       int error = 0;
+       struct necp_session_policy *policy;
+
+       LIST_FOREACH(policy, &session->policies, chain) {
+               if (!policy->pending_deletion) {
+                       num_policies++;
+               }
+       }
+
+       if (num_policies > NECP_MAX_POLICY_LIST_COUNT) {
+               NECPLOG(LOG_ERR, "necp_session_list_all size too large to copy (%u policies)", num_policies);
+               error = EINVAL;
+               goto done;
+       }
+
+       response_size = num_policies * tlv_size;
+       if (uap->out_buffer_length < response_size || uap->out_buffer == 0) {
+               NECPLOG(LOG_ERR, "necp_session_list_all buffer not large enough (%u < %u)", uap->out_buffer_length, response_size);
+               error = EINVAL;
+               goto done;
+       }
+
+       // Create a response with one Policy ID TLV for each policy
+       MALLOC(response, u_int8_t *, response_size, M_NECP, M_WAITOK | M_ZERO);
+       if (response == NULL) {
+               error = ENOMEM;
+               goto done;
+       }
+
+       u_int8_t *cursor = response;
+       LIST_FOREACH(policy, &session->policies, chain) {
+               if (!policy->pending_deletion && cur_policy_index < num_policies) {
+                       cursor = necp_buffer_write_tlv(cursor, NECP_TLV_POLICY_ID, sizeof(u_int32_t), &policy->id, response, response_size);
+                       cur_policy_index++;
+               }
+       }
+
+       error = copyout(response, uap->out_buffer, response_size);
+       if (error != 0) {
+               NECPLOG(LOG_ERR, "necp_session_list_all TLV copyout error (%d)", error);
+               goto done;
+       }
+
+done:
+       if (response != NULL) {
+               FREE(response, M_NECP);
+               response = NULL;
+       }
+       *retval = error;
+
+       return (error);
+}
+
+
+static int
+necp_session_delete_all(struct necp_session *session, struct necp_session_action_args *uap, int *retval)
+{
+#pragma unused(uap)
+       necp_policy_mark_all_for_deletion(session);
+       *retval = 0;
+       return (0);
+}
+
+static int
+necp_session_set_session_priority(struct necp_session *session, struct necp_session_action_args *uap, int *retval)
+{
+       int error = 0;
+       struct necp_session_policy *policy = NULL;
+       struct necp_session_policy *temp_policy = NULL;
+
+       if (uap->in_buffer_length < sizeof(necp_session_priority) || uap->in_buffer == 0) {
+               NECPLOG(LOG_ERR, "necp_session_set_session_priority invalid input (%zu)", uap->in_buffer_length);
+               error = EINVAL;
+               goto done;
+       }
+
+       necp_session_priority requested_session_priority = 0;
+       error = copyin(uap->in_buffer, &requested_session_priority, sizeof(requested_session_priority));
+       if (error != 0) {
+               NECPLOG(LOG_ERR, "necp_session_set_session_priority priority copyin error (%d)", error);
+               goto done;
+       }
+
+       // Enforce special session priorities with entitlements
+       if (requested_session_priority == NECP_SESSION_PRIORITY_CONTROL ||
+               requested_session_priority == NECP_SESSION_PRIORITY_PRIVILEGED_TUNNEL) {
+               errno_t cred_result = priv_check_cred(kauth_cred_get(), PRIV_NET_PRIVILEGED_NECP_POLICIES, 0);
+               if (cred_result != 0) {
+                       NECPLOG(LOG_ERR, "Session does not hold necessary entitlement to claim priority level %d", requested_session_priority);
+                       error = EPERM;
+                       goto done;
+               }
+       }
+
+       if (session->session_priority != requested_session_priority) {
+               session->session_priority = requested_session_priority;
+               session->session_order = necp_allocate_new_session_order(session->session_priority, session->control_unit);
+               session->dirty = TRUE;
+
+               // Mark all policies as needing updates
+               LIST_FOREACH_SAFE(policy, &session->policies, chain, temp_policy) {
+                       policy->pending_update = TRUE;
+               }
+       }
+
+done:
+       *retval = error;
+       return (error);
+}
+
+static int
+necp_session_lock_to_process(struct necp_session *session, struct necp_session_action_args *uap, int *retval)
+{
+#pragma unused(uap)
+       session->proc_locked = TRUE;
+       *retval = 0;
+       return (0);
+}
+
+static int
+necp_session_register_service(struct necp_session *session, struct necp_session_action_args *uap, int *retval)
+{
+       int error = 0;
+       struct necp_service_registration *new_service = NULL;
+
+       if (uap->in_buffer_length < sizeof(uuid_t) || uap->in_buffer == 0) {
+               NECPLOG(LOG_ERR, "necp_session_register_service invalid input (%zu)", uap->in_buffer_length);
+               error = EINVAL;
+               goto done;
+       }
+
+       uuid_t service_uuid;
+       error = copyin(uap->in_buffer, service_uuid, sizeof(service_uuid));
+       if (error != 0) {
+               NECPLOG(LOG_ERR, "necp_session_register_service uuid copyin error (%d)", error);
+               goto done;
+       }
+
+       MALLOC(new_service, struct necp_service_registration *, sizeof(*new_service), M_NECP, M_WAITOK | M_ZERO);
+       if (new_service == NULL) {
+               NECPLOG0(LOG_ERR, "Failed to allocate service registration");
+               error = ENOMEM;
+               goto done;
+       }
+
+       lck_rw_lock_exclusive(&necp_kernel_policy_lock);
+       new_service->service_id = necp_create_uuid_service_id_mapping(service_uuid);
+       LIST_INSERT_HEAD(&session->services, new_service, session_chain);
+       LIST_INSERT_HEAD(&necp_registered_service_list, new_service, kernel_chain);
+       lck_rw_done(&necp_kernel_policy_lock);
+
+done:
+       *retval = error;
+       return (error);
+}
+
+static int
+necp_session_unregister_service(struct necp_session *session, struct necp_session_action_args *uap, int *retval)
+{
+       int error = 0;
+       struct necp_service_registration *service = NULL;
+       struct necp_service_registration *temp_service = NULL;
+       struct necp_uuid_id_mapping *mapping = NULL;
+
+       if (uap->in_buffer_length < sizeof(uuid_t) || uap->in_buffer == 0) {
+               NECPLOG(LOG_ERR, "necp_session_unregister_service invalid input (%zu)", uap->in_buffer_length);
+               error = EINVAL;
+               goto done;
+       }
+
+       uuid_t service_uuid;
+       error = copyin(uap->in_buffer, service_uuid, sizeof(service_uuid));
+       if (error != 0) {
+               NECPLOG(LOG_ERR, "necp_session_unregister_service uuid copyin error (%d)", error);
+               goto done;
+       }
+
+       // Remove all matching services for this session
+       lck_rw_lock_exclusive(&necp_kernel_policy_lock);
+       mapping = necp_uuid_lookup_service_id_locked(service_uuid);
+       if (mapping != NULL) {
+               LIST_FOREACH_SAFE(service, &session->services, session_chain, temp_service) {
+                       if (service->service_id == mapping->id) {
+                               LIST_REMOVE(service, session_chain);
+                               LIST_REMOVE(service, kernel_chain);
+                               FREE(service, M_NECP);
+                       }
+               }
+               necp_remove_uuid_service_id_mapping(service_uuid);
+       }
+       lck_rw_done(&necp_kernel_policy_lock);
+
+done:
+       *retval = error;
+       return (error);
+}
+
+static int
+necp_session_dump_all(struct necp_session *session, struct necp_session_action_args *uap, int *retval)
+{
+       int error = 0;
+
+       if (uap->out_buffer_length == 0 || uap->out_buffer == 0) {
+               NECPLOG(LOG_ERR, "necp_session_dump_all invalid output buffer (%zu)", uap->out_buffer_length);
+               error = EINVAL;
+               goto done;
+       }
+
+       error = necp_handle_policy_dump_all(session, 0, NULL, uap->out_buffer, uap->out_buffer_length, 0);
+done:
+       *retval = error;
+       return (error);
+}
+
+int
+necp_session_action(struct proc *p, struct necp_session_action_args *uap, int *retval)
+{
+#pragma unused(p)
+       int error = 0;
+       int return_value = 0;
+       struct necp_session *session = NULL;
+       error = necp_session_find_from_fd(uap->necp_fd, &session);
+       if (error != 0) {
+               NECPLOG(LOG_ERR, "necp_session_action find fd error (%d)", error);
+               return (error);
+       }
+
+       NECP_SESSION_LOCK(session);
+
+       if (session->proc_locked) {
+               // Verify that the calling process is allowed to do actions
+               uuid_t proc_uuid;
+               proc_getexecutableuuid(current_proc(), proc_uuid, sizeof(proc_uuid));
+               if (uuid_compare(proc_uuid, session->proc_uuid) != 0) {
+                       error = EPERM;
+                       goto done;
+               }
+       } else {
+               // If not locked, update the proc_uuid and proc_pid of the session
+               proc_getexecutableuuid(current_proc(), session->proc_uuid, sizeof(session->proc_uuid));
+               session->proc_pid = proc_pid(current_proc());
+       }
+
+       u_int32_t action = uap->action;
+       switch (action) {
+               case NECP_SESSION_ACTION_POLICY_ADD: {
+                       return_value = necp_session_add_policy(session, uap, retval);
+                       break;
+               }
+               case NECP_SESSION_ACTION_POLICY_GET: {
+                       return_value = necp_session_get_policy(session, uap, retval);
+                       break;
+               }
+               case NECP_SESSION_ACTION_POLICY_DELETE:  {
+                       return_value = necp_session_delete_policy(session, uap, retval);
+                       break;
+               }
+               case NECP_SESSION_ACTION_POLICY_APPLY_ALL: {
+                       return_value = necp_session_apply_all(session, uap, retval);
+                       break;
+               }
+               case NECP_SESSION_ACTION_POLICY_LIST_ALL: {
+                       return_value = necp_session_list_all(session, uap, retval);
+                       break;
+               }
+               case NECP_SESSION_ACTION_POLICY_DELETE_ALL: {
+                       return_value = necp_session_delete_all(session, uap, retval);
+                       break;
+               }
+               case NECP_SESSION_ACTION_SET_SESSION_PRIORITY: {
+                       return_value = necp_session_set_session_priority(session, uap, retval);
+                       break;
+               }
+               case NECP_SESSION_ACTION_LOCK_SESSION_TO_PROC: {
+                       return_value = necp_session_lock_to_process(session, uap, retval);
+                       break;
+               }
+               case NECP_SESSION_ACTION_REGISTER_SERVICE: {
+                       return_value = necp_session_register_service(session, uap, retval);
+                       break;
+               }
+               case NECP_SESSION_ACTION_UNREGISTER_SERVICE: {
+                       return_value = necp_session_unregister_service(session, uap, retval);
+                       break;
+               }
+               case NECP_SESSION_ACTION_POLICY_DUMP_ALL: {
+                       return_value = necp_session_dump_all(session, uap, retval);
+                       break;
+               }
+               default: {
+                       NECPLOG(LOG_ERR, "necp_session_action unknown action (%u)", action);
+                       return_value = EINVAL;
+                       break;
+               }
+       }
+
+done:
+       NECP_SESSION_UNLOCK(session);
+       file_drop(uap->necp_fd);
+
+       return (return_value);
+}
 
 // Kernel Control functions
 static errno_t necp_register_control(void);
@@ -521,6 +1172,10 @@ necp_init(void)
 
        lck_rw_init(&necp_route_rule_lock, necp_route_rule_mtx_grp, necp_route_rule_mtx_attr);
 
+       necp_client_init();
+
+       TAILQ_INIT(&necp_session_list);
+
        LIST_INIT(&necp_kernel_socket_policies);
        LIST_INIT(&necp_kernel_ip_output_policies);
 
@@ -654,8 +1309,8 @@ necp_post_change_event(struct kev_necp_policies_changed_data *necp_event_data)
 static errno_t
 necp_ctl_connect(kern_ctl_ref kctlref, struct sockaddr_ctl *sac, void **unitinfo)
 {
-#pragma unused(kctlref)
-       *unitinfo = necp_create_session(sac->sc_unit);
+#pragma unused(kctlref, sac)
+       *unitinfo = necp_create_session();
        if (*unitinfo == NULL) {
                // Could not allocate session
                return (ENOBUFS);
@@ -723,7 +1378,7 @@ necp_packet_get_tlv_at_offset(mbuf_t packet, int tlv_offset, u_int32_t buff_len,
        u_int32_t       length;
 
        if (tlv_offset < 0) {
-               return (error);
+               return (EINVAL);
        }
 
        error = mbuf_copydata(packet, tlv_offset + sizeof(u_int8_t), sizeof(length), &length);
@@ -753,20 +1408,6 @@ necp_packet_get_tlv_at_offset(mbuf_t packet, int tlv_offset, u_int32_t buff_len,
        return (0);
 }
 
-static int
-necp_packet_get_tlv(mbuf_t packet, int offset, u_int8_t type, u_int32_t buff_len, void *buff, u_int32_t *value_size)
-{
-       int             error           = 0;
-       int             tlv_offset;
-
-       tlv_offset = necp_packet_find_tlv(packet, offset, type, &error, 0);
-       if (tlv_offset < 0) {
-               return (error);
-       }
-
-       return (necp_packet_get_tlv_at_offset(packet, tlv_offset, buff_len, buff, value_size));
-}
-
 static u_int8_t *
 necp_buffer_write_packet_header(u_int8_t *buffer, u_int8_t packet_type, u_int8_t flags, u_int32_t message_id)
 {
@@ -776,19 +1417,69 @@ necp_buffer_write_packet_header(u_int8_t *buffer, u_int8_t packet_type, u_int8_t
        return (buffer + sizeof(struct necp_packet_header));
 }
 
-static u_int8_t *
-necp_buffer_write_tlv(u_int8_t *buffer, u_int8_t type, u_int32_t length, const void *value)
+static inline bool
+necp_buffer_write_tlv_validate(u_int8_t *cursor, u_int8_t type, u_int32_t length,
+                                                          u_int8_t *buffer, u_int32_t buffer_length)
+{
+       if (cursor < buffer || (uintptr_t)(cursor - buffer) > buffer_length) {
+               NECPLOG0(LOG_ERR, "Cannot write TLV in buffer (invalid cursor)");
+               return (false);
+       }
+       u_int8_t *next_tlv = (u_int8_t *)(cursor + sizeof(type) + sizeof(length) + length);
+       if (next_tlv <= buffer || // make sure the next TLV start doesn't overflow
+               (uintptr_t)(next_tlv - buffer) > buffer_length) { // make sure the next TLV has enough room in buffer
+               NECPLOG(LOG_ERR, "Cannot write TLV in buffer (TLV length %u, buffer length %u)",
+                               length, buffer_length);
+               return (false);
+       }
+       return (true);
+}
+
+u_int8_t *
+necp_buffer_write_tlv_if_different(u_int8_t *cursor, u_int8_t type,
+                                                                  u_int32_t length, const void *value, bool *updated,
+                                                                  u_int8_t *buffer, u_int32_t buffer_length)
+{
+       if (!necp_buffer_write_tlv_validate(cursor, type, length, buffer, buffer_length)) {
+               return (NULL);
+       }
+       u_int8_t *next_tlv = (u_int8_t *)(cursor + sizeof(type) + sizeof(length) + length);
+       if (*updated || *(u_int8_t *)(cursor) != type) {
+               *(u_int8_t *)(cursor) = type;
+               *updated = TRUE;
+       }
+       if (*updated || *(u_int32_t *)(void *)(cursor + sizeof(type)) != length) {
+               *(u_int32_t *)(void *)(cursor + sizeof(type)) = length;
+               *updated = TRUE;
+       }
+       if (length > 0) {
+               if (*updated || memcmp((u_int8_t *)(cursor + sizeof(type) + sizeof(length)), value, length) != 0) {
+                       memcpy((u_int8_t *)(cursor + sizeof(type) + sizeof(length)), value, length);
+                       *updated = TRUE;
+               }
+       }
+       return (next_tlv);
+}
+
+u_int8_t *
+necp_buffer_write_tlv(u_int8_t *cursor, u_int8_t type,
+                                         u_int32_t length, const void *value,
+                                         u_int8_t *buffer, u_int32_t buffer_length)
 {
-       *(u_int8_t *)(buffer) = type;
-       *(u_int32_t *)(void *)(buffer + sizeof(type)) = length;
+       if (!necp_buffer_write_tlv_validate(cursor, type, length, buffer, buffer_length)) {
+               return (NULL);
+       }
+       u_int8_t *next_tlv = (u_int8_t *)(cursor + sizeof(type) + sizeof(length) + length);
+       *(u_int8_t *)(cursor) = type;
+       *(u_int32_t *)(void *)(cursor + sizeof(type)) = length;
        if (length > 0) {
-               memcpy((u_int8_t *)(buffer + sizeof(type) + sizeof(length)), value, length);
+               memcpy((u_int8_t *)(cursor + sizeof(type) + sizeof(length)), value, length);
        }
 
-       return ((u_int8_t *)(buffer + sizeof(type) + sizeof(length) + length));
+       return (next_tlv);
 }
 
-static u_int8_t
+u_int8_t
 necp_buffer_get_tlv_type(u_int8_t *buffer, int tlv_offset)
 {
        u_int8_t *type = NULL;
@@ -801,7 +1492,7 @@ necp_buffer_get_tlv_type(u_int8_t *buffer, int tlv_offset)
        return (type ? *type : 0);
 }
 
-static u_int32_t
+u_int32_t
 necp_buffer_get_tlv_length(u_int8_t *buffer, int tlv_offset)
 {
        u_int32_t *length = NULL;
@@ -814,7 +1505,7 @@ necp_buffer_get_tlv_length(u_int8_t *buffer, int tlv_offset)
        return (length ? *length : 0);
 }
 
-static u_int8_t *
+u_int8_t *
 necp_buffer_get_tlv_value(u_int8_t *buffer, int tlv_offset, u_int32_t *value_size)
 {
        u_int8_t *value = NULL;
@@ -831,39 +1522,127 @@ necp_buffer_get_tlv_value(u_int8_t *buffer, int tlv_offset, u_int32_t *value_siz
        return (value);
 }
 
+int
+necp_buffer_find_tlv(u_int8_t *buffer, u_int32_t buffer_length, int offset, u_int8_t type, int next)
+{
+       if (offset < 0) {
+               return (-1);
+       }
+       int cursor = offset;
+       int next_cursor;
+       u_int32_t curr_length;
+       u_int8_t curr_type;
+
+       while (TRUE) {
+               if ((((u_int32_t)cursor) + sizeof(curr_type) + sizeof(curr_length)) > buffer_length) {
+                       return (-1);
+               }
+               if (!next) {
+                       curr_type = necp_buffer_get_tlv_type(buffer, cursor);
+               } else {
+                       next = 0;
+                       curr_type = NECP_TLV_NIL;
+               }
+               curr_length = necp_buffer_get_tlv_length(buffer, cursor);
+               if (curr_length > buffer_length - ((u_int32_t)cursor + sizeof(curr_type) + sizeof(curr_length))) {
+                       return (-1);
+               }
+
+               next_cursor = (cursor + sizeof(curr_type) + sizeof(curr_length) + curr_length);
+               if (curr_type == type) {
+                       // check if entire TLV fits inside buffer
+                       if (((u_int32_t)next_cursor) <= buffer_length) {
+                               return (cursor);
+                       } else {
+                               return (-1);
+                       }
+               }
+               cursor = next_cursor;
+       }
+}
+
+static int
+necp_find_tlv(mbuf_t packet, u_int8_t *buffer, u_int32_t buffer_length, int offset, u_int8_t type, int *err, int next)
+{
+       int cursor = -1;
+       if (packet != NULL) {
+               cursor = necp_packet_find_tlv(packet, offset, type, err, next);
+       } else if (buffer != NULL) {
+               cursor = necp_buffer_find_tlv(buffer, buffer_length, offset, type, next);
+       }
+       return (cursor);
+}
+
+static int
+necp_get_tlv_at_offset(mbuf_t packet, u_int8_t *buffer, u_int32_t buffer_length,
+                                          int tlv_offset, u_int32_t out_buffer_length, void *out_buffer, u_int32_t *value_size)
+{
+       if (packet != NULL) {
+               // Handle mbuf parsing
+               return necp_packet_get_tlv_at_offset(packet, tlv_offset, out_buffer_length, out_buffer, value_size);
+       }
+
+       if (buffer == NULL) {
+               NECPLOG0(LOG_ERR, "necp_get_tlv_at_offset buffer is NULL");
+               return (EINVAL);
+       }
+
+       // Handle buffer parsing
+
+       // Validate that buffer has enough room for any TLV
+       if (tlv_offset + sizeof(u_int8_t) + sizeof(u_int32_t) > buffer_length) {
+               NECPLOG(LOG_ERR, "necp_get_tlv_at_offset buffer_length is too small for TLV (%u < %u)",
+                               buffer_length, tlv_offset + sizeof(u_int8_t) + sizeof(u_int32_t));
+               return (EINVAL);
+       }
+
+       // Validate that buffer has enough room for this TLV
+       u_int32_t tlv_length = necp_buffer_get_tlv_length(buffer, tlv_offset);
+       if (tlv_length > buffer_length - (tlv_offset + sizeof(u_int8_t) + sizeof(u_int32_t))) {
+               NECPLOG(LOG_ERR, "necp_get_tlv_at_offset buffer_length is too small for TLV of length %u (%u < %u)",
+                               tlv_length, buffer_length, tlv_offset + sizeof(u_int8_t) + sizeof(u_int32_t) + tlv_length);
+               return (EINVAL);
+       }
+
+       if (out_buffer != NULL && out_buffer_length > 0) {
+               // Validate that out buffer is large enough for  value
+               if (out_buffer_length < tlv_length) {
+                       NECPLOG(LOG_ERR, "necp_get_tlv_at_offset out_buffer_length is too small for TLV value (%u < %u)",
+                                       out_buffer_length, tlv_length);
+                       return (EINVAL);
+               }
+
+               // Get value pointer
+               u_int8_t *tlv_value = necp_buffer_get_tlv_value(buffer, tlv_offset, NULL);
+               if (tlv_value == NULL) {
+                       NECPLOG0(LOG_ERR, "necp_get_tlv_at_offset tlv_value is NULL");
+                       return (ENOENT);
+               }
+
+               // Copy value
+               memcpy(out_buffer, tlv_value, tlv_length);
+       }
+
+       // Copy out length
+       if (value_size != NULL) {
+               *value_size = tlv_length;
+       }
+
+       return (0);
+}
+
 static int
-necp_buffer_find_tlv(u_int8_t *buffer, u_int32_t buffer_length, int offset, u_int8_t type, int next)
+necp_get_tlv(mbuf_t packet, u_int8_t *buffer, u_int32_t buffer_length,
+                        int offset, u_int8_t type, u_int32_t buff_len, void *buff, u_int32_t *value_size)
 {
-       if (offset < 0) {
-               return (-1);
-       }
-       int cursor = offset;
-       int next_cursor;
-       u_int32_t curr_length;
-       u_int8_t curr_type;
+       int error = 0;
 
-       while (TRUE) {
-               if ((((u_int32_t)cursor) + sizeof(curr_type) + sizeof(curr_length)) > buffer_length) {
-                       return (-1);
-               }
-               if (!next) {
-                       curr_type = necp_buffer_get_tlv_type(buffer, cursor);
-               } else {
-                       next = 0;
-                       curr_type = NECP_TLV_NIL;
-               }
-               curr_length = necp_buffer_get_tlv_length(buffer, cursor);
-               next_cursor = (cursor + sizeof(curr_type) + sizeof(curr_length) + curr_length);
-               if (curr_type == type) {
-                       // check if entire TLV fits inside buffer
-                       if (((u_int32_t)next_cursor) <= buffer_length) {
-                               return (cursor);
-                       } else {
-                               return (-1);
-                       }
-               }
-               cursor = next_cursor;
+       int tlv_offset = necp_find_tlv(packet, buffer, buffer_length, offset, type, &error, 0);
+       if (tlv_offset < 0) {
+               return (error);
        }
+
+       return (necp_get_tlv_at_offset(packet, buffer, buffer_length, tlv_offset, buff_len, buff, value_size));
 }
 
 static bool
@@ -893,7 +1672,7 @@ necp_send_success_response(struct necp_session *session, u_int8_t packet_type, u
        }
        cursor = response;
        cursor = necp_buffer_write_packet_header(cursor, packet_type, NECP_PACKET_FLAGS_RESPONSE, message_id);
-       cursor = necp_buffer_write_tlv(cursor, NECP_TLV_NIL, 0, NULL);
+       cursor = necp_buffer_write_tlv(cursor, NECP_TLV_NIL, 0, NULL, response, response_size);
 
        if (!(success = necp_send_ctl_data(session, (u_int8_t *)response, response_size))) {
                NECPLOG0(LOG_ERR, "Failed to send response");
@@ -916,7 +1695,7 @@ necp_send_error_response(struct necp_session *session, u_int8_t packet_type, u_i
        }
        cursor = response;
        cursor = necp_buffer_write_packet_header(cursor, packet_type, NECP_PACKET_FLAGS_RESPONSE, message_id);
-       cursor = necp_buffer_write_tlv(cursor, NECP_TLV_ERROR, sizeof(error), &error);
+       cursor = necp_buffer_write_tlv(cursor, NECP_TLV_ERROR, sizeof(error), &error, response, response_size);
 
        if (!(success = necp_send_ctl_data(session, (u_int8_t *)response, response_size))) {
                NECPLOG0(LOG_ERR, "Failed to send response");
@@ -939,7 +1718,7 @@ necp_send_policy_id_response(struct necp_session *session, u_int8_t packet_type,
        }
        cursor = response;
        cursor = necp_buffer_write_packet_header(cursor, packet_type, NECP_PACKET_FLAGS_RESPONSE, message_id);
-       cursor = necp_buffer_write_tlv(cursor, NECP_TLV_POLICY_ID, sizeof(policy_id), &policy_id);
+       cursor = necp_buffer_write_tlv(cursor, NECP_TLV_POLICY_ID, sizeof(policy_id), &policy_id, response, response_size);
 
        if (!(success = necp_send_ctl_data(session, (u_int8_t *)response, response_size))) {
                NECPLOG0(LOG_ERR, "Failed to send response");
@@ -992,7 +1771,7 @@ necp_ctl_send(kern_ctl_ref kctlref, u_int32_t unit, void *unitinfo, mbuf_t packe
 
        switch (header.packet_type) {
                case NECP_PACKET_TYPE_POLICY_ADD: {
-                       necp_handle_policy_add(session, header.message_id, packet, sizeof(header));
+                       necp_handle_policy_add(session, header.message_id, packet, NULL, 0, sizeof(header), NULL);
                        break;
                }
                case NECP_PACKET_TYPE_POLICY_GET: {
@@ -1015,6 +1794,10 @@ necp_ctl_send(kern_ctl_ref kctlref, u_int32_t unit, void *unitinfo, mbuf_t packe
                        necp_handle_policy_delete_all(session, header.message_id, packet, sizeof(header));
                        break;
                }
+               case NECP_PACKET_TYPE_POLICY_DUMP_ALL: {
+                       necp_handle_policy_dump_all(session, header.message_id, packet, 0, 0, sizeof(header));
+                       break;
+               }
                case NECP_PACKET_TYPE_SET_SESSION_PRIORITY: {
                        necp_handle_set_session_priority(session, header.message_id, packet, sizeof(header));
                        break;
@@ -1065,29 +1848,55 @@ necp_ctl_setopt(kern_ctl_ref kctlref, u_int32_t unit, void *unitinfo, int opt, v
 }
 
 // Session Management
+
 static struct necp_session *
-necp_create_session(u_int32_t control_unit)
+necp_create_session(void)
 {
        struct necp_session *new_session = NULL;
 
-       MALLOC(new_session, struct necp_session *, sizeof(*new_session), M_NECP, M_WAITOK);
+       MALLOC(new_session, struct necp_session *, sizeof(*new_session), M_NECP, M_WAITOK | M_ZERO);
        if (new_session == NULL) {
                goto done;
        }
-       if (necp_debug) {
-               NECPLOG(LOG_DEBUG, "Create NECP session, control unit %d", control_unit);
-       }
-       memset(new_session, 0, sizeof(*new_session));
+
+       new_session->necp_fd_type = necp_fd_type_session;
        new_session->session_priority = NECP_SESSION_PRIORITY_UNKNOWN;
-       new_session->session_order = necp_allocate_new_session_order(new_session->session_priority, control_unit);
-       new_session->control_unit = control_unit;
        new_session->dirty = FALSE;
        LIST_INIT(&new_session->policies);
+       lck_mtx_init(&new_session->lock, necp_kernel_policy_mtx_grp, necp_kernel_policy_mtx_attr);
 
+       // Take the lock
        lck_rw_lock_exclusive(&necp_kernel_policy_lock);
+
+       // Find the next available control unit
+       u_int32_t control_unit = 1;
+       struct necp_session *next_session = NULL;
+       TAILQ_FOREACH(next_session, &necp_session_list, chain) {
+               if (next_session->control_unit > control_unit) {
+                       // Found a gap, grab this control unit
+                       break;
+               }
+
+               // Try the next control unit, loop around
+               control_unit = next_session->control_unit + 1;
+       }
+
+       new_session->control_unit = control_unit;
+       new_session->session_order = necp_allocate_new_session_order(new_session->session_priority, control_unit);
+
+       if (next_session != NULL) {
+               TAILQ_INSERT_BEFORE(next_session, new_session, chain);
+       } else {
+               TAILQ_INSERT_TAIL(&necp_session_list, new_session, chain);
+       }
+
        necp_session_count++;
        lck_rw_done(&necp_kernel_policy_lock);
 
+       if (necp_debug) {
+               NECPLOG(LOG_DEBUG, "Created NECP session, control unit %d", control_unit);
+       }
+
 done:
        return (new_session);
 }
@@ -1108,15 +1917,19 @@ necp_delete_session(struct necp_session *session)
                if (necp_debug) {
                        NECPLOG0(LOG_DEBUG, "Deleted NECP session");
                }
-               FREE(session, M_NECP);
 
                lck_rw_lock_exclusive(&necp_kernel_policy_lock);
+               TAILQ_REMOVE(&necp_session_list, session, chain);
                necp_session_count--;
                lck_rw_done(&necp_kernel_policy_lock);
+
+               lck_mtx_destroy(&session->lock, necp_kernel_policy_mtx_grp);
+               FREE(session, M_NECP);
        }
 }
 
 // Session Policy Management
+
 static inline u_int8_t
 necp_policy_result_get_type_from_buffer(u_int8_t *buffer, u_int32_t length)
 {
@@ -1145,6 +1958,18 @@ necp_policy_result_requires_route_rules(u_int8_t *buffer, u_int32_t length)
        return (FALSE);
 }
 
+static inline bool
+necp_address_is_valid(struct sockaddr *address)
+{
+       if (address->sa_family == AF_INET) {
+               return (address->sa_len == sizeof(struct sockaddr_in));
+       } else if (address->sa_family == AF_INET6) {
+               return (address->sa_len == sizeof(struct sockaddr_in6));
+       } else {
+               return (FALSE);
+       }
+}
+
 static bool
 necp_policy_result_is_valid(u_int8_t *buffer, u_int32_t length)
 {
@@ -1253,12 +2078,24 @@ necp_policy_condition_is_application(u_int8_t *buffer, u_int32_t length)
        return (necp_policy_condition_get_type_from_buffer(buffer, length) == NECP_POLICY_CONDITION_APPLICATION);
 }
 
+static inline bool
+necp_policy_condition_is_real_application(u_int8_t *buffer, u_int32_t length)
+{
+       return (necp_policy_condition_get_type_from_buffer(buffer, length) == NECP_POLICY_CONDITION_REAL_APPLICATION);
+}
+
 static inline bool
 necp_policy_condition_requires_application(u_int8_t *buffer, u_int32_t length)
 {
        u_int8_t type = necp_policy_condition_get_type_from_buffer(buffer, length);
-       return (type == NECP_POLICY_CONDITION_REAL_APPLICATION ||
-                       type == NECP_POLICY_CONDITION_ENTITLEMENT);
+       return (type == NECP_POLICY_CONDITION_REAL_APPLICATION);
+}
+
+static inline bool
+necp_policy_condition_requires_real_application(u_int8_t *buffer, u_int32_t length)
+{
+       u_int8_t type = necp_policy_condition_get_type_from_buffer(buffer, length);
+       return (type == NECP_POLICY_CONDITION_ENTITLEMENT);
 }
 
 static bool
@@ -1333,14 +2170,17 @@ necp_policy_condition_is_valid(u_int8_t *buffer, u_int32_t length, u_int8_t poli
                }
                case NECP_POLICY_CONDITION_LOCAL_ADDR:
                case NECP_POLICY_CONDITION_REMOTE_ADDR: {
-                       if (!result_cannot_have_ip_layer && condition_length >= sizeof(struct necp_policy_condition_addr)) {
+                       if (!result_cannot_have_ip_layer && condition_length >= sizeof(struct necp_policy_condition_addr) &&
+                               necp_address_is_valid(&((struct necp_policy_condition_addr *)(void *)condition_value)->address.sa)) {
                                validated = TRUE;
                        }
                        break;
                }
                case NECP_POLICY_CONDITION_LOCAL_ADDR_RANGE:
                case NECP_POLICY_CONDITION_REMOTE_ADDR_RANGE: {
-                       if (!result_cannot_have_ip_layer && condition_length >= sizeof(struct necp_policy_condition_addr_range)) {
+                       if (!result_cannot_have_ip_layer && condition_length >= sizeof(struct necp_policy_condition_addr_range) &&
+                               necp_address_is_valid(&((struct necp_policy_condition_addr_range *)(void *)condition_value)->start_address.sa) &&
+                               necp_address_is_valid(&((struct necp_policy_condition_addr_range *)(void *)condition_value)->end_address.sa)) {
                                validated = TRUE;
                        }
                        break;
@@ -1379,6 +2219,10 @@ necp_policy_route_rule_is_valid(u_int8_t *buffer, u_int32_t length)
                        validated = TRUE;
                        break;
                }
+               case NECP_ROUTE_RULE_QOS_MARKING: {
+                       validated = TRUE;
+                       break;
+               }
                default: {
                        validated = FALSE;
                        break;
@@ -1392,6 +2236,30 @@ necp_policy_route_rule_is_valid(u_int8_t *buffer, u_int32_t length)
        return (validated);
 }
 
+static int
+necp_get_posix_error_for_necp_error(int response_error)
+{
+       switch (response_error) {
+               case NECP_ERROR_UNKNOWN_PACKET_TYPE:
+               case NECP_ERROR_INVALID_TLV:
+               case NECP_ERROR_POLICY_RESULT_INVALID:
+               case NECP_ERROR_POLICY_CONDITIONS_INVALID:
+               case NECP_ERROR_ROUTE_RULES_INVALID: {
+                       return (EINVAL);
+               }
+               case NECP_ERROR_POLICY_ID_NOT_FOUND: {
+                       return (ENOENT);
+               }
+               case NECP_ERROR_INVALID_PROCESS: {
+                       return (EPERM);
+               }
+               case NECP_ERROR_INTERNAL:
+               default: {
+                       return (ENOMEM);
+               }
+       }
+}
+
 static void
 necp_handle_set_session_priority(struct necp_session *session, u_int32_t message_id, mbuf_t packet, int offset)
 {
@@ -1402,7 +2270,7 @@ necp_handle_set_session_priority(struct necp_session *session, u_int32_t message
        u_int32_t requested_session_priority = NECP_SESSION_PRIORITY_UNKNOWN;
 
        // Read policy id
-       error = necp_packet_get_tlv(packet, offset, NECP_TLV_SESSION_PRIORITY, sizeof(requested_session_priority), &requested_session_priority, NULL);
+       error = necp_get_tlv(packet, NULL, 0, offset, NECP_TLV_SESSION_PRIORITY, sizeof(requested_session_priority), &requested_session_priority, NULL);
        if (error) {
                NECPLOG(LOG_ERR, "Failed to get session priority: %d", error);
                response_error = NECP_ERROR_INVALID_TLV;
@@ -1475,7 +2343,7 @@ necp_handle_register_service(struct necp_session *session, u_int32_t message_id,
        }
 
        // Read service uuid
-       error = necp_packet_get_tlv(packet, offset, NECP_TLV_SERVICE_UUID, sizeof(uuid_t), service_uuid, NULL);
+       error = necp_get_tlv(packet, NULL, 0, offset, NECP_TLV_SERVICE_UUID, sizeof(uuid_t), service_uuid, NULL);
        if (error) {
                NECPLOG(LOG_ERR, "Failed to get service UUID: %d", error);
                response_error = NECP_ERROR_INVALID_TLV;
@@ -1488,7 +2356,7 @@ necp_handle_register_service(struct necp_session *session, u_int32_t message_id,
                response_error = NECP_ERROR_INTERNAL;
                goto fail;
        }
-       
+
        lck_rw_lock_exclusive(&necp_kernel_policy_lock);
        memset(new_service, 0, sizeof(*new_service));
        new_service->service_id = necp_create_uuid_service_id_mapping(service_uuid);
@@ -1520,7 +2388,7 @@ necp_handle_unregister_service(struct necp_session *session, u_int32_t message_i
        }
 
        // Read service uuid
-       error = necp_packet_get_tlv(packet, offset, NECP_TLV_SERVICE_UUID, sizeof(uuid_t), service_uuid, NULL);
+       error = necp_get_tlv(packet, NULL, 0, offset, NECP_TLV_SERVICE_UUID, sizeof(uuid_t), service_uuid, NULL);
        if (error) {
                NECPLOG(LOG_ERR, "Failed to get service UUID: %d", error);
                response_error = NECP_ERROR_INVALID_TLV;
@@ -1548,13 +2416,16 @@ fail:
        necp_send_error_response(session, NECP_PACKET_TYPE_UNREGISTER_SERVICE, message_id, response_error);
 }
 
-static void
-necp_handle_policy_add(struct necp_session *session, u_int32_t message_id, mbuf_t packet, int offset)
+static necp_policy_id
+necp_handle_policy_add(struct necp_session *session, u_int32_t message_id, mbuf_t packet,
+                                          u_int8_t *tlv_buffer, size_t tlv_buffer_length, int offset, int *return_error)
 {
        bool has_default_condition = FALSE;
        bool has_non_default_condition = FALSE;
        bool has_application_condition = FALSE;
+       bool has_real_application_condition = FALSE;
        bool requires_application_condition = FALSE;
+       bool requires_real_application_condition = FALSE;
        u_int8_t *conditions_array = NULL;
        u_int32_t conditions_array_size = 0;
        int conditions_array_cursor;
@@ -1574,7 +2445,7 @@ necp_handle_policy_add(struct necp_session *session, u_int32_t message_id, mbuf_
        u_int32_t policy_result_size = 0;
 
        // Read policy order
-       error = necp_packet_get_tlv(packet, offset, NECP_TLV_POLICY_ORDER, sizeof(order), &order, NULL);
+       error = necp_get_tlv(packet, tlv_buffer, tlv_buffer_length, offset, NECP_TLV_POLICY_ORDER, sizeof(order), &order, NULL);
        if (error) {
                NECPLOG(LOG_ERR, "Failed to get policy order: %d", error);
                response_error = NECP_ERROR_INVALID_TLV;
@@ -1582,20 +2453,25 @@ necp_handle_policy_add(struct necp_session *session, u_int32_t message_id, mbuf_
        }
 
        // Read policy result
-       cursor = necp_packet_find_tlv(packet, offset, NECP_TLV_POLICY_RESULT, &error, 0);
-       error = necp_packet_get_tlv_at_offset(packet, cursor, 0, NULL, &policy_result_size);
+       cursor = necp_find_tlv(packet, tlv_buffer, tlv_buffer_length, offset, NECP_TLV_POLICY_RESULT, &error, 0);
+       error = necp_get_tlv_at_offset(packet, tlv_buffer, tlv_buffer_length, cursor, 0, NULL, &policy_result_size);
        if (error || policy_result_size == 0) {
                NECPLOG(LOG_ERR, "Failed to get policy result length: %d", error);
                response_error = NECP_ERROR_INVALID_TLV;
                goto fail;
        }
+       if (policy_result_size > NECP_MAX_POLICY_RESULT_SIZE) {
+               NECPLOG(LOG_ERR, "Policy result length too large: %u", policy_result_size);
+               response_error = NECP_ERROR_INVALID_TLV;
+               goto fail;
+       }
        MALLOC(policy_result, u_int8_t *, policy_result_size, M_NECP, M_WAITOK);
        if (policy_result == NULL) {
                NECPLOG(LOG_ERR, "Failed to allocate a policy result buffer (size %d)", policy_result_size);
                response_error = NECP_ERROR_INTERNAL;
                goto fail;
        }
-       error = necp_packet_get_tlv_at_offset(packet, cursor, policy_result_size, policy_result, NULL);
+       error = necp_get_tlv_at_offset(packet, tlv_buffer, tlv_buffer_length, cursor, policy_result_size, policy_result, NULL);
        if (error) {
                NECPLOG(LOG_ERR, "Failed to get policy result: %d", error);
                response_error = NECP_ERROR_POLICY_RESULT_INVALID;
@@ -1609,11 +2485,11 @@ necp_handle_policy_add(struct necp_session *session, u_int32_t message_id, mbuf_
 
        if (necp_policy_result_requires_route_rules(policy_result, policy_result_size)) {
                // Read route rules conditions
-               for (cursor = necp_packet_find_tlv(packet, offset, NECP_TLV_ROUTE_RULE, &error, 0);
+               for (cursor = necp_find_tlv(packet, tlv_buffer, tlv_buffer_length, offset, NECP_TLV_ROUTE_RULE, &error, 0);
                         cursor >= 0;
-                        cursor = necp_packet_find_tlv(packet, cursor, NECP_TLV_ROUTE_RULE, &error, 1)) {
+                        cursor = necp_find_tlv(packet, tlv_buffer, tlv_buffer_length, cursor, NECP_TLV_ROUTE_RULE, &error, 1)) {
                        u_int32_t route_rule_size = 0;
-                       necp_packet_get_tlv_at_offset(packet, cursor, 0, NULL, &route_rule_size);
+                       necp_get_tlv_at_offset(packet, tlv_buffer, tlv_buffer_length, cursor, 0, NULL, &route_rule_size);
                        if (route_rule_size > 0) {
                                route_rules_array_size += (sizeof(u_int8_t) + sizeof(u_int32_t) + route_rule_size);
                        }
@@ -1624,7 +2500,11 @@ necp_handle_policy_add(struct necp_session *session, u_int32_t message_id, mbuf_
                        response_error = NECP_ERROR_INVALID_TLV;
                        goto fail;
                }
-
+               if (route_rules_array_size > NECP_MAX_ROUTE_RULES_ARRAY_SIZE) {
+                       NECPLOG(LOG_ERR, "Route rules length too large: %u", route_rules_array_size);
+                       response_error = NECP_ERROR_INVALID_TLV;
+                       goto fail;
+               }
                MALLOC(route_rules_array, u_int8_t *, route_rules_array_size, M_NECP, M_WAITOK);
                if (route_rules_array == NULL) {
                        NECPLOG(LOG_ERR, "Failed to allocate a policy route rules array (size %d)", route_rules_array_size);
@@ -1633,12 +2513,12 @@ necp_handle_policy_add(struct necp_session *session, u_int32_t message_id, mbuf_
                }
 
                route_rules_array_cursor = 0;
-               for (cursor = necp_packet_find_tlv(packet, offset, NECP_TLV_ROUTE_RULE, &error, 0);
+               for (cursor = necp_find_tlv(packet, tlv_buffer, tlv_buffer_length, offset, NECP_TLV_ROUTE_RULE, &error, 0);
                         cursor >= 0;
-                        cursor = necp_packet_find_tlv(packet, cursor, NECP_TLV_ROUTE_RULE, &error, 1)) {
+                        cursor = necp_find_tlv(packet, tlv_buffer, tlv_buffer_length, cursor, NECP_TLV_ROUTE_RULE, &error, 1)) {
                        u_int8_t route_rule_type = NECP_TLV_ROUTE_RULE;
                        u_int32_t route_rule_size = 0;
-                       necp_packet_get_tlv_at_offset(packet, cursor, 0, NULL, &route_rule_size);
+                       necp_get_tlv_at_offset(packet, tlv_buffer, tlv_buffer_length, cursor, 0, NULL, &route_rule_size);
                        if (route_rule_size > 0 && route_rule_size <= (route_rules_array_size - route_rules_array_cursor)) {
                                // Add type
                                memcpy((route_rules_array + route_rules_array_cursor), &route_rule_type, sizeof(route_rule_type));
@@ -1649,7 +2529,7 @@ necp_handle_policy_add(struct necp_session *session, u_int32_t message_id, mbuf_
                                route_rules_array_cursor += sizeof(route_rule_size);
 
                                // Add value
-                               necp_packet_get_tlv_at_offset(packet, cursor, route_rule_size, (route_rules_array + route_rules_array_cursor), NULL);
+                               necp_get_tlv_at_offset(packet, tlv_buffer, tlv_buffer_length, cursor, route_rule_size, (route_rules_array + route_rules_array_cursor), NULL);
 
                                if (!necp_policy_route_rule_is_valid((route_rules_array + route_rules_array_cursor), route_rule_size)) {
                                        NECPLOG0(LOG_ERR, "Failed to validate policy route rule");
@@ -1672,11 +2552,11 @@ necp_handle_policy_add(struct necp_session *session, u_int32_t message_id, mbuf_
        }
 
        // Read policy conditions
-       for (cursor = necp_packet_find_tlv(packet, offset, NECP_TLV_POLICY_CONDITION, &error, 0);
+       for (cursor = necp_find_tlv(packet, tlv_buffer, tlv_buffer_length, offset, NECP_TLV_POLICY_CONDITION, &error, 0);
                cursor >= 0;
-               cursor = necp_packet_find_tlv(packet, cursor, NECP_TLV_POLICY_CONDITION, &error, 1)) {
+               cursor = necp_find_tlv(packet, tlv_buffer, tlv_buffer_length, cursor, NECP_TLV_POLICY_CONDITION, &error, 1)) {
                u_int32_t condition_size = 0;
-               necp_packet_get_tlv_at_offset(packet, cursor, 0, NULL, &condition_size);
+               necp_get_tlv_at_offset(packet, tlv_buffer, tlv_buffer_length, cursor, 0, NULL, &condition_size);
 
                if (condition_size > 0) {
                        conditions_array_size += (sizeof(u_int8_t) + sizeof(u_int32_t) + condition_size);
@@ -1688,6 +2568,11 @@ necp_handle_policy_add(struct necp_session *session, u_int32_t message_id, mbuf_
                response_error = NECP_ERROR_INVALID_TLV;
                goto fail;
        }
+       if (conditions_array_size > NECP_MAX_CONDITIONS_ARRAY_SIZE) {
+               NECPLOG(LOG_ERR, "Conditions length too large: %u", conditions_array_size);
+               response_error = NECP_ERROR_INVALID_TLV;
+               goto fail;
+       }
        MALLOC(conditions_array, u_int8_t *, conditions_array_size, M_NECP, M_WAITOK);
        if (conditions_array == NULL) {
                NECPLOG(LOG_ERR, "Failed to allocate a policy conditions array (size %d)", conditions_array_size);
@@ -1696,12 +2581,12 @@ necp_handle_policy_add(struct necp_session *session, u_int32_t message_id, mbuf_
        }
 
        conditions_array_cursor = 0;
-       for (cursor = necp_packet_find_tlv(packet, offset, NECP_TLV_POLICY_CONDITION, &error, 0);
+       for (cursor = necp_find_tlv(packet, tlv_buffer, tlv_buffer_length, offset, NECP_TLV_POLICY_CONDITION, &error, 0);
                cursor >= 0;
-               cursor = necp_packet_find_tlv(packet, cursor, NECP_TLV_POLICY_CONDITION, &error, 1)) {
+               cursor = necp_find_tlv(packet, tlv_buffer, tlv_buffer_length, cursor, NECP_TLV_POLICY_CONDITION, &error, 1)) {
                u_int8_t condition_type = NECP_TLV_POLICY_CONDITION;
                u_int32_t condition_size = 0;
-               necp_packet_get_tlv_at_offset(packet, cursor, 0, NULL, &condition_size);
+               necp_get_tlv_at_offset(packet, tlv_buffer, tlv_buffer_length, cursor, 0, NULL, &condition_size);
                if (condition_size > 0 && condition_size <= (conditions_array_size - conditions_array_cursor)) {
                        // Add type
                        memcpy((conditions_array + conditions_array_cursor), &condition_type, sizeof(condition_type));
@@ -1712,7 +2597,7 @@ necp_handle_policy_add(struct necp_session *session, u_int32_t message_id, mbuf_
                        conditions_array_cursor += sizeof(condition_size);
 
                        // Add value
-                       necp_packet_get_tlv_at_offset(packet, cursor, condition_size, (conditions_array + conditions_array_cursor), NULL);
+                       necp_get_tlv_at_offset(packet, tlv_buffer, tlv_buffer_length, cursor, condition_size, (conditions_array + conditions_array_cursor), NULL);
                        if (!necp_policy_condition_is_valid((conditions_array + conditions_array_cursor), condition_size, necp_policy_result_get_type_from_buffer(policy_result, policy_result_size))) {
                                NECPLOG0(LOG_ERR, "Failed to validate policy condition");
                                response_error = NECP_ERROR_POLICY_CONDITIONS_INVALID;
@@ -1729,32 +2614,48 @@ necp_handle_policy_add(struct necp_session *session, u_int32_t message_id, mbuf_
                                response_error = NECP_ERROR_POLICY_CONDITIONS_INVALID;
                                goto fail;
                        }
-                       
+
                        if (necp_policy_condition_is_application((conditions_array + conditions_array_cursor), condition_size)) {
                                has_application_condition = TRUE;
                        }
-                       
+
+                       if (necp_policy_condition_is_real_application((conditions_array + conditions_array_cursor), condition_size)) {
+                               has_real_application_condition = TRUE;
+                       }
+
                        if (necp_policy_condition_requires_application((conditions_array + conditions_array_cursor), condition_size)) {
                                requires_application_condition = TRUE;
                        }
 
+                       if (necp_policy_condition_requires_real_application((conditions_array + conditions_array_cursor), condition_size)) {
+                               requires_real_application_condition = TRUE;
+                       }
+
                        conditions_array_cursor += condition_size;
                }
        }
-       
+
        if (requires_application_condition && !has_application_condition) {
                NECPLOG0(LOG_ERR, "Failed to validate conditions; did not contain application condition");
                response_error = NECP_ERROR_POLICY_CONDITIONS_INVALID;
                goto fail;
        }
 
+       if (requires_real_application_condition && !has_real_application_condition) {
+               NECPLOG0(LOG_ERR, "Failed to validate conditions; did not contain real application condition");
+               response_error = NECP_ERROR_POLICY_CONDITIONS_INVALID;
+               goto fail;
+       }
+
        if ((policy = necp_policy_create(session, order, conditions_array, conditions_array_size, route_rules_array, route_rules_array_size, policy_result, policy_result_size)) == NULL) {
                response_error = NECP_ERROR_INTERNAL;
                goto fail;
        }
 
-       necp_send_policy_id_response(session, NECP_PACKET_TYPE_POLICY_ADD, message_id, policy->id);
-       return;
+       if (packet != NULL) {
+               necp_send_policy_id_response(session, NECP_PACKET_TYPE_POLICY_ADD, message_id, policy->id);
+       }
+       return (policy->id);
 
 fail:
        if (policy_result != NULL) {
@@ -1767,7 +2668,13 @@ fail:
                FREE(route_rules_array, M_NECP);
        }
 
-       necp_send_error_response(session, NECP_PACKET_TYPE_POLICY_ADD, message_id, response_error);
+       if (packet != NULL) {
+               necp_send_error_response(session, NECP_PACKET_TYPE_POLICY_ADD, message_id, response_error);
+       }
+       if (return_error != NULL) {
+               *return_error = necp_get_posix_error_for_necp_error(response_error);
+       }
+       return (0);
 }
 
 static void
@@ -1786,7 +2693,7 @@ necp_handle_policy_get(struct necp_session *session, u_int32_t message_id, mbuf_
        struct necp_session_policy *policy = NULL;
 
        // Read policy id
-       error = necp_packet_get_tlv(packet, offset, NECP_TLV_POLICY_ID, sizeof(policy_id), &policy_id, NULL);
+       error = necp_get_tlv(packet, NULL, 0, offset, NECP_TLV_POLICY_ID, sizeof(policy_id), &policy_id, NULL);
        if (error) {
                NECPLOG(LOG_ERR, "Failed to get policy id: %d", error);
                response_error = NECP_ERROR_INVALID_TLV;
@@ -1805,16 +2712,16 @@ necp_handle_policy_get(struct necp_session *session, u_int32_t message_id, mbuf_
        response_size = sizeof(struct necp_packet_header) + order_tlv_size + result_tlv_size + policy->conditions_size;
        MALLOC(response, u_int8_t *, response_size, M_NECP, M_WAITOK);
        if (response == NULL) {
-               necp_send_error_response(session, NECP_PACKET_TYPE_POLICY_LIST_ALL, message_id, NECP_ERROR_INTERNAL);
+               necp_send_error_response(session, NECP_PACKET_TYPE_POLICY_GET, message_id, NECP_ERROR_INTERNAL);
                return;
        }
 
        cursor = response;
        cursor = necp_buffer_write_packet_header(cursor, NECP_PACKET_TYPE_POLICY_GET, NECP_PACKET_FLAGS_RESPONSE, message_id);
-       cursor = necp_buffer_write_tlv(cursor, NECP_TLV_POLICY_ORDER, sizeof(necp_policy_order), &policy->order);
+       cursor = necp_buffer_write_tlv(cursor, NECP_TLV_POLICY_ORDER, sizeof(necp_policy_order), &policy->order, response, response_size);
 
        if (result_tlv_size) {
-               cursor = necp_buffer_write_tlv(cursor, NECP_TLV_POLICY_RESULT, policy->result_size, &policy->result);
+               cursor = necp_buffer_write_tlv(cursor, NECP_TLV_POLICY_RESULT, policy->result_size, &policy->result, response, response_size);
        }
        if (policy->conditions_size) {
                memcpy(((u_int8_t *)(void *)(cursor)), policy->conditions, policy->conditions_size);
@@ -1841,7 +2748,7 @@ necp_handle_policy_delete(struct necp_session *session, u_int32_t message_id, mb
        struct necp_session_policy *policy = NULL;
 
        // Read policy id
-       error = necp_packet_get_tlv(packet, offset, NECP_TLV_POLICY_ID, sizeof(policy_id), &policy_id, NULL);
+       error = necp_get_tlv(packet, NULL, 0, offset, NECP_TLV_POLICY_ID, sizeof(policy_id), &policy_id, NULL);
        if (error) {
                NECPLOG(LOG_ERR, "Failed to get policy id: %d", error);
                response_error = NECP_ERROR_INVALID_TLV;
@@ -1888,62 +2795,555 @@ necp_handle_policy_list_all(struct necp_session *session, u_int32_t message_id,
                if (!policy->pending_deletion) {
                        num_policies++;
                }
-       }
+       }
+
+       // Create a response with one Policy ID TLV for each policy
+       response_size = sizeof(struct necp_packet_header) + num_policies * tlv_size;
+       MALLOC(response, u_int8_t *, response_size, M_NECP, M_WAITOK);
+       if (response == NULL) {
+               necp_send_error_response(session, NECP_PACKET_TYPE_POLICY_LIST_ALL, message_id, NECP_ERROR_INTERNAL);
+               return;
+       }
+
+       cursor = response;
+       cursor = necp_buffer_write_packet_header(cursor, NECP_PACKET_TYPE_POLICY_LIST_ALL, NECP_PACKET_FLAGS_RESPONSE, message_id);
+
+       LIST_FOREACH(policy, &session->policies, chain) {
+               if (!policy->pending_deletion && cur_policy_index < num_policies) {
+                       cursor = necp_buffer_write_tlv(cursor, NECP_TLV_POLICY_ID, sizeof(u_int32_t), &policy->id, response, response_size);
+                       cur_policy_index++;
+               }
+       }
+
+       if (!necp_send_ctl_data(session, (u_int8_t *)response, response_size)) {
+               NECPLOG0(LOG_ERR, "Failed to send response");
+       }
+
+       FREE(response, M_NECP);
+}
+
+static void
+necp_handle_policy_delete_all(struct necp_session *session, u_int32_t message_id, mbuf_t packet, int offset)
+{
+#pragma unused(packet, offset)
+       necp_policy_mark_all_for_deletion(session);
+       necp_send_success_response(session, NECP_PACKET_TYPE_POLICY_DELETE_ALL, message_id);
+}
+
+static necp_policy_id
+necp_policy_get_new_id(void)
+{
+       necp_policy_id newid = 0;
+
+       lck_rw_lock_exclusive(&necp_kernel_policy_lock);
+
+       necp_last_policy_id++;
+       if (necp_last_policy_id < 1) {
+               necp_last_policy_id = 1;
+       }
+
+       newid = necp_last_policy_id;
+       lck_rw_done(&necp_kernel_policy_lock);
+
+       if (newid == 0) {
+               NECPLOG0(LOG_DEBUG, "Allocate policy id failed.\n");
+               return (0);
+       }
+
+       return (newid);
+}
+
+/*
+ *     For the policy dump response this is the structure:
+ *
+ *     <NECP_PACKET_HEADER>
+ *     {
+ *             type    :       NECP_TLV_POLICY_DUMP
+ *             length  :       ...
+ *             value   :
+ *             {
+ *                     {
+ *                             type    :       NECP_TLV_POLICY_ID
+ *                             len             :       ...
+ *                             value   :       ...
+ *                     }
+ *                     {
+ *                             type    :       NECP_TLV_POLICY_ORDER
+ *                             len             :       ...
+ *                             value   :       ...
+ *                     }
+ *                     {
+ *                             type    :       NECP_TLV_POLICY_RESULT_STRING
+ *                             len             :       ...
+ *                             value   :       ...
+ *                     }
+ *                     {
+ *                             type    :       NECP_TLV_POLICY_OWNER
+ *                             len             :       ...
+ *                             value   :       ...
+ *                     }
+ *                     {
+ *                             type    :       NECP_TLV_POLICY_CONDITION
+ *                             len             :       ...
+ *                             value   :
+ *                             {
+ *                                     {
+ *                                             type    :       NECP_POLICY_CONDITION_ALL_INTERFACES
+ *                                             len             :       ...
+ *                                             value   :       ...
+ *                                     }
+ *                                     {
+ *                                             type    :       NECP_POLICY_CONDITION_BOUND_INTERFACES
+ *                                             len             :       ...
+ *                                             value   :       ...
+ *                                     }
+ *                                     ...
+ *                             }
+ *                     }
+ *             }
+ *     }
+ *     {
+ *             type    :       NECP_TLV_POLICY_DUMP
+ *             length  :       ...
+ *             value   :
+ *             {
+ *                     {
+ *                             type    :       NECP_TLV_POLICY_ID
+ *                             len             :       ...
+ *                             value   :       ...
+ *                     }
+ *                     {
+ *                             type    :       NECP_TLV_POLICY_ORDER
+ *                             len             :       ...
+ *                             value   :       ...
+ *                     }
+ *                     {
+ *                             type    :       NECP_TLV_POLICY_RESULT_STRING
+ *                             len             :       ...
+ *                             value   :       ...
+ *                     }
+ *                     {
+ *                             type    :       NECP_TLV_POLICY_OWNER
+ *                             len             :       ...
+ *                             value   :       ...
+ *                     }
+ *                     {
+ *                             type    :       NECP_TLV_POLICY_CONDITION
+ *                             len             :       ...
+ *                             value   :
+ *                             {
+ *                                     {
+ *                                             type    :       NECP_POLICY_CONDITION_ALL_INTERFACES
+ *                                             len             :       ...
+ *                                             value   :       ...
+ *                                     }
+ *                                     {
+ *                                             type    :       NECP_POLICY_CONDITION_BOUND_INTERFACES
+ *                                             len             :       ...
+ *                                             value   :       ...
+ *                                     }
+ *                                     ...
+ *                             }
+ *                     }
+ *             }
+ *     }
+ *     ...
+ */
+static int
+necp_handle_policy_dump_all(struct necp_session *session, u_int32_t message_id, mbuf_t packet,
+                                                       user_addr_t out_buffer, size_t out_buffer_length, int offset)
+{
+#pragma unused(offset)
+       struct necp_kernel_socket_policy *policy = NULL;
+       int policy_i;
+       int policy_count = 0;
+       u_int8_t **tlv_buffer_pointers = NULL;
+       u_int32_t *tlv_buffer_lengths = NULL;
+       u_int32_t total_tlv_len = 0;
+       u_int8_t *result_buf = NULL;
+       u_int8_t *result_buf_cursor = result_buf;
+       char result_string[MAX_RESULT_STRING_LEN];
+       char proc_name_string[MAXCOMLEN + 1];
+
+       int error_code = 0;
+       bool error_occured = false;
+       u_int32_t response_error = NECP_ERROR_INTERNAL;
+
+#define REPORT_ERROR(error) error_occured = true;              \
+                                                       response_error = error;         \
+                                                       goto done
+
+#define UNLOCK_AND_REPORT_ERROR(lock, error)   lck_rw_done(lock);      \
+                                                                                               REPORT_ERROR(error)
+
+       errno_t cred_result = priv_check_cred(kauth_cred_get(), PRIV_NET_PRIVILEGED_NECP_POLICIES, 0);
+       if (cred_result != 0) {
+               NECPLOG0(LOG_ERR, "Session does not hold the necessary entitlement to get Network Extension Policy information");
+               REPORT_ERROR(NECP_ERROR_INTERNAL);
+       }
+
+       // LOCK
+       lck_rw_lock_shared(&necp_kernel_policy_lock);
+
+       if (necp_debug) {
+               NECPLOG0(LOG_DEBUG, "Gathering policies");
+       }
+
+       policy_count = necp_kernel_application_policies_count;
+
+       MALLOC(tlv_buffer_pointers, u_int8_t **, sizeof(u_int8_t *) * policy_count, M_NECP, M_NOWAIT | M_ZERO);
+       if (tlv_buffer_pointers == NULL) {
+               NECPLOG(LOG_DEBUG, "Failed to allocate tlv_buffer_pointers (%u bytes)", sizeof(u_int8_t *) * policy_count);
+               UNLOCK_AND_REPORT_ERROR(&necp_kernel_policy_lock, NECP_ERROR_INTERNAL);
+       }
+
+       MALLOC(tlv_buffer_lengths, u_int32_t *, sizeof(u_int32_t) * policy_count, M_NECP, M_NOWAIT | M_ZERO);
+       if (tlv_buffer_lengths == NULL) {
+               NECPLOG(LOG_DEBUG, "Failed to allocate tlv_buffer_lengths (%u bytes)", sizeof(u_int32_t) * policy_count);
+               UNLOCK_AND_REPORT_ERROR(&necp_kernel_policy_lock, NECP_ERROR_INTERNAL);
+       }
+
+       for (policy_i = 0; necp_kernel_socket_policies_app_layer_map != NULL && necp_kernel_socket_policies_app_layer_map[policy_i] != NULL; policy_i++) {
+               policy = necp_kernel_socket_policies_app_layer_map[policy_i];
+
+               memset(result_string, 0, MAX_RESULT_STRING_LEN);
+               memset(proc_name_string, 0, MAXCOMLEN + 1);
+
+               necp_get_result_description(result_string, policy->result, policy->result_parameter);
+               proc_name(policy->session_pid, proc_name_string, MAXCOMLEN);
+
+               u_int16_t proc_name_len = strlen(proc_name_string) + 1;
+               u_int16_t result_string_len = strlen(result_string) + 1;
+
+               if (necp_debug) {
+                       NECPLOG(LOG_DEBUG, "Policy: process: %s, result: %s", proc_name_string, result_string);
+               }
+
+               u_int32_t total_allocated_bytes =       sizeof(u_int8_t) + sizeof(u_int32_t) + sizeof(policy->id) +                                     // NECP_TLV_POLICY_ID
+                                                                                       sizeof(u_int8_t) + sizeof(u_int32_t) + sizeof(policy->order) +                          // NECP_TLV_POLICY_ORDER
+                                                                                       sizeof(u_int8_t) + sizeof(u_int32_t) + sizeof(policy->session_order) +          // NECP_TLV_POLICY_SESSION_ORDER
+                                                                                       sizeof(u_int8_t) + sizeof(u_int32_t) + result_string_len +                                      // NECP_TLV_POLICY_RESULT_STRING
+                                                                                       sizeof(u_int8_t) + sizeof(u_int32_t) + proc_name_len +                                          // NECP_TLV_POLICY_OWNER
+                                                                                       sizeof(u_int8_t) + sizeof(u_int32_t);                                                                           // NECP_TLV_POLICY_CONDITION
+
+               // We now traverse the condition_mask to see how much space we need to allocate
+               u_int32_t condition_mask = policy->condition_mask;
+               u_int8_t num_conditions = 0;
+               struct necp_string_id_mapping *account_id_entry = NULL;
+               char if_name[IFXNAMSIZ];
+               u_int32_t condition_tlv_length = 0;
+               memset(if_name, 0, sizeof(if_name));
+
+               if (condition_mask == NECP_POLICY_CONDITION_DEFAULT) {
+                       num_conditions++;
+               } else {
+                       if (condition_mask & NECP_KERNEL_CONDITION_ALL_INTERFACES) {
+                               num_conditions++;
+                       }
+                       if (condition_mask & NECP_KERNEL_CONDITION_BOUND_INTERFACE) {
+                               snprintf(if_name, IFXNAMSIZ, "%s%d", ifnet_name(policy->cond_bound_interface), ifnet_unit(policy->cond_bound_interface));
+                               condition_tlv_length += strlen(if_name) + 1;
+                               num_conditions++;
+                       }
+                       if (condition_mask & NECP_KERNEL_CONDITION_PROTOCOL) {
+                               condition_tlv_length += sizeof(policy->cond_protocol);
+                               num_conditions++;
+                       }
+                       if (condition_mask & NECP_KERNEL_CONDITION_APP_ID) {
+                               condition_tlv_length += sizeof(uuid_t);
+                               num_conditions++;
+                       }
+                       if (condition_mask & NECP_KERNEL_CONDITION_REAL_APP_ID) {
+                               condition_tlv_length += sizeof(uuid_t);
+                               num_conditions++;
+                       }
+                       if (condition_mask & NECP_KERNEL_CONDITION_DOMAIN) {
+                               u_int32_t domain_len = strlen(policy->cond_domain) + 1;
+                               condition_tlv_length += domain_len;
+                               num_conditions++;
+                       }
+                       if (condition_mask & NECP_KERNEL_CONDITION_ACCOUNT_ID) {
+                               account_id_entry = necp_lookup_string_with_id_locked(&necp_account_id_list, policy->cond_account_id);
+                               u_int32_t account_id_len = 0;
+                               if (account_id_entry) {
+                                       account_id_len = account_id_entry->string ? strlen(account_id_entry->string) + 1 : 0;
+                               }
+                               condition_tlv_length += account_id_len;
+                               num_conditions++;
+                       }
+                       if (condition_mask & NECP_KERNEL_CONDITION_PID) {
+                               condition_tlv_length += sizeof(pid_t);
+                               num_conditions++;
+                       }
+                       if (condition_mask & NECP_KERNEL_CONDITION_UID) {
+                               condition_tlv_length += sizeof(uid_t);
+                               num_conditions++;
+                       }
+                       if (condition_mask & NECP_KERNEL_CONDITION_TRAFFIC_CLASS) {
+                               condition_tlv_length += sizeof(struct necp_policy_condition_tc_range);
+                               num_conditions++;
+                       }
+                       if (condition_mask & NECP_KERNEL_CONDITION_ENTITLEMENT) {
+                               num_conditions++;
+                       }
+                       if (condition_mask & NECP_KERNEL_CONDITION_CUSTOM_ENTITLEMENT) {
+                               u_int32_t entitlement_len = strlen(policy->cond_custom_entitlement) + 1;
+                               condition_tlv_length += entitlement_len;
+                               num_conditions++;
+                       }
+                       if (condition_mask & NECP_KERNEL_CONDITION_LOCAL_START) {
+                               if (condition_mask & NECP_KERNEL_CONDITION_LOCAL_END) {
+                                       condition_tlv_length += sizeof(struct necp_policy_condition_addr_range);
+                               } else {
+                                       condition_tlv_length += sizeof(struct necp_policy_condition_addr);
+                               }
+                               num_conditions++;
+                       }
+                       if (condition_mask & NECP_KERNEL_CONDITION_REMOTE_START) {
+                               if (condition_mask & NECP_KERNEL_CONDITION_REMOTE_END) {
+                                       condition_tlv_length += sizeof(struct necp_policy_condition_addr_range);
+                               } else {
+                                       condition_tlv_length += sizeof(struct necp_policy_condition_addr);
+                               }
+                               num_conditions++;
+                       }
+               }
+
+               condition_tlv_length += num_conditions * (sizeof(u_int8_t) + sizeof(u_int32_t)); // These are for the condition TLVs. The space for "value" is already accounted for above.
+               total_allocated_bytes += condition_tlv_length;
+
+               u_int8_t *tlv_buffer;
+               MALLOC(tlv_buffer, u_int8_t *, total_allocated_bytes, M_NECP, M_NOWAIT | M_ZERO);
+               if (tlv_buffer == NULL) {
+                       NECPLOG(LOG_DEBUG, "Failed to allocate tlv_buffer (%u bytes)", total_allocated_bytes);
+                       continue;
+               }
+
+               u_int8_t *cursor = tlv_buffer;
+               cursor = necp_buffer_write_tlv(cursor, NECP_TLV_POLICY_ID, sizeof(policy->id), &policy->id, tlv_buffer, total_allocated_bytes);
+               cursor = necp_buffer_write_tlv(cursor, NECP_TLV_POLICY_ORDER, sizeof(necp_policy_order), &policy->order, tlv_buffer, total_allocated_bytes);
+               cursor = necp_buffer_write_tlv(cursor, NECP_TLV_POLICY_SESSION_ORDER, sizeof(policy->session_order), &policy->session_order, tlv_buffer, total_allocated_bytes);
+               cursor = necp_buffer_write_tlv(cursor, NECP_TLV_POLICY_RESULT_STRING, result_string_len, result_string, tlv_buffer, total_allocated_bytes);
+               cursor = necp_buffer_write_tlv(cursor, NECP_TLV_POLICY_OWNER, proc_name_len, proc_name_string, tlv_buffer, total_allocated_bytes);
+
+#define N_QUICK 256
+               u_int8_t q_cond_buf[N_QUICK]; // Minor optimization
+
+               u_int8_t *cond_buf; // To be used for condition TLVs
+               if (condition_tlv_length <= N_QUICK) {
+                       cond_buf = q_cond_buf;
+               } else {
+                       MALLOC(cond_buf, u_int8_t *, condition_tlv_length, M_NECP, M_NOWAIT);
+                       if (cond_buf == NULL) {
+                               NECPLOG(LOG_DEBUG, "Failed to allocate cond_buffer (%u bytes)", condition_tlv_length);
+                               FREE(tlv_buffer, M_NECP);
+                               continue;
+                       }
+               }
+
+               memset(cond_buf, 0, condition_tlv_length);
+               u_int8_t *cond_buf_cursor = cond_buf;
+               if (condition_mask == NECP_POLICY_CONDITION_DEFAULT) {
+                       cond_buf_cursor = necp_buffer_write_tlv(cond_buf_cursor, NECP_POLICY_CONDITION_DEFAULT, 0, "", cond_buf, condition_tlv_length);
+               } else {
+                       if (condition_mask & NECP_KERNEL_CONDITION_ALL_INTERFACES) {
+                               cond_buf_cursor = necp_buffer_write_tlv(cond_buf_cursor, NECP_POLICY_CONDITION_ALL_INTERFACES, 0, "", cond_buf, condition_tlv_length);
+                       }
+                       if (condition_mask & NECP_KERNEL_CONDITION_BOUND_INTERFACE) {
+                               cond_buf_cursor = necp_buffer_write_tlv(cond_buf_cursor, NECP_POLICY_CONDITION_BOUND_INTERFACE, strlen(if_name) + 1,
+                                                                                                               if_name, cond_buf, condition_tlv_length);
+                       }
+                       if (condition_mask & NECP_KERNEL_CONDITION_PROTOCOL) {
+                               cond_buf_cursor = necp_buffer_write_tlv(cond_buf_cursor, NECP_POLICY_CONDITION_IP_PROTOCOL, sizeof(policy->cond_protocol), &policy->cond_protocol,
+                                                                                                               cond_buf, condition_tlv_length);
+                       }
+                       if (condition_mask & NECP_KERNEL_CONDITION_APP_ID) {
+                               struct necp_uuid_id_mapping *entry = necp_uuid_lookup_uuid_with_app_id_locked(policy->cond_app_id);
+                               if (entry != NULL) {
+                                       cond_buf_cursor = necp_buffer_write_tlv(cond_buf_cursor, NECP_POLICY_CONDITION_APPLICATION, sizeof(entry->uuid), entry->uuid,
+                                                                                                                       cond_buf, condition_tlv_length);
+                               }
+                       }
+                       if (condition_mask & NECP_KERNEL_CONDITION_REAL_APP_ID) {
+                               struct necp_uuid_id_mapping *entry = necp_uuid_lookup_uuid_with_app_id_locked(policy->cond_real_app_id);
+                               if (entry != NULL) {
+                                       cond_buf_cursor = necp_buffer_write_tlv(cond_buf_cursor, NECP_POLICY_CONDITION_REAL_APPLICATION, sizeof(entry->uuid), entry->uuid,
+                                                                                                                       cond_buf, condition_tlv_length);
+                               }
+                       }
+                       if (condition_mask & NECP_KERNEL_CONDITION_DOMAIN) {
+                               cond_buf_cursor = necp_buffer_write_tlv(cond_buf_cursor, NECP_POLICY_CONDITION_DOMAIN, strlen(policy->cond_domain) + 1, policy->cond_domain,
+                                                                                                               cond_buf, condition_tlv_length);
+                       }
+                       if (condition_mask & NECP_KERNEL_CONDITION_ACCOUNT_ID) {
+                               if (account_id_entry != NULL) {
+                                       cond_buf_cursor = necp_buffer_write_tlv(cond_buf_cursor, NECP_POLICY_CONDITION_ACCOUNT, strlen(account_id_entry->string) + 1, account_id_entry->string,
+                                                                                                                       cond_buf, condition_tlv_length);
+                               }
+                       }
+                       if (condition_mask & NECP_KERNEL_CONDITION_PID) {
+                               cond_buf_cursor = necp_buffer_write_tlv(cond_buf_cursor, NECP_POLICY_CONDITION_PID, sizeof(policy->cond_pid), &policy->cond_pid,
+                                                                                                               cond_buf, condition_tlv_length);
+                       }
+                       if (condition_mask & NECP_KERNEL_CONDITION_UID) {
+                               cond_buf_cursor = necp_buffer_write_tlv(cond_buf_cursor, NECP_POLICY_CONDITION_UID, sizeof(policy->cond_uid), &policy->cond_uid,
+                                                                                                               cond_buf, condition_tlv_length);
+                       }
+                       if (condition_mask & NECP_KERNEL_CONDITION_TRAFFIC_CLASS) {
+                               cond_buf_cursor = necp_buffer_write_tlv(cond_buf_cursor, NECP_POLICY_CONDITION_TRAFFIC_CLASS, sizeof(policy->cond_traffic_class), &policy->cond_traffic_class,
+                                                                                                               cond_buf, condition_tlv_length);
+                       }
+                       if (condition_mask & NECP_KERNEL_CONDITION_ENTITLEMENT) {
+                               cond_buf_cursor = necp_buffer_write_tlv(cond_buf_cursor, NECP_POLICY_CONDITION_ENTITLEMENT, 0, "",
+                                                                                                               cond_buf, condition_tlv_length);
+                       }
+                       if (condition_mask & NECP_KERNEL_CONDITION_CUSTOM_ENTITLEMENT) {
+                               cond_buf_cursor = necp_buffer_write_tlv(cond_buf_cursor, NECP_POLICY_CONDITION_ENTITLEMENT, strlen(policy->cond_custom_entitlement) + 1, policy->cond_custom_entitlement,
+                                                                                                               cond_buf, condition_tlv_length);
+                       }
+                       if (condition_mask & NECP_KERNEL_CONDITION_LOCAL_START) {
+                               if (condition_mask & NECP_KERNEL_CONDITION_LOCAL_END) {
+                                       struct necp_policy_condition_addr_range range;
+                                       memcpy(&range.start_address, &policy->cond_local_start, sizeof(policy->cond_local_start));
+                                       memcpy(&range.end_address, &policy->cond_local_end, sizeof(policy->cond_local_end));
+                                       cond_buf_cursor = necp_buffer_write_tlv(cond_buf_cursor, NECP_POLICY_CONDITION_LOCAL_ADDR_RANGE, sizeof(range), &range,
+                                                                                                                       cond_buf, condition_tlv_length);
+                               } else {
+                                       struct necp_policy_condition_addr addr;
+                                       addr.prefix = policy->cond_local_prefix;
+                                       memcpy(&addr.address, &policy->cond_local_start, sizeof(policy->cond_local_start));
+                                       cond_buf_cursor = necp_buffer_write_tlv(cond_buf_cursor, NECP_POLICY_CONDITION_LOCAL_ADDR, sizeof(addr), &addr,
+                                                                                                                       cond_buf, condition_tlv_length);
+                               }
+                       }
+                       if (condition_mask & NECP_KERNEL_CONDITION_REMOTE_START) {
+                               if (condition_mask & NECP_KERNEL_CONDITION_REMOTE_END) {
+                                       struct necp_policy_condition_addr_range range;
+                                       memcpy(&range.start_address, &policy->cond_remote_start, sizeof(policy->cond_remote_start));
+                                       memcpy(&range.end_address, &policy->cond_remote_end, sizeof(policy->cond_remote_end));
+                                       cond_buf_cursor = necp_buffer_write_tlv(cond_buf_cursor, NECP_POLICY_CONDITION_REMOTE_ADDR_RANGE, sizeof(range), &range,
+                                                                                                                       cond_buf, condition_tlv_length);
+                               } else {
+                                       struct necp_policy_condition_addr addr;
+                                       addr.prefix = policy->cond_remote_prefix;
+                                       memcpy(&addr.address, &policy->cond_remote_start, sizeof(policy->cond_remote_start));
+                                       cond_buf_cursor = necp_buffer_write_tlv(cond_buf_cursor, NECP_POLICY_CONDITION_REMOTE_ADDR, sizeof(addr), &addr,
+                                                                                                                       cond_buf, condition_tlv_length);
+                               }
+                       }
+               }
 
-       // Create a response with one Policy ID TLV for each policy
-       response_size = sizeof(struct necp_packet_header) + num_policies * tlv_size;
-       MALLOC(response, u_int8_t *, response_size, M_NECP, M_WAITOK);
-       if (response == NULL) {
-               necp_send_error_response(session, NECP_PACKET_TYPE_POLICY_LIST_ALL, message_id, NECP_ERROR_INTERNAL);
-               return;
+               cursor = necp_buffer_write_tlv(cursor, NECP_TLV_POLICY_CONDITION, cond_buf_cursor - cond_buf, cond_buf, tlv_buffer, total_allocated_bytes);
+               if (cond_buf != q_cond_buf) {
+                       FREE(cond_buf, M_NECP);
+               }
+
+               tlv_buffer_pointers[policy_i] = tlv_buffer;
+               tlv_buffer_lengths[policy_i] = (cursor - tlv_buffer);
+
+               // This is the length of the TLV for NECP_TLV_POLICY_DUMP
+               total_tlv_len += sizeof(u_int8_t) + sizeof(u_int32_t) + (cursor - tlv_buffer);
        }
 
-       cursor = response;
-       cursor = necp_buffer_write_packet_header(cursor, NECP_PACKET_TYPE_POLICY_LIST_ALL, NECP_PACKET_FLAGS_RESPONSE, message_id);
+       // UNLOCK
+       lck_rw_done(&necp_kernel_policy_lock);
 
-       LIST_FOREACH(policy, &session->policies, chain) {
-               if (!policy->pending_deletion && cur_policy_index < num_policies) {
-                       cursor = necp_buffer_write_tlv(cursor, NECP_TLV_POLICY_ID, sizeof(u_int32_t), &policy->id);
-                       cur_policy_index++;
+       // Send packet
+       if (packet != NULL) {
+               u_int32_t total_result_length = sizeof(struct necp_packet_header) + total_tlv_len;
+
+               // Allow malloc to wait, since the total buffer may be large and we are not holding any locks
+               MALLOC(result_buf, u_int8_t *, total_result_length, M_NECP, M_WAITOK | M_ZERO);
+               if (result_buf == NULL) {
+                       NECPLOG(LOG_DEBUG, "Failed to allocate result_buffer (%u bytes)", total_result_length);
+                       REPORT_ERROR(NECP_ERROR_INTERNAL);
                }
-       }
 
-       if (!necp_send_ctl_data(session, (u_int8_t *)response, response_size)) {
-               NECPLOG0(LOG_ERR, "Failed to send response");
+               result_buf_cursor = result_buf;
+               result_buf_cursor = necp_buffer_write_packet_header(result_buf_cursor, NECP_PACKET_TYPE_POLICY_DUMP_ALL, NECP_PACKET_FLAGS_RESPONSE, message_id);
+
+               for (int i = 0; i < policy_count; i++) {
+                       if (tlv_buffer_pointers[i] != NULL) {
+                               result_buf_cursor = necp_buffer_write_tlv(result_buf_cursor, NECP_TLV_POLICY_DUMP, tlv_buffer_lengths[i], tlv_buffer_pointers[i], result_buf, total_result_length);
+                       }
+               }
+
+               if (!necp_send_ctl_data(session, result_buf, result_buf_cursor - result_buf)) {
+                       NECPLOG(LOG_ERR, "Failed to send response (%u bytes)", result_buf_cursor - result_buf);
+               } else {
+                       NECPLOG(LOG_ERR, "Sent data worth %u bytes. Total result buffer length was %u bytes", result_buf_cursor - result_buf, total_result_length);
+               }
        }
 
-       FREE(response, M_NECP);
-}
+       // Copy out
+       if (out_buffer != 0) {
+               if (out_buffer_length < total_tlv_len + sizeof(u_int32_t)) {
+                       NECPLOG(LOG_DEBUG, "out_buffer_length too small (%u < %u)", out_buffer_length, total_tlv_len + sizeof(u_int32_t));
+                       REPORT_ERROR(NECP_ERROR_INVALID_TLV);
+               }
 
-static void
-necp_handle_policy_delete_all(struct necp_session *session, u_int32_t message_id, mbuf_t packet, int offset)
-{
-#pragma unused(packet, offset)
-       necp_policy_mark_all_for_deletion(session);
-       necp_send_success_response(session, NECP_PACKET_TYPE_POLICY_DELETE_ALL, message_id);
-}
+               // Allow malloc to wait, since the total buffer may be large and we are not holding any locks
+               MALLOC(result_buf, u_int8_t *, total_tlv_len + sizeof(u_int32_t), M_NECP, M_WAITOK | M_ZERO);
+               if (result_buf == NULL) {
+                       NECPLOG(LOG_DEBUG, "Failed to allocate result_buffer (%u bytes)", total_tlv_len + sizeof(u_int32_t));
+                       REPORT_ERROR(NECP_ERROR_INTERNAL);
+               }
 
-static necp_policy_id
-necp_policy_get_new_id(void)
-{
-       necp_policy_id newid = 0;
+               // Add four bytes for total length at the start
+               memcpy(result_buf, &total_tlv_len, sizeof(u_int32_t));
 
-       lck_rw_lock_exclusive(&necp_kernel_policy_lock);
+               // Copy the TLVs
+               result_buf_cursor = result_buf + sizeof(u_int32_t);
+               for (int i = 0; i < policy_count; i++) {
+                       if (tlv_buffer_pointers[i] != NULL) {
+                               result_buf_cursor = necp_buffer_write_tlv(result_buf_cursor, NECP_TLV_POLICY_DUMP, tlv_buffer_lengths[i], tlv_buffer_pointers[i],
+                                                                                                                 result_buf, total_tlv_len + sizeof(u_int32_t));
+                       }
+               }
 
-       necp_last_policy_id++;
-       if (necp_last_policy_id < 1) {
-               necp_last_policy_id = 1;
+               int copy_error = copyout(result_buf, out_buffer, total_tlv_len + sizeof(u_int32_t));
+               if (copy_error) {
+                       NECPLOG(LOG_DEBUG, "Failed to copy out result_buffer (%u bytes)", total_tlv_len + sizeof(u_int32_t));
+                       REPORT_ERROR(NECP_ERROR_INTERNAL);
+               }
        }
 
-       newid = necp_last_policy_id;
-       lck_rw_done(&necp_kernel_policy_lock);
+done:
 
-       if (newid == 0) {
-               NECPLOG0(LOG_DEBUG, "Allocate policy id failed.\n");
-               return (0);
+       if (error_occured) {
+               if (packet != NULL) {
+                       if(!necp_send_error_response(session, NECP_PACKET_TYPE_POLICY_DUMP_ALL, message_id, response_error)) {
+                               NECPLOG0(LOG_ERR, "Failed to send error response");
+                       } else {
+                               NECPLOG0(LOG_ERR, "Sent error response");
+                       }
+               }
+               error_code = necp_get_posix_error_for_necp_error(response_error);
        }
 
-       return (newid);
+       if (result_buf != NULL) {
+               FREE(result_buf, M_NECP);
+       }
+
+       if (tlv_buffer_pointers != NULL) {
+               for (int i = 0; i < policy_count; i++) {
+                       if (tlv_buffer_pointers[i] != NULL) {
+                               FREE(tlv_buffer_pointers[i], M_NECP);
+                               tlv_buffer_pointers[i] = NULL;
+                       }
+               }
+               FREE(tlv_buffer_pointers, M_NECP);
+       }
+
+       if (tlv_buffer_lengths != NULL) {
+               FREE(tlv_buffer_lengths, M_NECP);
+       }
+#undef N_QUICK
+#undef RESET_COND_BUF
+#undef REPORT_ERROR
+#undef UNLOCK_AND_REPORT_ERROR
+
+       return (error_code);
 }
 
 static struct necp_session_policy *
@@ -1961,7 +3361,7 @@ necp_policy_create(struct necp_session *session, necp_policy_order order, u_int8
                goto done;
        }
 
-       memset(new_policy, 0, sizeof(*new_policy));
+       memset(new_policy, 0, sizeof(*new_policy)); // M_ZERO is not supported for MALLOC_ZONE
        new_policy->applied = FALSE;
        new_policy->pending_deletion = FALSE;
        new_policy->pending_update = FALSE;
@@ -2079,6 +3479,11 @@ necp_policy_delete(struct necp_session *session, struct necp_session_policy *pol
                policy->conditions = NULL;
        }
 
+       if (policy->route_rules) {
+               FREE(policy->route_rules, M_NECP);
+               policy->route_rules = NULL;
+       }
+
        FREE_ZONE(policy, sizeof(*policy), M_NECP_SESSION_POLICY);
 
        if (necp_debug) {
@@ -2095,7 +3500,7 @@ necp_policy_unapply(struct necp_session_policy *policy)
                return (FALSE);
        }
 
-       lck_rw_assert(&necp_kernel_policy_lock, LCK_RW_ASSERT_EXCLUSIVE);
+       LCK_RW_ASSERT(&necp_kernel_policy_lock, LCK_RW_ASSERT_EXCLUSIVE);
 
        // Release local uuid mappings
        if (!uuid_is_null(policy->applied_app_uuid)) {
@@ -2171,6 +3576,7 @@ necp_policy_apply(struct necp_session *session, struct necp_session_policy *poli
 
        bool socket_layer_non_id_conditions = FALSE;
        bool ip_output_layer_non_id_conditions = FALSE;
+       bool ip_output_layer_non_id_only = FALSE;
        bool ip_output_layer_id_condition = FALSE;
        bool ip_output_layer_tunnel_condition_from_id = FALSE;
        bool ip_output_layer_tunnel_condition_from_non_id = FALSE;
@@ -2181,6 +3587,7 @@ necp_policy_apply(struct necp_session *session, struct necp_session_policy *poli
        ifnet_t cond_bound_interface = NULL;
        u_int32_t cond_account_id = 0;
        char *cond_domain = NULL;
+       char *cond_custom_entitlement = NULL;
        pid_t cond_pid = 0;
        uid_t cond_uid = 0;
        necp_app_id cond_app_id = 0;
@@ -2208,7 +3615,7 @@ necp_policy_apply(struct necp_session *session, struct necp_session_policy *poli
                return (FALSE);
        }
 
-       lck_rw_assert(&necp_kernel_policy_lock, LCK_RW_ASSERT_EXCLUSIVE);
+       LCK_RW_ASSERT(&necp_kernel_policy_lock, LCK_RW_ASSERT_EXCLUSIVE);
 
        // Process conditions
        while (offset < policy->conditions_size) {
@@ -2231,8 +3638,18 @@ necp_policy_apply(struct necp_session *session, struct necp_session_policy *poli
                                break;
                        }
                        case NECP_POLICY_CONDITION_ENTITLEMENT: {
-                               master_condition_mask |= NECP_KERNEL_CONDITION_ENTITLEMENT;
-                               socket_only_conditions = TRUE;
+                               if (condition_length > 0) {
+                                       if (cond_custom_entitlement == NULL) {
+                                               cond_custom_entitlement = necp_copy_string((char *)condition_value, condition_length);
+                                               if (cond_custom_entitlement != NULL) {
+                                                       master_condition_mask |= NECP_KERNEL_CONDITION_CUSTOM_ENTITLEMENT;
+                                                       socket_only_conditions = TRUE;
+                                               }
+                                       }
+                               } else {
+                                       master_condition_mask |= NECP_KERNEL_CONDITION_ENTITLEMENT;
+                                       socket_only_conditions = TRUE;
+                               }
                                break;
                        }
                        case NECP_POLICY_CONDITION_DOMAIN: {
@@ -2372,6 +3789,10 @@ necp_policy_apply(struct necp_session *session, struct necp_session_policy *poli
                        }
                        case NECP_POLICY_CONDITION_LOCAL_ADDR: {
                                struct necp_policy_condition_addr *address_struct = (struct necp_policy_condition_addr *)(void *)condition_value;
+                               if (!necp_address_is_valid(&address_struct->address.sa)) {
+                                       break;
+                               }
+
                                cond_local_prefix = address_struct->prefix;
                                memcpy(&cond_local_start, &address_struct->address, sizeof(address_struct->address));
                                master_condition_mask |= NECP_KERNEL_CONDITION_LOCAL_START;
@@ -2385,6 +3806,10 @@ necp_policy_apply(struct necp_session *session, struct necp_session_policy *poli
                        }
                        case NECP_POLICY_CONDITION_REMOTE_ADDR: {
                                struct necp_policy_condition_addr *address_struct = (struct necp_policy_condition_addr *)(void *)condition_value;
+                               if (!necp_address_is_valid(&address_struct->address.sa)) {
+                                       break;
+                               }
+
                                cond_remote_prefix = address_struct->prefix;
                                memcpy(&cond_remote_start, &address_struct->address, sizeof(address_struct->address));
                                master_condition_mask |= NECP_KERNEL_CONDITION_REMOTE_START;
@@ -2398,6 +3823,11 @@ necp_policy_apply(struct necp_session *session, struct necp_session_policy *poli
                        }
                        case NECP_POLICY_CONDITION_LOCAL_ADDR_RANGE: {
                                struct necp_policy_condition_addr_range *address_struct = (struct necp_policy_condition_addr_range *)(void *)condition_value;
+                               if (!necp_address_is_valid(&address_struct->start_address.sa) ||
+                                       !necp_address_is_valid(&address_struct->end_address.sa)) {
+                                       break;
+                               }
+
                                memcpy(&cond_local_start, &address_struct->start_address, sizeof(address_struct->start_address));
                                memcpy(&cond_local_end, &address_struct->end_address, sizeof(address_struct->end_address));
                                master_condition_mask |= NECP_KERNEL_CONDITION_LOCAL_START;
@@ -2411,6 +3841,11 @@ necp_policy_apply(struct necp_session *session, struct necp_session_policy *poli
                        }
                        case NECP_POLICY_CONDITION_REMOTE_ADDR_RANGE: {
                                struct necp_policy_condition_addr_range *address_struct = (struct necp_policy_condition_addr_range *)(void *)condition_value;
+                               if (!necp_address_is_valid(&address_struct->start_address.sa) ||
+                                       !necp_address_is_valid(&address_struct->end_address.sa)) {
+                                       break;
+                               }
+
                                memcpy(&cond_remote_start, &address_struct->start_address, sizeof(address_struct->start_address));
                                memcpy(&cond_remote_end, &address_struct->end_address, sizeof(address_struct->end_address));
                                master_condition_mask |= NECP_KERNEL_CONDITION_REMOTE_START;
@@ -2450,6 +3885,7 @@ necp_policy_apply(struct necp_session *session, struct necp_session_policy *poli
                        } else if (socket_ip_conditions) {
                                socket_layer_non_id_conditions = TRUE;
                                ip_output_layer_non_id_conditions = TRUE;
+                               ip_output_layer_non_id_only = TRUE; // Only apply drop to packets that didn't go through socket layer
                        }
                        break;
                }
@@ -2487,6 +3923,7 @@ necp_policy_apply(struct necp_session *session, struct necp_session_policy *poli
                                tunnel_parameters.interface_name[tunnel_parameters_length - sizeof(u_int32_t) - 1] = 0; // Make sure the string is NULL terminated
                                if (ifnet_find_by_name(tunnel_parameters.interface_name, &tunnel_interface) == 0) {
                                        ultimate_result_parameter.tunnel_interface_index = tunnel_interface->if_index;
+                                       ifnet_release(tunnel_interface);
                                }
 
                                secondary_result = tunnel_parameters.secondary_result;
@@ -2557,8 +3994,10 @@ necp_policy_apply(struct necp_session *session, struct necp_session_policy *poli
                                if (ifnet_find_by_name(interface_name, &scope_interface) == 0) {
                                        ultimate_result_parameter.scoped_interface_index = scope_interface->if_index;
                                        socket_layer_non_id_conditions = TRUE;
+                                       ifnet_release(scope_interface);
                                }
                        }
+                       break;
                }
                case NECP_POLICY_RESULT_ROUTE_RULES: {
                        if (policy->route_rules != NULL && policy->route_rules_size > 0) {
@@ -2569,6 +4008,7 @@ necp_policy_apply(struct necp_session *session, struct necp_session_policy *poli
                                        socket_layer_non_id_conditions = TRUE;
                                }
                        }
+                       break;
                }
                default: {
                        break;
@@ -2576,7 +4016,7 @@ necp_policy_apply(struct necp_session *session, struct necp_session_policy *poli
        }
 
        if (socket_layer_non_id_conditions) {
-               necp_kernel_policy_id policy_id = necp_kernel_socket_policy_add(policy->id, policy->order, session->session_order, session->proc_pid, master_condition_mask, master_condition_negated_mask, cond_app_id, cond_real_app_id, cond_account_id, cond_domain, cond_pid, cond_uid, cond_bound_interface, cond_traffic_class, cond_protocol, &cond_local_start, &cond_local_end, cond_local_prefix, &cond_remote_start, &cond_remote_end, cond_remote_prefix, ultimate_result, ultimate_result_parameter);
+               necp_kernel_policy_id policy_id = necp_kernel_socket_policy_add(policy->id, policy->order, session->session_order, session->proc_pid, master_condition_mask, master_condition_negated_mask, cond_app_id, cond_real_app_id, cond_custom_entitlement, cond_account_id, cond_domain, cond_pid, cond_uid, cond_bound_interface, cond_traffic_class, cond_protocol, &cond_local_start, &cond_local_end, cond_local_prefix, &cond_remote_start, &cond_remote_end, cond_remote_prefix, ultimate_result, ultimate_result_parameter);
 
                if (policy_id == 0) {
                        NECPLOG0(LOG_DEBUG, "Error applying socket kernel policy");
@@ -2588,7 +4028,11 @@ necp_policy_apply(struct necp_session *session, struct necp_session_policy *poli
        }
 
        if (ip_output_layer_non_id_conditions) {
-               necp_kernel_policy_id policy_id = necp_kernel_ip_output_policy_add(policy->id, policy->order, NECP_KERNEL_POLICY_SUBORDER_NON_ID_CONDITIONS, session->session_order, session->proc_pid, master_condition_mask, master_condition_negated_mask, NECP_KERNEL_POLICY_ID_NONE, cond_bound_interface, 0, cond_protocol, &cond_local_start, &cond_local_end, cond_local_prefix, &cond_remote_start, &cond_remote_end, cond_remote_prefix, ultimate_result, ultimate_result_parameter);
+               u_int32_t condition_mask = master_condition_mask;
+               if (ip_output_layer_non_id_only) {
+                       condition_mask |= NECP_KERNEL_CONDITION_POLICY_ID;
+               }
+               necp_kernel_policy_id policy_id = necp_kernel_ip_output_policy_add(policy->id, policy->order, NECP_KERNEL_POLICY_SUBORDER_NON_ID_CONDITIONS, session->session_order, session->proc_pid, condition_mask, master_condition_negated_mask, NECP_KERNEL_POLICY_ID_NONE, cond_bound_interface, 0, cond_protocol, &cond_local_start, &cond_local_end, cond_local_prefix, &cond_remote_start, &cond_remote_end, cond_remote_prefix, ultimate_result, ultimate_result_parameter);
 
                if (policy_id == 0) {
                        NECPLOG0(LOG_DEBUG, "Error applying IP output kernel policy");
@@ -2678,6 +4122,7 @@ necp_policy_apply_all(struct necp_session *session)
 
        lck_rw_done(&necp_kernel_policy_lock);
 
+       necp_update_all_clients();
        necp_post_change_event(&kev_data);
 
        if (necp_debug) {
@@ -2689,18 +4134,30 @@ necp_policy_apply_all(struct necp_session *session)
 // ---------------------
 // Kernel policies are derived from session policies
 static necp_kernel_policy_id
-necp_kernel_policy_get_new_id(void)
+necp_kernel_policy_get_new_id(bool socket_level)
 {
+       static necp_kernel_policy_id necp_last_kernel_socket_policy_id = 0;
+       static necp_kernel_policy_id necp_last_kernel_ip_policy_id = 0;
+
        necp_kernel_policy_id newid = NECP_KERNEL_POLICY_ID_NONE;
 
-       lck_rw_assert(&necp_kernel_policy_lock, LCK_RW_ASSERT_EXCLUSIVE);
+       LCK_RW_ASSERT(&necp_kernel_policy_lock, LCK_RW_ASSERT_EXCLUSIVE);
 
-       necp_last_kernel_policy_id++;
-       if (necp_last_kernel_policy_id < NECP_KERNEL_POLICY_ID_FIRST_VALID) {
-               necp_last_kernel_policy_id = NECP_KERNEL_POLICY_ID_FIRST_VALID;
+       if (socket_level) {
+               necp_last_kernel_socket_policy_id++;
+               if (necp_last_kernel_socket_policy_id < NECP_KERNEL_POLICY_ID_FIRST_VALID_SOCKET ||
+                       necp_last_kernel_socket_policy_id >= NECP_KERNEL_POLICY_ID_FIRST_VALID_IP) {
+                       necp_last_kernel_socket_policy_id = NECP_KERNEL_POLICY_ID_FIRST_VALID_SOCKET;
+               }
+               newid = necp_last_kernel_socket_policy_id;
+       } else {
+               necp_last_kernel_ip_policy_id++;
+               if (necp_last_kernel_ip_policy_id < NECP_KERNEL_POLICY_ID_FIRST_VALID_IP) {
+                       necp_last_kernel_ip_policy_id = NECP_KERNEL_POLICY_ID_FIRST_VALID_IP;
+               }
+               newid = necp_last_kernel_ip_policy_id;
        }
 
-       newid = necp_last_kernel_policy_id;
        if (newid == NECP_KERNEL_POLICY_ID_NONE) {
                NECPLOG0(LOG_DEBUG, "Allocate kernel policy id failed.\n");
                return (0);
@@ -2709,9 +4166,9 @@ necp_kernel_policy_get_new_id(void)
        return (newid);
 }
 
-#define        NECP_KERNEL_VALID_SOCKET_CONDITIONS (NECP_KERNEL_CONDITION_APP_ID | NECP_KERNEL_CONDITION_REAL_APP_ID | NECP_KERNEL_CONDITION_DOMAIN | NECP_KERNEL_CONDITION_ACCOUNT_ID | NECP_KERNEL_CONDITION_PID | NECP_KERNEL_CONDITION_UID | NECP_KERNEL_CONDITION_ALL_INTERFACES | NECP_KERNEL_CONDITION_BOUND_INTERFACE | NECP_KERNEL_CONDITION_TRAFFIC_CLASS | NECP_KERNEL_CONDITION_PROTOCOL | NECP_KERNEL_CONDITION_LOCAL_START | NECP_KERNEL_CONDITION_LOCAL_END | NECP_KERNEL_CONDITION_LOCAL_PREFIX | NECP_KERNEL_CONDITION_REMOTE_START | NECP_KERNEL_CONDITION_REMOTE_END | NECP_KERNEL_CONDITION_REMOTE_PREFIX | NECP_KERNEL_CONDITION_ENTITLEMENT)
+#define        NECP_KERNEL_VALID_SOCKET_CONDITIONS (NECP_KERNEL_CONDITION_APP_ID | NECP_KERNEL_CONDITION_REAL_APP_ID | NECP_KERNEL_CONDITION_DOMAIN | NECP_KERNEL_CONDITION_ACCOUNT_ID | NECP_KERNEL_CONDITION_PID | NECP_KERNEL_CONDITION_UID | NECP_KERNEL_CONDITION_ALL_INTERFACES | NECP_KERNEL_CONDITION_BOUND_INTERFACE | NECP_KERNEL_CONDITION_TRAFFIC_CLASS | NECP_KERNEL_CONDITION_PROTOCOL | NECP_KERNEL_CONDITION_LOCAL_START | NECP_KERNEL_CONDITION_LOCAL_END | NECP_KERNEL_CONDITION_LOCAL_PREFIX | NECP_KERNEL_CONDITION_REMOTE_START | NECP_KERNEL_CONDITION_REMOTE_END | NECP_KERNEL_CONDITION_REMOTE_PREFIX | NECP_KERNEL_CONDITION_ENTITLEMENT | NECP_KERNEL_CONDITION_CUSTOM_ENTITLEMENT)
 static necp_kernel_policy_id
-necp_kernel_socket_policy_add(necp_policy_id parent_policy_id, necp_policy_order order, u_int32_t session_order, int session_pid, u_int32_t condition_mask, u_int32_t condition_negated_mask, necp_app_id cond_app_id, necp_app_id cond_real_app_id, u_int32_t cond_account_id, char *cond_domain, pid_t cond_pid, uid_t cond_uid, ifnet_t cond_bound_interface, struct necp_policy_condition_tc_range cond_traffic_class, u_int16_t cond_protocol, union necp_sockaddr_union *cond_local_start, union necp_sockaddr_union *cond_local_end, u_int8_t cond_local_prefix, union necp_sockaddr_union *cond_remote_start, union necp_sockaddr_union *cond_remote_end, u_int8_t cond_remote_prefix, necp_kernel_policy_result result, necp_kernel_policy_result_parameter result_parameter)
+necp_kernel_socket_policy_add(necp_policy_id parent_policy_id, necp_policy_order order, u_int32_t session_order, int session_pid, u_int32_t condition_mask, u_int32_t condition_negated_mask, necp_app_id cond_app_id, necp_app_id cond_real_app_id, char *cond_custom_entitlement, u_int32_t cond_account_id, char *cond_domain, pid_t cond_pid, uid_t cond_uid, ifnet_t cond_bound_interface, struct necp_policy_condition_tc_range cond_traffic_class, u_int16_t cond_protocol, union necp_sockaddr_union *cond_local_start, union necp_sockaddr_union *cond_local_end, u_int8_t cond_local_prefix, union necp_sockaddr_union *cond_remote_start, union necp_sockaddr_union *cond_remote_end, u_int8_t cond_remote_prefix, necp_kernel_policy_result result, necp_kernel_policy_result_parameter result_parameter)
 {
        struct necp_kernel_socket_policy *new_kernel_policy = NULL;
        struct necp_kernel_socket_policy *tmp_kernel_policy = NULL;
@@ -2721,9 +4178,9 @@ necp_kernel_socket_policy_add(necp_policy_id parent_policy_id, necp_policy_order
                goto done;
        }
 
-       memset(new_kernel_policy, 0, sizeof(*new_kernel_policy));
+       memset(new_kernel_policy, 0, sizeof(*new_kernel_policy)); // M_ZERO is not supported for MALLOC_ZONE
        new_kernel_policy->parent_policy_id = parent_policy_id;
-       new_kernel_policy->id = necp_kernel_policy_get_new_id();
+       new_kernel_policy->id = necp_kernel_policy_get_new_id(true);
        new_kernel_policy->order = order;
        new_kernel_policy->session_order = session_order;
        new_kernel_policy->session_pid = session_pid;
@@ -2754,6 +4211,10 @@ necp_kernel_socket_policy_add(necp_policy_id parent_policy_id, necp_policy_order
        if (new_kernel_policy->condition_mask & NECP_KERNEL_CONDITION_REAL_APP_ID) {
                new_kernel_policy->cond_real_app_id = cond_real_app_id;
        }
+       if (new_kernel_policy->condition_mask & NECP_KERNEL_CONDITION_CUSTOM_ENTITLEMENT) {
+               new_kernel_policy->cond_custom_entitlement = cond_custom_entitlement;
+               new_kernel_policy->cond_custom_entitlement_matched = necp_boolean_state_unknown;
+       }
        if (new_kernel_policy->condition_mask & NECP_KERNEL_CONDITION_ACCOUNT_ID) {
                new_kernel_policy->cond_account_id = cond_account_id;
        }
@@ -2833,7 +4294,7 @@ necp_kernel_socket_policy_delete(necp_kernel_policy_id policy_id)
 {
        struct necp_kernel_socket_policy *policy = NULL;
 
-       lck_rw_assert(&necp_kernel_policy_lock, LCK_RW_ASSERT_EXCLUSIVE);
+       LCK_RW_ASSERT(&necp_kernel_policy_lock, LCK_RW_ASSERT_EXCLUSIVE);
 
        policy = necp_kernel_socket_policy_find(policy_id);
        if (policy) {
@@ -2843,12 +4304,17 @@ necp_kernel_socket_policy_delete(necp_kernel_policy_id policy_id)
                        ifnet_release(policy->cond_bound_interface);
                        policy->cond_bound_interface = NULL;
                }
-               
+
                if (policy->cond_domain) {
                        FREE(policy->cond_domain, M_NECP);
                        policy->cond_domain = NULL;
                }
 
+               if (policy->cond_custom_entitlement) {
+                       FREE(policy->cond_custom_entitlement, M_NECP);
+                       policy->cond_custom_entitlement = NULL;
+               }
+
                FREE_ZONE(policy, sizeof(*policy), M_NECP_SOCKET_POLICY);
                return (TRUE);
        }
@@ -2856,23 +4322,26 @@ necp_kernel_socket_policy_delete(necp_kernel_policy_id policy_id)
        return (FALSE);
 }
 
-#define MAX_RESULT_STRING_LEN 64
 static inline const char *
 necp_get_result_description(char *result_string, necp_kernel_policy_result result, necp_kernel_policy_result_parameter result_parameter)
 {
        uuid_string_t uuid_string;
        switch (result) {
                case NECP_KERNEL_POLICY_RESULT_NONE: {
-                       return ("None");
+                       snprintf(result_string, MAX_RESULT_STRING_LEN, "None");
+                       break;
                }
                case NECP_KERNEL_POLICY_RESULT_PASS: {
-                       return ("Pass");
+                       snprintf(result_string, MAX_RESULT_STRING_LEN, "Pass");
+                       break;
                }
                case NECP_KERNEL_POLICY_RESULT_SKIP: {
-                       return ("Skip");
+                       snprintf(result_string, MAX_RESULT_STRING_LEN, "Skip (%u)", result_parameter.skip_policy_order);
+                       break;
                }
                case NECP_KERNEL_POLICY_RESULT_DROP: {
-                       return ("Drop");
+                       snprintf(result_string, MAX_RESULT_STRING_LEN, "Drop");
+                       break;
                }
                case NECP_KERNEL_POLICY_RESULT_SOCKET_DIVERT: {
                        snprintf(result_string, MAX_RESULT_STRING_LEN, "SocketDivert (%d)", result_parameter.flow_divert_control_unit);
@@ -2888,7 +4357,8 @@ necp_get_result_description(char *result_string, necp_kernel_policy_result resul
                        break;
                }
                case NECP_KERNEL_POLICY_RESULT_IP_FILTER: {
-                       return ("IPFilter");
+                       snprintf(result_string, MAX_RESULT_STRING_LEN, "IPFilter");
+                       break;
                }
                case NECP_KERNEL_POLICY_RESULT_SOCKET_SCOPED: {
                        ifnet_t interface = ifindex2ifnet[result_parameter.scoped_interface_index];
@@ -2900,7 +4370,6 @@ necp_get_result_description(char *result_string, necp_kernel_policy_result resul
                        char interface_names[IFXNAMSIZ][MAX_ROUTE_RULE_INTERFACES];
                        struct necp_route_rule *route_rule = necp_lookup_route_rule_locked(&necp_route_rules, result_parameter.route_rule_id);
                        if (route_rule != NULL) {
-                               bool default_drop = (route_rule->default_action == NECP_ROUTE_RULE_DENY_INTERFACE);
                                for (index = 0; index < MAX_ROUTE_RULE_INTERFACES; index++) {
                                        if (route_rule->exception_if_indices[index] != 0) {
                                                ifnet_t interface = ifindex2ifnet[route_rule->exception_if_indices[index]];
@@ -2909,7 +4378,8 @@ necp_get_result_description(char *result_string, necp_kernel_policy_result resul
                                                memset(interface_names[index], 0, IFXNAMSIZ);
                                        }
                                }
-                               if (default_drop) {
+                               switch (route_rule->default_action) {
+                               case NECP_ROUTE_RULE_DENY_INTERFACE:
                                        snprintf(result_string, MAX_RESULT_STRING_LEN, "RouteRules (Only %s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s)",
                                                         (route_rule->cellular_action == NECP_ROUTE_RULE_ALLOW_INTERFACE) ? "Cell " : "",
                                                         (route_rule->wifi_action == NECP_ROUTE_RULE_ALLOW_INTERFACE) ? "WiFi " : "",
@@ -2934,7 +4404,8 @@ necp_get_result_description(char *result_string, necp_kernel_policy_result resul
                                                         (route_rule->exception_if_actions[8] == NECP_ROUTE_RULE_ALLOW_INTERFACE) ? interface_names[8] : "",
                                                         (route_rule->exception_if_actions[8] == NECP_ROUTE_RULE_ALLOW_INTERFACE) ? " " : "",
                                                         (route_rule->exception_if_actions[9] == NECP_ROUTE_RULE_ALLOW_INTERFACE) ? interface_names[9] : "");
-                               } else {
+                                       break;
+                               case NECP_ROUTE_RULE_ALLOW_INTERFACE:
                                        snprintf(result_string, MAX_RESULT_STRING_LEN, "RouteRules (%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s)",
                                                         (route_rule->cellular_action == NECP_ROUTE_RULE_DENY_INTERFACE) ? "!Cell " : "",
                                                         (route_rule->wifi_action == NECP_ROUTE_RULE_DENY_INTERFACE) ? "!WiFi " : "",
@@ -2960,9 +4431,37 @@ necp_get_result_description(char *result_string, necp_kernel_policy_result resul
                                                         (route_rule->exception_if_actions[8] == NECP_ROUTE_RULE_DENY_INTERFACE) ? interface_names[8] : "",
                                                         (route_rule->exception_if_actions[9] == NECP_ROUTE_RULE_DENY_INTERFACE) ? "!" : "",
                                                         (route_rule->exception_if_actions[9] == NECP_ROUTE_RULE_DENY_INTERFACE) ? interface_names[9] : "");
+                                       break;
+                               case NECP_ROUTE_RULE_QOS_MARKING:
+                                       snprintf(result_string, MAX_RESULT_STRING_LEN, "RouteRules (QoSMarking %s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s)",
+                                                        (route_rule->cellular_action == NECP_ROUTE_RULE_QOS_MARKING) ? "Cell " : "",
+                                                        (route_rule->wifi_action == NECP_ROUTE_RULE_QOS_MARKING) ? "WiFi " : "",
+                                                        (route_rule->wired_action == NECP_ROUTE_RULE_QOS_MARKING) ? "Wired " : "",
+                                                        (route_rule->expensive_action == NECP_ROUTE_RULE_QOS_MARKING) ? "Exp " : "",
+                                                        (route_rule->exception_if_actions[0] == NECP_ROUTE_RULE_QOS_MARKING) ? interface_names[0] : "",
+                                                        (route_rule->exception_if_actions[0] == NECP_ROUTE_RULE_QOS_MARKING) ? " " : "",
+                                                        (route_rule->exception_if_actions[1] == NECP_ROUTE_RULE_QOS_MARKING) ? interface_names[1] : "",
+                                                        (route_rule->exception_if_actions[1] == NECP_ROUTE_RULE_QOS_MARKING) ? " " : "",
+                                                        (route_rule->exception_if_actions[2] == NECP_ROUTE_RULE_QOS_MARKING) ? interface_names[2] : "",
+                                                        (route_rule->exception_if_actions[2] == NECP_ROUTE_RULE_QOS_MARKING) ? " " : "",
+                                                        (route_rule->exception_if_actions[3] == NECP_ROUTE_RULE_QOS_MARKING) ? interface_names[3] : "",
+                                                        (route_rule->exception_if_actions[3] == NECP_ROUTE_RULE_QOS_MARKING) ? " " : "",
+                                                        (route_rule->exception_if_actions[4] == NECP_ROUTE_RULE_QOS_MARKING) ? interface_names[4] : "",
+                                                        (route_rule->exception_if_actions[4] == NECP_ROUTE_RULE_QOS_MARKING) ? " " : "",
+                                                        (route_rule->exception_if_actions[5] == NECP_ROUTE_RULE_QOS_MARKING) ? interface_names[5] : "",
+                                                        (route_rule->exception_if_actions[5] == NECP_ROUTE_RULE_QOS_MARKING) ? " " : "",
+                                                        (route_rule->exception_if_actions[6] == NECP_ROUTE_RULE_QOS_MARKING) ? interface_names[6] : "",
+                                                        (route_rule->exception_if_actions[6] == NECP_ROUTE_RULE_QOS_MARKING) ? " " : "",
+                                                        (route_rule->exception_if_actions[7] == NECP_ROUTE_RULE_QOS_MARKING) ? interface_names[7] : "",
+                                                        (route_rule->exception_if_actions[7] == NECP_ROUTE_RULE_QOS_MARKING) ? " " : "",
+                                                        (route_rule->exception_if_actions[8] == NECP_ROUTE_RULE_QOS_MARKING) ? interface_names[8] : "",
+                                                        (route_rule->exception_if_actions[8] == NECP_ROUTE_RULE_QOS_MARKING) ? " " : "",
+                                                        (route_rule->exception_if_actions[9] == NECP_ROUTE_RULE_QOS_MARKING) ? interface_names[9] : "");
+                                       break;
+                               default:
+                                       snprintf(result_string, MAX_RESULT_STRING_LEN, "RouteRules (Unknown)");
+                                       break;
                                }
-                       } else {
-                               snprintf(result_string, MAX_RESULT_STRING_LEN, "RouteRules (Unknown)");
                        }
                        break;
                }
@@ -3167,6 +4666,11 @@ necp_kernel_socket_policy_is_unnecessary(struct necp_kernel_socket_policy *polic
                        continue;
                }
 
+               if (compared_policy->condition_mask & NECP_KERNEL_CONDITION_CUSTOM_ENTITLEMENT &&
+                       strcmp(compared_policy->cond_custom_entitlement, policy->cond_custom_entitlement) != 0) {
+                       continue;
+               }
+
                if (compared_policy->condition_mask & NECP_KERNEL_CONDITION_ACCOUNT_ID &&
                        compared_policy->cond_account_id != policy->cond_account_id) {
                        continue;
@@ -3255,7 +4759,7 @@ necp_kernel_socket_policies_reprocess(void)
        int app_layer_current_free_index = 0;
        struct necp_kernel_socket_policy *kernel_policy = NULL;
 
-       lck_rw_assert(&necp_kernel_policy_lock, LCK_RW_ASSERT_EXCLUSIVE);
+       LCK_RW_ASSERT(&necp_kernel_policy_lock, LCK_RW_ASSERT_EXCLUSIVE);
 
        // Reset mask to 0
        necp_kernel_application_policies_condition_mask = 0;
@@ -3377,7 +4881,7 @@ necp_get_new_string_id(void)
 {
        u_int32_t newid = 0;
 
-       lck_rw_assert(&necp_kernel_policy_lock, LCK_RW_ASSERT_EXCLUSIVE);
+       LCK_RW_ASSERT(&necp_kernel_policy_lock, LCK_RW_ASSERT_EXCLUSIVE);
 
        necp_last_string_id++;
        if (necp_last_string_id < 1) {
@@ -3409,13 +4913,29 @@ necp_lookup_string_to_id_locked(struct necp_string_id_mapping_list *list, char *
        return (foundentry);
 }
 
+static struct necp_string_id_mapping *
+necp_lookup_string_with_id_locked(struct necp_string_id_mapping_list *list, u_int32_t local_id)
+{
+       struct necp_string_id_mapping *searchentry = NULL;
+       struct necp_string_id_mapping *foundentry = NULL;
+
+       LIST_FOREACH(searchentry, list, chain) {
+               if (searchentry->id == local_id) {
+                       foundentry = searchentry;
+                       break;
+               }
+       }
+
+       return (foundentry);
+}
+
 static u_int32_t
 necp_create_string_to_id_mapping(struct necp_string_id_mapping_list *list, char *string)
 {
        u_int32_t string_id = 0;
        struct necp_string_id_mapping *existing_mapping = NULL;
 
-       lck_rw_assert(&necp_kernel_policy_lock, LCK_RW_ASSERT_EXCLUSIVE);
+       LCK_RW_ASSERT(&necp_kernel_policy_lock, LCK_RW_ASSERT_EXCLUSIVE);
 
        existing_mapping = necp_lookup_string_to_id_locked(list, string);
        if (existing_mapping != NULL) {
@@ -3449,7 +4969,7 @@ necp_remove_string_to_id_mapping(struct necp_string_id_mapping_list *list, char
 {
        struct necp_string_id_mapping *existing_mapping = NULL;
 
-       lck_rw_assert(&necp_kernel_policy_lock, LCK_RW_ASSERT_EXCLUSIVE);
+       LCK_RW_ASSERT(&necp_kernel_policy_lock, LCK_RW_ASSERT_EXCLUSIVE);
 
        existing_mapping = necp_lookup_string_to_id_locked(list, string);
        if (existing_mapping != NULL) {
@@ -3469,7 +4989,7 @@ necp_get_new_route_rule_id(void)
 {
        u_int32_t newid = 0;
 
-       lck_rw_assert(&necp_kernel_policy_lock, LCK_RW_ASSERT_EXCLUSIVE);
+       LCK_RW_ASSERT(&necp_kernel_policy_lock, LCK_RW_ASSERT_EXCLUSIVE);
 
        necp_last_route_rule_id++;
        if (necp_last_route_rule_id < 1 || necp_last_route_rule_id > UINT16_MAX) {
@@ -3490,7 +5010,7 @@ necp_get_new_aggregate_route_rule_id(void)
 {
        u_int32_t newid = 0;
 
-       lck_rw_assert(&necp_route_rule_lock, LCK_RW_ASSERT_EXCLUSIVE);
+       LCK_RW_ASSERT(&necp_route_rule_lock, LCK_RW_ASSERT_EXCLUSIVE);
 
        necp_last_aggregate_route_rule_id++;
        if (necp_last_aggregate_route_rule_id <= UINT16_MAX) {
@@ -3590,7 +5110,7 @@ necp_create_route_rule(struct necp_route_rule_list *list, u_int8_t *route_rules_
        u_int8_t if_actions[MAX_ROUTE_RULE_INTERFACES];
        memset(&if_actions, 0, sizeof(if_actions));
 
-       lck_rw_assert(&necp_kernel_policy_lock, LCK_RW_ASSERT_EXCLUSIVE);
+       LCK_RW_ASSERT(&necp_kernel_policy_lock, LCK_RW_ASSERT_EXCLUSIVE);
 
        if (route_rules_array == NULL || route_rules_array_size == 0) {
                return (0);
@@ -3638,13 +5158,15 @@ necp_create_route_rule(struct necp_route_rule_list *list, u_int8_t *route_rules_
                        continue;
                }
 
-               memcpy(interface_name, rule_value, rule_length);
-               interface_name[length - 1] = 0; // Make sure the string is NULL terminated
-               if (ifnet_find_by_name(interface_name, &rule_interface) == 0) {
-                       if_actions[num_valid_indices] = rule_type;
-                       if_indices[num_valid_indices++] = rule_interface->if_index;
+               if (rule_length <= IFXNAMSIZ) {
+                       memcpy(interface_name, rule_value, rule_length);
+                       interface_name[rule_length - 1] = 0; // Make sure the string is NULL terminated
+                       if (ifnet_find_by_name(interface_name, &rule_interface) == 0) {
+                               if_actions[num_valid_indices] = rule_type;
+                               if_indices[num_valid_indices++] = rule_interface->if_index;
+                               ifnet_release(rule_interface);
+                       }
                }
-
                offset += sizeof(u_int8_t) + sizeof(u_int32_t) + length;
        }
 
@@ -3702,7 +5224,7 @@ necp_remove_route_rule(struct necp_route_rule_list *list, u_int32_t route_rule_i
 {
        struct necp_route_rule *existing_rule = NULL;
 
-       lck_rw_assert(&necp_kernel_policy_lock, LCK_RW_ASSERT_EXCLUSIVE);
+       LCK_RW_ASSERT(&necp_kernel_policy_lock, LCK_RW_ASSERT_EXCLUSIVE);
 
        existing_rule = necp_lookup_route_rule_locked(list, route_rule_id);
        if (existing_rule != NULL) {
@@ -3779,7 +5301,7 @@ necp_get_new_uuid_id(void)
 {
        u_int32_t newid = 0;
 
-       lck_rw_assert(&necp_kernel_policy_lock, LCK_RW_ASSERT_EXCLUSIVE);
+       LCK_RW_ASSERT(&necp_kernel_policy_lock, LCK_RW_ASSERT_EXCLUSIVE);
 
        necp_last_uuid_id++;
        if (necp_last_uuid_id < (NECP_NULL_SERVICE_ID + 1)) {
@@ -3811,13 +5333,32 @@ necp_uuid_lookup_app_id_locked(uuid_t uuid)
        return (foundentry);
 }
 
+static struct necp_uuid_id_mapping *
+necp_uuid_lookup_uuid_with_app_id_locked(u_int32_t local_id)
+{
+       struct necp_uuid_id_mapping *searchentry = NULL;
+       struct necp_uuid_id_mapping *foundentry = NULL;
+
+       struct necp_uuid_id_mapping_head *uuid_list_head = NULL;
+       for (uuid_list_head = &necp_uuid_app_id_hashtbl[necp_uuid_app_id_hash_num_buckets - 1]; uuid_list_head >= necp_uuid_app_id_hashtbl; uuid_list_head--) {
+               LIST_FOREACH(searchentry, uuid_list_head, chain) {
+                       if (searchentry->id == local_id) {
+                               foundentry = searchentry;
+                               break;
+                       }
+               }
+       }
+
+       return (foundentry);
+}
+
 static u_int32_t
 necp_create_uuid_app_id_mapping(uuid_t uuid, bool *allocated_mapping, bool uuid_policy_table)
 {
        u_int32_t local_id = 0;
        struct necp_uuid_id_mapping *existing_mapping = NULL;
 
-       lck_rw_assert(&necp_kernel_policy_lock, LCK_RW_ASSERT_EXCLUSIVE);
+       LCK_RW_ASSERT(&necp_kernel_policy_lock, LCK_RW_ASSERT_EXCLUSIVE);
 
        if (allocated_mapping) {
                *allocated_mapping = FALSE;
@@ -3861,7 +5402,7 @@ necp_remove_uuid_app_id_mapping(uuid_t uuid, bool *removed_mapping, bool uuid_po
 {
        struct necp_uuid_id_mapping *existing_mapping = NULL;
 
-       lck_rw_assert(&necp_kernel_policy_lock, LCK_RW_ASSERT_EXCLUSIVE);
+       LCK_RW_ASSERT(&necp_kernel_policy_lock, LCK_RW_ASSERT_EXCLUSIVE);
 
        if (removed_mapping) {
                *removed_mapping = FALSE;
@@ -3891,7 +5432,7 @@ necp_uuid_get_null_service_id_mapping(void)
        static struct necp_uuid_id_mapping null_mapping;
        uuid_clear(null_mapping.uuid);
        null_mapping.id = NECP_NULL_SERVICE_ID;
-       
+
        return (&null_mapping);
 }
 
@@ -3900,11 +5441,11 @@ necp_uuid_lookup_service_id_locked(uuid_t uuid)
 {
        struct necp_uuid_id_mapping *searchentry = NULL;
        struct necp_uuid_id_mapping *foundentry = NULL;
-       
+
        if (uuid_is_null(uuid)) {
                return necp_uuid_get_null_service_id_mapping();
        }
-       
+
        LIST_FOREACH(searchentry, &necp_uuid_service_id_list, chain) {
                if (uuid_compare(searchentry->uuid, uuid) == 0) {
                        foundentry = searchentry;
@@ -3920,11 +5461,11 @@ necp_uuid_lookup_uuid_with_service_id_locked(u_int32_t local_id)
 {
        struct necp_uuid_id_mapping *searchentry = NULL;
        struct necp_uuid_id_mapping *foundentry = NULL;
-       
+
        if (local_id == NECP_NULL_SERVICE_ID) {
                return necp_uuid_get_null_service_id_mapping();
        }
-       
+
        LIST_FOREACH(searchentry, &necp_uuid_service_id_list, chain) {
                if (searchentry->id == local_id) {
                        foundentry = searchentry;
@@ -3940,12 +5481,12 @@ necp_create_uuid_service_id_mapping(uuid_t uuid)
 {
        u_int32_t local_id = 0;
        struct necp_uuid_id_mapping *existing_mapping = NULL;
-       
+
        if (uuid_is_null(uuid)) {
                return (NECP_NULL_SERVICE_ID);
        }
-       
-       lck_rw_assert(&necp_kernel_policy_lock, LCK_RW_ASSERT_EXCLUSIVE);
+
+       LCK_RW_ASSERT(&necp_kernel_policy_lock, LCK_RW_ASSERT_EXCLUSIVE);
 
        existing_mapping = necp_uuid_lookup_service_id_locked(uuid);
        if (existing_mapping != NULL) {
@@ -3972,12 +5513,12 @@ static bool
 necp_remove_uuid_service_id_mapping(uuid_t uuid)
 {
        struct necp_uuid_id_mapping *existing_mapping = NULL;
-       
+
        if (uuid_is_null(uuid)) {
                return (TRUE);
        }
-       
-       lck_rw_assert(&necp_kernel_policy_lock, LCK_RW_ASSERT_EXCLUSIVE);
+
+       LCK_RW_ASSERT(&necp_kernel_policy_lock, LCK_RW_ASSERT_EXCLUSIVE);
 
        existing_mapping = necp_uuid_lookup_app_id_locked(uuid);
        if (existing_mapping != NULL) {
@@ -3995,7 +5536,7 @@ necp_remove_uuid_service_id_mapping(uuid_t uuid)
 static bool
 necp_kernel_socket_policies_update_uuid_table(void)
 {
-       lck_rw_assert(&necp_kernel_policy_lock, LCK_RW_ASSERT_EXCLUSIVE);
+       LCK_RW_ASSERT(&necp_kernel_policy_lock, LCK_RW_ASSERT_EXCLUSIVE);
 
        if (necp_uuid_app_id_mappings_dirty) {
                if (proc_uuid_policy_kernel(PROC_UUID_POLICY_OPERATION_CLEAR, NULL, PROC_UUID_NECP_APP_POLICY) < 0) {
@@ -4034,9 +5575,9 @@ necp_kernel_ip_output_policy_add(necp_policy_id parent_policy_id, necp_policy_or
                goto done;
        }
 
-       memset(new_kernel_policy, 0, sizeof(*new_kernel_policy));
+       memset(new_kernel_policy, 0, sizeof(*new_kernel_policy)); // M_ZERO is not supported for MALLOC_ZONE
        new_kernel_policy->parent_policy_id = parent_policy_id;
-       new_kernel_policy->id = necp_kernel_policy_get_new_id();
+       new_kernel_policy->id = necp_kernel_policy_get_new_id(false);
        new_kernel_policy->suborder = suborder;
        new_kernel_policy->order = order;
        new_kernel_policy->session_order = session_order;
@@ -4125,7 +5666,7 @@ necp_kernel_ip_output_policy_delete(necp_kernel_policy_id policy_id)
 {
        struct necp_kernel_ip_output_policy *policy = NULL;
 
-       lck_rw_assert(&necp_kernel_policy_lock, LCK_RW_ASSERT_EXCLUSIVE);
+       LCK_RW_ASSERT(&necp_kernel_policy_lock, LCK_RW_ASSERT_EXCLUSIVE);
 
        policy = necp_kernel_ip_output_policy_find(policy_id);
        if (policy) {
@@ -4308,7 +5849,7 @@ necp_kernel_ip_output_policies_reprocess(void)
        int bucket_current_free_index[NECP_KERNEL_IP_OUTPUT_POLICIES_MAP_NUM_ID_BUCKETS];
        struct necp_kernel_ip_output_policy *kernel_policy = NULL;
 
-       lck_rw_assert(&necp_kernel_policy_lock, LCK_RW_ASSERT_EXCLUSIVE);
+       LCK_RW_ASSERT(&necp_kernel_policy_lock, LCK_RW_ASSERT_EXCLUSIVE);
 
        // Reset mask to 0
        necp_kernel_ip_output_policies_condition_mask = 0;
@@ -4464,7 +6005,8 @@ necp_check_suffix(struct substring parent, struct substring suffix, bool require
                }
        }
 
-       return (memcmp(parent.string + length_difference, suffix.string, suffix.length) == 0);
+       // strncasecmp does case-insensitive check for all UTF-8 strings (ignores non-ASCII characters)
+       return (strncasecmp(parent.string + length_difference, suffix.string, suffix.length) == 0);
 }
 
 static bool
@@ -4479,8 +6021,9 @@ necp_hostname_matches_domain(struct substring hostname_substring, u_int8_t hostn
        domain_substring.length = strlen(domain);
 
        if (hostname_dot_count == domain_dot_count) {
+               // strncasecmp does case-insensitive check for all UTF-8 strings (ignores non-ASCII characters)
                if (hostname_substring.length == domain_substring.length &&
-                       memcmp(hostname_substring.string, domain_substring.string, hostname_substring.length) == 0) {
+                       strncasecmp(hostname_substring.string, domain_substring.string, hostname_substring.length) == 0) {
                        return (TRUE);
                }
        } else if (domain_dot_count < hostname_dot_count) {
@@ -4492,9 +6035,25 @@ necp_hostname_matches_domain(struct substring hostname_substring, u_int8_t hostn
        return (FALSE);
 }
 
+static char *
+necp_copy_string(char *string, size_t length)
+{
+       char *copied_string = NULL;
+
+       MALLOC(copied_string, char *, length + 1, M_NECP, M_WAITOK);
+       if (copied_string == NULL) {
+               return (NULL);
+       }
+
+       memcpy(copied_string, string, length);
+       copied_string[length] = 0;
+
+       return (copied_string);
+}
+
 #define        NECP_KERNEL_ADDRESS_TYPE_CONDITIONS (NECP_KERNEL_CONDITION_LOCAL_START | NECP_KERNEL_CONDITION_LOCAL_END | NECP_KERNEL_CONDITION_LOCAL_PREFIX | NECP_KERNEL_CONDITION_REMOTE_START | NECP_KERNEL_CONDITION_REMOTE_END | NECP_KERNEL_CONDITION_REMOTE_PREFIX)
 static void
-necp_application_fillout_info_locked(uuid_t application_uuid, uuid_t real_application_uuid, char *account, char *domain, pid_t pid, uid_t uid, u_int16_t protocol, u_int32_t bound_interface_index, u_int32_t traffic_class, union necp_sockaddr_union *local_addr, union necp_sockaddr_union *remote_addr, struct necp_socket_info *info)
+necp_application_fillout_info_locked(uuid_t application_uuid, uuid_t real_application_uuid, char *account, char *domain, pid_t pid, uid_t uid, u_int16_t protocol, u_int32_t bound_interface_index, u_int32_t traffic_class, union necp_sockaddr_union *local_addr, union necp_sockaddr_union *remote_addr, proc_t proc, struct necp_socket_info *info)
 {
        memset(info, 0, sizeof(struct necp_socket_info));
 
@@ -4503,7 +6062,10 @@ necp_application_fillout_info_locked(uuid_t application_uuid, uuid_t real_applic
        info->protocol = protocol;
        info->bound_interface_index = bound_interface_index;
        info->traffic_class = traffic_class;
-       info->cred_result = 0; // Don't check the entitlement here, only in the socket layer
+
+       if (necp_kernel_application_policies_condition_mask & NECP_KERNEL_CONDITION_ENTITLEMENT && proc != NULL) {
+               info->cred_result = priv_check_cred(proc_ucred(proc), PRIV_NET_PRIVILEGED_NECP_MATCH, 0);
+       }
 
        if (necp_kernel_application_policies_condition_mask & NECP_KERNEL_CONDITION_APP_ID && !uuid_is_null(application_uuid)) {
                struct necp_uuid_id_mapping *existing_mapping = necp_uuid_lookup_app_id_locked(application_uuid);
@@ -4545,7 +6107,7 @@ necp_application_fillout_info_locked(uuid_t application_uuid, uuid_t real_applic
 }
 
 static void
-necp_send_application_cell_denied_event(pid_t pid, uuid_t proc_uuid)
+necp_send_application_interface_denied_event(pid_t pid, uuid_t proc_uuid, u_int32_t if_functional_type)
 {
        struct kev_netpolicy_ifdenied ev_ifdenied;
 
@@ -4553,12 +6115,34 @@ necp_send_application_cell_denied_event(pid_t pid, uuid_t proc_uuid)
 
        ev_ifdenied.ev_data.epid = pid;
        uuid_copy(ev_ifdenied.ev_data.euuid, proc_uuid);
+       ev_ifdenied.ev_if_functional_type = if_functional_type;
 
        netpolicy_post_msg(KEV_NETPOLICY_IFDENIED, &ev_ifdenied.ev_data, sizeof(ev_ifdenied));
 }
 
-static int
-necp_application_find_policy_match_internal(u_int8_t *parameters, u_int32_t parameters_size, struct necp_aggregate_result *returned_result)
+extern char *proc_name_address(void *p);
+
+#define NECP_VERIFY_DELEGATION_ENTITLEMENT(_p, _d) \
+       if (!has_checked_delegation_entitlement) { \
+               has_delegation_entitlement = (priv_check_cred(proc_ucred(_p), PRIV_NET_PRIVILEGED_SOCKET_DELEGATE, 0) == 0); \
+               has_checked_delegation_entitlement = TRUE; \
+       } \
+       if (!has_delegation_entitlement) { \
+               NECPLOG(LOG_ERR, "%s(%d) does not hold the necessary entitlement to delegate network traffic for other processes by %s", \
+                                                 proc_name_address(_p), proc_pid(_p), _d); \
+               break; \
+       }
+
+int
+necp_application_find_policy_match_internal(proc_t proc,
+                                                                                       u_int8_t *parameters,
+                                                                                       u_int32_t parameters_size,
+                                                                                       struct necp_aggregate_result *returned_result,
+                                                                                       u_int32_t *flags,
+                                                                                       u_int required_interface_index,
+                                                                                       const union necp_sockaddr_union *override_local_addr,
+                                                                                       const union necp_sockaddr_union *override_remote_addr,
+                                                                                       struct rtentry **returned_route, bool ignore_address)
 {
        int error = 0;
        size_t offset = 0;
@@ -4570,21 +6154,37 @@ necp_application_find_policy_match_internal(u_int8_t *parameters, u_int32_t para
        necp_kernel_policy_result service_action = 0;
        necp_kernel_policy_service service = { 0, 0 };
 
-       pid_t pid = 0;
-       uid_t uid = 0;
        u_int16_t protocol = 0;
-       u_int32_t bound_interface_index = 0;
+       u_int32_t bound_interface_index = required_interface_index;
        u_int32_t traffic_class = 0;
+       u_int32_t client_flags = 0;
        union necp_sockaddr_union local_addr;
        union necp_sockaddr_union remote_addr;
        bool no_remote_addr = FALSE;
+       u_int8_t remote_family = 0;
+       bool no_local_addr = FALSE;
+
+       if (override_local_addr) {
+               memcpy(&local_addr, override_local_addr, sizeof(local_addr));
+       } else {
+               memset(&local_addr, 0, sizeof(local_addr));
+       }
+       if (override_remote_addr) {
+               memcpy(&remote_addr, override_remote_addr, sizeof(remote_addr));
+       } else {
+               memset(&remote_addr, 0, sizeof(remote_addr));
+       }
 
-       memset(&local_addr, 0, sizeof(local_addr));
-       memset(&remote_addr, 0, sizeof(remote_addr));
+       // Initialize UID, PID, and UUIDs to the current process
+       uid_t uid = kauth_cred_getuid(proc_ucred(proc));
+       pid_t pid = proc_pid(proc);
        uuid_t application_uuid;
        uuid_clear(application_uuid);
        uuid_t real_application_uuid;
        uuid_clear(real_application_uuid);
+       proc_getexecutableuuid(proc, real_application_uuid, sizeof(real_application_uuid));
+       uuid_copy(application_uuid, real_application_uuid);
+
        char *domain = NULL;
        char *account = NULL;
 
@@ -4592,6 +6192,9 @@ necp_application_find_policy_match_internal(u_int8_t *parameters, u_int32_t para
        memset(&netagent_ids, 0, sizeof(netagent_ids));
        int netagent_cursor;
 
+       bool has_checked_delegation_entitlement = FALSE;
+       bool has_delegation_entitlement = FALSE;
+
        if (returned_result == NULL) {
                return (EINVAL);
        }
@@ -4612,57 +6215,91 @@ necp_application_find_policy_match_internal(u_int8_t *parameters, u_int32_t para
                u_int8_t type = necp_buffer_get_tlv_type(parameters, offset);
                u_int32_t length = necp_buffer_get_tlv_length(parameters, offset);
 
-               if (length > 0 && (offset + sizeof(u_int8_t) + sizeof(u_int32_t) + length) <= parameters_size) {
+               if (length > (parameters_size - (offset + sizeof(u_int8_t) + sizeof(u_int32_t)))) {
+                       // If the length is larger than what can fit in the remaining parameters size, bail
+                       NECPLOG(LOG_ERR, "Invalid TLV length (%u)", length);
+                       break;
+               }
+
+               if (length > 0) {
                        u_int8_t *value = necp_buffer_get_tlv_value(parameters, offset, NULL);
                        if (value != NULL) {
                                switch (type) {
-                                       case NECP_POLICY_CONDITION_APPLICATION: {
+                                       case NECP_CLIENT_PARAMETER_APPLICATION: {
                                                if (length >= sizeof(uuid_t)) {
+                                                       if (uuid_compare(application_uuid, value) == 0) {
+                                                               // No delegation
+                                                               break;
+                                                       }
+
+                                                       NECP_VERIFY_DELEGATION_ENTITLEMENT(proc, "euuid");
+
                                                        uuid_copy(application_uuid, value);
                                                }
                                                break;
                                        }
-                                       case NECP_POLICY_CONDITION_REAL_APPLICATION: {
+                                       case NECP_CLIENT_PARAMETER_REAL_APPLICATION: {
                                                if (length >= sizeof(uuid_t)) {
+                                                       if (uuid_compare(real_application_uuid, value) == 0) {
+                                                               // No delegation
+                                                               break;
+                                                       }
+
+                                                       NECP_VERIFY_DELEGATION_ENTITLEMENT(proc, "uuid");
+
                                                        uuid_copy(real_application_uuid, value);
                                                }
                                                break;
                                        }
-                                       case NECP_POLICY_CONDITION_DOMAIN: {
+                                       case NECP_CLIENT_PARAMETER_PID: {
+                                               if (length >= sizeof(pid_t)) {
+                                                       if (memcmp(&pid, value, sizeof(pid_t)) == 0) {
+                                                               // No delegation
+                                                               break;
+                                                       }
+
+                                                       NECP_VERIFY_DELEGATION_ENTITLEMENT(proc, "pid");
+
+                                                       memcpy(&pid, value, sizeof(pid_t));
+                                               }
+                                               break;
+                                       }
+                                       case NECP_CLIENT_PARAMETER_UID: {
+                                               if (length >= sizeof(uid_t)) {
+                                                       if (memcmp(&uid, value, sizeof(uid_t)) == 0) {
+                                                               // No delegation
+                                                               break;
+                                                       }
+
+                                                       NECP_VERIFY_DELEGATION_ENTITLEMENT(proc, "uid");
+
+                                                       memcpy(&uid, value, sizeof(uid_t));
+                                               }
+                                               break;
+                                       }
+                                       case NECP_CLIENT_PARAMETER_DOMAIN: {
                                                domain = (char *)value;
                                                domain[length - 1] = 0;
                                                break;
                                        }
-                                       case NECP_POLICY_CONDITION_ACCOUNT: {
+                                       case NECP_CLIENT_PARAMETER_ACCOUNT: {
                                                account = (char *)value;
                                                account[length - 1] = 0;
                                                break;
                                        }
-                                       case NECP_POLICY_CONDITION_TRAFFIC_CLASS: {
+                                       case NECP_CLIENT_PARAMETER_TRAFFIC_CLASS: {
                                                if (length >= sizeof(u_int32_t)) {
                                                        memcpy(&traffic_class, value, sizeof(u_int32_t));
                                                }
                                                break;
                                        }
-                                       case NECP_POLICY_CONDITION_PID: {
-                                               if (length >= sizeof(pid_t)) {
-                                                       memcpy(&pid, value, sizeof(pid_t));
-                                               }
-                                               break;
-                                       }
-                                       case NECP_POLICY_CONDITION_UID: {
-                                               if (length >= sizeof(uid_t)) {
-                                                       memcpy(&uid, value, sizeof(uid_t));
-                                               }
-                                               break;
-                                       }
-                                       case NECP_POLICY_CONDITION_IP_PROTOCOL: {
+                                       case NECP_CLIENT_PARAMETER_IP_PROTOCOL: {
                                                if (length >= sizeof(u_int16_t)) {
                                                        memcpy(&protocol, value, sizeof(u_int16_t));
                                                }
                                                break;
                                        }
-                                       case NECP_POLICY_CONDITION_BOUND_INTERFACE: {
+                                       case NECP_CLIENT_PARAMETER_BOUND_INTERFACE: {
                                                if (length <= IFXNAMSIZ && length > 0) {
                                                        ifnet_t bound_interface = NULL;
                                                        char interface_name[IFXNAMSIZ];
@@ -4670,24 +6307,42 @@ necp_application_find_policy_match_internal(u_int8_t *parameters, u_int32_t para
                                                        interface_name[length - 1] = 0; // Make sure the string is NULL terminated
                                                        if (ifnet_find_by_name(interface_name, &bound_interface) == 0) {
                                                                bound_interface_index = bound_interface->if_index;
+                                                               ifnet_release(bound_interface);
                                                        }
                                                }
                                                break;
                                        }
-                                       case NECP_POLICY_CONDITION_LOCAL_ADDR: {
+                                       case NECP_CLIENT_PARAMETER_LOCAL_ADDRESS: {
+                                               if (ignore_address) {
+                                                       break;
+                                               }
+
                                                if (length >= sizeof(struct necp_policy_condition_addr)) {
                                                        struct necp_policy_condition_addr *address_struct = (struct necp_policy_condition_addr *)(void *)value;
-                                                       memcpy(&local_addr, &address_struct->address, sizeof(address_struct->address));
+                                                       if (necp_address_is_valid(&address_struct->address.sa)) {
+                                                               memcpy(&local_addr, &address_struct->address, sizeof(address_struct->address));
+                                                       }
                                                }
                                                break;
                                        }
-                                       case NECP_POLICY_CONDITION_REMOTE_ADDR: {
+                                       case NECP_CLIENT_PARAMETER_REMOTE_ADDRESS: {
+                                               if (ignore_address) {
+                                                       break;
+                                               }
+
                                                if (length >= sizeof(struct necp_policy_condition_addr)) {
                                                        struct necp_policy_condition_addr *address_struct = (struct necp_policy_condition_addr *)(void *)value;
-                                                       memcpy(&remote_addr, &address_struct->address, sizeof(address_struct->address));
+                                                       if (necp_address_is_valid(&address_struct->address.sa)) {
+                                                               memcpy(&remote_addr, &address_struct->address, sizeof(address_struct->address));
+                                                       }
                                                }
                                                break;
                                        }
+                                       case NECP_CLIENT_PARAMETER_FLAGS: {
+                                               if (length >= sizeof(client_flags)) {
+                                                       memcpy(&client_flags, value, sizeof(client_flags));
+                                               }
+                                       }
                                        default: {
                                                break;
                                        }
@@ -4701,12 +6356,16 @@ necp_application_find_policy_match_internal(u_int8_t *parameters, u_int32_t para
        // Lock
        lck_rw_lock_shared(&necp_kernel_policy_lock);
 
-       necp_application_fillout_info_locked(application_uuid, real_application_uuid, account, domain, pid, uid, protocol, bound_interface_index, traffic_class, &local_addr, &remote_addr, &info);
-       matched_policy = necp_socket_find_policy_match_with_info_locked(necp_kernel_socket_policies_app_layer_map, &info, &filter_control_unit, &route_rule_id, &service_action, &service, netagent_ids, NECP_MAX_NETAGENTS);
+       necp_application_fillout_info_locked(application_uuid, real_application_uuid, account, domain, pid, uid, protocol, bound_interface_index, traffic_class, &local_addr, &remote_addr, proc, &info);
+       matched_policy = necp_socket_find_policy_match_with_info_locked(necp_kernel_socket_policies_app_layer_map, &info, &filter_control_unit, &route_rule_id, &service_action, &service, netagent_ids, NECP_MAX_NETAGENTS, proc);
        if (matched_policy) {
                returned_result->policy_id = matched_policy->id;
                returned_result->routing_result = matched_policy->result;
                memcpy(&returned_result->routing_result_parameter, &matched_policy->result_parameter, sizeof(returned_result->routing_result_parameter));
+       } else if (necp_drop_all_order > 0) {
+               // Mark socket as a drop if drop_all is set
+               returned_result->policy_id = NECP_KERNEL_POLICY_ID_NO_MATCH;
+               returned_result->routing_result = NECP_KERNEL_POLICY_RESULT_DROP;
        } else {
                returned_result->policy_id = 0;
                returned_result->routing_result = NECP_KERNEL_POLICY_RESULT_NONE;
@@ -4757,79 +6416,254 @@ necp_application_find_policy_match_internal(u_int8_t *parameters, u_int32_t para
                output_bound_interface = returned_result->routing_result_parameter.tunnel_interface_index;
        }
 
-       if (remote_addr.sa.sa_len == 0) {
+       if (local_addr.sa.sa_len == 0 ||
+               (local_addr.sa.sa_family == AF_INET && local_addr.sin.sin_addr.s_addr == 0) ||
+               (local_addr.sa.sa_family == AF_INET6 && IN6_IS_ADDR_UNSPECIFIED(&local_addr.sin6.sin6_addr))) {
+               no_local_addr = TRUE;
+       }
+
+       if (remote_addr.sa.sa_len == 0 ||
+               (remote_addr.sa.sa_family == AF_INET && remote_addr.sin.sin_addr.s_addr == 0) ||
+               (remote_addr.sa.sa_family == AF_INET6 && IN6_IS_ADDR_UNSPECIFIED(&remote_addr.sin6.sin6_addr))) {
                no_remote_addr = TRUE;
-               // Default to 0.0.0.0:0
-               remote_addr.sa.sa_family = AF_INET;
-               remote_addr.sa.sa_len = sizeof(struct sockaddr_in);
+               remote_family = remote_addr.sa.sa_family;
        }
 
-       struct rtentry *rt = NULL;
-       rt = rtalloc1_scoped((struct sockaddr *)&remote_addr, 0, 0, output_bound_interface);
+       returned_result->routed_interface_index = 0;
+       struct rtentry *rt = NULL;
+       if (!no_local_addr && (client_flags & NECP_CLIENT_PARAMETER_FLAG_LISTENER) != 0) {
+               // Treat the output bound interface as the routed interface for local address
+               // validation later.
+               returned_result->routed_interface_index = output_bound_interface;
+       } else {
+               if (no_remote_addr) {
+                       memset(&remote_addr, 0, sizeof(remote_addr));
+                       if (remote_family == AF_INET6) {
+                               // Reset address to ::
+                               remote_addr.sa.sa_family = AF_INET6;
+                               remote_addr.sa.sa_len = sizeof(struct sockaddr_in6);
+                       } else {
+                               // Reset address to 0.0.0.0
+                               remote_addr.sa.sa_family = AF_INET;
+                               remote_addr.sa.sa_len = sizeof(struct sockaddr_in);
+                       }
+               }
+
+               rt = rtalloc1_scoped((struct sockaddr *)&remote_addr, 0, 0,
+                       output_bound_interface);
+
+               if (no_remote_addr && remote_family == 0 &&
+                       (rt == NULL || rt->rt_ifp == NULL)) {
+                       // Route lookup for default IPv4 failed, try IPv6
+
+                       // Cleanup old route if necessary
+                       if (rt != NULL) {
+                               rtfree(rt);
+                               rt = NULL;
+                       }
+
+                       // Reset address to ::
+                       memset(&remote_addr, 0, sizeof(remote_addr));
+                       remote_addr.sa.sa_family = AF_INET6;
+                       remote_addr.sa.sa_len = sizeof(struct sockaddr_in6);
+
+                       // Get route
+                       rt = rtalloc1_scoped((struct sockaddr *)&remote_addr, 0, 0,
+                               output_bound_interface);
+               }
+
+               if (rt != NULL &&
+                       rt->rt_ifp != NULL) {
+                       returned_result->routed_interface_index = rt->rt_ifp->if_index;
+                       /*
+                        * For local addresses, we allow the interface scope to be
+                        * either the loopback interface or the interface hosting the
+                        * local address.
+                        */
+                       if (bound_interface_index != IFSCOPE_NONE &&
+                               rt->rt_ifa != NULL && rt->rt_ifa->ifa_ifp &&
+                               (output_bound_interface == lo_ifp->if_index ||
+                               rt->rt_ifp->if_index == lo_ifp->if_index ||
+                               rt->rt_ifa->ifa_ifp->if_index == bound_interface_index)) {
+                               struct sockaddr_storage dst;
+                               unsigned int ifscope = bound_interface_index;
+
+                               /*
+                                * Transform dst into the internal routing table form
+                                */
+                               (void) sa_copy((struct sockaddr *)&remote_addr,
+                                               &dst, &ifscope);
+
+                               if ((rt->rt_ifp->if_index == lo_ifp->if_index) ||
+                                       rt_ifa_is_dst((struct sockaddr *)&dst, rt->rt_ifa))
+                                       returned_result->routed_interface_index =
+                                               bound_interface_index;
+                       }
+               }
+       }
+
+       if (returned_result->routed_interface_index != 0 &&
+               returned_result->routed_interface_index != lo_ifp->if_index && // Loopback can accept any local address
+               !no_local_addr) {
+
+               // Transform local_addr into the ifaddr form
+               // IPv6 Scope IDs are always embedded in the ifaddr list
+               struct sockaddr_storage local_address_sanitized;
+               u_int ifscope = IFSCOPE_NONE;
+               (void)sa_copy(&local_addr.sa, &local_address_sanitized, &ifscope);
+               SIN(&local_address_sanitized)->sin_port = 0;
+               if (local_address_sanitized.ss_family == AF_INET6) {
+                       SIN6(&local_address_sanitized)->sin6_scope_id = 0;
+               }
+
+               // Validate local address on routed interface
+               struct ifaddr *ifa = ifa_ifwithaddr_scoped((struct sockaddr *)&local_address_sanitized, returned_result->routed_interface_index);
+               if (ifa == NULL) {
+                       // Interface address not found, reject route
+                       returned_result->routed_interface_index = 0;
+                       if (rt != NULL) {
+                               rtfree(rt);
+                               rt = NULL;
+                       }
+               } else {
+                       ifaddr_release(ifa);
+                       ifa = NULL;
+               }
+       }
+
+       if (flags != NULL) {
+               if ((client_flags & NECP_CLIENT_PARAMETER_FLAG_LISTENER) == 0) {
+                       // Check for local/direct
+                       bool is_local = FALSE;
+                       if (rt != NULL && (rt->rt_flags & RTF_LOCAL)) {
+                               is_local = TRUE;
+                       } else if (returned_result->routed_interface_index != 0 &&
+                               !no_remote_addr) {
+                               // Check if remote address is an interface address
+                               struct ifaddr *ifa = ifa_ifwithaddr(&remote_addr.sa);
+                               if (ifa != NULL && ifa->ifa_ifp != NULL) {
+                                       u_int if_index_for_remote_addr = ifa->ifa_ifp->if_index;
+                                       if (if_index_for_remote_addr == returned_result->routed_interface_index ||
+                                               if_index_for_remote_addr == lo_ifp->if_index) {
+                                               is_local = TRUE;
+                                       }
+                               }
+                               if (ifa != NULL) {
+                                       ifaddr_release(ifa);
+                                       ifa = NULL;
+                               }
+                       }
+
+                       if (is_local) {
+                               *flags |= (NECP_CLIENT_RESULT_FLAG_IS_LOCAL | NECP_CLIENT_RESULT_FLAG_IS_DIRECT);
+                       } else {
+                               if (rt != NULL &&
+                                       !(rt->rt_flags & RTF_GATEWAY) &&
+                                       (rt->rt_ifa && rt->rt_ifa->ifa_ifp && !(rt->rt_ifa->ifa_ifp->if_flags & IFF_POINTOPOINT))) {
+                                       // Route is directly accessible
+                                       *flags |= NECP_CLIENT_RESULT_FLAG_IS_DIRECT;
+                               }
+                       }
+
+                       if (rt != NULL &&
+                               rt->rt_ifp != NULL) {
+                               // Check probe status
+                               if (rt->rt_ifp->if_eflags & IFEF_PROBE_CONNECTIVITY) {
+                                       *flags |= NECP_CLIENT_RESULT_FLAG_PROBE_CONNECTIVITY;
+                               }
+
+                               if (rt->rt_ifp->if_type == IFT_CELLULAR) {
+                                       struct if_cellular_status_v1 *ifsr;
+
+                                       ifnet_lock_shared(rt->rt_ifp);
+                                       lck_rw_lock_exclusive(&rt->rt_ifp->if_link_status_lock);
+
+                                       if (rt->rt_ifp->if_link_status != NULL) {
+                                               ifsr = &rt->rt_ifp->if_link_status->ifsr_u.ifsr_cell.if_cell_u.if_status_v1;
+
+                                               if (ifsr->valid_bitmask & IF_CELL_UL_MSS_RECOMMENDED_VALID) {
+                                                       if (ifsr->mss_recommended == IF_CELL_UL_MSS_RECOMMENDED_NONE) {
+                                                               returned_result->mss_recommended = NECP_CLIENT_RESULT_RECOMMENDED_MSS_NONE;
+                                                       } else if (ifsr->mss_recommended == IF_CELL_UL_MSS_RECOMMENDED_MEDIUM) {
+                                                               returned_result->mss_recommended = NECP_CLIENT_RESULT_RECOMMENDED_MSS_MEDIUM;
+                                                       } else if (ifsr->mss_recommended == IF_CELL_UL_MSS_RECOMMENDED_LOW) {
+                                                               returned_result->mss_recommended = NECP_CLIENT_RESULT_RECOMMENDED_MSS_LOW;
+                                                       }
+                                               }
+                                       }
+                                       lck_rw_done(&rt->rt_ifp->if_link_status_lock);
+                                       ifnet_lock_done(rt->rt_ifp);
+                               }
 
-       if (no_remote_addr &&
-               (rt == NULL || rt->rt_ifp == NULL)) {
-               // Route lookup for default IPv4 failed, try IPv6
+                               // Check link quality
+                               if ((client_flags & NECP_CLIENT_PARAMETER_FLAG_DISCRETIONARY) &&
+                                       (rt->rt_ifp->if_interface_state.valid_bitmask & IF_INTERFACE_STATE_LQM_STATE_VALID) &&
+                                       rt->rt_ifp->if_interface_state.lqm_state == IFNET_LQM_THRESH_ABORT) {
+                                       *flags |= NECP_CLIENT_RESULT_FLAG_LINK_QUALITY_ABORT;
+                               }
 
-               // Cleanup old route if necessary
-               if (rt != NULL) {
-                       rtfree(rt);
-                       rt = NULL;
+                               // Check QoS marking (fastlane)
+                               if (necp_update_qos_marking(rt->rt_ifp, route_rule_id)) {
+                                       *flags |= NECP_CLIENT_RESULT_FLAG_ALLOW_QOS_MARKING;
+                               }
+                       }
                }
 
-               // Reset address to ::
-               memset(&remote_addr, 0, sizeof(remote_addr));
-               remote_addr.sa.sa_family = AF_INET6;
-               remote_addr.sa.sa_len = sizeof(struct sockaddr_in6);
+               if (returned_result->routed_interface_index != 0) {
+                       union necp_sockaddr_union default_address;
+                       struct rtentry *v4Route = NULL;
+                       struct rtentry *v6Route = NULL;
 
-               // Get route
-               rt = rtalloc1_scoped((struct sockaddr *)&remote_addr, 0, 0, output_bound_interface);
-       }
+                       memset(&default_address, 0, sizeof(default_address));
 
-       returned_result->routed_interface_index = 0;
-       if (rt != NULL &&
-           rt->rt_ifp != NULL) {
-               returned_result->routed_interface_index = rt->rt_ifp->if_index;
-               /*
-                * For local addresses, we allow the interface scope to be
-                * either the loopback interface or the interface hosting the
-                * local address.
-                */
-               if (bound_interface_index != IFSCOPE_NONE &&
-                   rt->rt_ifa != NULL && rt->rt_ifa->ifa_ifp &&
-                   (output_bound_interface == lo_ifp->if_index ||
-                   rt->rt_ifp->if_index == lo_ifp->if_index ||
-                   rt->rt_ifa->ifa_ifp->if_index == bound_interface_index)) {
-                       struct sockaddr_storage dst;
-                       unsigned int ifscope = bound_interface_index;
+                       // Reset address to 0.0.0.0
+                       default_address.sa.sa_family = AF_INET;
+                       default_address.sa.sa_len = sizeof(struct sockaddr_in);
+                       v4Route = rtalloc1_scoped((struct sockaddr *)&default_address, 0, 0,
+                           returned_result->routed_interface_index);
 
-                       /*
-                        * Transform dst into the internal routing table form
-                        */
-                       (void) sa_copy((struct sockaddr *)&remote_addr,
-                                       &dst, &ifscope);
+                       // Reset address to ::
+                       default_address.sa.sa_family = AF_INET6;
+                       default_address.sa.sa_len = sizeof(struct sockaddr_in6);
+                       v6Route = rtalloc1_scoped((struct sockaddr *)&default_address, 0, 0,
+                           returned_result->routed_interface_index);
+
+                       if (v4Route != NULL) {
+                               if (v4Route->rt_ifp != NULL) {
+                                       *flags |= NECP_CLIENT_RESULT_FLAG_HAS_IPV4;
+                               }
+                               rtfree(v4Route);
+                               v4Route = NULL;
+                       }
 
-                       if ((rt->rt_ifp->if_index == lo_ifp->if_index) ||
-                           rt_ifa_is_dst((struct sockaddr *)&dst, rt->rt_ifa))
-                               returned_result->routed_interface_index =
-                                       bound_interface_index;
+                       if (v6Route != NULL) {
+                               if (v6Route->rt_ifp != NULL) {
+                                       *flags |= NECP_CLIENT_RESULT_FLAG_HAS_IPV6;
+                               }
+                               rtfree(v6Route);
+                               v6Route = NULL;
+                       }
                }
        }
 
-       bool cellular_denied = FALSE;
-       bool route_is_allowed = necp_route_is_allowed(rt, NULL, route_rule_id, &cellular_denied);
+       u_int32_t interface_type_denied = IFRTYPE_FUNCTIONAL_UNKNOWN;
+       bool route_is_allowed = necp_route_is_allowed(rt, NULL, route_rule_id, &interface_type_denied);
        if (!route_is_allowed) {
                // If the route is blocked, treat the lookup as a drop
                returned_result->routing_result = NECP_KERNEL_POLICY_RESULT_DROP;
                memset(&returned_result->routing_result_parameter, 0, sizeof(returned_result->routing_result_parameter));
 
-               if (cellular_denied) {
-                       necp_send_application_cell_denied_event(pid, application_uuid);
+               if (interface_type_denied != IFRTYPE_FUNCTIONAL_UNKNOWN) {
+                       necp_send_application_interface_denied_event(pid, application_uuid, interface_type_denied);
                }
        }
 
        if (rt != NULL) {
-               rtfree(rt);
+               if (returned_route != NULL) {
+                       *returned_route = rt;
+               } else {
+                       rtfree(rt);
+               }
                rt = NULL;
        }
        // Unlock
@@ -4838,56 +6672,8 @@ necp_application_find_policy_match_internal(u_int8_t *parameters, u_int32_t para
        return (error);
 }
 
-#define NECP_MAX_MATCH_POLICY_PARAMETER_SIZE 1024
-
-int
-necp_match_policy(struct proc *p, struct necp_match_policy_args *uap, int32_t *retval)
-{
-#pragma unused(p, retval)
-       u_int8_t *parameters = NULL;
-       struct necp_aggregate_result returned_result;
-       int error = 0;
-
-       if (uap == NULL) {
-               error = EINVAL;
-               goto done;
-       }
-
-       if (uap->parameters == 0 || uap->parameters_size == 0 || uap->parameters_size > NECP_MAX_MATCH_POLICY_PARAMETER_SIZE || uap->returned_result == 0) {
-               error = EINVAL;
-               goto done;
-       }
-
-       MALLOC(parameters, u_int8_t *, uap->parameters_size, M_NECP, M_WAITOK);
-       if (parameters == NULL) {
-               error = ENOMEM;
-               goto done;
-       }
-       // Copy parameters in
-       error = copyin(uap->parameters, parameters, uap->parameters_size);
-       if (error) {
-               goto done;
-       }
-
-       error = necp_application_find_policy_match_internal(parameters, uap->parameters_size, &returned_result);
-       if (error) {
-               goto done;
-       }
-
-       // Copy return value back
-       error = copyout(&returned_result, uap->returned_result, sizeof(struct necp_aggregate_result));
-       if (error) {
-               goto done;
-       }
-done:
-       if (parameters != NULL) {
-               FREE(parameters, M_NECP);
-       }
-       return (error);
-}
-
 static bool
-necp_socket_check_policy(struct necp_kernel_socket_policy *kernel_policy, necp_app_id app_id, necp_app_id real_app_id, errno_t cred_result, u_int32_t account_id, struct substring domain, u_int8_t domain_dot_count, pid_t pid, uid_t uid, u_int32_t bound_interface_index, u_int32_t traffic_class, u_int16_t protocol, union necp_sockaddr_union *local, union necp_sockaddr_union *remote)
+necp_socket_check_policy(struct necp_kernel_socket_policy *kernel_policy, necp_app_id app_id, necp_app_id real_app_id, errno_t cred_result, u_int32_t account_id, struct substring domain, u_int8_t domain_dot_count, pid_t pid, uid_t uid, u_int32_t bound_interface_index, u_int32_t traffic_class, u_int16_t protocol, union necp_sockaddr_union *local, union necp_sockaddr_union *remote, proc_t proc)
 {
        if (!(kernel_policy->condition_mask & NECP_KERNEL_CONDITION_ALL_INTERFACES)) {
                if (kernel_policy->condition_mask & NECP_KERNEL_CONDITION_BOUND_INTERFACE) {
@@ -4942,7 +6728,7 @@ necp_socket_check_policy(struct necp_kernel_socket_policy *kernel_policy, necp_a
                        }
                }
        }
-       
+
        if (kernel_policy->condition_mask & NECP_KERNEL_CONDITION_ENTITLEMENT) {
                if (cred_result != 0) {
                        // Process is missing entitlement
@@ -4950,6 +6736,29 @@ necp_socket_check_policy(struct necp_kernel_socket_policy *kernel_policy, necp_a
                }
        }
 
+       if (kernel_policy->condition_mask & NECP_KERNEL_CONDITION_CUSTOM_ENTITLEMENT) {
+               if (kernel_policy->cond_custom_entitlement_matched == necp_boolean_state_false) {
+                       // Process is missing entitlement based on previous check
+                       return (FALSE);
+               } else if (kernel_policy->cond_custom_entitlement_matched == necp_boolean_state_unknown) {
+                       if (kernel_policy->cond_custom_entitlement != NULL) {
+                               if (proc == NULL) {
+                                       // No process found, cannot check entitlement
+                                       return (FALSE);
+                               }
+                               task_t task = proc_task(proc);
+                               if (task == NULL ||
+                                       !IOTaskHasEntitlement(task, kernel_policy->cond_custom_entitlement)) {
+                                       // Process is missing custom entitlement
+                                       kernel_policy->cond_custom_entitlement_matched = necp_boolean_state_false;
+                                       return (FALSE);
+                               } else {
+                                       kernel_policy->cond_custom_entitlement_matched = necp_boolean_state_true;
+                               }
+                       }
+               }
+       }
+
        if (kernel_policy->condition_mask & NECP_KERNEL_CONDITION_DOMAIN) {
                bool domain_matches = necp_hostname_matches_domain(domain, domain_dot_count, kernel_policy->cond_domain, kernel_policy->cond_domain_dot_count);
                if (kernel_policy->condition_negated_mask & NECP_KERNEL_CONDITION_DOMAIN) {
@@ -5006,7 +6815,7 @@ necp_socket_check_policy(struct necp_kernel_socket_policy *kernel_policy, necp_a
                        }
                }
        }
-       
+
        if (kernel_policy->condition_mask & NECP_KERNEL_CONDITION_TRAFFIC_CLASS) {
                if (kernel_policy->condition_negated_mask & NECP_KERNEL_CONDITION_TRAFFIC_CLASS) {
                        if (traffic_class >= kernel_policy->cond_traffic_class.start_tc &&
@@ -5114,7 +6923,7 @@ necp_socket_fillout_info_locked(struct inpcb *inp, struct sockaddr *override_loc
        if (necp_kernel_socket_policies_condition_mask & NECP_KERNEL_CONDITION_UID) {
                info->uid = kauth_cred_getuid(so->so_cred);
        }
-       
+
        if (necp_kernel_socket_policies_condition_mask & NECP_KERNEL_CONDITION_TRAFFIC_CLASS) {
                info->traffic_class = so->so_traffic_class;
        }
@@ -5141,7 +6950,7 @@ necp_socket_fillout_info_locked(struct inpcb *inp, struct sockaddr *override_loc
                                info->real_application_id = real_existing_mapping->id;
                        }
                }
-               
+
                if (necp_kernel_socket_policies_condition_mask & NECP_KERNEL_CONDITION_ENTITLEMENT) {
                        info->cred_result = priv_check_cred(so->so_cred, PRIV_NET_PRIVILEGED_NECP_MATCH, 0);
                }
@@ -5169,7 +6978,9 @@ necp_socket_fillout_info_locked(struct inpcb *inp, struct sockaddr *override_loc
        if (necp_kernel_socket_policies_condition_mask & NECP_KERNEL_ADDRESS_TYPE_CONDITIONS) {
                if (inp->inp_vflag & INP_IPV4) {
                        if (override_local_addr) {
-                               memcpy(&info->local_addr, override_local_addr, override_local_addr->sa_len);
+                               if (override_local_addr->sa_len <= sizeof(struct sockaddr_in)) {
+                                       memcpy(&info->local_addr, override_local_addr, override_local_addr->sa_len);
+                               }
                        } else {
                                ((struct sockaddr_in *)&info->local_addr)->sin_family = AF_INET;
                                ((struct sockaddr_in *)&info->local_addr)->sin_len = sizeof(struct sockaddr_in);
@@ -5178,7 +6989,9 @@ necp_socket_fillout_info_locked(struct inpcb *inp, struct sockaddr *override_loc
                        }
 
                        if (override_remote_addr) {
-                               memcpy(&info->remote_addr, override_remote_addr, override_remote_addr->sa_len);
+                               if (override_remote_addr->sa_len <= sizeof(struct sockaddr_in)) {
+                                       memcpy(&info->remote_addr, override_remote_addr, override_remote_addr->sa_len);
+                               }
                        } else {
                                ((struct sockaddr_in *)&info->remote_addr)->sin_family = AF_INET;
                                ((struct sockaddr_in *)&info->remote_addr)->sin_len = sizeof(struct sockaddr_in);
@@ -5187,7 +7000,9 @@ necp_socket_fillout_info_locked(struct inpcb *inp, struct sockaddr *override_loc
                        }
                } else if (inp->inp_vflag & INP_IPV6) {
                        if (override_local_addr) {
-                               memcpy(&info->local_addr, override_local_addr, override_local_addr->sa_len);
+                               if (override_local_addr->sa_len <= sizeof(struct sockaddr_in6)) {
+                                       memcpy(&info->local_addr, override_local_addr, override_local_addr->sa_len);
+                               }
                        } else {
                                ((struct sockaddr_in6 *)&info->local_addr)->sin6_family = AF_INET6;
                                ((struct sockaddr_in6 *)&info->local_addr)->sin6_len = sizeof(struct sockaddr_in6);
@@ -5196,7 +7011,9 @@ necp_socket_fillout_info_locked(struct inpcb *inp, struct sockaddr *override_loc
                        }
 
                        if (override_remote_addr) {
-                               memcpy(&info->remote_addr, override_remote_addr, override_remote_addr->sa_len);
+                               if (override_remote_addr->sa_len <= sizeof(struct sockaddr_in6)) {
+                                       memcpy(&info->remote_addr, override_remote_addr, override_remote_addr->sa_len);
+                               }
                        } else {
                                ((struct sockaddr_in6 *)&info->remote_addr)->sin6_family = AF_INET6;
                                ((struct sockaddr_in6 *)&info->remote_addr)->sin6_len = sizeof(struct sockaddr_in6);
@@ -5208,7 +7025,7 @@ necp_socket_fillout_info_locked(struct inpcb *inp, struct sockaddr *override_loc
 }
 
 static inline struct necp_kernel_socket_policy *
-necp_socket_find_policy_match_with_info_locked(struct necp_kernel_socket_policy **policy_search_array, struct necp_socket_info *info, necp_kernel_policy_filter *return_filter, u_int32_t *return_route_rule_id, necp_kernel_policy_result *return_service_action, necp_kernel_policy_service *return_service, u_int32_t *return_netagent_array, size_t netagent_array_count)
+necp_socket_find_policy_match_with_info_locked(struct necp_kernel_socket_policy **policy_search_array, struct necp_socket_info *info, necp_kernel_policy_filter *return_filter, u_int32_t *return_route_rule_id, necp_kernel_policy_result *return_service_action, necp_kernel_policy_service *return_service, u_int32_t *return_netagent_array, size_t netagent_array_count, proc_t proc)
 {
        struct necp_kernel_socket_policy *matched_policy = NULL;
        u_int32_t skip_order = 0;
@@ -5263,7 +7080,7 @@ necp_socket_find_policy_match_with_info_locked(struct necp_kernel_socket_policy
                                // Skip this policy
                                continue;
                        }
-                       if (necp_socket_check_policy(policy_search_array[i], info->application_id, info->real_application_id, info->cred_result, info->account_id, domain_substring, domain_dot_count, info->pid, info->uid, info->bound_interface_index, info->traffic_class, info->protocol, &info->local_addr, &info->remote_addr)) {
+                       if (necp_socket_check_policy(policy_search_array[i], info->application_id, info->real_application_id, info->cred_result, info->account_id, domain_substring, domain_dot_count, info->pid, info->uid, info->bound_interface_index, info->traffic_class, info->protocol, &info->local_addr, &info->remote_addr, proc)) {
                                if (policy_search_array[i]->result == NECP_KERNEL_POLICY_RESULT_SOCKET_FILTER) {
                                        if (return_filter && *return_filter == 0) {
                                                *return_filter = policy_search_array[i]->result_parameter.filter_control_unit;
@@ -5307,13 +7124,15 @@ necp_socket_find_policy_match_with_info_locked(struct necp_kernel_socket_policy
                                        continue;
                                }
 
-                               // Passed all tests, found a match
-                               matched_policy = policy_search_array[i];
+                               // Matched policy is a skip. Do skip and continue.
                                if (policy_search_array[i]->result == NECP_KERNEL_POLICY_RESULT_SKIP) {
                                        skip_order = policy_search_array[i]->result_parameter.skip_policy_order;
                                        skip_session_order = policy_search_array[i]->session_order + 1;
                                        continue;
                                }
+
+                               // Passed all tests, found a match
+                               matched_policy = policy_search_array[i];
                                break;
                        }
                }
@@ -5337,23 +7156,23 @@ necp_socket_uses_interface(struct inpcb *inp, u_int32_t interface_index)
        int i;
        int family = AF_INET;
        ifnet_t interface = ifindex2ifnet[interface_index];
-       
+
        if (inp == NULL || interface == NULL) {
                return (FALSE);
        }
-       
+
        if (inp->inp_vflag & INP_IPV4) {
                family = AF_INET;
        } else if (inp->inp_vflag & INP_IPV6) {
                family = AF_INET6;
        }
-       
+
        result = ifnet_get_address_list_family(interface, &addresses, family);
        if (result != 0) {
                NECPLOG(LOG_ERR, "Failed to get address list for %s%d", ifnet_name(interface), ifnet_unit(interface));
                return (FALSE);
        }
-       
+
        for (i = 0; addresses[i] != NULL; i++) {
                if (ifaddr_address(addresses[i], &address_storage.sa, sizeof(address_storage)) == 0) {
                        if (family == AF_INET) {
@@ -5369,7 +7188,7 @@ necp_socket_uses_interface(struct inpcb *inp, u_int32_t interface_index)
                        }
                }
        }
-       
+
 done:
        ifnet_free_address_list(addresses);
        addresses = NULL;
@@ -5382,6 +7201,19 @@ necp_socket_is_connected(struct inpcb *inp)
        return (inp->inp_socket->so_state & (SS_ISCONNECTING | SS_ISCONNECTED | SS_ISDISCONNECTING));
 }
 
+static inline bool
+necp_socket_bypass(struct sockaddr *override_local_addr, struct sockaddr *override_remote_addr, struct inpcb *inp)
+{
+
+       if (necp_pass_loopback > 0 && necp_is_loopback(override_local_addr, override_remote_addr, inp, NULL)) {
+               return (true);
+       } else if (necp_is_intcoproc(inp, NULL)) {
+               return (true);
+       }
+
+       return (false);
+}
+
 necp_kernel_policy_id
 necp_socket_find_policy_match(struct inpcb *inp, struct sockaddr *override_local_addr, struct sockaddr *override_remote_addr, u_int32_t override_bound_interface)
 {
@@ -5403,6 +7235,16 @@ necp_socket_find_policy_match(struct inpcb *inp, struct sockaddr *override_local
                return (NECP_KERNEL_POLICY_ID_NONE);
        }
 
+       // Ignore invalid addresses
+       if (override_local_addr != NULL &&
+               !necp_address_is_valid(override_local_addr)) {
+               override_local_addr = NULL;
+       }
+       if (override_remote_addr != NULL &&
+               !necp_address_is_valid(override_remote_addr)) {
+               override_remote_addr = NULL;
+       }
+
        so = inp->inp_socket;
 
        // Don't lock. Possible race condition, but we don't want the performance hit.
@@ -5411,11 +7253,11 @@ necp_socket_find_policy_match(struct inpcb *inp, struct sockaddr *override_local
                if (necp_drop_all_order > 0) {
                        inp->inp_policyresult.policy_id = NECP_KERNEL_POLICY_ID_NO_MATCH;
                        inp->inp_policyresult.policy_gencount = 0;
+                       inp->inp_policyresult.app_id = 0;
                        inp->inp_policyresult.flowhash = 0;
                        inp->inp_policyresult.results.filter_control_unit = 0;
                        inp->inp_policyresult.results.route_rule_id = 0;
-                       if (necp_pass_loopback > 0 &&
-                               necp_is_loopback(override_local_addr, override_remote_addr, inp, NULL)) {
+                       if (necp_socket_bypass(override_local_addr, override_remote_addr, inp)) {
                                inp->inp_policyresult.results.result = NECP_KERNEL_POLICY_RESULT_PASS;
                        } else {
                                inp->inp_policyresult.results.result = NECP_KERNEL_POLICY_RESULT_DROP;
@@ -5423,13 +7265,13 @@ necp_socket_find_policy_match(struct inpcb *inp, struct sockaddr *override_local
                }
                return (NECP_KERNEL_POLICY_ID_NONE);
        }
-       
+
        // Check for loopback exception
-       if (necp_pass_loopback > 0 &&
-               necp_is_loopback(override_local_addr, override_remote_addr, inp, NULL)) {
+       if (necp_socket_bypass(override_local_addr, override_remote_addr, inp)) {
                // Mark socket as a pass
                inp->inp_policyresult.policy_id = NECP_KERNEL_POLICY_ID_NO_MATCH;
                inp->inp_policyresult.policy_gencount = 0;
+               inp->inp_policyresult.app_id = 0;
                inp->inp_policyresult.flowhash = 0;
                inp->inp_policyresult.results.filter_control_unit = 0;
                inp->inp_policyresult.results.route_rule_id = 0;
@@ -5439,8 +7281,9 @@ necp_socket_find_policy_match(struct inpcb *inp, struct sockaddr *override_local
 
        // Lock
        lck_rw_lock_shared(&necp_kernel_policy_lock);
-       
+
        necp_socket_fillout_info_locked(inp, override_local_addr, override_remote_addr, override_bound_interface, &info);
+       inp->inp_policyresult.app_id = info.application_id;
 
        // Check info
        u_int32_t flowhash = necp_socket_calc_flowhash_locked(&info);
@@ -5456,7 +7299,7 @@ necp_socket_find_policy_match(struct inpcb *inp, struct sockaddr *override_local
        }
 
        // Match socket to policy
-       matched_policy = necp_socket_find_policy_match_with_info_locked(necp_kernel_socket_policies_map[NECP_SOCKET_MAP_APP_ID_TO_BUCKET(info.application_id)], &info, &filter_control_unit, &route_rule_id, &service_action, &service, netagent_ids, NECP_MAX_NETAGENTS);
+       matched_policy = necp_socket_find_policy_match_with_info_locked(necp_kernel_socket_policies_map[NECP_SOCKET_MAP_APP_ID_TO_BUCKET(info.application_id)], &info, &filter_control_unit, &route_rule_id, &service_action, &service, netagent_ids, NECP_MAX_NETAGENTS, current_proc());
        // If the socket matched a scoped service policy, mark as Drop if not registered.
        // This covers the cases in which a service is required (on demand) but hasn't started yet.
        if ((service_action == NECP_KERNEL_POLICY_RESULT_TRIGGER_SCOPED ||
@@ -5547,7 +7390,7 @@ necp_socket_find_policy_match(struct inpcb *inp, struct sockaddr *override_local
                        if (necp_debug) {
                                NECPLOG(LOG_DEBUG, "Marking socket in state %d as defunct", so->so_state);
                        }
-                       sosetdefunct(current_proc(), so, SHUTDOWN_SOCKET_LEVEL_DISCONNECT_ALL, TRUE);
+                       sosetdefunct(current_proc(), so, SHUTDOWN_SOCKET_LEVEL_NECP | SHUTDOWN_SOCKET_LEVEL_DISCONNECT_ALL, TRUE);
                } else if (necp_socket_is_connected(inp) &&
                                   matched_policy->result == NECP_KERNEL_POLICY_RESULT_IP_TUNNEL &&
                                   info.protocol == IPPROTO_TCP) {
@@ -5743,6 +7586,21 @@ necp_ip_output_find_policy_match_locked(necp_kernel_policy_id socket_policy_id,
        return (matched_policy);
 }
 
+static inline bool
+necp_output_bypass(struct mbuf *packet)
+{
+       if (necp_pass_loopback > 0 && necp_is_loopback(NULL, NULL, NULL, packet)) {
+               return (true);
+       }
+       if (necp_pass_keepalives > 0 && necp_get_is_keepalive_from_packet(packet)) {
+               return (true);
+       }
+       if (necp_is_intcoproc(NULL, packet)) {
+               return (true);
+       }
+       return (false);
+}
+
 necp_kernel_policy_id
 necp_ip_output_find_policy_match(struct mbuf *packet, int flags, struct ip_out_args *ipoa, necp_kernel_policy_result *result, necp_kernel_policy_result_parameter *result_parameter)
 {
@@ -5768,7 +7626,7 @@ necp_ip_output_find_policy_match(struct mbuf *packet, int flags, struct ip_out_a
        if (packet == NULL) {
                return (NECP_KERNEL_POLICY_ID_NONE);
        }
-       
+
        socket_policy_id = necp_get_policy_id_from_packet(packet);
 
        // Exit early for an empty list
@@ -5778,10 +7636,7 @@ necp_ip_output_find_policy_match(struct mbuf *packet, int flags, struct ip_out_a
                if (necp_drop_all_order > 0) {
                        matched_policy_id = NECP_KERNEL_POLICY_ID_NO_MATCH;
                        if (result) {
-                               if ((necp_pass_loopback > 0 &&
-                                        necp_is_loopback(NULL, NULL, NULL, packet)) ||
-                                       (necp_pass_keepalives > 0 &&
-                                        necp_get_is_keepalive_from_packet(packet))) {
+                               if (necp_output_bypass(packet)) {
                                        *result = NECP_KERNEL_POLICY_RESULT_PASS;
                                } else {
                                        *result = NECP_KERNEL_POLICY_RESULT_DROP;
@@ -5791,19 +7646,16 @@ necp_ip_output_find_policy_match(struct mbuf *packet, int flags, struct ip_out_a
 
                return (matched_policy_id);
        }
-       
+
        // Check for loopback exception
-       if ((necp_pass_loopback > 0 &&
-                necp_is_loopback(NULL, NULL, NULL, packet)) ||
-               (necp_pass_keepalives > 0 &&
-                necp_get_is_keepalive_from_packet(packet))) {
+       if (necp_output_bypass(packet)) {
                matched_policy_id = NECP_KERNEL_POLICY_ID_NO_MATCH;
                if (result) {
                        *result = NECP_KERNEL_POLICY_RESULT_PASS;
                }
                return (matched_policy_id);
        }
-       
+
        last_interface_index = necp_get_last_interface_index_from_packet(packet);
 
        // Process packet to get relevant fields
@@ -5912,7 +7764,7 @@ necp_ip6_output_find_policy_match(struct mbuf *packet, int flags, struct ip6_out
        }
 
        socket_policy_id = necp_get_policy_id_from_packet(packet);
-       
+
        // Exit early for an empty list
        // Don't lock. Possible race condition, but we don't want the performance hit.
        if (necp_kernel_ip_output_policies_count == 0 ||
@@ -5920,10 +7772,7 @@ necp_ip6_output_find_policy_match(struct mbuf *packet, int flags, struct ip6_out
                if (necp_drop_all_order > 0) {
                        matched_policy_id = NECP_KERNEL_POLICY_ID_NO_MATCH;
                        if (result) {
-                               if ((necp_pass_loopback > 0 &&
-                                        necp_is_loopback(NULL, NULL, NULL, packet)) ||
-                                       (necp_pass_keepalives > 0 &&
-                                        necp_get_is_keepalive_from_packet(packet))) {
+                               if (necp_output_bypass(packet)) {
                                        *result = NECP_KERNEL_POLICY_RESULT_PASS;
                                } else {
                                        *result = NECP_KERNEL_POLICY_RESULT_DROP;
@@ -5933,19 +7782,16 @@ necp_ip6_output_find_policy_match(struct mbuf *packet, int flags, struct ip6_out
 
                return (matched_policy_id);
        }
-       
+
        // Check for loopback exception
-       if ((necp_pass_loopback > 0 &&
-                necp_is_loopback(NULL, NULL, NULL, packet)) ||
-               (necp_pass_keepalives > 0 &&
-                necp_get_is_keepalive_from_packet(packet))) {
+       if (necp_output_bypass(packet)) {
                matched_policy_id = NECP_KERNEL_POLICY_ID_NO_MATCH;
                if (result) {
                        *result = NECP_KERNEL_POLICY_RESULT_PASS;
                }
                return (matched_policy_id);
        }
-       
+
        last_interface_index = necp_get_last_interface_index_from_packet(packet);
 
        // Process packet to get relevant fields
@@ -6224,164 +8070,132 @@ necp_buffer_compare_with_bit_prefix(u_int8_t *p1, u_int8_t *p2, u_int32_t bits)
        return (TRUE);
 }
 
-// Socket operations
-#define NECP_MAX_SOCKET_ATTRIBUTE_STRING_LENGTH 253
-
 static bool
-necp_set_socket_attribute(u_int8_t *buffer, size_t buffer_length, u_int8_t type, char **buffer_p)
+necp_update_qos_marking(struct ifnet *ifp, u_int32_t route_rule_id)
 {
-       int error = 0;
-       int cursor = 0;
-       size_t string_size = 0;
-       char *local_string = NULL;
-       u_int8_t *value = NULL;
+       bool qos_marking = FALSE;
+       int exception_index = 0;
+       struct necp_route_rule *route_rule = NULL;
 
-       cursor = necp_buffer_find_tlv(buffer, buffer_length, 0, type, 0);
-       if (cursor < 0) {
-               // This will clear out the parameter
+       route_rule = necp_lookup_route_rule_locked(&necp_route_rules, route_rule_id);
+       if (route_rule == NULL) {
+               qos_marking = FALSE;
                goto done;
        }
 
-       string_size = necp_buffer_get_tlv_length(buffer, cursor);
-       if (string_size == 0 || string_size > NECP_MAX_SOCKET_ATTRIBUTE_STRING_LENGTH) {
-               // This will clear out the parameter
+       qos_marking = (route_rule->default_action == NECP_ROUTE_RULE_QOS_MARKING) ? TRUE : FALSE;
+
+       if (ifp == NULL) {
                goto done;
        }
 
-       MALLOC(local_string, char *, string_size + 1, M_NECP, M_WAITOK);
-       if (local_string == NULL) {
-               NECPLOG(LOG_ERR, "Failed to allocate a socket attribute buffer (size %d)", string_size);
-               goto fail;
+       for (exception_index = 0; exception_index < MAX_ROUTE_RULE_INTERFACES; exception_index++) {
+               if (route_rule->exception_if_indices[exception_index] == 0) {
+                       break;
+               }
+               if (route_rule->exception_if_actions[exception_index] != NECP_ROUTE_RULE_QOS_MARKING) {
+                       continue;
+               }
+               if (route_rule->exception_if_indices[exception_index] == ifp->if_index) {
+                       qos_marking = TRUE;
+                       if (necp_debug > 2) {
+                               NECPLOG(LOG_DEBUG, "QoS Marking : Interface match %d for Rule %d Allowed %d",
+                                   route_rule->exception_if_indices[exception_index], route_rule_id, qos_marking);
+                       }
+                       goto done;
+               }
        }
 
-       value = necp_buffer_get_tlv_value(buffer, cursor, NULL);
-       if (value == NULL) {
-               NECPLOG0(LOG_ERR, "Failed to get socket attribute");
-               goto fail;
+       if ((route_rule->cellular_action == NECP_ROUTE_RULE_QOS_MARKING && IFNET_IS_CELLULAR(ifp)) ||
+           (route_rule->wifi_action == NECP_ROUTE_RULE_QOS_MARKING && IFNET_IS_WIFI(ifp)) ||
+           (route_rule->wired_action == NECP_ROUTE_RULE_QOS_MARKING && IFNET_IS_WIRED(ifp)) ||
+           (route_rule->expensive_action == NECP_ROUTE_RULE_QOS_MARKING && IFNET_IS_EXPENSIVE(ifp))) {
+               qos_marking = TRUE;
+               if (necp_debug > 2) {
+                       NECPLOG(LOG_DEBUG, "QoS Marking: C:%d WF:%d W:%d E:%d for Rule %d Allowed %d",
+                           route_rule->cellular_action, route_rule->wifi_action, route_rule->wired_action,
+                           route_rule->expensive_action, route_rule_id, qos_marking);
+               }
+               goto done;
        }
-
-       memcpy(local_string, value, string_size);
-       local_string[string_size] = 0;
-
 done:
-       if (*buffer_p != NULL) {
-               FREE(*buffer_p, M_NECP);
-               *buffer_p = NULL;
-       }
-
-       *buffer_p = local_string;
-       return (0);
-fail:
-       if (local_string != NULL) {
-               FREE(local_string, M_NECP);
+       if (necp_debug > 1) {
+               NECPLOG(LOG_DEBUG, "QoS Marking: Rule %d ifp %s Allowed %d",
+                   route_rule_id, ifp ? ifp->if_xname : "", qos_marking);
        }
-       return (error);
+       return (qos_marking);
 }
 
-errno_t
-necp_set_socket_attributes(struct socket *so, struct sockopt *sopt)
+void
+necp_socket_update_qos_marking(struct inpcb *inp, struct rtentry *route, struct ifnet *interface, u_int32_t route_rule_id)
 {
-       int error = 0;
-       u_int8_t *buffer = NULL;
-       struct inpcb *inp = NULL;
-    
-    if ((SOCK_DOM(so) != PF_INET
-#if INET6
-         && SOCK_DOM(so) != PF_INET6
-#endif
-         )) {
-        error = EINVAL;
-        goto done;
-    }
-    
-       inp = sotoinpcb(so);
-
-       size_t valsize = sopt->sopt_valsize;
-       if (valsize == 0 ||
-               valsize > ((sizeof(u_int8_t) + sizeof(u_int32_t) + NECP_MAX_SOCKET_ATTRIBUTE_STRING_LENGTH) * 2)) {
-               goto done;
-       }
-
-       MALLOC(buffer, u_int8_t *, valsize, M_NECP, M_WAITOK);
-       if (buffer == NULL) {
-               goto done;
-       }
-
-       error = sooptcopyin(sopt, buffer, valsize, 0);
-       if (error) {
-               goto done;
-       }
+       bool qos_marking = FALSE;
+       struct ifnet *ifp = interface = NULL;
 
-       error = necp_set_socket_attribute(buffer, valsize, NECP_TLV_ATTRIBUTE_DOMAIN, &inp->inp_necp_attributes.inp_domain);
-       if (error) {
-               NECPLOG0(LOG_ERR, "Could not set domain TLV for socket attributes");
-               goto done;
+       if (net_qos_policy_restricted == 0) {
+               return;
        }
-
-       error = necp_set_socket_attribute(buffer, valsize, NECP_TLV_ATTRIBUTE_ACCOUNT, &inp->inp_necp_attributes.inp_account);
-       if (error) {
-               NECPLOG0(LOG_ERR, "Could not set account TLV for socket attributes");
-               goto done;
+       if (inp->inp_socket == NULL) {
+               return;
        }
-
-       if (necp_debug) {
-               NECPLOG(LOG_DEBUG, "Set on socket: Domain %s, Account %s", inp->inp_necp_attributes.inp_domain, inp->inp_necp_attributes.inp_account);
+       if ((inp->inp_socket->so_flags1 & SOF1_QOSMARKING_POLICY_OVERRIDE)) {
+               return;
        }
-done:
-       if (buffer != NULL) {
-               FREE(buffer, M_NECP);
+       /*
+        * This is racy but we do not need the performance hit of taking necp_kernel_policy_lock
+        */
+       if (inp->inp_policyresult.results.qos_marking_gencount == necp_kernel_socket_policies_gencount) {
+               return;
        }
 
-       return (error);
-}
+       lck_rw_lock_shared(&necp_kernel_policy_lock);
 
-errno_t
-necp_get_socket_attributes(struct socket *so, struct sockopt *sopt)
-{
-       int error = 0;
-       u_int8_t *buffer = NULL;
-       u_int8_t *cursor = NULL;
-       size_t valsize = 0;
-       struct inpcb *inp = sotoinpcb(so);
-       
-       if (inp->inp_necp_attributes.inp_domain != NULL) {
-               valsize += sizeof(u_int8_t) + sizeof(u_int32_t) + strlen(inp->inp_necp_attributes.inp_domain);
-       }
-       if (inp->inp_necp_attributes.inp_account != NULL) {
-               valsize += sizeof(u_int8_t) + sizeof(u_int32_t) + strlen(inp->inp_necp_attributes.inp_account);
-       }
-       if (valsize == 0) {
-               goto done;
+       if (ifp == NULL && route != NULL) {
+               ifp = route->rt_ifp;
        }
-
-       MALLOC(buffer, u_int8_t *, valsize, M_NECP, M_WAITOK);
-       if (buffer == NULL) {
+       /*
+        * By default, until we have a interface, do not mark and reevaluate the Qos marking policy
+        */
+       if (ifp == NULL || route_rule_id == 0) {
+               qos_marking = FALSE;
                goto done;
        }
 
-       cursor = buffer;
-       if (inp->inp_necp_attributes.inp_domain != NULL) {
-               cursor = necp_buffer_write_tlv(cursor, NECP_TLV_ATTRIBUTE_DOMAIN, strlen(inp->inp_necp_attributes.inp_domain), inp->inp_necp_attributes.inp_domain);
-       }
-
-       if (inp->inp_necp_attributes.inp_account != NULL) {
-               cursor = necp_buffer_write_tlv(cursor, NECP_TLV_ATTRIBUTE_ACCOUNT, strlen(inp->inp_necp_attributes.inp_account), inp->inp_necp_attributes.inp_account);
+       if (ROUTE_RULE_IS_AGGREGATE(route_rule_id)) {
+               struct necp_aggregate_route_rule *aggregate_route_rule = necp_lookup_aggregate_route_rule_locked(route_rule_id);
+               if (aggregate_route_rule != NULL) {
+                       int index = 0;
+                       for (index = 0; index < MAX_AGGREGATE_ROUTE_RULES; index++) {
+                               u_int32_t sub_route_rule_id = aggregate_route_rule->rule_ids[index];
+                               if (sub_route_rule_id == 0) {
+                                       break;
+                               }
+                               qos_marking = necp_update_qos_marking(ifp, sub_route_rule_id);
+                               if (qos_marking == TRUE) {
+                                       break;
+                               }
+                       }
+               }
+       } else {
+               qos_marking = necp_update_qos_marking(ifp, route_rule_id);
        }
+       /*
+        * Now that we have an interface we remember the gencount
+        */
+       inp->inp_policyresult.results.qos_marking_gencount = necp_kernel_socket_policies_gencount;
 
-       error = sooptcopyout(sopt, buffer, valsize);
-       if (error) {
-               goto done;
-       }
 done:
-       if (buffer != NULL) {
-               FREE(buffer, M_NECP);
-       }
+       lck_rw_done(&necp_kernel_policy_lock);
 
-       return (error);
+       if (qos_marking == TRUE) {
+               inp->inp_socket->so_flags1 |= SOF1_QOSMARKING_ALLOWED;
+       } else {
+               inp->inp_socket->so_flags1 &= ~SOF1_QOSMARKING_ALLOWED;
+       }
 }
 
 static bool
-necp_route_is_allowed_inner(struct rtentry *route, struct ifnet *ifp, u_int32_t route_rule_id, bool *cellular_denied)
+necp_route_is_allowed_inner(struct rtentry *route, struct ifnet *ifp, u_int32_t route_rule_id, u_int32_t *interface_type_denied)
 {
        bool default_is_allowed = TRUE;
        u_int8_t type_aggregate_action = NECP_ROUTE_RULE_NONE;
@@ -6410,6 +8224,9 @@ necp_route_is_allowed_inner(struct rtentry *route, struct ifnet *ifp, u_int32_t
                if (route_rule->exception_if_indices[exception_index] == 0) {
                        break;
                }
+               if (IS_NECP_ROUTE_RULE_ALLOW_OR_DENY(route_rule->exception_if_actions[exception_index]) == FALSE) {
+                       continue;
+               }
                if (route_rule->exception_if_indices[exception_index] == ifp->if_index ||
                        (delegated_ifp != NULL && route_rule->exception_if_indices[exception_index] == delegated_ifp->if_index)) {
                        if (necp_debug > 1) {
@@ -6419,11 +8236,10 @@ necp_route_is_allowed_inner(struct rtentry *route, struct ifnet *ifp, u_int32_t
                }
        }
 
-       if (route_rule->cellular_action != NECP_ROUTE_RULE_NONE &&
+       if (IS_NECP_ROUTE_RULE_ALLOW_OR_DENY(route_rule->cellular_action) &&
                IFNET_IS_CELLULAR(ifp)) {
-               if (cellular_denied != NULL) {
-                       // Let clients know that cellular was blocked
-                       *cellular_denied = TRUE;
+               if (interface_type_denied != NULL) {
+                       *interface_type_denied = IFRTYPE_FUNCTIONAL_CELLULAR;
                }
                if (type_aggregate_action == NECP_ROUTE_RULE_NONE ||
                        (type_aggregate_action == NECP_ROUTE_RULE_ALLOW_INTERFACE &&
@@ -6433,8 +8249,11 @@ necp_route_is_allowed_inner(struct rtentry *route, struct ifnet *ifp, u_int32_t
                        }
        }
 
-       if (route_rule->wifi_action != NECP_ROUTE_RULE_NONE &&
+       if (IS_NECP_ROUTE_RULE_ALLOW_OR_DENY(route_rule->wifi_action) &&
                IFNET_IS_WIFI(ifp)) {
+               if (interface_type_denied != NULL) {
+                       *interface_type_denied = IFRTYPE_FUNCTIONAL_WIFI_INFRA;
+               }
                if (type_aggregate_action == NECP_ROUTE_RULE_NONE ||
                        (type_aggregate_action == NECP_ROUTE_RULE_ALLOW_INTERFACE &&
                         route_rule->wifi_action == NECP_ROUTE_RULE_DENY_INTERFACE)) {
@@ -6443,8 +8262,11 @@ necp_route_is_allowed_inner(struct rtentry *route, struct ifnet *ifp, u_int32_t
                        }
        }
 
-       if (route_rule->wired_action != NECP_ROUTE_RULE_NONE &&
+       if (IS_NECP_ROUTE_RULE_ALLOW_OR_DENY(route_rule->wired_action) &&
                IFNET_IS_WIRED(ifp)) {
+               if (interface_type_denied != NULL) {
+                       *interface_type_denied = IFRTYPE_FUNCTIONAL_WIRED;
+               }
                if (type_aggregate_action == NECP_ROUTE_RULE_NONE ||
                        (type_aggregate_action == NECP_ROUTE_RULE_ALLOW_INTERFACE &&
                         route_rule->wired_action == NECP_ROUTE_RULE_DENY_INTERFACE)) {
@@ -6453,7 +8275,7 @@ necp_route_is_allowed_inner(struct rtentry *route, struct ifnet *ifp, u_int32_t
                        }
        }
 
-       if (route_rule->expensive_action != NECP_ROUTE_RULE_NONE &&
+       if (IS_NECP_ROUTE_RULE_ALLOW_OR_DENY(route_rule->expensive_action) &&
                IFNET_IS_EXPENSIVE(ifp)) {
                if (type_aggregate_action == NECP_ROUTE_RULE_NONE ||
                        (type_aggregate_action == NECP_ROUTE_RULE_ALLOW_INTERFACE &&
@@ -6477,7 +8299,7 @@ necp_route_is_allowed_inner(struct rtentry *route, struct ifnet *ifp, u_int32_t
 }
 
 static bool
-necp_route_is_allowed(struct rtentry *route, struct ifnet *interface, u_int32_t route_rule_id, bool *cellular_denied)
+necp_route_is_allowed(struct rtentry *route, struct ifnet *interface, u_int32_t route_rule_id, u_int32_t *interface_type_denied)
 {
        if ((route == NULL && interface == NULL) || route_rule_id == 0) {
                if (necp_debug > 1) {
@@ -6495,13 +8317,13 @@ necp_route_is_allowed(struct rtentry *route, struct ifnet *interface, u_int32_t
                                if (sub_route_rule_id == 0) {
                                        break;
                                }
-                               if (!necp_route_is_allowed_inner(route, interface, sub_route_rule_id, cellular_denied)) {
+                               if (!necp_route_is_allowed_inner(route, interface, sub_route_rule_id, interface_type_denied)) {
                                        return (FALSE);
                                }
                        }
                }
        } else {
-               return (necp_route_is_allowed_inner(route, interface, route_rule_id, cellular_denied));
+               return (necp_route_is_allowed_inner(route, interface, route_rule_id, interface_type_denied));
        }
 
        return (TRUE);
@@ -6558,7 +8380,7 @@ necp_socket_is_allowed_to_send_recv_internal(struct inpcb *inp, struct sockaddr
        necp_kernel_policy_service service = { 0, 0 };
        u_int32_t route_rule_id = 0;
        struct rtentry *route = NULL;
-       bool cellular_denied = FALSE;
+       u_int32_t interface_type_denied = IFRTYPE_FUNCTIONAL_UNKNOWN;
 
        u_int32_t netagent_ids[NECP_MAX_NETAGENTS];
        memset(&netagent_ids, 0, sizeof(netagent_ids));
@@ -6580,8 +8402,7 @@ necp_socket_is_allowed_to_send_recv_internal(struct inpcb *inp, struct sockaddr
        if (necp_kernel_socket_policies_count == 0 ||
                (!(inp->inp_flags2 & INP2_WANT_APP_POLICY) && necp_kernel_socket_policies_non_app_count == 0)) {
                if (necp_drop_all_order > 0) {
-                       if (necp_pass_loopback > 0 &&
-                               necp_is_loopback(override_local_addr, override_remote_addr, inp, NULL)) {
+                       if (necp_socket_bypass(override_local_addr, override_remote_addr, inp)) {
                                allowed_to_receive = TRUE;
                        } else {
                                allowed_to_receive = FALSE;
@@ -6594,16 +8415,18 @@ necp_socket_is_allowed_to_send_recv_internal(struct inpcb *inp, struct sockaddr
        if ((necp_socket_is_connected(inp) || (override_local_addr == NULL && override_remote_addr == NULL)) && inp->inp_policyresult.policy_id != NECP_KERNEL_POLICY_ID_NONE) {
                bool policies_have_changed = FALSE;
                bool route_allowed = TRUE;
-               lck_rw_lock_shared(&necp_kernel_policy_lock);
+
                if (inp->inp_policyresult.policy_gencount != necp_kernel_socket_policies_gencount) {
                        policies_have_changed = TRUE;
                } else {
-                       if (inp->inp_policyresult.results.route_rule_id != 0 &&
-                               !necp_route_is_allowed(route, interface, inp->inp_policyresult.results.route_rule_id, &cellular_denied)) {
-                               route_allowed = FALSE;
+                       if (inp->inp_policyresult.results.route_rule_id != 0) {
+                               lck_rw_lock_shared(&necp_kernel_policy_lock);
+                               if (!necp_route_is_allowed(route, interface, inp->inp_policyresult.results.route_rule_id, &interface_type_denied)) {
+                                       route_allowed = FALSE;
+                               }
+                               lck_rw_done(&necp_kernel_policy_lock);
                        }
                }
-               lck_rw_done(&necp_kernel_policy_lock);
 
                if (!policies_have_changed) {
                        if (!route_allowed ||
@@ -6625,8 +8448,7 @@ necp_socket_is_allowed_to_send_recv_internal(struct inpcb *inp, struct sockaddr
        }
 
        // Check for loopback exception
-       if (necp_pass_loopback > 0 &&
-               necp_is_loopback(override_local_addr, override_remote_addr, inp, NULL)) {
+       if (necp_socket_bypass(override_local_addr, override_remote_addr, inp)) {
                allowed_to_receive = TRUE;
                goto done;
        }
@@ -6644,7 +8466,7 @@ necp_socket_is_allowed_to_send_recv_internal(struct inpcb *inp, struct sockaddr
                        (inp->inp_policyresult.results.result == NECP_KERNEL_POLICY_RESULT_IP_TUNNEL && interface &&
                        inp->inp_policyresult.results.result_parameter.tunnel_interface_index != verifyifindex) ||
                        (inp->inp_policyresult.results.route_rule_id != 0 &&
-                        !necp_route_is_allowed(route, interface, inp->inp_policyresult.results.route_rule_id, &cellular_denied))) {
+                        !necp_route_is_allowed(route, interface, inp->inp_policyresult.results.route_rule_id, &interface_type_denied))) {
                        allowed_to_receive = FALSE;
                } else {
                        if (return_policy_id) {
@@ -6658,7 +8480,7 @@ necp_socket_is_allowed_to_send_recv_internal(struct inpcb *inp, struct sockaddr
                goto done;
        }
 
-       struct necp_kernel_socket_policy *matched_policy = necp_socket_find_policy_match_with_info_locked(necp_kernel_socket_policies_map[NECP_SOCKET_MAP_APP_ID_TO_BUCKET(info.application_id)], &info, NULL, &route_rule_id, &service_action, &service, netagent_ids, NECP_MAX_NETAGENTS);
+       struct necp_kernel_socket_policy *matched_policy = necp_socket_find_policy_match_with_info_locked(necp_kernel_socket_policies_map[NECP_SOCKET_MAP_APP_ID_TO_BUCKET(info.application_id)], &info, NULL, &route_rule_id, &service_action, &service, netagent_ids, NECP_MAX_NETAGENTS, current_proc());
        if (matched_policy != NULL) {
                if (matched_policy->result == NECP_KERNEL_POLICY_RESULT_DROP ||
                        matched_policy->result == NECP_KERNEL_POLICY_RESULT_SOCKET_DIVERT ||
@@ -6668,7 +8490,7 @@ necp_socket_is_allowed_to_send_recv_internal(struct inpcb *inp, struct sockaddr
                          service_action == NECP_KERNEL_POLICY_RESULT_NO_TRIGGER_SCOPED) &&
                         service.identifier != 0 && service.identifier != NECP_NULL_SERVICE_ID) ||
                        (route_rule_id != 0 &&
-                        !necp_route_is_allowed(route, interface, route_rule_id, &cellular_denied)) ||
+                        !necp_route_is_allowed(route, interface, route_rule_id, &interface_type_denied)) ||
                        !necp_netagents_allow_traffic(netagent_ids, NECP_MAX_NETAGENTS)) {
                        allowed_to_receive = FALSE;
                } else {
@@ -6687,12 +8509,19 @@ necp_socket_is_allowed_to_send_recv_internal(struct inpcb *inp, struct sockaddr
                goto done;
        } else if (necp_drop_all_order > 0) {
                allowed_to_receive = FALSE;
+       } else {
+               if (return_policy_id) {
+                       *return_policy_id = NECP_KERNEL_POLICY_ID_NO_MATCH;
+               }
+               if (return_route_rule_id) {
+                       *return_route_rule_id = route_rule_id;
+               }
        }
 
        lck_rw_done(&necp_kernel_policy_lock);
 
 done:
-       if (!allowed_to_receive && cellular_denied) {
+       if (!allowed_to_receive && interface_type_denied != IFRTYPE_FUNCTIONAL_UNKNOWN) {
                soevent(inp->inp_socket, (SO_FILT_HINT_LOCKED | SO_FILT_HINT_IFDENIED));
        }
 
@@ -6738,7 +8567,7 @@ necp_socket_is_allowed_to_send_recv(struct inpcb *inp, necp_kernel_policy_id *re
 int
 necp_mark_packet_from_socket(struct mbuf *packet, struct inpcb *inp, necp_kernel_policy_id policy_id, u_int32_t route_rule_id)
 {
-       if (packet == NULL || inp == NULL) {
+       if (packet == NULL || inp == NULL || !(packet->m_flags & M_PKTHDR)) {
                return (EINVAL);
        }
 
@@ -6757,6 +8586,7 @@ necp_mark_packet_from_socket(struct mbuf *packet, struct inpcb *inp, necp_kernel
        } else {
                packet->m_pkthdr.necp_mtag.necp_route_rule_id = inp->inp_policyresult.results.route_rule_id;
        }
+       packet->m_pkthdr.necp_mtag.necp_app_id = inp->inp_policyresult.app_id;
 
        return (0);
 }
@@ -6764,7 +8594,7 @@ necp_mark_packet_from_socket(struct mbuf *packet, struct inpcb *inp, necp_kernel
 int
 necp_mark_packet_from_ip(struct mbuf *packet, necp_kernel_policy_id policy_id)
 {
-       if (packet == NULL) {
+       if (packet == NULL || !(packet->m_flags & M_PKTHDR)) {
                return (EINVAL);
        }
 
@@ -6781,7 +8611,7 @@ necp_mark_packet_from_ip(struct mbuf *packet, necp_kernel_policy_id policy_id)
 int
 necp_mark_packet_from_interface(struct mbuf *packet, ifnet_t interface)
 {
-       if (packet == NULL) {
+       if (packet == NULL || !(packet->m_flags & M_PKTHDR)) {
                return (EINVAL);
        }
 
@@ -6796,33 +8626,33 @@ necp_mark_packet_from_interface(struct mbuf *packet, ifnet_t interface)
 int
 necp_mark_packet_as_keepalive(struct mbuf *packet, bool is_keepalive)
 {
-       if (packet == NULL) {
+       if (packet == NULL || !(packet->m_flags & M_PKTHDR)) {
                return (EINVAL);
        }
-       
+
        if (is_keepalive) {
                packet->m_pkthdr.pkt_flags |= PKTF_KEEPALIVE;
        } else {
                packet->m_pkthdr.pkt_flags &= ~PKTF_KEEPALIVE;
        }
-       
+
        return (0);
 }
 
 necp_kernel_policy_id
 necp_get_policy_id_from_packet(struct mbuf *packet)
 {
-       if (packet == NULL) {
+       if (packet == NULL || !(packet->m_flags & M_PKTHDR)) {
                return (NECP_KERNEL_POLICY_ID_NONE);
        }
-       
+
        return (packet->m_pkthdr.necp_mtag.necp_policy_id);
 }
 
 u_int32_t
 necp_get_last_interface_index_from_packet(struct mbuf *packet)
 {
-       if (packet == NULL) {
+       if (packet == NULL || !(packet->m_flags & M_PKTHDR)) {
                return (0);
        }
 
@@ -6832,20 +8662,44 @@ necp_get_last_interface_index_from_packet(struct mbuf *packet)
 u_int32_t
 necp_get_route_rule_id_from_packet(struct mbuf *packet)
 {
-       if (packet == NULL) {
+       if (packet == NULL || !(packet->m_flags & M_PKTHDR)) {
                return (0);
        }
 
        return (packet->m_pkthdr.necp_mtag.necp_route_rule_id);
 }
 
+int
+necp_get_app_uuid_from_packet(struct mbuf *packet,
+                                                         uuid_t app_uuid)
+{
+       if (packet == NULL || !(packet->m_flags & M_PKTHDR)) {
+               return (EINVAL);
+       }
+
+       bool found_mapping = FALSE;
+       if (packet->m_pkthdr.necp_mtag.necp_app_id != 0) {
+               lck_rw_lock_shared(&necp_kernel_policy_lock);
+               struct necp_uuid_id_mapping *entry = necp_uuid_lookup_uuid_with_app_id_locked(packet->m_pkthdr.necp_mtag.necp_app_id);
+               if (entry != NULL) {
+                       uuid_copy(app_uuid, entry->uuid);
+                       found_mapping = true;
+               }
+               lck_rw_done(&necp_kernel_policy_lock);
+       }
+       if (!found_mapping) {
+               uuid_clear(app_uuid);
+       }
+       return (0);
+}
+
 bool
 necp_get_is_keepalive_from_packet(struct mbuf *packet)
 {
-       if (packet == NULL) {
+       if (packet == NULL || !(packet->m_flags & M_PKTHDR)) {
                return (FALSE);
        }
-       
+
        return (packet->m_pkthdr.pkt_flags & PKTF_KEEPALIVE);
 }
 
@@ -6853,7 +8707,7 @@ u_int32_t
 necp_socket_get_content_filter_control_unit(struct socket *so)
 {
        struct inpcb *inp = sotoinpcb(so);
-       
+
        if (inp == NULL) {
                return (0);
        }
@@ -6890,7 +8744,7 @@ necp_socket_should_rescope(struct inpcb *inp)
        if (inp == NULL) {
                return (FALSE);
        }
-       
+
        return (inp->inp_policyresult.results.result == NECP_KERNEL_POLICY_RESULT_SOCKET_SCOPED);
 }
 
@@ -6900,11 +8754,11 @@ necp_socket_get_rescope_if_index(struct inpcb *inp)
        if (inp == NULL) {
                return (0);
        }
-       
+
        if (inp->inp_policyresult.results.result == NECP_KERNEL_POLICY_RESULT_SOCKET_SCOPED) {
                return (inp->inp_policyresult.results.result_parameter.scoped_interface_index);
        }
-       
+
        return (0);
 }
 
@@ -7034,13 +8888,13 @@ necp_addr_is_loopback(struct sockaddr *address)
        if (address == NULL) {
                return (FALSE);
        }
-       
+
        if (address->sa_family == AF_INET) {
                return (ntohl(((struct sockaddr_in *)(void *)address)->sin_addr.s_addr) == INADDR_LOOPBACK);
        } else if (address->sa_family == AF_INET6) {
                return IN6_IS_ADDR_LOOPBACK(&((struct sockaddr_in6 *)(void *)address)->sin6_addr);
        }
-       
+
        return (FALSE);
 }
 
@@ -7055,11 +8909,11 @@ necp_is_loopback(struct sockaddr *local_addr, struct sockaddr *remote_addr, stru
        if (local_addr != NULL && necp_addr_is_loopback(local_addr)) {
                return (TRUE);
        }
-       
+
        if (remote_addr != NULL && necp_addr_is_loopback(remote_addr)) {
                return (TRUE);
        }
-       
+
        if (inp != NULL) {
                if ((inp->inp_flags & INP_BOUND_IF) && inp->inp_boundifp && (inp->inp_boundifp->if_flags & IFF_LOOPBACK)) {
                        return (TRUE);
@@ -7076,7 +8930,7 @@ necp_is_loopback(struct sockaddr *local_addr, struct sockaddr *remote_addr, stru
                        }
                }
        }
-       
+
        if (packet != NULL) {
                struct ip *ip = mtod(packet, struct ip *);
                if (ip->ip_v == 4) {
@@ -7096,6 +8950,26 @@ necp_is_loopback(struct sockaddr *local_addr, struct sockaddr *remote_addr, stru
                        }
                }
        }
-       
+
        return (FALSE);
 }
+
+static bool
+necp_is_intcoproc(struct inpcb *inp, struct mbuf *packet)
+{
+
+       if (inp != NULL) {
+               return (sflt_permission_check(inp) ? true : false);
+       }
+       if (packet != NULL) {
+               struct ip6_hdr *ip6 = mtod(packet, struct ip6_hdr *);
+               if ((ip6->ip6_vfc & IPV6_VERSION_MASK) == IPV6_VERSION &&
+                   IN6_IS_ADDR_LINKLOCAL(&ip6->ip6_dst) &&
+                   ip6->ip6_dst.s6_addr32[2] == ntohl(0xaede48ff) &&
+                   ip6->ip6_dst.s6_addr32[3] == ntohl(0xfe334455)) {
+                       return (true);
+               }
+       }
+
+       return (false);
+}