Passed
Push — master ( 1136d4...310572 )
by Stefan
10:37
created

Telepath::checkFedEtlrUplink()   C

Complexity

Conditions 7
Paths 7

Size

Total Lines 34
Code Lines 23

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
eloc 23
nc 7
nop 1
dl 0
loc 34
rs 6.7272
c 0
b 0
f 0
1
<?php
2
3
/*
4
 * ******************************************************************************
5
 * Copyright 2011-2017 DANTE Ltd. and GÉANT on behalf of the GN3, GN3+, GN4-1 
6
 * and GN4-2 consortia
7
 *
8
 * License: see the web/copyright.php file in the file structure
9
 * ******************************************************************************
10
 */
11
12
namespace core\diag;
13
14
use \Exception;
15
16
require_once(dirname(dirname(__DIR__)) . "/config/_config.php");
17
18
/**
19
 * The overall coordination class that runs all kinds of tests to find out where
20
 * and what is wrong. Operates on the realm of a user. Can do more magic if it
21
 * also knows which federation the user is currently positioned in, or even 
22
 * which exact hotspot to analyse.
23
 */
24
class Telepath extends AbstractTest {
25
26
    private $realm;
27
    private $visitedFlr;
28
    private $visitedHotspot;
29
    private $catProfile;
30
    private $dbIdP;
31
32
    /**
33
     *
34
     * @var string|null
35
     */
36
    private $idPFederation;
37
    private $testsuite;
38
39
    /**
40
     * prime the Telepath with info it needs to know to successfully meditate over the problem
41
     * @param string $realm the realm of the user
42
     * @param string|null $visitedFlr which NRO is the user visiting
43
     * @param string|null $visitedHotspot external DB ID of the hotspot he visited
44
     */
45
    public function __construct(string $realm, $visitedFlr = NULL, $visitedHotspot = NULL) {
46
        // Telepath is the first one in a chain, no previous inputs allowed
47
        if (isset($_SESSION) && isset($_SESSION["SUSPECTS"])) {
48
            unset($_SESSION["SUSPECTS"]);
49
        }
50
        if (isset($_SESSION) && isset($_SESSION["EVIDENCE"])) {
51
            unset($_SESSION["EVIDENCE"]);
52
        }
53
        // now fill with default values
54
        parent::__construct();
55
        $this->realm = $realm;
56
        $this->visitedFlr = $visitedFlr;
57
        $this->visitedHotspot = $visitedHotspot;
58
        $links = \core\Federation::determineIdPIdByRealm($realm);
59
        $this->catProfile = $links["CAT"];
60
        $this->dbIdP = $links["EXTERNAL"];
61
        $this->idPFederation = $links["FEDERATION"];
62
        // this is NULL if the realm is not known in either DB
63
        // if so, let's try a regex to extract the ccTLD if any
64
        $matches = [];
65
        if ($this->idPFederation === NULL && preg_match("/\.(..)$/", $realm, $matches)) {
66
            $this->idPFederation = strtoupper($matches[1]);
67
        }
68
        $this->loggerInstance->debug(4, "XYZ: IdP-side NRO is " . $this->idPFederation . "\n");
69
    }
70
71
    /* The eduroam OT monitoring has the following return codes:
72
     * 
73
74
      Status codes
75
76
      0 - O.K.
77
      -1 - Accept O.K. Reject No
78
      -2 - Reject O.K. Accept No
79
      -3 - Accept No Reject No
80
      -9 - system error
81
      -10 - Accept O.K. Reject timeou
82
      -11 - Accept O.K. Reject no EAP
83
      -20 - Reject O.K. Accept timeou
84
      -21 - Reject O.K. Accept no EAP
85
      -31 - Accept No  Reject timeou
86
      -32 - Accept Timeout Reject no
87
      -33 - Accept Timeout Reject timeou
88
      -35 - Accept No Reject no EAP
89
      -36 - Reject No Accept no EAP
90
      -37 - Reject No EAP Accept no EAP
91
      -40 - UDP test error
92
93
     */
94
95
    /**
96
     * ask the monitoring API about the things it knows
97
     * @param string $type which type of test to execute
98
     * @param string $param1 test-specific parameter number 1, if any
99
     * @param string $param2 test-specific parameter number 2, if any
100
     * @return array
101
     */
102
    private function genericAPIStatus($type, $param1 = NULL, $param2 = NULL) {
103
        $endpoints = [
104
            'tlr_test' => "https://monitor.eduroam.org/mapi/index.php?type=tlr_test&tlr=$param1",
105
            'federation_via_tlr' => "https://monitor.eduroam.org/mapi/index.php?type=federation_via_tlr&federation=$param1",
106
            'flrs_test' => "https://monitor.eduroam.org/mapi/index.php?type=flrs_test&federation=$param1",
107
            'flr_by_federation' => "https://monitor.eduroam.org/mapi/index.php?type=flr_by_federation&federation=$param2&with=$param1",
108
        ];
109
        $ignore = [
110
            'tlr_test' => 'tlr',
111
            'federation_via_tlr' => 'fed',
112
            'flrs_test' => 'fed',
113
            'flr_by_federation' => 'fed',
114
        ];
115
        $this->loggerInstance->debug(4, "Doing Monitoring API check with $endpoints[$type]\n");
116
        $jsonResult = \core\common\OutsideComm::downloadFile($endpoints[$type]);
117
        $this->loggerInstance->debug(4, "Monitoring API Result: $jsonResult\n");
118
        $decoded = json_decode($jsonResult, TRUE);
119
        $retval = [];
120
        $retval["RAW"] = $decoded;
121
        $atLeastOneFunctional = FALSE;
122
        $allFunctional = TRUE;
123
        if (!isset($decoded[$type]) || isset($decoded['ERROR'])) {
124
            $retval["STATUS"] = AbstractTest::STATUS_MONITORINGFAIL;
125
            return $retval;
126
        }
127
        foreach ($decoded[$type] as $instance => $resultset) {
128
            if ($instance == $ignore[$type]) {
129
                // don't care
130
                continue;
131
            }
132
            // TLR test has statuscode on this level, otherwise need to recurse
133
            // one more level
134
            switch ($type) {
135
                case "tlr_test":
136 View Code Duplication
                    switch ($resultset['status_code']) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
137
                        case 0:
138
                            $atLeastOneFunctional = TRUE;
139
                            break;
140
                        case 9: // monitoring itself has an error, no effect on our verdict
141
                        case -1: // Reject test fails, but we diagnose supposed-working connection, so no effect on our verdict
142
                        case -10: // same as previous
143
                        case -11: // same as previous
144
                            break;
145
                        default:
146
                            $allFunctional = FALSE;
147
                    }
148
                    break;
149
                default:
150
                    foreach ($resultset as $particularInstance => $particularResultset) {
151 View Code Duplication
                        switch ($particularResultset['status_code']) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
152
                            case 0:
153
                                $atLeastOneFunctional = TRUE;
154
                                break;
155
                            case 9: // monitoring itself has an error, no effect on our verdict
156
                            case -1: // Reject test fails, but we diagnose supposed-working connection, so no effect on our verdict
157
                            case -10: // same as previous
158
                            case -11: // same as previous
159
                                break;
160
                            default:
161
                                $allFunctional = FALSE;
162
                        }
163
                    }
164
            }
165
        }
