+ if ((parsed_parameters.flags & NECP_CLIENT_PARAMETER_FLAG_ECN_DISABLE) ==
+ NECP_CLIENT_PARAMETER_FLAG_ECN_DISABLE) {
+ break;
+ }
+
+ if (client->current_route != NULL) {
+ if (client->current_route->rt_ifp->if_eflags & IFEF_ECN_ENABLE) {
+ check_ecn = true;
+ break;
+ }
+ if (client->current_route->rt_ifp->if_eflags & IFEF_ECN_DISABLE) {
+ break;
+ }
+ }
+
+ bool inbound = ((parsed_parameters.flags & NECP_CLIENT_PARAMETER_FLAG_LISTENER) == 0);
+ if ((inbound && tcp_ecn_inbound == 1) ||
+ (!inbound && tcp_ecn_outbound == 1)) {
+ check_ecn = true;
+ }
+ } while (false);
+
+ if (check_ecn) {
+ if (tcp_heuristic_do_ecn_with_address(client->current_route->rt_ifp,
+ (union sockaddr_in_4_6 *)&flow->local_addr)) {
+ *flags |= NECP_CLIENT_RESULT_FLAG_ECN_ENABLED;
+ }
+ }
+
+ if ((parsed_parameters.flags & NECP_CLIENT_PARAMETER_FLAG_TFO_ENABLE) ==
+ NECP_CLIENT_PARAMETER_FLAG_TFO_ENABLE) {
+ if (!tcp_heuristic_do_tfo_with_address(client->current_route->rt_ifp,
+ (union sockaddr_in_4_6 *)&flow->local_addr,
+ (union sockaddr_in_4_6 *)&flow->remote_addr,
+ tfo_cookie, tfo_cookie_len)) {
+ *flags |= NECP_CLIENT_RESULT_FLAG_FAST_OPEN_BLOCKED;
+ *tfo_cookie_len = 0;
+ }
+ } else {
+ *flags |= NECP_CLIENT_RESULT_FLAG_FAST_OPEN_BLOCKED;
+ *tfo_cookie_len = 0;
+ }
+do_unlock:
+ NECP_CLIENT_ROUTE_UNLOCK(client);
+
+ return error;
+}
+
+static size_t
+necp_client_calculate_flow_tlv_size(struct necp_client_flow_registration *flow_registration)
+{
+ size_t assigned_results_size = 0;
+ struct necp_client_flow *flow = NULL;
+ LIST_FOREACH(flow, &flow_registration->flow_list, flow_chain) {
+ if (flow->assigned) {
+ size_t header_length = 0;
+ if (flow->nexus) {
+ header_length = sizeof(struct necp_client_nexus_flow_header);
+ } else {
+ header_length = sizeof(struct necp_client_flow_header);
+ }
+ assigned_results_size += (header_length + flow->assigned_results_length);
+
+ if (flow->has_protoctl_event) {
+ assigned_results_size += sizeof(struct necp_client_flow_protoctl_event_header);
+ }
+ }
+ }
+ return assigned_results_size;
+}
+
+static int
+necp_client_fillout_flow_tlvs(struct necp_client *client,
+ bool client_is_observed,
+ struct necp_client_flow_registration *flow_registration,
+ struct necp_client_action_args *uap,
+ size_t *assigned_results_cursor)
+{
+ int error = 0;
+ struct necp_client_flow *flow = NULL;
+ LIST_FOREACH(flow, &flow_registration->flow_list, flow_chain) {
+ if (flow->assigned) {
+ // Write TLV headers
+ struct necp_client_nexus_flow_header header = {};
+ u_int32_t length = 0;
+ u_int32_t flags = 0;
+ u_int8_t tfo_cookie_len = 0;
+ u_int8_t type = 0;
+
+ type = NECP_CLIENT_RESULT_FLOW_ID;
+ length = sizeof(header.flow_header.flow_id);
+ memcpy(&header.flow_header.flow_id_tlv_header.type, &type, sizeof(type));
+ memcpy(&header.flow_header.flow_id_tlv_header.length, &length, sizeof(length));
+ uuid_copy(header.flow_header.flow_id, flow_registration->registration_id);
+
+ if (flow->nexus) {
+ if (flow->check_tcp_heuristics) {
+ u_int8_t tfo_cookie[NECP_TFO_COOKIE_LEN_MAX];
+ tfo_cookie_len = NECP_TFO_COOKIE_LEN_MAX;
+
+ if (necp_client_check_tcp_heuristics(client, flow, &flags,
+ tfo_cookie, &tfo_cookie_len) != 0) {
+ tfo_cookie_len = 0;
+ } else {
+ flow->check_tcp_heuristics = FALSE;
+
+ if (tfo_cookie_len != 0) {
+ type = NECP_CLIENT_RESULT_TFO_COOKIE;
+ length = tfo_cookie_len;
+ memcpy(&header.tfo_cookie_tlv_header.type, &type, sizeof(type));
+ memcpy(&header.tfo_cookie_tlv_header.length, &length, sizeof(length));
+ memcpy(&header.tfo_cookie_value, tfo_cookie, tfo_cookie_len);
+ }
+ }
+ }
+ }
+
+ size_t header_length = 0;
+ if (flow->nexus) {
+ if (tfo_cookie_len != 0) {
+ header_length = sizeof(struct necp_client_nexus_flow_header) - (NECP_TFO_COOKIE_LEN_MAX - tfo_cookie_len);
+ } else {
+ header_length = sizeof(struct necp_client_nexus_flow_header) - sizeof(struct necp_tlv_header) - NECP_TFO_COOKIE_LEN_MAX;
+ }
+ } else {
+ header_length = sizeof(struct necp_client_flow_header);
+ }
+
+ type = NECP_CLIENT_RESULT_FLAGS;
+ length = sizeof(header.flow_header.flags_value);
+ memcpy(&header.flow_header.flags_tlv_header.type, &type, sizeof(type));
+ memcpy(&header.flow_header.flags_tlv_header.length, &length, sizeof(length));
+ if (flow->assigned) {
+ flags |= NECP_CLIENT_RESULT_FLAG_FLOW_ASSIGNED;
+ }
+ if (flow->viable) {
+ flags |= NECP_CLIENT_RESULT_FLAG_FLOW_VIABLE;
+ }
+ if (flow_registration->defunct) {
+ flags |= NECP_CLIENT_RESULT_FLAG_DEFUNCT;
+ }
+ flags |= flow->necp_flow_flags;
+ memcpy(&header.flow_header.flags_value, &flags, sizeof(flags));
+
+ type = NECP_CLIENT_RESULT_INTERFACE;
+ length = sizeof(header.flow_header.interface_value);
+ memcpy(&header.flow_header.interface_tlv_header.type, &type, sizeof(type));
+ memcpy(&header.flow_header.interface_tlv_header.length, &length, sizeof(length));
+
+ struct necp_client_result_interface interface_struct;
+ interface_struct.generation = 0;
+ interface_struct.index = flow->interface_index;
+
+ memcpy(&header.flow_header.interface_value, &interface_struct, sizeof(interface_struct));
+ if (flow->nexus) {
+ type = NECP_CLIENT_RESULT_NETAGENT;
+ length = sizeof(header.agent_value);
+ memcpy(&header.agent_tlv_header.type, &type, sizeof(type));
+ memcpy(&header.agent_tlv_header.length, &length, sizeof(length));
+
+ struct necp_client_result_netagent agent_struct;
+ agent_struct.generation = 0;
+ uuid_copy(agent_struct.netagent_uuid, flow->u.nexus_agent);
+
+ memcpy(&header.agent_value, &agent_struct, sizeof(agent_struct));
+ }
+
+ // Don't include outer TLV header in length field
+ type = NECP_CLIENT_RESULT_FLOW;
+ length = (header_length - sizeof(struct necp_tlv_header) + flow->assigned_results_length);
+ if (flow->has_protoctl_event) {
+ length += sizeof(struct necp_client_flow_protoctl_event_header);
+ }
+ memcpy(&header.flow_header.outer_header.type, &type, sizeof(type));
+ memcpy(&header.flow_header.outer_header.length, &length, sizeof(length));