Coverage Report

Created: 2026-03-11 06:29

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wpantund/src/ncp-spinel/SpinelNCPTaskJoin.cpp
Line
Count
Source
1
/*
2
 *
3
 * Copyright (c) 2016 Nest Labs, Inc.
4
 * All rights reserved.
5
 *
6
 * Licensed under the Apache License, Version 2.0 (the "License");
7
 * you may not use this file except in compliance with the License.
8
 * You may obtain a copy of the License at
9
 *
10
 *     http://www.apache.org/licenses/LICENSE-2.0
11
 *
12
 * Unless required by applicable law or agreed to in writing, software
13
 * distributed under the License is distributed on an "AS IS" BASIS,
14
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
 * See the License for the specific language governing permissions and
16
 * limitations under the License.
17
 *
18
 */
19
20
#if HAVE_CONFIG_H
21
#include <config.h>
22
#endif
23
24
#include "assert-macros.h"
25
#include <syslog.h>
26
#include <errno.h>
27
#include "SpinelNCPTaskJoin.h"
28
#include "SpinelNCPInstance.h"
29
#include "any-to.h"
30
#include "spinel-extra.h"
31
32
using namespace nl;
33
using namespace nl::wpantund;
34
35
nl::wpantund::SpinelNCPTaskJoin::SpinelNCPTaskJoin(
36
  SpinelNCPInstance* instance,
37
  CallbackWithStatusArg1 cb,
38
  const ValueMap& options
39
0
):  SpinelNCPTask(instance, cb), mOptions(options), mLastState(instance->get_ncp_state())
40
0
{
41
0
}
42
43
void
44
nl::wpantund::SpinelNCPTaskJoin::finish(int status, const boost::any& value)
45
0
{
46
0
  SpinelNCPTask::finish(status, value);
47
0
  if ( (status != kWPANTUNDStatus_InProgress)
48
0
    && !ncp_state_is_associated(mInstance->get_ncp_state())
49
0
  ) {
50
0
    mInstance->change_ncp_state(mLastState);
51
0
  }
52
0
}
53
54
int
55
nl::wpantund::SpinelNCPTaskJoin::vprocess_event(int event, va_list args)
56
0
{
57
0
  int ret = kWPANTUNDStatus_Failure;
58
0
  int last_status = peek_ncp_callback_status(event, args);
59
60
0
  EH_BEGIN();
61
62
63
0
  if (!mInstance->mEnabled) {
64
0
    ret = kWPANTUNDStatus_InvalidWhenDisabled;
65
0
    finish(ret);
66
0
    EH_EXIT();
67
0
  }
68
69
0
  if (mInstance->get_ncp_state() == UPGRADING) {
70
0
    ret = kWPANTUNDStatus_InvalidForCurrentState;
71
0
    finish(ret);
72
0
    EH_EXIT();
73
0
  }
74
75
  // Wait for a bit to see if the NCP will enter the right state.
76
0
  EH_REQUIRE_WITHIN(
77
0
    NCP_DEFAULT_COMMAND_RESPONSE_TIMEOUT,
78
0
    !ncp_state_is_initializing(mInstance->get_ncp_state()) && !mInstance->is_initializing_ncp(),
79
0
    on_error
80
0
  );
81
82
0
  if (ncp_state_is_associated(mInstance->get_ncp_state())) {
83
0
    ret = kWPANTUNDStatus_Already;
84
0
    finish(ret);
85
0
    EH_EXIT();
86
0
  }
87
88
  // The first event to a task is EVENT_STARTING_TASK. The following
89
  // line makes sure that we don't start processing this task
90
  // until it is properly scheduled. All tasks immediately receive
91
  // the initial `EVENT_STARTING_TASK` event, but further events
92
  // will only be received by that task once it is that task's turn
93
  // to execute.
94
0
  EH_WAIT_UNTIL(EVENT_STARTING_TASK != event);
95
96
  // Clear any previously saved network settings
97
0
  mNextCommand = SpinelPackData(SPINEL_FRAME_PACK_CMD_NET_CLEAR);
98
0
  EH_SPAWN(&mSubPT, vprocess_send_command(event, args));
99
0
  ret = mNextCommandRet;
100
101
0
  check_noerr(ret);
102
103
0
  mLastState = mInstance->get_ncp_state();
104
0
  mInstance->change_ncp_state(ASSOCIATING);
105
106
0
  if (mOptions.count(kWPANTUNDProperty_NetworkNodeType)) {
107
0
    NodeType node_type;
108
0
    bool router_role_enabled;
109
110
0
    node_type = string_to_node_type(any_to_string(mOptions[kWPANTUNDProperty_NetworkNodeType]));
111
112
0
    if ((node_type == ROUTER) || (node_type == LEADER)) {
113
0
      if (!mInstance->mCapabilities.count(SPINEL_CAP_ROLE_ROUTER)) {
114
        // We cannot join as a router/leader unless we are router-capable
115
0
        ret = kWPANTUNDStatus_FeatureNotSupported;
116
0
        goto on_error;
117
0
      }
118
0
      router_role_enabled = true;
119
120
0
    } else if (node_type == END_DEVICE) {
121
0
      router_role_enabled = false;
122
123
0
    } else if (node_type == SLEEPY_END_DEVICE) {
124
0
      if (!mInstance->mCapabilities.count(SPINEL_CAP_ROLE_SLEEPY)) {
125
0
        ret = kWPANTUNDStatus_FeatureNotSupported;
126
0
        goto on_error;
127
0
      }
128
0
      router_role_enabled = false;
129
130
0
    } else if (node_type == LURKER) {
131
0
      if (!mInstance->mCapabilities.count(SPINEL_CAP_NEST_LEGACY_INTERFACE)) {
132
0
        ret = kWPANTUNDStatus_FeatureNotSupported;
133
0
        goto on_error;
134
0
      }
135
0
      router_role_enabled = true;
136
137
0
    } else {
138
0
      ret = kWPANTUNDStatus_InvalidArgument;
139
0
      goto on_error;
140
0
    }
141
142
0
    if (mInstance->mCapabilities.count(SPINEL_CAP_CONFIG_FTD)) {
143
0
      mNextCommand = SpinelPackData(
144
0
          SPINEL_FRAME_PACK_CMD_PROP_VALUE_SET(SPINEL_DATATYPE_BOOL_S),
145
0
          SPINEL_PROP_THREAD_ROUTER_ROLE_ENABLED,
146
0
          router_role_enabled
147
0
          );
148
0
    }
149
150
0
    EH_SPAWN(&mSubPT, vprocess_send_command(event, args));
151
152
0
    ret = mNextCommandRet;
153
154
0
    require_noerr(ret, on_error);
155
0
  }
156
157
0
  if (mOptions.count(kWPANTUNDProperty_NetworkNodeType)) {
158
0
    NodeType node_type;
159
0
    uint8_t new_thread_mode;
160
161
0
    node_type = string_to_node_type(any_to_string(mOptions[kWPANTUNDProperty_NetworkNodeType]));
162
163
0
    new_thread_mode = mInstance->get_thread_mode();
164
165
    // The validity of node type (and related capabilities) is already checked
166
    // when setting the `router_role_enabled` (in code above).
167
168
0
    if (node_type == SLEEPY_END_DEVICE) {
169
0
      new_thread_mode &= ~ (SPINEL_THREAD_MODE_RX_ON_WHEN_IDLE | SPINEL_THREAD_MODE_FULL_THREAD_DEV);
170
0
    } else {
171
0
      new_thread_mode |= SPINEL_THREAD_MODE_RX_ON_WHEN_IDLE;
172
0
    }
173
174
0
    mNextCommand = SpinelPackData(
175
0
      SPINEL_FRAME_PACK_CMD_PROP_VALUE_SET(SPINEL_DATATYPE_UINT8_S),
176
0
      SPINEL_PROP_THREAD_MODE,
177
0
      new_thread_mode
178
0
    );
179
180
0
    EH_SPAWN(&mSubPT, vprocess_send_command(event, args));
181
182
0
    ret = mNextCommandRet;
183
184
0
    require_noerr(ret, on_error);
185
0
  }
186
187
  // Turn off promiscuous mode, if it happens to be on
188
0
  mNextCommand = SpinelPackData(
189
0
    SPINEL_FRAME_PACK_CMD_PROP_VALUE_SET(SPINEL_DATATYPE_UINT8_S),
190
0
    SPINEL_PROP_MAC_PROMISCUOUS_MODE,
191
0
    SPINEL_MAC_PROMISCUOUS_MODE_OFF
192
0
  );
193
0
  EH_SPAWN(&mSubPT, vprocess_send_command(event, args));
194
0
  ret = mNextCommandRet;
195
0
  check_noerr(ret);
196
197
0
  if (mOptions.count(kWPANTUNDProperty_NCPChannel)) {
198
0
    mNextCommand = SpinelPackData(
199
0
      SPINEL_FRAME_PACK_CMD_PROP_VALUE_SET(SPINEL_DATATYPE_UINT8_S),
200
0
      SPINEL_PROP_PHY_CHAN,
201
0
      any_to_int(mOptions[kWPANTUNDProperty_NCPChannel])
202
0
    );
203
204
0
    EH_SPAWN(&mSubPT, vprocess_send_command(event, args));
205
206
0
    ret = mNextCommandRet;
207
208
0
    require_noerr(ret, on_error);
209
210
0
  }
211
212
0
  if (mOptions.count(kWPANTUNDProperty_NetworkPANID)) {
213
0
    mNextCommand = SpinelPackData(
214
0
      SPINEL_FRAME_PACK_CMD_PROP_VALUE_SET(SPINEL_DATATYPE_UINT16_S),
215
0
      SPINEL_PROP_MAC_15_4_PANID,
216
0
      any_to_int(mOptions[kWPANTUNDProperty_NetworkPANID])
217
0
    );
218
219
0
    EH_SPAWN(&mSubPT, vprocess_send_command(event, args));
220
221
0
    ret = mNextCommandRet;
222
223
0
    require_noerr(ret, on_error);
224
0
  }
225
226
0
  if (mOptions.count(kWPANTUNDProperty_NetworkXPANID)) {
227
0
    {
228
0
      uint64_t xpanid(any_to_uint64(mOptions[kWPANTUNDProperty_NetworkXPANID], /* expect_hex_str */ true));
229
230
0
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
231
0
      reverse_bytes(reinterpret_cast<uint8_t*>(&xpanid), sizeof(xpanid));
232
0
#endif
233
234
0
      mNextCommand = SpinelPackData(
235
0
        SPINEL_FRAME_PACK_CMD_PROP_VALUE_SET(SPINEL_DATATYPE_DATA_S),
236
0
        SPINEL_PROP_NET_XPANID,
237
0
        &xpanid,
238
0
        sizeof(xpanid)
239
0
      );
240
0
    }
241
242
0
    EH_SPAWN(&mSubPT, vprocess_send_command(event, args));
243
244
0
    ret = mNextCommandRet;
245
246
0
    require_noerr(ret, on_error);
247
0
  }
248
249
0
  if (mOptions.count(kWPANTUNDProperty_NetworkName)) {
250
0
    mNextCommand = SpinelPackData(
251
0
      SPINEL_FRAME_PACK_CMD_PROP_VALUE_SET(SPINEL_DATATYPE_UTF8_S),
252
0
      SPINEL_PROP_NET_NETWORK_NAME,
253
0
      any_to_string(mOptions[kWPANTUNDProperty_NetworkName]).c_str()
254
0
    );
255
256
0
    EH_SPAWN(&mSubPT, vprocess_send_command(event, args));
257
258
0
    ret = mNextCommandRet;
259
260
0
    require_noerr(ret, on_error);
261
0
  }
262
263
0
  if (mOptions.count(kWPANTUNDProperty_NetworkKey)) {
264
0
    {
265
0
      nl::Data data(any_to_data(mOptions[kWPANTUNDProperty_NetworkKey]));
266
0
      mNextCommand = SpinelPackData(
267
0
        SPINEL_FRAME_PACK_CMD_PROP_VALUE_SET(SPINEL_DATATYPE_DATA_S),
268
0
        SPINEL_PROP_NET_MASTER_KEY,
269
0
        data.data(),
270
0
        data.size()
271
0
      );
272
0
    }
273
274
0
    EH_SPAWN(&mSubPT, vprocess_send_command(event, args));
275
276
0
    ret = mNextCommandRet;
277
278
0
    require_noerr(ret, on_error);
279
0
  }
280
281
0
  if (mOptions.count(kWPANTUNDProperty_NetworkKeyIndex)) {
282
0
    mNextCommand = SpinelPackData(
283
0
      SPINEL_FRAME_PACK_CMD_PROP_VALUE_SET(SPINEL_DATATYPE_UINT32_S),
284
0
      SPINEL_PROP_NET_KEY_SEQUENCE_COUNTER,
285
0
      any_to_int(mOptions[kWPANTUNDProperty_NetworkKeyIndex])
286
0
    );
287
288
0
    EH_SPAWN(&mSubPT, vprocess_send_command(event, args));
289
290
0
    ret = mNextCommandRet;
291
292
0
    require_noerr(ret, on_error);
293
0
  }
294
295
0
  if (mOptions.count(kWPANTUNDProperty_IPv6MeshLocalAddress)) {
296
0
    {
297
0
      struct in6_addr addr = any_to_ipv6(mOptions[kWPANTUNDProperty_IPv6MeshLocalAddress]);
298
0
      mNextCommand = SpinelPackData(
299
0
        SPINEL_FRAME_PACK_CMD_PROP_VALUE_SET(SPINEL_DATATYPE_IPv6ADDR_S SPINEL_DATATYPE_UINT8_S),
300
0
        SPINEL_PROP_IPV6_ML_PREFIX,
301
0
        &addr,
302
0
        64
303
0
      );
304
0
    }
305
306
0
    EH_SPAWN(&mSubPT, vprocess_send_command(event, args));
307
308
0
    ret = mNextCommandRet;
309
310
0
    require_noerr(ret, on_error);
311
0
  } else if (mOptions.count(kWPANTUNDProperty_IPv6MeshLocalPrefix)) {
312
0
    {
313
0
      struct in6_addr addr = any_to_ipv6(mOptions[kWPANTUNDProperty_IPv6MeshLocalPrefix]);
314
0
      mNextCommand = SpinelPackData(
315
0
        SPINEL_FRAME_PACK_CMD_PROP_VALUE_SET(SPINEL_DATATYPE_IPv6ADDR_S SPINEL_DATATYPE_UINT8_S),
316
0
        SPINEL_PROP_IPV6_ML_PREFIX,
317
0
        &addr,
318
0
        64
319
0
      );
320
0
    }
321
322
0
    EH_SPAWN(&mSubPT, vprocess_send_command(event, args));
323
324
0
    ret = mNextCommandRet;
325
326
0
    require_noerr(ret, on_error);
327
0
  }
328
329
330
  // Now bring up the network by bringing up the interface and the stack.
331
332
0
  mNextCommand = SpinelPackData(
333
0
    SPINEL_FRAME_PACK_CMD_PROP_VALUE_SET(SPINEL_DATATYPE_BOOL_S),
334
0
    SPINEL_PROP_NET_IF_UP,
335
0
    true
336
0
  );
337
338
0
  EH_SPAWN(&mSubPT, vprocess_send_command(event, args));
339
340
0
  ret = mNextCommandRet;
341
342
0
  require((ret == kWPANTUNDStatus_Ok) || (ret == kWPANTUNDStatus_Already), on_error);
343
344
0
  mNextCommand = SpinelPackData(
345
0
    SPINEL_FRAME_PACK_CMD_PROP_VALUE_SET(SPINEL_DATATYPE_BOOL_S),
346
0
    SPINEL_PROP_NET_REQUIRE_JOIN_EXISTING,
347
0
    true
348
0
  );
349
350
0
  EH_SPAWN(&mSubPT, vprocess_send_command(event, args));
351
352
0
  ret = mNextCommandRet;
353
354
0
  check_noerr(ret);
355
356
0
  mNextCommand = SpinelPackData(
357
0
    SPINEL_FRAME_PACK_CMD_PROP_VALUE_SET(SPINEL_DATATYPE_BOOL_S),
358
0
    SPINEL_PROP_NET_STACK_UP,
359
0
    true
360
0
  );
361
362
0
  EH_SPAWN(&mSubPT, vprocess_send_command(event, args));
363
364
0
  ret = mNextCommandRet;
365
366
0
  require_noerr(ret, on_error);
367
368
0
  EH_REQUIRE_WITHIN(
369
0
    NCP_JOIN_TIMEOUT,
370
0
    ((last_status >= SPINEL_STATUS_JOIN__BEGIN) && (last_status < SPINEL_STATUS_JOIN__END))
371
0
    || ncp_state_is_associated(mInstance->get_ncp_state()),
372
0
    on_error
373
0
  );
374
375
0
  ret = last_status
376
0
    ? WPANTUND_NCPERROR_TO_STATUS(last_status)
377
0
    : kWPANTUNDStatus_Ok;
378
379
0
  if ( (last_status == SPINEL_STATUS_JOIN_SECURITY)
380
0
    || (last_status == SPINEL_STATUS_JOIN_FAILURE)
381
0
  ) {
382
0
    mInstance->change_ncp_state(CREDENTIALS_NEEDED);
383
0
    ret = kWPANTUNDStatus_InProgress;
384
0
  } else if ((last_status >= SPINEL_STATUS_JOIN__BEGIN) && (last_status < SPINEL_STATUS_JOIN__END)) {
385
0
    ret = kWPANTUNDStatus_JoinFailedUnknown;
386
0
  }
387
388
0
  finish(ret);
389
390
0
  EH_EXIT();
391
392
0
on_error:
393
394
0
  if (ret == kWPANTUNDStatus_Ok) {
395
0
    ret = kWPANTUNDStatus_Failure;
396
0
  }
397
398
0
  syslog(LOG_ERR, "Join failed: %d", ret);
399
400
0
  finish(ret);
401
402
0
  EH_END();
403
0
}