166
167
        if ($allFunctional) {
168
            $retval["STATUS"] = AbstractTest::STATUS_GOOD;
169
            return $retval;
170
        }
171
        if ($atLeastOneFunctional) {
172
            $retval["STATUS"] = AbstractTest::STATUS_PARTIAL;
173
            return $retval;
174
        }
175
        $retval["STATUS"] = AbstractTest::STATUS_DOWN;
176
        return $retval;
177
    }
178
179
    /**
180
     * Are the ETLR servers in order?
181
     * @return array
182
     */
183
    private function checkEtlrStatus() {
184
        // TODO: we always check the European TLRs even though the connection in question might go via others and/or this one
185
        // needs a table to determine what goes where :-(
186
        $ret = $this->genericAPIStatus("tlr_test", "TLR_EU");
187
        $this->additionalFindings[AbstractTest::INFRA_ETLR][] = $ret;
188
        switch ($ret["STATUS"]) {
189
            case AbstractTest::STATUS_GOOD:
190
                unset($this->possibleFailureReasons[AbstractTest::INFRA_ETLR]);
191
                break;
192
            case AbstractTest::STATUS_PARTIAL:
193
            case AbstractTest::STATUS_MONITORINGFAIL:
194
                // one of the ETLRs is down, or there is a failure in the monitoring system? 
195
                // This probably doesn't impact the user unless he's unlucky and has his session fall into failover.
196
                // keep ETLR as a possible problem with original probability
197
                break;
198
            case AbstractTest::STATUS_DOWN:
199
                // Oh! Well if it is not international roaming, that still doesn't have an effect /in this case/. 
200
                if ($this->idPFederation == $this->visitedFlr) {
201
                    unset($this->possibleFailureReasons[AbstractTest::INFRA_ETLR]);
202
                    break;
203
                }
204
                // But it is about int'l roaming, and we are spot on here.
205
                // Raise probability by much (even monitoring is sometimes wrong, or a few minutes behind reality)
206
                $this->possibleFailureReasons[AbstractTest::INFRA_ETLR] = 0.95;
207
        }
208
    }
