friend.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633
  1. // SPDX-License-Identifier: LGPL-2.1-or-later
  2. /*
  3. *
  4. * BlueZ - Bluetooth protocol stack for Linux
  5. *
  6. * Copyright (C) 2018-2019 Intel Corporation. All rights reserved.
  7. *
  8. *
  9. */
  10. #ifdef HAVE_CONFIG_H
  11. #include <config.h>
  12. #endif
  13. #include <ell/ell.h>
  14. #include "mesh/mesh-defs.h"
  15. #include "mesh/net-keys.h"
  16. #include "mesh/net.h"
  17. #include "mesh/model.h"
  18. #include "mesh/util.h"
  19. #include "mesh/friend.h"
  20. #define MAX_FRND_GROUPS 20
  21. #define FRND_RELAY_WINDOW 250 /* 250 ms */
  22. #define FRND_CACHE_SIZE 16
  23. #define FRND_SUB_LIST_SIZE 8
  24. #define RESPONSE_DELAY (100 - 12) /* 100 ms - 12ms hw delay */
  25. #define MIN_RESP_DELAY 10 /* 10 ms */
  26. #define MAX_RESP_DELAY 255 /* 255 ms */
  27. /* Absolute maximum time to wait for LPN to choose us. */
  28. #define RESPONSE_POLL_DELAY 1300 /* 1.300 s */
  29. static uint8_t frnd_relay_window = FRND_RELAY_WINDOW;
  30. static uint8_t frnd_cache_size = FRND_CACHE_SIZE;
  31. static uint8_t frnd_sublist_size = FRND_SUB_LIST_SIZE;
  32. static uint16_t counter;
  33. static struct l_queue *retired_lpns;
  34. static void response_timeout(struct l_timeout *timeout, void *user_data)
  35. {
  36. struct mesh_friend *neg = user_data;
  37. struct l_queue *negotiations = mesh_net_get_negotiations(neg->net);
  38. /* LPN did not choose us */
  39. l_debug("Did not win negotiation for %4.4x", neg->lp_addr);
  40. net_key_unref(neg->net_key_cur);
  41. net_key_unref(neg->net_key_upd);
  42. l_queue_remove(negotiations, neg);
  43. l_timeout_remove(timeout);
  44. l_free(neg);
  45. }
  46. static void response_delay(struct l_timeout *timeout, void *user_data)
  47. {
  48. struct mesh_friend *neg = user_data;
  49. uint16_t net_idx = neg->net_idx;
  50. uint32_t key_id, seq;
  51. uint8_t msg[8];
  52. uint16_t n = 0;
  53. bool res;
  54. l_timeout_remove(timeout);
  55. /* Create key Set for this offer */
  56. res = mesh_net_get_key(neg->net, false, net_idx, &key_id);
  57. if (!res)
  58. goto cleanup;
  59. neg->net_key_cur = net_key_frnd_add(key_id, neg->lp_addr,
  60. mesh_net_get_address(neg->net),
  61. neg->lp_cnt, counter);
  62. if (!neg->net_key_cur)
  63. goto cleanup;
  64. neg->fn_cnt = counter++;
  65. msg[n++] = NET_OP_FRND_OFFER;
  66. msg[n++] = frnd_relay_window;
  67. msg[n++] = frnd_cache_size;
  68. msg[n++] = frnd_sublist_size;
  69. msg[n++] = neg->u.negotiate.rssi;
  70. l_put_be16(neg->fn_cnt, msg + n);
  71. n += 2;
  72. seq = mesh_net_next_seq_num(neg->net);
  73. print_packet("Tx-NET_OP_FRND_OFFER", msg, n);
  74. mesh_net_transport_send(neg->net, key_id, 0,
  75. mesh_net_get_iv_index(neg->net), 0,
  76. seq, 0, neg->lp_addr,
  77. msg, n);
  78. /* Offer expires in 1.3 seconds, which is the max time for LPN to
  79. * receive all offers, 1 second to make decision, and a little extra
  80. */
  81. neg->timeout = l_timeout_create_ms(1000 + MAX_RESP_DELAY,
  82. response_timeout, neg, NULL);
  83. return;
  84. cleanup:
  85. net_key_unref(neg->net_key_cur);
  86. net_key_unref(neg->net_key_upd);
  87. l_queue_remove(mesh_net_get_negotiations(neg->net), neg);
  88. l_free(neg);
  89. }
  90. static uint8_t cache_size(uint8_t power)
  91. {
  92. return 1 << power;
  93. }
  94. static bool match_by_lpn(const void *a, const void *b)
  95. {
  96. const struct mesh_friend *neg = a;
  97. uint16_t lpn = L_PTR_TO_UINT(b);
  98. return neg->lp_addr == lpn;
  99. }
  100. /* Scaling factors in 1/10 ms */
  101. static const int32_t scaling[] = {
  102. 10,
  103. 15,
  104. 20,
  105. 15,
  106. };
  107. void friend_request(struct mesh_net *net, uint16_t net_idx, uint16_t src,
  108. uint8_t minReq, uint8_t delay, uint32_t timeout,
  109. uint16_t prev, uint8_t num_ele, uint16_t cntr,
  110. int8_t rssi)
  111. {
  112. struct l_queue *negotiations = mesh_net_get_negotiations(net);
  113. struct mesh_friend *neg;
  114. uint8_t rssiScale = (minReq >> 5) & 3;
  115. uint8_t winScale = (minReq >> 3) & 3;
  116. uint8_t minCache = (minReq >> 0) & 7;
  117. int32_t rsp_delay;
  118. l_debug("RSSI of Request: %d dbm", rssi);
  119. l_debug("Delay: %d ms", delay);
  120. l_debug("Poll Timeout of Request: %d ms", timeout * 100);
  121. l_debug("Previous Friend: %4.4x", prev);
  122. l_debug("Num Elem: %2.2x", num_ele);
  123. l_debug("Cache Requested: %d", cache_size(minCache));
  124. l_debug("Cache to offer: %d", frnd_cache_size);
  125. /* Determine our own suitability before
  126. * deciding to participate in negotiation
  127. */
  128. if (minCache == 0 || num_ele == 0)
  129. return;
  130. if (delay < 0x0A)
  131. return;
  132. if (timeout < 0x00000A || timeout > 0x34BBFF)
  133. return;
  134. if (cache_size(minCache) > frnd_cache_size)
  135. return;
  136. /* TODO: Check RSSI, and then start Negotiation if appropriate */
  137. /* We are participating in this Negotiation */
  138. neg = l_new(struct mesh_friend, 1);
  139. l_queue_push_head(negotiations, neg);
  140. neg->net = net;
  141. neg->lp_addr = src;
  142. neg->lp_cnt = cntr;
  143. neg->u.negotiate.rssi = rssi;
  144. neg->receive_delay = delay;
  145. neg->poll_timeout = timeout;
  146. neg->old_friend = prev;
  147. neg->ele_cnt = num_ele;
  148. neg->net_idx = net_idx;
  149. /* RSSI (Negative Factor, larger values == less time)
  150. * Scaling factor 0-3 == multiplier of 1.0 - 2.5
  151. * Minimum factor of 1. Bit 1 adds additional factor
  152. * of 1, bit zero and additional 0.5
  153. */
  154. rsp_delay = -(rssi * scaling[rssiScale]);
  155. l_debug("RSSI Factor: %d ms", rsp_delay / 10);
  156. /* Relay Window (Positive Factor, larger values == more time)
  157. * Scaling factor 0-3 == multiplier of 1.0 - 2.5
  158. * Minimum factor of 1. Bit 1 adds additional factor
  159. * of 1, bit zero and additional 0.5
  160. */
  161. rsp_delay += frnd_relay_window * scaling[winScale];
  162. l_debug("Win Size Factor: %d ms",
  163. (frnd_relay_window * scaling[winScale]) / 10);
  164. /* Normalize to ms */
  165. rsp_delay /= 10;
  166. /* Range limits are 10-255 ms */
  167. if (rsp_delay < MIN_RESP_DELAY)
  168. rsp_delay = MIN_RESP_DELAY;
  169. else if (rsp_delay > MAX_RESP_DELAY)
  170. rsp_delay = MAX_RESP_DELAY;
  171. l_debug("Total Response Delay: %d ms", rsp_delay);
  172. /* Add in 100ms delay before start of "Offer Period" */
  173. rsp_delay += RESPONSE_DELAY;
  174. neg->timeout = l_timeout_create_ms(rsp_delay,
  175. response_delay, neg, NULL);
  176. }
  177. void friend_clear_confirm(struct mesh_net *net, uint16_t src,
  178. uint16_t lpn, uint16_t lpnCounter)
  179. {
  180. struct l_queue *negotiations = mesh_net_get_negotiations(net);
  181. struct mesh_friend *neg = l_queue_remove_if(negotiations,
  182. match_by_lpn, L_UINT_TO_PTR(lpn));
  183. l_debug("Friend Clear confirmed %4.4x (cnt %4.4x)", lpn, lpnCounter);
  184. if (!neg)
  185. return;
  186. l_timeout_remove(neg->timeout);
  187. l_queue_remove(negotiations, neg);
  188. l_free(neg);
  189. }
  190. static void friend_poll_timeout(struct l_timeout *timeout, void *user_data)
  191. {
  192. struct mesh_friend *frnd = user_data;
  193. if (mesh_friend_clear(frnd->net, frnd))
  194. l_debug("Friend Poll Timeout %4.4x", frnd->lp_addr);
  195. l_timeout_remove(frnd->timeout);
  196. frnd->timeout = NULL;
  197. /* Friend may be in either Network or Retired list, so try both */
  198. l_queue_remove(retired_lpns, frnd);
  199. mesh_friend_free(frnd);
  200. }
  201. void friend_clear(struct mesh_net *net, uint16_t src, uint16_t lpn,
  202. uint16_t lpnCounter, struct mesh_friend *frnd)
  203. {
  204. struct l_queue *negotiations = mesh_net_get_negotiations(net);
  205. uint8_t msg[5] = { NET_OP_FRND_CLEAR_CONFIRM };
  206. bool removed = false;
  207. uint16_t lpnDelta;
  208. if (frnd) {
  209. lpnDelta = lpnCounter - frnd->lp_cnt;
  210. /* Ignore old Friend Clear commands */
  211. if (lpnDelta > 0x100)
  212. return;
  213. /* Move friend from Network list to Retired list */
  214. removed = mesh_friend_clear(net, frnd);
  215. if (removed) {
  216. struct mesh_friend *neg, *old;
  217. neg = l_queue_remove_if(negotiations, match_by_lpn,
  218. L_UINT_TO_PTR(lpn));
  219. /* Cancel any negotiations or clears */
  220. if (neg) {
  221. l_timeout_remove(neg->timeout);
  222. l_free(neg);
  223. }
  224. /* Find any duplicates */
  225. old = l_queue_find(retired_lpns, match_by_lpn,
  226. L_UINT_TO_PTR(lpn));
  227. /* Force time-out of old friendship */
  228. if (old)
  229. friend_poll_timeout(old->timeout, old);
  230. if (!retired_lpns)
  231. retired_lpns = l_queue_new();
  232. /* Retire this LPN (keeps timeout running) */
  233. l_queue_push_tail(retired_lpns, frnd);
  234. }
  235. } else {
  236. frnd = l_queue_find(retired_lpns, match_by_lpn,
  237. L_UINT_TO_PTR(lpn));
  238. if (!frnd)
  239. return;
  240. lpnDelta = lpnCounter - frnd->lp_cnt;
  241. /* Ignore old Friend Clear commands */
  242. if (!lpnDelta || (lpnDelta > 0x100))
  243. return;
  244. }
  245. l_debug("Friend Cleared %4.4x (%4.4x)", lpn, lpnCounter);
  246. l_put_be16(lpn, msg + 1);
  247. l_put_be16(lpnCounter, msg + 3);
  248. mesh_net_transport_send(net, 0, 0,
  249. mesh_net_get_iv_index(net), DEFAULT_TTL,
  250. 0, 0, src,
  251. msg, sizeof(msg));
  252. }
  253. static void clear_retry(struct l_timeout *timeout, void *user_data)
  254. {
  255. struct mesh_friend *neg = user_data;
  256. struct l_queue *negotiations = mesh_net_get_negotiations(neg->net);
  257. uint8_t msg[5] = { NET_OP_FRND_CLEAR };
  258. uint32_t secs = 1 << neg->receive_delay;
  259. l_put_be16(neg->lp_addr, msg + 1);
  260. l_put_be16(neg->lp_cnt, msg + 3);
  261. mesh_net_transport_send(neg->net, 0, 0,
  262. mesh_net_get_iv_index(neg->net), DEFAULT_TTL,
  263. 0, 0, neg->old_friend,
  264. msg, sizeof(msg));
  265. if (secs && ((secs << 1) < neg->poll_timeout/10)) {
  266. neg->receive_delay++;
  267. l_debug("Try FRND_CLR again in %d seconds (total timeout %d)",
  268. secs, neg->poll_timeout/10);
  269. l_timeout_modify(neg->timeout, secs);
  270. } else {
  271. l_debug("FRND_CLR timed out %d", secs);
  272. l_timeout_remove(timeout);
  273. l_queue_remove(negotiations, neg);
  274. l_free(neg);
  275. }
  276. }
  277. static void friend_delay_rsp(struct l_timeout *timeout, void *user_data)
  278. {
  279. struct mesh_friend *frnd = user_data;
  280. struct mesh_friend_msg *pkt = frnd->pkt;
  281. struct mesh_net *net = frnd->net;
  282. uint32_t net_seq, iv_index;
  283. uint8_t upd[7] = { NET_OP_FRND_UPDATE };
  284. l_timeout_remove(timeout);
  285. if (pkt == NULL)
  286. goto update;
  287. if (pkt->ctl) {
  288. /* Make sure we don't change the bit-sense of MD,
  289. * once it has been set because that would cause
  290. * a "Dirty Nonce" security violation
  291. */
  292. if (((pkt->u.one[0].hdr >> OPCODE_HDR_SHIFT) & OPCODE_MASK) ==
  293. NET_OP_SEG_ACKNOWLEDGE) {
  294. bool rly = !!((pkt->u.one[0].hdr >> RELAY_HDR_SHIFT) &
  295. true);
  296. uint16_t seqZero = pkt->u.one[0].hdr >>
  297. SEQ_ZERO_HDR_SHIFT;
  298. seqZero &= SEQ_ZERO_MASK;
  299. l_debug("Fwd ACK pkt %6.6x-%8.8x",
  300. pkt->u.one[0].seq,
  301. pkt->iv_index);
  302. pkt->u.one[0].sent = true;
  303. mesh_net_ack_send(net, frnd->net_key_cur,
  304. pkt->iv_index, pkt->ttl,
  305. pkt->u.one[0].seq, pkt->src, pkt->dst,
  306. rly, seqZero,
  307. l_get_be32(pkt->u.one[0].data));
  308. } else {
  309. l_debug("Fwd CTL pkt %6.6x-%8.8x",
  310. pkt->u.one[0].seq,
  311. pkt->iv_index);
  312. print_packet("Frnd-CTL",
  313. pkt->u.one[0].data, pkt->last_len);
  314. pkt->u.one[0].sent = true;
  315. mesh_net_transport_send(net, frnd->net_key_cur, 0,
  316. pkt->iv_index, pkt->ttl,
  317. pkt->u.one[0].seq, pkt->src, pkt->dst,
  318. pkt->u.one[0].data, pkt->last_len);
  319. }
  320. } else {
  321. /* If segments after this one, then More Data must be TRUE */
  322. uint8_t len;
  323. if (pkt->cnt_out < pkt->cnt_in)
  324. len = sizeof(pkt->u.s12[0].data);
  325. else
  326. len = pkt->last_len;
  327. l_debug("Fwd FRND pkt %6.6x",
  328. pkt->u.s12[pkt->cnt_out].seq);
  329. print_packet("Frnd-Msg", pkt->u.s12[pkt->cnt_out].data, len);
  330. pkt->u.s12[pkt->cnt_out].sent = true;
  331. mesh_net_send_seg(net, frnd->net_key_cur,
  332. pkt->iv_index,
  333. pkt->ttl,
  334. pkt->u.s12[pkt->cnt_out].seq,
  335. pkt->src, pkt->dst,
  336. pkt->u.s12[pkt->cnt_out].hdr,
  337. pkt->u.s12[pkt->cnt_out].data, len);
  338. }
  339. return;
  340. update:
  341. /* No More Data -- send Update message with md = false */
  342. net_seq = mesh_net_get_seq_num(net);
  343. l_debug("Fwd FRND UPDATE %6.6x with MD == 0", net_seq);
  344. frnd->u.active.last = frnd->u.active.seq;
  345. mesh_net_get_snb_state(net, upd + 1, &iv_index);
  346. l_put_be32(iv_index, upd + 2);
  347. upd[6] = false; /* Queue is Empty */
  348. print_packet("Update", upd, sizeof(upd));
  349. mesh_net_transport_send(net, frnd->net_key_cur, 0,
  350. mesh_net_get_iv_index(net), 0,
  351. net_seq, 0, frnd->lp_addr,
  352. upd, sizeof(upd));
  353. mesh_net_next_seq_num(net);
  354. }
  355. void friend_poll(struct mesh_net *net, uint16_t src, bool seq,
  356. struct mesh_friend *frnd)
  357. {
  358. struct l_queue *negotiations = mesh_net_get_negotiations(net);
  359. struct mesh_friend *neg;
  360. struct mesh_friend_msg *pkt;
  361. bool md;
  362. l_debug("POLL-RXED");
  363. neg = l_queue_find(negotiations, match_by_lpn, L_UINT_TO_PTR(src));
  364. if (neg && !neg->u.negotiate.clearing) {
  365. uint8_t msg[5] = { NET_OP_FRND_CLEAR };
  366. l_debug("Won negotiation for %4.4x", neg->lp_addr);
  367. /* This call will clean-up and replace if already friends */
  368. frnd = mesh_friend_new(net, src, neg->ele_cnt,
  369. neg->receive_delay,
  370. neg->frw,
  371. neg->poll_timeout,
  372. neg->fn_cnt, neg->lp_cnt);
  373. frnd->timeout = l_timeout_create_ms(
  374. frnd->poll_timeout * 100,
  375. friend_poll_timeout, frnd, NULL);
  376. l_timeout_remove(neg->timeout);
  377. net_key_unref(neg->net_key_cur);
  378. net_key_unref(neg->net_key_upd);
  379. neg->net_key_upd = neg->net_key_cur = 0;
  380. if (neg->old_friend == 0 ||
  381. neg->old_friend == mesh_net_get_address(net)) {
  382. l_queue_remove(negotiations, neg);
  383. l_free(neg);
  384. } else {
  385. neg->u.negotiate.clearing = true;
  386. l_put_be16(neg->lp_addr, msg + 1);
  387. l_put_be16(neg->lp_cnt, msg + 3);
  388. mesh_net_transport_send(net, 0, 0,
  389. mesh_net_get_iv_index(net), DEFAULT_TTL,
  390. 0, 0, neg->old_friend,
  391. msg, sizeof(msg));
  392. /* Reuse receive_delay as a shift counter to
  393. * time-out FRIEND_CLEAR
  394. */
  395. neg->receive_delay = 1;
  396. neg->timeout = l_timeout_create(1, clear_retry,
  397. neg, NULL);
  398. }
  399. }
  400. if (!frnd)
  401. return;
  402. /* Reset Poll Timeout */
  403. l_timeout_modify_ms(frnd->timeout, frnd->poll_timeout * 100);
  404. if (!l_queue_length(frnd->pkt_cache))
  405. goto update;
  406. if (frnd->u.active.seq != frnd->u.active.last &&
  407. frnd->u.active.seq != seq) {
  408. pkt = l_queue_peek_head(frnd->pkt_cache);
  409. if (pkt->cnt_out < pkt->cnt_in) {
  410. pkt->cnt_out++;
  411. } else {
  412. pkt = l_queue_pop_head(frnd->pkt_cache);
  413. l_free(pkt);
  414. }
  415. }
  416. pkt = l_queue_peek_head(frnd->pkt_cache);
  417. if (!pkt)
  418. goto update;
  419. frnd->u.active.seq = seq;
  420. frnd->u.active.last = !seq;
  421. md = !!(l_queue_length(frnd->pkt_cache) > 1);
  422. if (pkt->ctl) {
  423. /* Make sure we don't change the bit-sense of MD,
  424. * once it has been set because that would cause
  425. * a "Dirty Nonce" security violation
  426. */
  427. if (!(pkt->u.one[0].sent))
  428. pkt->u.one[0].md = md;
  429. } else {
  430. /* If segments after this one, then More Data must be TRUE */
  431. if (pkt->cnt_out < pkt->cnt_in)
  432. md = true;
  433. /* Make sure we don't change the bit-sense of MD, once
  434. * it has been set because that would cause a
  435. * "Dirty Nonce" security violation
  436. */
  437. if (!(pkt->u.s12[pkt->cnt_out].sent))
  438. pkt->u.s12[pkt->cnt_out].md = md;
  439. }
  440. frnd->pkt = pkt;
  441. l_timeout_create_ms(frnd->frd, friend_delay_rsp, frnd, NULL);
  442. return;
  443. update:
  444. frnd->pkt = NULL;
  445. l_timeout_create_ms(frnd->frd, friend_delay_rsp, frnd, NULL);
  446. }
  447. void friend_sub_add(struct mesh_net *net, struct mesh_friend *frnd,
  448. const uint8_t *pkt, uint8_t len)
  449. {
  450. uint16_t *new_list;
  451. uint32_t net_seq;
  452. uint8_t plen = len;
  453. uint8_t msg[] = { NET_OP_PROXY_SUB_CONFIRM, 0 };
  454. if (!frnd || MAX_FRND_GROUPS < frnd->u.active.grp_cnt + (len/2))
  455. return;
  456. msg[1] = *pkt++;
  457. plen--;
  458. /* Sanity Check Values, abort if any illegal */
  459. while (plen >= 2) {
  460. plen -= 2;
  461. if (l_get_be16(pkt + plen) < 0x8000)
  462. return;
  463. }
  464. new_list = l_malloc(frnd->u.active.grp_cnt * sizeof(uint16_t) + len);
  465. if (frnd->u.active.grp_list)
  466. memcpy(new_list, frnd->u.active.grp_list,
  467. frnd->u.active.grp_cnt * sizeof(uint16_t));
  468. while (len >= 2) {
  469. new_list[frnd->u.active.grp_cnt++] = l_get_be16(pkt);
  470. pkt += 2;
  471. len -= 2;
  472. }
  473. l_free(frnd->u.active.grp_list);
  474. frnd->u.active.grp_list = new_list;
  475. print_packet("Tx-NET_OP_PROXY_SUB_CONFIRM", msg, sizeof(msg));
  476. net_seq = mesh_net_get_seq_num(net);
  477. mesh_net_transport_send(net, frnd->net_key_cur, 0,
  478. mesh_net_get_iv_index(net), 0,
  479. net_seq, 0, frnd->lp_addr,
  480. msg, sizeof(msg));
  481. mesh_net_next_seq_num(net);
  482. }
  483. void friend_sub_del(struct mesh_net *net, struct mesh_friend *frnd,
  484. const uint8_t *pkt, uint8_t len)
  485. {
  486. uint32_t net_seq;
  487. uint8_t msg[] = { NET_OP_PROXY_SUB_CONFIRM, 0 };
  488. int i;
  489. if (!frnd)
  490. return;
  491. msg[1] = *pkt++;
  492. len--;
  493. while (len >= 2) {
  494. uint16_t grp = l_get_be16(pkt);
  495. for (i = frnd->u.active.grp_cnt - 1; i >= 0; i--) {
  496. if (frnd->u.active.grp_list[i] == grp) {
  497. frnd->u.active.grp_cnt--;
  498. memcpy(&frnd->u.active.grp_list[i],
  499. &frnd->u.active.grp_list[i + 1],
  500. (frnd->u.active.grp_cnt - i) * 2);
  501. break;
  502. }
  503. }
  504. len -= 2;
  505. pkt += 2;
  506. }
  507. print_packet("Tx-NET_OP_PROXY_SUB_CONFIRM", msg, sizeof(msg));
  508. net_seq = mesh_net_get_seq_num(net);
  509. mesh_net_transport_send(net, frnd->net_key_cur, 0,
  510. mesh_net_get_iv_index(net), 0,
  511. net_seq, 0, frnd->lp_addr,
  512. msg, sizeof(msg));
  513. mesh_net_next_seq_num(net);
  514. }