using namespace snort;
-bool do_discovery(AppIdSession&, IpProtocol, Packet*, AppidSessionDirection&);
+bool do_tp_discovery(AppIdSession&, IpProtocol, Packet*, AppidSessionDirection&);
AppIdDiscovery::AppIdDiscovery(AppIdInspector& ins)
: inspector(ins)
return APPID_EINVALID;
}
+void AppIdDiscovery::do_application_discovery(Packet* p, AppIdInspector& inspector)
+{
+ IpProtocol protocol = IpProtocol::PROTO_NOT_SET;
+ AppidSessionDirection direction = APP_ID_FROM_INITIATOR;
+ AppIdSession* asd = (AppIdSession*)p->flow->get_flow_data(AppIdSession::inspector_id);
+
+ if (!do_pre_discovery(p, &asd, inspector, protocol, direction)) return;
+
+ bool is_discovery_done = do_discovery(p, *asd, protocol, direction);
+
+ do_post_discovery(p, *asd, direction, is_discovery_done);
+}
+
static inline int match_pe_network(const SfIp* pktAddr, const PortExclusion* pe)
{
const uint32_t* pkt = pktAddr->get_ip6_ptr();
}
else if ( p->is_rebuilt() && !p->flow->is_proxied() )
{
+ // FIXIT-M: In snort2x, a rebuilt packet was ignored whether it had a session or not.
+ // Here, we are ignoring rebuilt packet only if it has a session. Why?
if ( asd )
{
AppIdHttpSession* hsession = asd->get_http_session();
hsession->get_field_offset(REQ_COOKIE_FID),
hsession->get_field_end_offset(REQ_COOKIE_FID));
}
- appid_stats.ignored_packets++;
- return true;
+ appid_stats.ignored_packets++;
+ return true;
}
}
}
}
-void AppIdDiscovery::do_application_discovery(Packet* p, AppIdInspector& inspector)
+bool AppIdDiscovery::handle_unmonitored_session(AppIdSession* asd, const Packet* p,
+ IpProtocol protocol, AppidSessionDirection dir, AppIdInspector& inspector,
+ uint64_t& flow_flags)
{
- IpProtocol protocol = IpProtocol::PROTO_NOT_SET;
- bool isTpAppidDiscoveryDone = true;
- AppidSessionDirection direction = APP_ID_FROM_INITIATOR;
-
- AppIdSession* asd = (AppIdSession*)p->flow->get_flow_data(AppIdSession::inspector_id);
- if ( !set_network_attributes(asd, p, protocol, direction) )
- {
- appid_stats.ignored_packets++;
- return;
- }
-
- if (appidDebug->is_enabled())
- appidDebug->activate(p->flow, asd, inspector.get_appid_config()->mod_config->log_all_sessions);
-
- if ( is_packet_ignored(asd, p, direction) )
- return;
-
- // Create a TPDiscovery object from a TPLibHandle.
- uint64_t flow_flags;
if (asd)
- flow_flags = is_session_monitored(*asd, p, direction, inspector);
+ flow_flags = is_session_monitored(*asd, p, dir, inspector);
else
- flow_flags = is_session_monitored(p, direction, inspector);
+ flow_flags = is_session_monitored(p, dir, inspector);
+
+ if ( flow_flags & (APPID_SESSION_DISCOVER_APP | APPID_SESSION_SPECIAL_MONITORED) )
+ return false;
- if ( !( flow_flags & (APPID_SESSION_DISCOVER_APP | APPID_SESSION_SPECIAL_MONITORED) ) )
+ if ( !asd )
{
- if ( !asd )
- {
- uint16_t port = 0;
+ uint16_t port = 0;
- const SfIp* ip = (direction == APP_ID_FROM_INITIATOR) ?
- p->ptrs.ip_api.get_src() : p->ptrs.ip_api.get_dst();
- if ((protocol == IpProtocol::TCP || protocol == IpProtocol::UDP)
- && p->ptrs.sp != p->ptrs.dp)
- {
- port = (direction == APP_ID_FROM_INITIATOR) ? p->ptrs.sp : p->ptrs.dp;
- }
+ const SfIp* ip = (dir == APP_ID_FROM_INITIATOR) ?
+ p->ptrs.ip_api.get_src() : p->ptrs.ip_api.get_dst();
+ if ((protocol == IpProtocol::TCP || protocol == IpProtocol::UDP)
+ && p->ptrs.sp != p->ptrs.dp)
+ {
+ port = (dir == APP_ID_FROM_INITIATOR) ? p->ptrs.sp : p->ptrs.dp;
+ }
- // FIXIT-H - Creating AppId session even when flow is ignored (not monitored, e.g.,
- // when AppId discovery is disabled) will consume a lot of unneeded memory and perform
- // unneeded tasks in constructor. Snort2 uses static APPID_SESSION_STRUCT_FLAG ignore_fsf.
- // Snort3 may use something like that or a dummy class/object having only common.flow_type
- // to let us know that it is APPID_FLOW_TYPE_IGNORE type and thus being returned early
- // from this method due to set_network_attributes() checking.
- AppIdSession* tmp_session = new AppIdSession(protocol, ip, port, inspector);
+ // FIXIT-H - Creating AppId session even when flow is ignored (not monitored, e.g.,
+ // when AppId discovery is disabled) will consume a lot of unneeded memory and perform
+ // unneeded tasks in constructor. Snort2 uses static APPID_SESSION_STRUCT_FLAG ignore_fsf.
+ // Snort3 may use something like that or a dummy class/object having only common.flow_type
+ // to let us know that it is APPID_FLOW_TYPE_IGNORE type and thus being returned early
+ // from this method due to set_network_attributes() checking.
+ AppIdSession* tmp_session = new AppIdSession(protocol, ip, port, inspector);
- if ((flow_flags & APPID_SESSION_BIDIRECTIONAL_CHECKED) ==
- APPID_SESSION_BIDIRECTIONAL_CHECKED)
- {
- tmp_session->common.flow_type = APPID_FLOW_TYPE_IGNORE;
- if (appidDebug->is_active())
- LogMessage("AppIdDbg %s Not monitored\n", appidDebug->get_debug_session());
- }
- else
- {
- tmp_session->common.flow_type = APPID_FLOW_TYPE_TMP;
- if (appidDebug->is_active())
- LogMessage("AppIdDbg %s Unknown monitoring\n", appidDebug->get_debug_session());
- }
- tmp_session->common.flags = flow_flags;
- tmp_session->common.policyId = inspector.get_appid_config()->appIdPolicyId;
- p->flow->set_flow_data(tmp_session);
+ if ((flow_flags & APPID_SESSION_BIDIRECTIONAL_CHECKED) ==
+ APPID_SESSION_BIDIRECTIONAL_CHECKED)
+ {
+ tmp_session->common.flow_type = APPID_FLOW_TYPE_IGNORE;
+ if (appidDebug->is_active())
+ LogMessage("AppIdDbg %s Not monitored\n", appidDebug->get_debug_session());
}
else
{
- asd->common.flags = flow_flags;
- if ( ( flow_flags & APPID_SESSION_BIDIRECTIONAL_CHECKED) ==
- APPID_SESSION_BIDIRECTIONAL_CHECKED )
- asd->common.flow_type = APPID_FLOW_TYPE_IGNORE;
- asd->common.policyId = asd->config->appIdPolicyId;
+ tmp_session->common.flow_type = APPID_FLOW_TYPE_TMP;
if (appidDebug->is_active())
- LogMessage("AppIdDbg %s Not monitored\n", appidDebug->get_debug_session());
+ LogMessage("AppIdDbg %s Unknown monitoring\n", appidDebug->get_debug_session());
}
+ tmp_session->common.flags = flow_flags;
+ tmp_session->common.policyId = inspector.get_appid_config()->appIdPolicyId;
+ p->flow->set_flow_data(tmp_session);
+ }
+ else
+ {
+ asd->common.flags = flow_flags;
+ if ( ( flow_flags & APPID_SESSION_BIDIRECTIONAL_CHECKED) ==
+ APPID_SESSION_BIDIRECTIONAL_CHECKED )
+ asd->common.flow_type = APPID_FLOW_TYPE_IGNORE;
+ asd->common.policyId = asd->config->appIdPolicyId;
+ if (appidDebug->is_active())
+ LogMessage("AppIdDbg %s Not monitored\n", appidDebug->get_debug_session());
+ }
+
+ return true;
+}
+
+// Return false if the packet or the session doesn't need to be inspected
+bool AppIdDiscovery::do_pre_discovery(Packet* p, AppIdSession** p_asd, AppIdInspector& inspector,
+ IpProtocol& protocol, AppidSessionDirection& direction)
+{
+ AppIdSession* asd = *p_asd;
- return;
+ if ( !set_network_attributes(asd, p, protocol, direction) )
+ {
+ appid_stats.ignored_packets++;
+ return false;
}
+ if ( appidDebug->is_enabled() )
+ appidDebug->activate(p->flow, asd, inspector.get_appid_config()->mod_config->log_all_sessions);
+
+ if ( is_packet_ignored(asd, p, direction) )
+ return false;
+
+ uint64_t flow_flags;
+ if ( handle_unmonitored_session(asd, p, protocol, direction, inspector, flow_flags) )
+ return false;
+
+ // FIXIT-M - Potential memory leak for TMP sessions. handle_unmonitored_session() already
+ // TMP session and that is not being freed before creating the new one below
if ( !asd || asd->common.flow_type == APPID_FLOW_TYPE_TMP )
{
- asd = AppIdSession::allocate_session(p, protocol, direction, inspector);
+ *p_asd = asd = AppIdSession::allocate_session(p, protocol, direction, inspector);
if (p->flow->get_session_flags() & SSNFLAG_MIDSTREAM)
{
asd->set_session_flags(APPID_SESSION_MID);
LogMessage("AppIdDbg %s New AppId session\n", appidDebug->get_debug_session());
}
- // FIXIT-L - from this point on we always have a valid ptr to an AppIdSession and a Packet
- // refactor to pass these as refs and delete any checks for null
+ // FIXIT-L - from this point on we always have a valid ptr to a Packet
+ // refactor to pass this as ref and delete any checks for null
appid_stats.processed_packets++;
asd->session_packet_count++;
appidDebug->get_debug_session(), asd->service.get_id());
}
- return;
+ return false;
}
- // FIXIT-H - Bring APPID_SESSION_OOO related changes from snort2 ASAP for performance reason
- if (p->packet_flags & PKT_STREAM_ORDER_BAD)
- asd->set_session_flags(APPID_SESSION_OOO);
- else if ( p->is_tcp() && p->ptrs.tcph )
+ if (p->ptrs.tcph and !asd->get_session_flags(APPID_SESSION_OOO))
{
- const auto* tcph = p->ptrs.tcph;
- if ( tcph->is_rst() && asd->previous_tcp_flags == TH_SYN )
+ if ((p->packet_flags & PKT_STREAM_ORDER_BAD) ||
+ (p->dsize && !(p->packet_flags & (PKT_STREAM_ORDER_OK | PKT_REBUILT_STREAM))))
{
- uint16_t port = 0;
- const SfIp* ip = nullptr;
-
- asd->set_session_flags(APPID_SESSION_SYN_RST);
- if (asd->service_ip.is_set())
+ asd->set_session_flags(APPID_SESSION_OOO);
+ if (appidDebug->is_active())
+ LogMessage("AppIdDbg %s Packet out-of-order, %s%sflow\n", appidDebug->get_debug_session(),
+ (p->packet_flags & PKT_STREAM_ORDER_BAD)? "bad ":"not-ok ",
+ asd->get_session_flags(APPID_SESSION_MID)? "mid-stream ":"");
+
+ // Shut off service/client discoveries, since they skip not-ok data packets and
+ // may keep failing on subsequent data packets causing performance degradation
+ if (!asd->get_session_flags(APPID_SESSION_MID) ||
+ (p->ptrs.sp != 21 && p->ptrs.dp != 21)) // exception for ftp-control
{
- ip = &asd->service_ip;
- port = asd->service_port;
+ asd->service_disco_state = APPID_DISCO_STATE_FINISHED;
+ asd->client_disco_state = APPID_DISCO_STATE_FINISHED;
+ asd->set_session_flags(APPID_SESSION_SERVICE_DETECTED | APPID_SESSION_CLIENT_DETECTED);
+ if (appidDebug->is_active())
+ LogMessage("AppIdDbg %s stopped service/client discovery\n", appidDebug->get_debug_session());
}
- else
+ }
+ else
+ {
+ const auto* tcph = p->ptrs.tcph;
+ if ( tcph->is_rst() && asd->previous_tcp_flags == TH_SYN )
{
- ip = p->ptrs.ip_api.get_src();
- port = p->ptrs.sp;
+ uint16_t port = 0;
+ const SfIp* ip = nullptr;
+
+ asd->set_session_flags(APPID_SESSION_SYN_RST);
+ if (asd->service_ip.is_set())
+ {
+ ip = &asd->service_ip;
+ port = asd->service_port;
+ }
+ else
+ {
+ ip = p->ptrs.ip_api.get_src();
+ port = p->ptrs.sp;
+ }
+
+ AppIdServiceState::check_reset(*asd, ip, port);
}
- AppIdServiceState::check_reset(*asd, ip, port);
+ asd->previous_tcp_flags = p->ptrs.tcph->th_flags;
}
-
- asd->previous_tcp_flags = p->ptrs.tcph->th_flags;
}
- /*HostPort based AppId. */
- if ( !(asd->scan_flags & SCAN_HOST_PORT_FLAG) )
- lookup_appid_by_host_port(*asd, p, protocol, direction);
+ return true;
+}
- asd->check_app_detection_restart();
+bool AppIdDiscovery::do_discovery(Packet* p, AppIdSession& asd, IpProtocol protocol,
+ AppidSessionDirection direction)
+{
+ bool is_discovery_done = false;
+
+ // {host, port} based detection
+ if ( !(asd.scan_flags & SCAN_HOST_PORT_FLAG) )
+ lookup_appid_by_host_port(asd, p, protocol, direction);
+ asd.check_app_detection_restart();
+
+ // Third party detection
#ifdef ENABLE_APPID_THIRD_PARTY
- if(asd->config->have_tp())
- isTpAppidDiscoveryDone = do_discovery(*asd,protocol,p,direction);
+ if(asd.config->have_tp())
+ is_discovery_done = do_tp_discovery(asd,protocol,p,direction);
#endif
- if ( !asd->get_session_flags(APPID_SESSION_PORT_SERVICE_DONE) )
+ // Port-based service detection
+ if ( !asd.get_session_flags(APPID_SESSION_PORT_SERVICE_DONE) )
{
switch (protocol)
{
case IpProtocol::TCP:
- if (asd->get_session_flags(APPID_SESSION_SYN_RST)) // TCP-specific exception
+ if (asd.get_session_flags(APPID_SESSION_SYN_RST)) // TCP-specific exception
break;
// fallthrough
case IpProtocol::UDP:
// Both TCP and UDP need this test to be made
- // against only the p->src_port of the response.
+ // against only the p->src_port of the response.
// For all other cases the port parameter is never checked.
if (direction != APP_ID_FROM_RESPONDER)
break;
// All protocols other than TCP and UDP come straight here.
default:
{
- asd->service.set_port_service_id(asd->config->get_port_service_id(protocol, p->ptrs.sp));
- if (appidDebug->is_active())
- LogMessage("AppIdDbg %s Port service %d\n",
- appidDebug->get_debug_session(), asd->service.get_port_service_id());
- asd->set_session_flags(APPID_SESSION_PORT_SERVICE_DONE);
+ AppId id = asd.config->get_port_service_id(protocol, p->ptrs.sp);
+ if (id > APP_ID_NONE)
+ {
+ asd.service.set_port_service_id(id);
+ if (appidDebug->is_active())
+ LogMessage("AppIdDbg %s Port service %d from port\n",
+ appidDebug->get_debug_session(), asd.service.get_port_service_id());
+ }
+ asd.set_session_flags(APPID_SESSION_PORT_SERVICE_DONE);
}
break;
}
}
- /* Length-based detectors. */
- /* Only check if:
- * - Port service didn't find anything (and we haven't yet either).
- * - We haven't hit the max packets allowed for detector sequence matches.
- * - Packet has data (we'll ignore 0-sized packets in sequencing). */
- if ( (asd->service.get_port_service_id() <= APP_ID_NONE)
- && (asd->length_sequence.sequence_cnt < LENGTH_SEQUENCE_CNT_MAX)
- && (p->dsize > 0))
- {
- uint8_t index = asd->length_sequence.sequence_cnt;
- asd->length_sequence.proto = protocol;
- asd->length_sequence.sequence_cnt++;
- asd->length_sequence.sequence[index].direction = direction;
- asd->length_sequence.sequence[index].length = p->dsize;
- AppId id = find_length_app_cache(&asd->length_sequence);
+ // Length-based service detection
+ if ( (p->dsize > 0) and (asd.service.get_port_service_id() <= APP_ID_NONE)
+ and (asd.length_sequence.sequence_cnt < LENGTH_SEQUENCE_CNT_MAX) )
+ {
+ uint8_t index = asd.length_sequence.sequence_cnt;
+ asd.length_sequence.proto = protocol;
+ asd.length_sequence.sequence_cnt++;
+ asd.length_sequence.sequence[index].direction = direction;
+ asd.length_sequence.sequence[index].length = p->dsize;
+ AppId id = find_length_app_cache(&asd.length_sequence);
if (id > APP_ID_NONE)
{
- asd->service.set_port_service_id(id);
- asd->set_session_flags(APPID_SESSION_PORT_SERVICE_DONE);
+ asd.service.set_port_service_id(id);
+ if (appidDebug->is_active())
+ LogMessage("AppIdDbg %s Port service %d from length\n",
+ appidDebug->get_debug_session(), asd.service.get_port_service_id());
+ asd.set_session_flags(APPID_SESSION_PORT_SERVICE_DONE);
}
}
- /* exceptions for rexec and any other service detector that needs to see SYN and SYN/ACK */
- if (asd->get_session_flags(APPID_SESSION_REXEC_STDERR))
+ // exceptions for rexec and any other service detector that need to see SYN and SYN/ACK
+ if (asd.get_session_flags(APPID_SESSION_REXEC_STDERR))
{
- ServiceDiscovery::get_instance().identify_service(*asd, p, direction);
- AppIdDnsSession* dsession = asd->get_dns_session();
+ ServiceDiscovery::get_instance().identify_service(asd, p, direction);
+ AppIdDnsSession* dsession = asd.get_dns_session();
- if (asd->service.get_id() == APP_ID_DNS &&
- asd->config->mod_config->dns_host_reporting && dsession->get_host() )
+ if (asd.service.get_id() == APP_ID_DNS &&
+ asd.config->mod_config->dns_host_reporting && dsession->get_host() )
{
AppId client_id = APP_ID_NONE, payload_id = APP_ID_NONE;
dns_host_scan_hostname((const uint8_t*)dsession->get_host(), dsession->get_host_len(),
&client_id, &payload_id);
- asd->set_client_appid_data(client_id, nullptr);
+ asd.set_client_appid_data(client_id, nullptr);
}
- else if (asd->service.get_id() == APP_ID_RTMP)
- asd->examine_rtmp_metadata();
- else if (asd->get_session_flags(APPID_SESSION_SSL_SESSION) && asd->tsession)
- asd->examine_ssl_metadata(p);
- }
- // FIXIT-M - snort 2.x has added a check for midstream pickup to this if, do we need that?
- else if (protocol != IpProtocol::TCP || !p->dsize || (p->packet_flags & PKT_STREAM_ORDER_OK))
- {
- if (asd->service_disco_state != APPID_DISCO_STATE_FINISHED)
- isTpAppidDiscoveryDone =
- ServiceDiscovery::get_instance().do_service_discovery(*asd, p, direction);
- if (asd->client_disco_state != APPID_DISCO_STATE_FINISHED)
- isTpAppidDiscoveryDone =
- ClientDiscovery::get_instance().do_client_discovery(*asd, p, direction);
- asd->set_session_flags(APPID_SESSION_ADDITIONAL_PACKET);
+ else if (asd.service.get_id() == APP_ID_RTMP)
+ asd.examine_rtmp_metadata();
+ else if (asd.get_session_flags(APPID_SESSION_SSL_SESSION) && asd.tsession)
+ asd.examine_ssl_metadata(p);
}
- else
+ // FIXIT-M - snort 2.x has added a check for midstream pickup to this, do we need that?
+ else if (protocol != IpProtocol::TCP || (p->packet_flags & PKT_STREAM_ORDER_OK))
{
- if (appidDebug->is_active() && p->dsize &&
- !asd->get_session_flags(APPID_SESSION_OOO_LOGGED))
- {
- asd->set_session_flags(APPID_SESSION_OOO_LOGGED);
- LogMessage("AppIdDbg %s Packet out-of-order\n", appidDebug->get_debug_session());
- }
+ if (asd.service_disco_state != APPID_DISCO_STATE_FINISHED)
+ is_discovery_done =
+ ServiceDiscovery::get_instance().do_service_discovery(asd, p, direction);
+ if (asd.client_disco_state != APPID_DISCO_STATE_FINISHED)
+ is_discovery_done =
+ ClientDiscovery::get_instance().do_client_discovery(asd, p, direction);
+ asd.set_session_flags(APPID_SESSION_ADDITIONAL_PACKET);
}
- AppId service_id = asd->pick_service_app_id();
- AppId payload_id = asd->pick_payload_app_id();
+ return is_discovery_done;
+}
+
+void AppIdDiscovery::do_post_discovery(Packet* p, AppIdSession& asd,
+ AppidSessionDirection direction, bool is_discovery_done)
+{
+ AppId service_id = asd.pick_service_app_id();
+ AppId payload_id = asd.pick_payload_app_id();
if (service_id > APP_ID_NONE)
{
- if (asd->get_session_flags(APPID_SESSION_DECRYPTED))
+ if (asd.get_session_flags(APPID_SESSION_DECRYPTED))
{
- if (asd->misc_app_id == APP_ID_NONE)
- asd->update_encrypted_app_id(service_id);
+ if (asd.misc_app_id == APP_ID_NONE)
+ asd.update_encrypted_app_id(service_id);
}
// FIXIT-M Need to determine what api to use for this _dpd function
#if 1
- UNUSED(isTpAppidDiscoveryDone);
+ UNUSED(is_discovery_done);
#else
- else if (isTpAppidDiscoveryDone && isSslServiceAppId(service_id) &&
+ else if (is_discovery_done && isSslServiceAppId(service_id) &&
_dpd.isSSLPolicyEnabled(nullptr))
- asd->set_session_flags(APPID_SESSION_CONTINUE);
+ asd.set_session_flags(APPID_SESSION_CONTINUE);
#endif
}
- asd->set_application_ids(service_id, asd->pick_client_app_id(), payload_id,
- asd->pick_misc_app_id());
+ asd.set_application_ids(service_id, asd.pick_client_app_id(), payload_id,
+ asd.pick_misc_app_id());
- /* Set the field that the Firewall queries to see if we have a search engine. */
- if (asd->search_support_type == UNKNOWN_SEARCH_ENGINE && payload_id > APP_ID_NONE)
+ // Set the field that the Firewall queries to see if we have a search engine
+ if (asd.search_support_type == UNKNOWN_SEARCH_ENGINE && payload_id > APP_ID_NONE)
{
uint flags = AppInfoManager::get_instance().get_app_info_flags(payload_id,
APPINFO_FLAG_SEARCH_ENGINE | APPINFO_FLAG_SUPPORTED_SEARCH);
- asd->search_support_type =
+ asd.search_support_type =
(flags & APPINFO_FLAG_SEARCH_ENGINE) ?
((flags & APPINFO_FLAG_SUPPORTED_SEARCH) ? SUPPORTED_SEARCH_ENGINE :
UNSUPPORTED_SEARCH_ENGINE )
if (appidDebug->is_active())
{
const char* typeString;
- switch ( asd->search_support_type )
+ switch ( asd.search_support_type )
{
case NOT_A_SEARCH_ENGINE: typeString = "NOT_A_SEARCH_ENGINE"; break;
case SUPPORTED_SEARCH_ENGINE: typeString = "SUPPORTED_SEARCH_ENGINE"; break;
if ( service_id != APP_ID_NONE )
{
- if ( payload_id != APP_ID_NONE && payload_id != asd->past_indicator)
+ if ( payload_id != APP_ID_NONE && payload_id != asd.past_indicator)
{
- asd->past_indicator = payload_id;
+ asd.past_indicator = payload_id;
check_session_for_AF_indicator(p, direction, (AppId)payload_id);
}
- if (asd->payload.get_id() == APP_ID_NONE && asd->past_forecast != service_id &&
- asd->past_forecast != APP_ID_UNKNOWN)
+ if (asd.payload.get_id() == APP_ID_NONE && asd.past_forecast != service_id &&
+ asd.past_forecast != APP_ID_UNKNOWN)
{
- asd->past_forecast = check_session_for_AF_forecast(*asd, p, direction,
+ asd.past_forecast = check_session_for_AF_forecast(asd, p, direction,
(AppId)service_id);
}
}