209
210
    /**
211
     * Is the uplink between an NRO server and the ETLRs in order?
212
     * @param int $whichSide
213
     * @return array
214
     */
215
    private function checkFedEtlrUplink($whichSide) {
216
        // TODO: we always check the European TLRs even though the connection in question might go via others and/or this one
217
        // needs a table to determine what goes where :-(
218
                switch ($whichSide) {
219
            case AbstractTest::INFRA_NRO_IDP:
220
                $fed = $this->idPFederation;
221
                $linkVariant = AbstractTest::INFRA_LINK_ETLR_NRO_IDP;
222
                break;
223
            case AbstractTest::INFRA_NRO_SP:
224
                $fed = $this->visitedFlr;
0 ignored issues
show
Unused Code introduced by
$fed is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
225
                $linkVariant = AbstractTest::INFRA_LINK_ETLR_NRO_SP;
0 ignored issues
show
Unused Code introduced by
$linkVariant is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
226
            default:
227
            throw new Exception("This function operates on the IdP- or SP-side FLR, nothing else!");
228
        }
229
230
        $ret = $this->genericAPIStatus("federation_via_tlr", $fed);
231
        $this->additionalFindings[AbstractTest::INFRA_NRO_IDP][] = $ret;
232
                switch ($ret["STATUS"]) {
233
                    case AbstractTest::STATUS_GOOD:
234
                        unset($this->possibleFailureReasons[$whichSide]);
235
                        unset($this->possibleFailureReasons[$linkVariant]);
236
                        break;
237
                    case AbstractTest::STATUS_PARTIAL:
238
                        // a subset of the FLRs is down? This probably doesn't impact the user unless he's unlucky and has his session fall into failover.
239
                        // keep FLR as a possible problem with original probability
240
                        break;
241
                    case AbstractTest::STATUS_DOWN:
242
                        // Raise probability by much (even monitoring is sometimes wrong, or a few minutes behind reality)
243
                        // if earlier test found the server itself to be the problem, keep it, otherwise put the blame on the link
244
                        if ($this->possibleFailureReasons[$whichSide] != 0.95) {
245
                            $this->possibleFailureReasons[$linkVariant] = 0.95;
246
                        }
247
                }
248
    }
249
250
    /**
251
     * Is the NRO server itself in order?
252
     * @param int $whichSide
253
     * @return array
254
     */
255
    private function checkFlrServerStatus($whichSide) {
256
        switch ($whichSide) {
257
            case AbstractTest::INFRA_NRO_IDP:
258
                $fed = $this->idPFederation;
259
                break;
260
            case AbstractTest::INFRA_NRO_SP:
261
                $fed = $this->visitedFlr;
0 ignored issues
show
Unused Code introduced by
$fed is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
262
            default:
263
            throw new Exception("This function operates on the IdP- or SP-side FLR, nothing else!");
264
        }
265
        
266
        $ret = $this->genericAPIStatus("flrs_test", $fed);
267
        $this->additionalFindings[$whichSide][] = $ret;
268
        switch ($ret["STATUS"]) {
269
            case AbstractTest::STATUS_GOOD:
270
                unset($this->possibleFailureReasons[$whichSide]);
271
                break;
272
            case AbstractTest::STATUS_PARTIAL:
273
                // a subset of the FLRs is down? This probably doesn't impact the user unless he's unlucky and has his session fall into failover.
274
                // keep FLR as a possible problem with original probability
275
                break;
276
            case AbstractTest::STATUS_DOWN:
277
                // Raise probability by much (even monitoring is sometimes wrong, or a few minutes behind reality)
278
                $this->possibleFailureReasons[$whichSide] = 0.95;
279
        }
280
    }
