Coverage Report

Created: 2026-05-30 06:55

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wpantund/src/ncp-spinel/SpinelNCPTaskForm.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 "SpinelNCPTaskForm.h"
28
#include "SpinelNCPInstance.h"
29
#include "SpinelNCPTaskScan.h"
30
#include "any-to.h"
31
#include "spinel-extra.h"
32
#include "sec-random.h"
33
34
using namespace nl;
35
using namespace nl::wpantund;
36
37
nl::wpantund::SpinelNCPTaskForm::SpinelNCPTaskForm(
38
  SpinelNCPInstance* instance,
39
  CallbackWithStatusArg1 cb,
40
  const ValueMap& options
41
0
):  SpinelNCPTask(instance, cb), mOptions(options), mLastState(instance->get_ncp_state())
42
0
{
43
0
  if (!mOptions.count(kWPANTUNDProperty_NetworkPANID)) {
44
0
    uint16_t panid = instance->mCurrentNetworkInstance.panid;
45
46
0
    if (panid == 0xffff) {
47
0
      sec_random_fill(reinterpret_cast<uint8_t*>(&panid), sizeof(panid));
48
0
    }
49
50
0
    mOptions[kWPANTUNDProperty_NetworkPANID] = panid;
51
0
  }
52
53
0
  if (!mOptions.count(kWPANTUNDProperty_NetworkXPANID)) {
54
0
    uint64_t xpanid = 0;
55
56
0
    if (instance->mXPANIDWasExplicitlySet) {
57
0
      xpanid = instance->mCurrentNetworkInstance.get_xpanid_as_uint64();
58
0
    }
59
60
0
    if (xpanid == 0) {
61
0
      sec_random_fill(reinterpret_cast<uint8_t*>(&xpanid), sizeof(xpanid));
62
0
    }
63
64
0
    mOptions[kWPANTUNDProperty_NetworkXPANID] = xpanid;
65
0
  }
66
67
68
0
  if (!mOptions.count(kWPANTUNDProperty_IPv6MeshLocalAddress)) {
69
0
    union {
70
0
      uint64_t xpanid;
71
0
      uint8_t bytes[1];
72
0
    } x = { any_to_uint64(mOptions[kWPANTUNDProperty_NetworkXPANID], /* expect_hex_str */ true) };
73
74
0
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
75
0
    reverse_bytes(x.bytes, sizeof(x.xpanid));
76
0
#endif
77
78
    // Default ML Prefix to be derived from XPANID.
79
0
    struct in6_addr mesh_local_prefix = {{{
80
0
      0xfd, x.bytes[0], x.bytes[1], x.bytes[2], x.bytes[3], x.bytes[4], 0, 0
81
0
    }}};
82
83
0
    mOptions[kWPANTUNDProperty_IPv6MeshLocalAddress] = mesh_local_prefix;
84
0
  }
85
86
0
  if (instance->mNetworkKey.empty()) {
87
0
    if (!mOptions.count(kWPANTUNDProperty_NetworkKey)) {
88
0
      uint8_t net_key[NCP_NETWORK_KEY_SIZE];
89
90
0
      sec_random_fill(net_key, sizeof(net_key));
91
92
0
      mOptions[kWPANTUNDProperty_NetworkKey] = Data(net_key, sizeof(net_key));
93
0
    }
94
95
0
    if (!mOptions.count(kWPANTUNDProperty_NetworkKeyIndex)) {
96
0
      mOptions[kWPANTUNDProperty_NetworkKeyIndex] = 1;
97
0
    }
98
0
  }
99
0
}
100
101
void
102
nl::wpantund::SpinelNCPTaskForm::finish(int status, const boost::any& value)
103
0
{
104
0
  if (!ncp_state_is_associated(mInstance->get_ncp_state())) {
105
0
    mInstance->change_ncp_state(mLastState);
106
0
  }
107
0
  SpinelNCPTask::finish(status, value);
108
0
}
109
110
int
111
nl::wpantund::SpinelNCPTaskForm::vprocess_event(int event, va_list args)
112
0
{
113
0
  int ret = kWPANTUNDStatus_Failure;
114
115
0
  EH_BEGIN();
116
117
118
0
  if (!mInstance->mEnabled) {
119
0
    ret = kWPANTUNDStatus_InvalidWhenDisabled;
120
0
    finish(ret);
121
0
    EH_EXIT();
122
0
  }
123
124
0
  if (mInstance->get_ncp_state() == UPGRADING) {
125
0
    ret = kWPANTUNDStatus_InvalidForCurrentState;
126
0
    finish(ret);
127
0
    EH_EXIT();
128
0
  }
129
130
  // Wait for a bit to see if the NCP will enter the right state.
131
0
  EH_REQUIRE_WITHIN(
132
0
    NCP_DEFAULT_COMMAND_RESPONSE_TIMEOUT,
133
0
    !ncp_state_is_initializing(mInstance->get_ncp_state()) && !mInstance->is_initializing_ncp(),
134
0
    on_error
135
0
  );
136
137
0
  if (ncp_state_is_associated(mInstance->get_ncp_state())) {
138
0
    ret = kWPANTUNDStatus_Already;
139
0
    finish(ret);
140
0
    EH_EXIT();
141
0
  }
142
143
0
  if (!mInstance->mCapabilities.count(SPINEL_CAP_ROLE_ROUTER)) {
144
    // We can't form unless we are router-capable
145
0
    ret = kWPANTUNDStatus_FeatureNotSupported;
146
0
    finish(ret);
147
0
    EH_EXIT();
148
0
  }
149
150
  // The first event to a task is EVENT_STARTING_TASK. The following
151
  // line makes sure that we don't start processing this task
152
  // until it is properly scheduled. All tasks immediately receive
153
  // the initial `EVENT_STARTING_TASK` event, but further events
154
  // will only be received by that task once it is that task's turn
155
  // to execute.
156
0
  EH_WAIT_UNTIL(EVENT_STARTING_TASK != event);
157
158
0
  mLastState = mInstance->get_ncp_state();
159
0
  mInstance->change_ncp_state(ASSOCIATING);
160
161
  // Clear any previously saved network settings
162
0
  mNextCommand = SpinelPackData(SPINEL_FRAME_PACK_CMD_NET_CLEAR);
163
0
  EH_SPAWN(&mSubPT, vprocess_send_command(event, args));
164
0
  ret = mNextCommandRet;
165
166
0
  check_noerr(ret);
167
168
  // TODO: We should do a scan to make sure we pick a good channel
169
  //       and don't have a panid collision.
170
171
  // Set the channel
172
0
  {
173
0
    uint8_t channel;
174
175
0
    if (mOptions.count(kWPANTUNDProperty_NCPChannel)) {
176
0
      channel = any_to_int(mOptions[kWPANTUNDProperty_NCPChannel]);
177
178
      // Make sure the channel is the supported channel set.
179
0
      if ((mInstance->mSupportedChannelMask & (1U << channel)) == 0) {
180
0
        syslog(LOG_ERR, "Channel %d is not supported by NCP. Supported channels mask is %08x",
181
0
          channel,
182
0
          mInstance->mSupportedChannelMask
183
0
        );
184
0
        ret = kWPANTUNDStatus_InvalidArgument;
185
0
        goto on_error;
186
0
      }
187
188
0
    } else {
189
0
      uint32_t mask;
190
191
0
      if (mOptions.count(kWPANTUNDProperty_NCPChannelMask)) {
192
0
        mask = any_to_int(mOptions[kWPANTUNDProperty_NCPChannelMask]);
193
0
      } else {
194
0
        mask = mInstance->mSupportedChannelMask;
195
0
      }
196
197
0
      if ((mask & mInstance->mSupportedChannelMask) == 0) {
198
0
        syslog(LOG_ERR, "Invalid channel mask 0x%08x. Supported channels mask is 0x%08x",
199
0
          mask,
200
0
          mInstance->mSupportedChannelMask
201
0
        );
202
0
        ret = kWPANTUNDStatus_InvalidArgument;
203
0
        goto on_error;
204
0
      }
205
206
0
      mask &= mInstance->mSupportedChannelMask;
207
208
      // Choose preferred channel mask if it has.
209
0
      if ((mask & mInstance->mPreferredChannelMask) != 0) {
210
0
        mask &= mInstance->mPreferredChannelMask;
211
0
      }
212
213
      // Randomly pick a channel from the given channel mask for now.
214
0
      do {
215
0
        sec_random_fill(&channel, 1);
216
0
        channel = (channel % 32);
217
0
      } while (0 == ((1 << channel) & mask));
218
0
    }
219
220
0
    mNextCommand = SpinelPackData(
221
0
      SPINEL_FRAME_PACK_CMD_PROP_VALUE_SET(SPINEL_DATATYPE_UINT8_S),
222
0
      SPINEL_PROP_PHY_CHAN,
223
0
      channel
224
0
    );
225
0
  }
226
227
0
  EH_SPAWN(&mSubPT, vprocess_send_command(event, args));
228
229
0
  ret = mNextCommandRet;
230
231
0
  require_noerr(ret, on_error);
232
233
  // Turn off promiscuous mode, if it happens to be on
234
0
  mNextCommand = SpinelPackData(
235
0
    SPINEL_FRAME_PACK_CMD_PROP_VALUE_SET(SPINEL_DATATYPE_UINT8_S),
236
0
    SPINEL_PROP_MAC_PROMISCUOUS_MODE,
237
0
    SPINEL_MAC_PROMISCUOUS_MODE_OFF
238
0
  );
239
240
0
  EH_SPAWN(&mSubPT, vprocess_send_command(event, args));
241
0
  ret = mNextCommandRet;
242
0
  check_noerr(ret);
243
244
0
  if (mOptions.count(kWPANTUNDProperty_NetworkPANID)) {
245
0
    mNextCommand = SpinelPackData(
246
0
      SPINEL_FRAME_PACK_CMD_PROP_VALUE_SET(SPINEL_DATATYPE_UINT16_S),
247
0
      SPINEL_PROP_MAC_15_4_PANID,
248
0
      any_to_int(mOptions[kWPANTUNDProperty_NetworkPANID])
249
0
    );
250
251
0
    EH_SPAWN(&mSubPT, vprocess_send_command(event, args));
252
253
0
    ret = mNextCommandRet;
254
255
0
    require_noerr(ret, on_error);
256
0
  }
257
258
0
  if (mOptions.count(kWPANTUNDProperty_NetworkXPANID)) {
259
0
    {
260
0
      uint64_t xpanid(any_to_uint64(mOptions[kWPANTUNDProperty_NetworkXPANID], /* expect_hex_str */ true));
261
262
0
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
263
0
      reverse_bytes(reinterpret_cast<uint8_t*>(&xpanid), sizeof(xpanid));
264
0
#endif
265
266
0
      mNextCommand = SpinelPackData(
267
0
        SPINEL_FRAME_PACK_CMD_PROP_VALUE_SET(SPINEL_DATATYPE_DATA_S),
268
0
        SPINEL_PROP_NET_XPANID,
269
0
        &xpanid,
270
0
        sizeof(xpanid)
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_NetworkName)) {
282
0
    mNextCommand = SpinelPackData(
283
0
      SPINEL_FRAME_PACK_CMD_PROP_VALUE_SET(SPINEL_DATATYPE_UTF8_S),
284
0
      SPINEL_PROP_NET_NETWORK_NAME,
285
0
      any_to_string(mOptions[kWPANTUNDProperty_NetworkName]).c_str()
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_NetworkKey)) {
296
0
    {
297
0
      nl::Data data(any_to_data(mOptions[kWPANTUNDProperty_NetworkKey]));
298
0
      mNextCommand = SpinelPackData(
299
0
        SPINEL_FRAME_PACK_CMD_PROP_VALUE_SET(SPINEL_DATATYPE_DATA_S),
300
0
        SPINEL_PROP_NET_MASTER_KEY,
301
0
        data.data(),
302
0
        data.size()
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
  }
312
313
0
  if (mOptions.count(kWPANTUNDProperty_NetworkKeyIndex)) {
314
0
    mNextCommand = SpinelPackData(
315
0
      SPINEL_FRAME_PACK_CMD_PROP_VALUE_SET(SPINEL_DATATYPE_UINT32_S),
316
0
      SPINEL_PROP_NET_KEY_SEQUENCE_COUNTER,
317
0
      any_to_int(mOptions[kWPANTUNDProperty_NetworkKeyIndex])
318
0
    );
319
320
0
    EH_SPAWN(&mSubPT, vprocess_send_command(event, args));
321
322
0
    ret = mNextCommandRet;
323
324
0
    require_noerr(ret, on_error);
325
0
  }
326
327
0
  if (mOptions.count(kWPANTUNDProperty_IPv6MeshLocalPrefix)) {
328
0
    {
329
0
      struct in6_addr addr = any_to_ipv6(mOptions[kWPANTUNDProperty_IPv6MeshLocalPrefix]);
330
0
      mNextCommand = SpinelPackData(
331
0
        SPINEL_FRAME_PACK_CMD_PROP_VALUE_SET(SPINEL_DATATYPE_IPv6ADDR_S SPINEL_DATATYPE_UINT8_S),
332
0
        SPINEL_PROP_IPV6_ML_PREFIX,
333
0
        &addr,
334
0
        64
335
0
      );
336
0
    }
337
338
0
    EH_SPAWN(&mSubPT, vprocess_send_command(event, args));
339
340
0
    ret = mNextCommandRet;
341
342
0
    require_noerr(ret, on_error);
343
0
  } else if (mOptions.count(kWPANTUNDProperty_IPv6MeshLocalAddress)) {
344
0
    {
345
0
      struct in6_addr addr = any_to_ipv6(mOptions[kWPANTUNDProperty_IPv6MeshLocalAddress]);
346
0
      mNextCommand = SpinelPackData(
347
0
        SPINEL_FRAME_PACK_CMD_PROP_VALUE_SET(SPINEL_DATATYPE_IPv6ADDR_S SPINEL_DATATYPE_UINT8_S),
348
0
        SPINEL_PROP_IPV6_ML_PREFIX,
349
0
        &addr,
350
0
        64
351
0
      );
352
0
    }
353
354
0
    EH_SPAWN(&mSubPT, vprocess_send_command(event, args));
355
356
0
    ret = mNextCommandRet;
357
358
0
    require_noerr(ret, on_error);
359
0
  }
360
361
0
  if (mOptions.count(kWPANTUNDProperty_NestLabs_LegacyMeshLocalPrefix)) {
362
0
    if (mInstance->mCapabilities.count(SPINEL_CAP_NEST_LEGACY_INTERFACE)) {
363
0
      {
364
0
        nl::Data data(any_to_data(mOptions[kWPANTUNDProperty_NestLabs_LegacyMeshLocalPrefix]));
365
0
        mNextCommand = SpinelPackData(
366
0
          SPINEL_FRAME_PACK_CMD_PROP_VALUE_SET(SPINEL_DATATYPE_DATA_S),
367
0
          SPINEL_PROP_NEST_LEGACY_ULA_PREFIX,
368
0
          data.data(),
369
0
          data.size()
370
0
        );
371
0
      }
372
373
0
      EH_SPAWN(&mSubPT, vprocess_send_command(event, args));
374
375
0
      ret = mNextCommandRet;
376
377
0
      require_noerr(ret, on_error);
378
0
    }
379
0
  }
380
381
  // Now bring up the network by bringing up the interface and the stack.
382
383
0
  mNextCommand = SpinelPackData(
384
0
    SPINEL_FRAME_PACK_CMD_PROP_VALUE_SET(SPINEL_DATATYPE_BOOL_S),
385
0
    SPINEL_PROP_NET_IF_UP,
386
0
    true
387
0
  );
388
389
0
  EH_SPAWN(&mSubPT, vprocess_send_command(event, args));
390
391
0
  ret = mNextCommandRet;
392
393
0
  require(ret == kWPANTUNDStatus_Ok || ret == kWPANTUNDStatus_Already, on_error);
394
395
0
  mNextCommand = SpinelPackData(
396
0
    SPINEL_FRAME_PACK_CMD_PROP_VALUE_SET(SPINEL_DATATYPE_BOOL_S),
397
0
    SPINEL_PROP_NET_STACK_UP,
398
0
    true
399
0
  );
400
401
0
  EH_SPAWN(&mSubPT, vprocess_send_command(event, args));
402
403
0
  ret = mNextCommandRet;
404
405
0
  require_noerr(ret, on_error);
406
407
0
  EH_REQUIRE_WITHIN(
408
0
    NCP_FORM_TIMEOUT,
409
0
    ncp_state_is_associated(mInstance->get_ncp_state()),
410
0
    on_error
411
0
  );
412
413
0
  ret = kWPANTUNDStatus_Ok;
414
415
0
  finish(ret);
416
417
0
  EH_EXIT();
418
419
0
on_error:
420
421
0
  if (ret == kWPANTUNDStatus_Ok) {
422
0
    ret = kWPANTUNDStatus_Failure;
423
0
  }
424
425
0
  syslog(LOG_ERR, "Form failed: %d", ret);
426
427
0
  finish(ret);
428
429
0
  EH_END();
430
0
}