Relay is now a cooperation between all the nodes on the daemon. If any one or more nodes have relay enabled and a received packet is addressed to a Group, or a non-present Unicast address, then the packet's TTL is decremented and it is resent. --- mesh/crypto.c | 8 +-- mesh/mesh-defs.h | 9 ++- mesh/net.c | 187 ++++++++++++++++++++++++++++++++----------------------- 3 files changed, 118 insertions(+), 86 deletions(-) diff --git a/mesh/crypto.c b/mesh/crypto.c index e68cc2d5f..451e2da71 100644 --- a/mesh/crypto.c +++ b/mesh/crypto.c @@ -1169,13 +1169,13 @@ bool mesh_crypto_packet_parse(const uint8_t *packet, uint8_t packet_len, *payload = packet + 9; if (payload_len) - *payload_len = packet_len - 9 - 8; + *payload_len = packet_len - 9; } else { if (payload) *payload = packet + 10; if (payload_len) - *payload_len = packet_len - 10 - 8; + *payload_len = packet_len - 10; } } else { if (cookie) @@ -1202,13 +1202,13 @@ bool mesh_crypto_packet_parse(const uint8_t *packet, uint8_t packet_len, *payload = packet + 13; if (payload_len) - *payload_len = packet_len - 13 - 4; + *payload_len = packet_len - 13; } else { if (payload) *payload = packet + 10; if (payload_len) - *payload_len = packet_len - 10 - 4; + *payload_len = packet_len - 10; } } diff --git a/mesh/mesh-defs.h b/mesh/mesh-defs.h index d40fc43e8..c30041e4a 100644 --- a/mesh/mesh-defs.h +++ b/mesh/mesh-defs.h @@ -62,7 +62,9 @@ #define VIRTUAL_ADDRESS_LOW 0x8000 #define VIRTUAL_ADDRESS_HIGH 0xbfff #define GROUP_ADDRESS_LOW 0xc000 -#define GROUP_ADDRESS_HIGH 0xff00 +#define GROUP_ADDRESS_HIGH 0xfeff +#define FIXED_GROUP_LOW 0xff00 +#define FIXED_GROUP_HIGH 0xffff #define NODE_IDENTITY_STOPPED 0x00 #define NODE_IDENTITY_RUNNING 0x01 @@ -79,6 +81,7 @@ ((x) < VIRTUAL_ADDRESS_LOW)) #define IS_VIRTUAL(x) (((x) >= VIRTUAL_ADDRESS_LOW) && \ ((x) <= VIRTUAL_ADDRESS_HIGH)) -#define IS_GROUP(x) (((x) >= GROUP_ADDRESS_LOW) && \ - ((x) <= GROUP_ADDRESS_HIGH)) +#define IS_GROUP(x) ((((x) >= GROUP_ADDRESS_LOW) && \ + ((x) < FIXED_GROUP_HIGH)) || \ + ((x) == ALL_NODES_ADDRESS)) #define IS_ALL_NODES(x) ((x) == ALL_NODES_ADDRESS) diff --git a/mesh/net.c b/mesh/net.c index 6ef056b56..4961c11c7 100644 --- a/mesh/net.c +++ b/mesh/net.c @@ -71,6 +71,13 @@ #define SAR_KEY(src, seq0) ((((uint32_t)(seq0)) << 16) | (src)) +enum _relay_advice { + RELAY_NONE, /* Relay not enabled in node */ + RELAY_ALLOWED, /* Relay enabled, msg not to node's unicast */ + RELAY_DISALLOWED, /* Msg was unicast handled by this node */ + RELAY_ALWAYS /* Relay enabled, msg to a group */ +}; + enum _iv_upd_state { /* Allows acceptance of any iv_index secure net beacon */ IV_UPD_INIT, @@ -160,7 +167,6 @@ struct mesh_net { struct l_queue *frnd_msgs; struct l_queue *friends; struct l_queue *destinations; - struct l_queue *fast_cache; uint8_t prov_priv_key[32]; @@ -248,6 +254,8 @@ struct net_decode { bool proxy; }; +#define FAST_CACHE_SIZE 8 +static struct l_queue *fast_cache; static struct l_queue *nets; static inline struct mesh_subnet *get_primary_subnet(struct mesh_net *net) @@ -673,7 +681,6 @@ struct mesh_net *mesh_net_new(struct mesh_node *node) net->tx_interval = DEFAULT_TRANSMIT_INTERVAL; net->subnets = l_queue_new(); - net->fast_cache = l_queue_new(); net->msg_cache = l_queue_new(); net->sar_in = l_queue_new(); net->sar_out = l_queue_new(); @@ -687,6 +694,9 @@ struct mesh_net *mesh_net_new(struct mesh_node *node) if (!nets) nets = l_queue_new(); + if (!fast_cache) + fast_cache = l_queue_new(); + return net; } @@ -696,7 +706,6 @@ void mesh_net_free(struct mesh_net *net) return; l_queue_destroy(net->subnets, subnet_free); - l_queue_destroy(net->fast_cache, mesh_msg_free); l_queue_destroy(net->msg_cache, mesh_msg_free); l_queue_destroy(net->sar_in, mesh_sar_free); l_queue_destroy(net->sar_out, mesh_sar_free); @@ -1021,7 +1030,6 @@ int mesh_net_add_key(struct mesh_net *net, bool update, uint16_t idx, void mesh_net_flush_msg_queues(struct mesh_net *net) { l_queue_clear(net->msg_cache, mesh_msg_free); - l_queue_clear(net->fast_cache, mesh_msg_free); } uint32_t mesh_net_get_iv_index(struct mesh_net *net) @@ -2210,23 +2218,23 @@ static bool find_fast_hash(const void *a, const void *b) return *entry == *test; } -static void *check_fast_cache(struct mesh_net *net, uint64_t hash) +static bool check_fast_cache(uint64_t hash) { - void *found = l_queue_find(net->fast_cache, find_fast_hash, &hash); + void *found = l_queue_find(fast_cache, find_fast_hash, &hash); uint64_t *new_hash; if (found) - return NULL; + return false; - if (l_queue_length(net->fast_cache) >= 8) - new_hash = l_queue_pop_head(net->fast_cache); + if (l_queue_length(fast_cache) >= FAST_CACHE_SIZE) + new_hash = l_queue_pop_head(fast_cache); else new_hash = l_malloc(sizeof(hash)); *new_hash = hash; - l_queue_push_tail(net->fast_cache, new_hash); + l_queue_push_tail(fast_cache, new_hash); - return new_hash; + return true; } static bool match_by_dst(const void *a, const void *b) @@ -2237,8 +2245,9 @@ static bool match_by_dst(const void *a, const void *b) return dest->dst == dst; } -static void send_relay_pkt(struct mesh_net *net, uint8_t *packet, uint8_t size) +static void send_relay_pkt(struct mesh_net *net, uint8_t *data, uint8_t size) { + uint8_t packet[30]; struct mesh_io *io = net->io; struct mesh_io_send_info info = { .type = MESH_IO_TIMING_TYPE_GENERAL, @@ -2249,8 +2258,9 @@ static void send_relay_pkt(struct mesh_net *net, uint8_t *packet, uint8_t size) }; packet[0] = MESH_AD_TYPE_NETWORK; + memcpy(packet + 1, data, size); - mesh_io_send(io, &info, packet, size); + mesh_io_send(io, &info, packet, size + 1); } static void send_msg_pkt(struct mesh_net *net, uint8_t *packet, uint8_t size) @@ -2270,15 +2280,12 @@ static void send_msg_pkt(struct mesh_net *net, uint8_t *packet, uint8_t size) mesh_io_send(io, &info, packet, size); } -static void packet_received(void *user_data, const void *data, uint8_t size, - int8_t rssi) +static enum _relay_advice packet_received(void *user_data, + uint32_t key_id, uint32_t iv_index, + const void *data, uint8_t size, int8_t rssi) { struct mesh_net *net = user_data; - uint32_t iv_index; - uint8_t iv_flag; const uint8_t *msg = data; - uint8_t *out; - size_t out_size; uint8_t app_msg_len; uint8_t net_ttl, net_key_id, net_segO, net_segN, net_opcode; uint32_t net_seq, cache_cookie; @@ -2287,50 +2294,13 @@ static void packet_received(void *user_data, const void *data, uint8_t size, bool net_ctl, net_segmented, net_szmic, net_relay; struct mesh_friend *net_frnd = NULL; bool drop = false; - uint64_t hash, *isNew = NULL; - uint32_t key_id; - - iv_flag = msg[0] >> 7; - iv_index = net->iv_index; - l_debug("%s iv_index %d NID: %2.2x", __func__, iv_index, msg[0] & 0x7f); - - if (sizeof(uint16_t) <= sizeof(void *)) { - /* Add in additional cache to allow us to - * avoid decrypting duplicatesr - * Fast 64 bit Hash, Network MIC doesn't matter - * With 64 bit hash, false pos chance is 1 in 1.8 * 10^19 - */ - hash = l_get_le64(msg + 1) ^ l_get_le64(msg + 9); - isNew = check_fast_cache(net, hash); - if (!isNew) - return; - - l_debug("New"); - } - - memcpy(packet + 2, data, size); - - if (iv_index && (iv_index & 0x01) != iv_flag) - iv_index--; /* Tester--Drop 90% of packets */ /* l_getrandom(&iv_flag, 1); */ /* if (iv_flag%10<9) drop = true; */ - if (!drop) - print_packet("RX: Network [enc] :", data, size); - - key_id = net_key_decrypt(iv_index, packet + 2, size, &out, &out_size); - if (!key_id) { - l_debug("Failed to decode packet"); - /* Remove fast-cache-hash */ - l_queue_remove(net->fast_cache, isNew); - l_free(isNew); - return; - } - - memcpy(packet + 2, out, out_size); + memcpy(packet + 2, data, size); if (!drop) print_packet("RX: Network [clr] :", packet + 2, size); @@ -2347,7 +2317,7 @@ static void packet_received(void *user_data, const void *data, uint8_t size, &net_segO, &net_segN, &msg, &app_msg_len)) { l_error("Failed to parse packet content"); - return; + return RELAY_NONE; } /* Ignore incoming packets if we are LPN and frnd bit not set */ @@ -2357,7 +2327,7 @@ static void packet_received(void *user_data, const void *data, uint8_t size, subnet = l_queue_find(net->subnets, match_key_id, L_UINT_TO_PTR(key_id)); if (subnet) - return; + return RELAY_NONE; /* If the queue is empty, stop polling */ if (net_ctl && net_opcode == NET_OP_FRND_UPDATE && !msg[5]) @@ -2368,16 +2338,16 @@ static void packet_received(void *user_data, const void *data, uint8_t size, } else if (net_dst == 0) { l_error("illegal parms: DST: %4.4x Ctl: %d TTL: %2.2x", net_dst, net_ctl, net_ttl); - return; + return RELAY_NONE; } /* Ignore if we originally sent this */ if (is_us(net, net_src, true)) - return; + return RELAY_NONE; if (drop) { l_info("Dropping SEQ 0x%06x", net_seq); - return; + return RELAY_NONE; } l_debug("check %08x", cache_cookie); @@ -2385,7 +2355,7 @@ static void packet_received(void *user_data, const void *data, uint8_t size, /* As a Relay, suppress repeats of last N packets that pass through */ /* The "cache_cookie" should be unique part of App message */ if (msg_in_cache(net, net_src, net_seq, cache_cookie)) - return; + return RELAY_NONE; l_debug("RX: Network %04x -> %04x : TTL 0x%02x : IV : %8.8x SEQ 0x%06x", net_src, net_dst, net_ttl, iv_index, net_seq); @@ -2403,12 +2373,12 @@ static void packet_received(void *user_data, const void *data, uint8_t size, if (net_opcode == NET_OP_SEG_ACKNOWLEDGE) { /* Illegal to send ACK to non-Unicast Addr */ if (net_dst & 0x8000) - return; + return RELAY_NONE; /* print_packet("Got ACK", msg, app_msg_len); */ /* Pedantic check for correct size */ if (app_msg_len != 7) - return; + return RELAY_NONE; /* If this is an ACK to our friend queue-only */ if (is_lpn_friend(net, net_dst, true)) @@ -2468,28 +2438,37 @@ static void packet_received(void *user_data, const void *data, uint8_t size, if (!!(net_frnd)) l_info("Ask for more data!"); - /* If this is one of our Unicast addresses, don't relay */ - if (net_dst <= 0x7fff) - return; + /* If this is one of our Unicast addresses, disallow relay */ + if (IS_UNICAST(net_dst)) + return RELAY_DISALLOWED; } + /* If relay not enable, or no more hops allowed */ if (!net->relay.enable || net_ttl < 0x02 || net_frnd) - return; + return RELAY_NONE; - packet[2 + 1] = (packet[2 + 1] & ~TTL_MASK) | (net_ttl - 1); + /* Group or Virtual destinations should *always* be relayed */ + if (IS_GROUP(net_dst) || IS_VIRTUAL(net_dst)) + return RELAY_ALWAYS; - if (!net_key_encrypt(key_id, iv_index, packet + 2, size)) { - l_error("Failed to encode relay packet"); - return; - } + /* Unicast destinations for other nodes *may* be relayed */ + else if (IS_UNICAST(net_dst)) + return RELAY_ALLOWED; - if (net->relay.enable) - send_relay_pkt(net, packet, size + 1); + /* Otherwise, do not make a relay decision */ + else + return RELAY_NONE; } struct net_queue_data { struct mesh_io_recv_info *info; + struct mesh_net *net; const uint8_t *data; + uint8_t *out; + size_t out_size; + enum _relay_advice relay_advice; + uint32_t key_id; + uint32_t iv_index; uint16_t len; }; @@ -2497,30 +2476,78 @@ static void net_rx(void *net_ptr, void *user_data) { struct net_queue_data *data = user_data; struct mesh_net *net = net_ptr; + enum _relay_advice relay_advice; + uint8_t *out; + size_t out_size; + uint32_t key_id; int8_t rssi = 0; + key_id = net_key_decrypt(net->iv_index, data->data, data->len, + &out, &out_size); + + if (!key_id) + return; + + print_packet("RX: Network [enc] :", data->data, data->len); + if (data->info) { net->instant = data->info->instant; net->chan = data->info->chan; rssi = data->info->rssi; } - packet_received(net, data->data, data->len, rssi); + relay_advice = packet_received(net, key_id, net->iv_index, + out, out_size, rssi); + if (relay_advice > data->relay_advice) { + bool iv_flag = !!(net->iv_index & 1); + bool iv_pkt = !!(data->data[0] & 0x80); + + data->iv_index = net->iv_index; + if (iv_pkt != iv_flag) + data->iv_index--; + + data->relay_advice = relay_advice; + data->key_id = key_id; + data->net = net; + data->out = out; + data->out_size = out_size; + } } static void net_msg_recv(void *user_data, struct mesh_io_recv_info *info, const uint8_t *data, uint16_t len) { + uint64_t hash; + bool isNew; struct net_queue_data net_data = { .info = info, .data = data + 1, .len = len - 1, + .relay_advice = RELAY_NONE, }; - if (len <= 2) + if (len < 9) + return; + + hash = l_get_le64(data + 1); + + /* Only process packet once per reception */ + isNew = check_fast_cache(hash); + if (!isNew) return; l_queue_foreach(nets, net_rx, &net_data); + + if (net_data.relay_advice == RELAY_ALWAYS || + net_data.relay_advice == RELAY_ALLOWED) { + uint8_t ttl = net_data.out[1] & TTL_MASK; + + net_data.out[1] &= ~TTL_MASK; + net_data.out[1] |= ttl - 1; + net_key_encrypt(net_data.key_id, net_data.iv_index, + net_data.out, net_data.out_size); + send_relay_pkt(net_data.net, net_data.out, net_data.out_size); + } } static void set_network_beacon(void *a, void *b) @@ -2815,7 +2842,6 @@ static void lpn_process_beacon(void *user_data, const void *data, /* print_packet("lpn: Secure Net Beacon RXed", data, size); */ rxed_key_refresh = (buf[0] & 0x01) == 0x01; - iv_update = (buf[0] & 0x02) == 0x02; iv_index = l_get_be32(buf + 1); /* Inhibit recognizing iv_update true-->false if we have outbound @@ -2927,6 +2953,9 @@ bool mesh_net_attach(struct mesh_net *net, struct mesh_io *io) if (!nets) nets = l_queue_new(); + if (!fast_cache) + fast_cache = l_queue_new(); + l_info("Register io cb"); mesh_io_register_recv_cb(io, MESH_IO_FILTER_BEACON, beacon_recv, net); -- 2.14.5