281
282
    /**
283
     * Does authentication traffic flow between a given source and destination NRO?
284
     * @return array
285
     */
286
    private function checkNROFlow() {
287
        return $this->genericAPIStatus("flr_by_federation", $this->idPFederation, $this->visitedFlr);
288
    }
289
290
    /**
291
     * Runs the CAT-internal diagnostics tests. Determines the state of the 
292
     * realm (and indirectly that of the links and statuses of involved proxies
293
     * and returns a judgment whether external Monitoring API tests are warranted
294
     * or not
295
     * @return boolean TRUE if external tests have to be run
296
     */
297
    private function CATInternalTests() {
298
        // we are expecting to get a REJECT from all runs, because that means the packet got through to the IdP.
299
        // (the ETLR sometimes does a "Reject instead of Ignore" but that is filtered out and changed into a timeout
300
        // by the test suite automatically, so it does not disturb the measurement)
301
        // If that's true, we can exclude two sources of problems (both proxy levels). Hooray!
302
        $allAreConversationReject = TRUE;
303
        $atLeastOneConversationReject = FALSE;
304
305
        foreach (CONFIG_DIAGNOSTICS['RADIUSTESTS']['UDP-hosts'] as $probeindex => $probe) {
306
            $reachCheck = $this->testsuite->udpReachability($probeindex);
307
            if ($reachCheck != RADIUSTests::RETVAL_CONVERSATION_REJECT) {
308
                $allAreConversationReject = FALSE;
309
            } else {
310
                $atLeastOneConversationReject = TRUE;
311
            }
312
313
            $this->additionalFindings[AbstractTest::INFRA_ETLR][] = ["DETAIL" => $this->testsuite->consolidateUdpResult($probeindex)];
314
            $this->additionalFindings[AbstractTest::INFRA_NRO_IDP][] = ["DETAIL" => $this->testsuite->consolidateUdpResult($probeindex)];
315
            $this->additionalFindings[AbstractTest::INFRA_IDP_RADIUS][] = ["DETAIL" => $this->testsuite->consolidateUdpResult($probeindex)];
316
        }
317
318
        if ($allAreConversationReject) {
319
            $this->additionalFindings[AbstractTest::INFRA_ETLR][] = ["CONNCHECK" => RADIUSTests::RETVAL_CONVERSATION_REJECT];
320
            $this->additionalFindings[AbstractTest::INFRA_NRO_IDP][] = ["CONNCHECK" => RADIUSTests::RETVAL_CONVERSATION_REJECT];
321
            $this->additionalFindings[AbstractTest::INFRA_IDP_RADIUS][] = ["CONNCHECK" => RADIUSTests::RETVAL_CONVERSATION_REJECT];
322
            $this->additionalFindings[AbstractTest::INFRA_LINK_ETLR_NRO_IDP][] = ["LINKCHECK" => RADIUSTests::L_OK];
323
            // we have actually reached an IdP, so all links are good, and the
324
            // realm is routable in eduroam. So even if it exists in neither DB
325
            // we can exclude the NONEXISTENTREALM case
326
            unset($this->possibleFailureReasons[AbstractTest::INFRA_ETLR]);
327
            unset($this->possibleFailureReasons[AbstractTest::INFRA_NRO_IDP]);
328
            unset($this->possibleFailureReasons[AbstractTest::INFRA_LINK_ETLR_NRO_IDP]);
329
            unset($this->possibleFailureReasons[AbstractTest::INFRA_NONEXISTENTREALM]);
330
        }
331
332
        if ($atLeastOneConversationReject) {
333
            // at least we can be sure it exists
334
            unset($this->possibleFailureReasons[AbstractTest::INFRA_NONEXISTENTREALM]);
335
            // It could still be an IdP RADIUS problem in that some cert oddities 
336
            // in combination with the device lead to a broken auth
337
            // if there is nothing beyond the "REMARK" level, then it's not an IdP problem
338
            // otherwise, add the corresponding warnings and errors to $additionalFindings
339
            switch ($this->additionalFindings[AbstractTest::INFRA_IDP_RADIUS][0]['DETAIL']['level']) {
340
                case RADIUSTests::L_OK:
341
                case RADIUSTests::L_REMARK:
342
                    // both are fine - the IdP is working and the user problem
343
                    // is not on the IdP RADIUS level
344
                    $this->additionalFindings[AbstractTest::INFRA_IDP_RADIUS][] = ["ODDITYLEVEL" => $this->additionalFindings[AbstractTest::INFRA_IDP_RADIUS][0]['DETAIL']['level']];
345
                    unset($this->possibleFailureReasons[AbstractTest::INFRA_IDP_RADIUS]);
346
                    break;
347
                case RADIUSTests::L_WARN:
348
                    $this->additionalFindings[AbstractTest::INFRA_IDP_RADIUS][] = ["ODDITYLEVEL" => RADIUSTests::L_WARN];
349
                    $this->possibleFailureReasons[AbstractTest::INFRA_IDP_RADIUS] = 0.3; // possibly we found the culprit - if RADIUS server is misconfigured AND user is on a device which reacts picky about exactly this oddity.
350
                    break;
351
                case RADIUSTests::L_ERROR:
352
                    $this->additionalFindings[AbstractTest::INFRA_IDP_RADIUS][] = ["ODDITYLEVEL" => RADIUSTests::L_ERROR];
353
                    $this->possibleFailureReasons[AbstractTest::INFRA_IDP_RADIUS] = 0.8; // errors are never good, so we can be reasonably sure we've hit the spot!
354
            }
355
        }
356
    }
357
358
    private function determineTestsuiteParameters() {
359
        if ($this->catProfile > 0) {
360
            $profileObject = \core\ProfileFactory::instantiate($this->catProfile);
361
            $readinessLevel = $profileObject->readinessLevel();
362
363
            switch ($readinessLevel) {
364
                case \core\AbstractProfile::READINESS_LEVEL_SHOWTIME:
365
                case \core\AbstractProfile::READINESS_LEVEL_SUFFICIENTCONFIG:
366
                    $this->additionalFindings[AbstractTest::INFRA_IDP_RADIUS][] = ["Profile" => $profileObject->identifier];
367
                    $this->testsuite = new RADIUSTests($this->realm, $profileObject->getRealmCheckOuterUsername(), $profileObject->getEapMethodsinOrderOfPreference(1), $profileObject->getCollapsedAttributes()['eap:server_name'], $profileObject->getCollapsedAttributes()["eap:ca_file"]);
368
369
                case \core\AbstractProfile::READINESS_LEVEL_NOTREADY:
370
                    $this->additionalFindings[AbstractTest::INFRA_IDP_RADIUS][] = ["Profile" => "UNCONCLUSIVE"];
371
                    $this->testsuite = new RADIUSTests($this->realm, "anonymous@" . $this->realm);
372
                    break;
373
                default:
374
            }
375
        } else {
376
            $this->testsuite = new RADIUSTests($this->realm, "anonymous@" . $this->realm);
377
        }
378
    }
379
380
    /**
381
     * Does the main meditation job
382
     * @return array the findings
383
     */
384
    public function magic() {
385
386
        // simple things first: do we know anything about the realm, either
387
        // because it's a CAT participant or because it's in the eduroam DB?
388
        // if so, we can exclude the INFRA_NONEXISTENTREALM cause
389
        $this->additionalFindings[AbstractTest::INFRA_NONEXISTENTREALM]['DATABASE_STATUS'] = ["ID1" => $this->catProfile, "ID2" => $this->dbIdP];
390
        if ($this->catProfile != \core\Federation::UNKNOWN_IDP || $this->dbIdP != \core\Federation::UNKNOWN_IDP) {
391
            unset($this->possibleFailureReasons[AbstractTest::INFRA_NONEXISTENTREALM]);
392
        }
393
        // do we operate on a non-ambiguous, fully configured CAT profile? Then
394
        // we run the more thorough check, otherwise the shallow one.
395
        $this->determineTestSuiteParameters();
396
        // let's do the least amount of testing needed:
397
        // - The CAT reachability test already covers ELTRs, IdP NRO level and the IdP itself.
398
        //   if the realm maps to a CAT IdP, we can run the more thorough tests; otherwise just
399
        //   the normal shallow ones
400
        // these are the normal "realm check" tests covering ETLR, LINK_NRO_IDP, NRO, IDP_RADIUS
401
        $this->CATInternalTests();
402
        // - if the test does NOT go through, we need to find out which of the three is guilty
403
        // - then, the international "via ETLR" check can be used to find out if the IdP alone
404
        //   is guilty. If that one fails, the direct monitoring of servers and ETLRs themselves
405
        //   closes the loop.
406
        // let's see if the ETLRs are up
407
        if (array_key_exists(AbstractTest::INFRA_ETLR, $this->possibleFailureReasons)) {
408
            $this->checkEtlrStatus();
409
        }
410
411
        // then let's check the IdP's FLR, if we know the IdP federation at all
412
        if ($this->idPFederation !== NULL) {
413
            if (array_key_exists(AbstractTest::INFRA_NRO_IDP, $this->possibleFailureReasons)) {
414
                // first the direct connectivity to the server
415
                $this->checkFlrServerStatus(AbstractTest::INFRA_NRO_IDP);
416
            }
417
            // now let's theck the link
418
            if (array_key_exists(AbstractTest::INFRA_LINK_ETLR_NRO_IDP, $this->possibleFailureReasons)) {
419
                $this->checkFedEtlrUplink(AbstractTest::INFRA_NRO_IDP);
420
            }
421
        }
422
        // now, if we know the country the user is currently in, let's see 
423
        // if the NRO SP-side is up
424
        if ($this->visitedFlr !== NULL) {
425
            $this->checkFlrServerStatus(AbstractTest::INFRA_NRO_SP);
426
            // and again its uplink to the ETLR
427
            $this->checkFedEtlrUplink(AbstractTest::INFRA_NRO_SP);
428
        }
429
        // the last thing we can do (but it's a bit redundant): check the country-to-country link
430
        // it's only needed if all three and their links are up, but we want to exclude funny routing blacklists 
431
        // which occur only in the *combination* of source and dest
432
        // if there is an issue at that point, blame the SP: once a request
433
        // would have reached the ETLRs, things would be all good (assuming
434
        // perfection on the ETLRs here!). So the SP has a wrong config.
435
        if ($this->idPFederation !== NULL &&
436
                $this->visitedFlr !== NULL &&
437
                !array_key_exists(AbstractTest::INFRA_ETLR, $this->possibleFailureReasons) &&
438
                !array_key_exists(AbstractTest::INFRA_LINK_ETLR_NRO_IDP, $this->possibleFailureReasons) &&
439
                !array_key_exists(AbstractTest::INFRA_NRO_IDP, $this->possibleFailureReasons) &&
440
                !array_key_exists(AbstractTest::INFRA_LINK_ETLR_NRO_SP, $this->possibleFailureReasons) &&
441
                !array_key_exists(AbstractTest::INFRA_NRO_SP, $this->possibleFailureReasons)
442
        ) {
443
            $countryToCountryStatus = $this->checkNROFlow();
444
            $this->additionalFindings[AbstractTest::INFRA_NRO_SP][] = $countryToCountryStatus;
445
            $this->additionalFindings[AbstractTest::INFRA_ETLR][] = $countryToCountryStatus;
446
            $this->additionalFindings[AbstractTest::INFRA_NRO_IDP][] = $countryToCountryStatus;
447
            switch ($countryToCountryStatus["STATUS"]) {
448
                case AbstractTest::STATUS_GOOD:
449
                    // all routes work
450
                    break;
451
                case AbstractTest::STATUS_PARTIAL:
452
                // at least one, or even all have a routing problem
453
                case AbstractTest::STATUS_DOWN:
454
                    // that's rather telling.
455
                    $this->possibleFailureReasons[AbstractTest::INFRA_NRO_SP] = 0.95;
456
            }
457
        }
458
459
        $this->normaliseResultSet();
460
461
        $_SESSION["SUSPECTS"] = $this->possibleFailureReasons;
462
        $_SESSION["EVIDENCE"] = $this->additionalFindings;
463
        return ["SUSPECTS" => $this->possibleFailureReasons, "EVIDENCE" => $this->additionalFindings];
464
    }
465
466
}
467