mirror of
https://git.kernel.org/pub/scm/bluetooth/bluez.git
synced 2024-11-15 16:24:28 +08:00
8457e6a3ad
Mesh specification requires that Replay Protection be preserved across node restarts. This adds that storage in <node_uuid>/rpl/<iv_index>/<src> Realtime access remains in an l_queue structure, and stored as messages are processed.
290 lines
5.9 KiB
C
290 lines
5.9 KiB
C
/*
|
|
*
|
|
* BlueZ - Bluetooth protocol stack for Linux
|
|
*
|
|
* Copyright (C) 2020 Intel Corporation. All rights reserved.
|
|
*
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include <config.h>
|
|
#endif
|
|
|
|
#define _GNU_SOURCE
|
|
#include <fcntl.h>
|
|
#include <limits.h>
|
|
#include <stdio.h>
|
|
#include <unistd.h>
|
|
#include <dirent.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <ell/ell.h>
|
|
|
|
#include "mesh/mesh-defs.h"
|
|
|
|
#include "mesh/node.h"
|
|
#include "mesh/net.h"
|
|
#include "mesh/util.h"
|
|
#include "mesh/rpl.h"
|
|
|
|
const char *rpl_dir = "/rpl";
|
|
|
|
bool rpl_put_entry(struct mesh_node *node, uint16_t src, uint32_t iv_index,
|
|
uint32_t seq)
|
|
{
|
|
const char *node_path;
|
|
char src_file[PATH_MAX];
|
|
char seq_txt[7];
|
|
bool result = false;
|
|
DIR *dir;
|
|
int fd;
|
|
|
|
if (!node || !IS_UNICAST(src))
|
|
return false;
|
|
|
|
node_path = node_get_storage_dir(node);
|
|
|
|
if (strlen(node_path) + strlen(rpl_dir) + 15 >= PATH_MAX)
|
|
return false;
|
|
|
|
snprintf(src_file, PATH_MAX, "%s%s/%8.8x", node_path, rpl_dir,
|
|
iv_index);
|
|
dir = opendir(src_file);
|
|
|
|
if (!dir)
|
|
mkdir(src_file, 0755);
|
|
else
|
|
closedir(dir);
|
|
|
|
snprintf(src_file, PATH_MAX, "%s%s/%8.8x/%4.4x", node_path, rpl_dir,
|
|
iv_index, src);
|
|
|
|
fd = open(src_file, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
|
|
if (fd >= 0) {
|
|
snprintf(seq_txt, 7, "%6.6x", seq);
|
|
if (write(fd, seq_txt, 6) == 6)
|
|
result = true;
|
|
|
|
close(fd);
|
|
}
|
|
|
|
if (!result)
|
|
return false;
|
|
|
|
/* Delete RPL entry from old iv_index (if it exists) */
|
|
iv_index--;
|
|
snprintf(src_file, PATH_MAX, "%s%s/%8.8x/%4.4x", node_path, rpl_dir,
|
|
iv_index, src);
|
|
remove(src_file);
|
|
|
|
|
|
return result;
|
|
}
|
|
|
|
void rpl_del_entry(struct mesh_node *node, uint16_t src)
|
|
{
|
|
const char *node_path;
|
|
char rpl_path[PATH_MAX];
|
|
struct dirent *entry;
|
|
DIR *dir;
|
|
|
|
if (!node || !IS_UNICAST(src))
|
|
return;
|
|
|
|
node_path = node_get_storage_dir(node);
|
|
|
|
if (strlen(node_path) + strlen(rpl_dir) + 15 >= PATH_MAX)
|
|
return;
|
|
|
|
snprintf(rpl_path, PATH_MAX, "%s%s", node_path, rpl_dir);
|
|
dir = opendir(rpl_path);
|
|
|
|
if (!dir)
|
|
return;
|
|
|
|
/* Remove all instances of src address */
|
|
while ((entry = readdir(dir)) != NULL) {
|
|
if (entry->d_type == DT_DIR && entry->d_name[0] != '.') {
|
|
snprintf(rpl_path, PATH_MAX, "%s%s/%s/%4.4x",
|
|
node_path, rpl_dir, entry->d_name, src);
|
|
remove(rpl_path);
|
|
}
|
|
}
|
|
|
|
closedir(dir);
|
|
}
|
|
|
|
static bool match_src(const void *a, const void *b)
|
|
{
|
|
const struct mesh_rpl *rpl = a;
|
|
uint16_t src = L_PTR_TO_UINT(b);
|
|
|
|
return rpl->src == src;
|
|
}
|
|
|
|
static void get_entries(const char *iv_path, struct l_queue *rpl_list)
|
|
{
|
|
struct mesh_rpl *rpl;
|
|
struct dirent *entry;
|
|
DIR *dir;
|
|
int fd;
|
|
const char *iv_txt;
|
|
char src_path[PATH_MAX];
|
|
char seq_txt[7];
|
|
uint32_t iv_index, seq;
|
|
uint16_t src;
|
|
|
|
dir = opendir(iv_path);
|
|
|
|
if (!dir)
|
|
return;
|
|
|
|
iv_txt = basename(iv_path);
|
|
if (sscanf(iv_txt, "%08x", &iv_index) != 1)
|
|
return;
|
|
|
|
memset(seq_txt, 0, sizeof(seq_txt));
|
|
|
|
while ((entry = readdir(dir)) != NULL) {
|
|
/* RPL sequences are stored in src files under iv_index */
|
|
if (entry->d_type == DT_REG) {
|
|
if (sscanf(entry->d_name, "%04hx", &src) != 1)
|
|
continue;
|
|
|
|
snprintf(src_path, PATH_MAX, "%s/%4.4x", iv_path, src);
|
|
fd = open(src_path, O_RDONLY);
|
|
|
|
if (fd < 0)
|
|
continue;
|
|
|
|
if (read(fd, seq_txt, 6) == 6 &&
|
|
sscanf(seq_txt, "%06x", &seq) == 1) {
|
|
|
|
rpl = l_queue_find(rpl_list, match_src,
|
|
L_UINT_TO_PTR(src));
|
|
|
|
if (rpl) {
|
|
/* Replace older entries */
|
|
if (rpl->iv_index < iv_index) {
|
|
rpl->iv_index = iv_index;
|
|
rpl->seq = seq;
|
|
}
|
|
} else if (seq <= SEQ_MASK && IS_UNICAST(src)) {
|
|
rpl = l_new(struct mesh_rpl, 1);
|
|
rpl->src = src;
|
|
rpl->iv_index = iv_index;
|
|
rpl->seq = seq;
|
|
|
|
l_queue_push_head(rpl_list, rpl);
|
|
}
|
|
}
|
|
close(fd);
|
|
}
|
|
}
|
|
|
|
closedir(dir);
|
|
}
|
|
|
|
bool rpl_get_list(struct mesh_node *node, struct l_queue *rpl_list)
|
|
{
|
|
const char *node_path;
|
|
struct dirent *entry;
|
|
char *rpl_path;
|
|
size_t len;
|
|
DIR *dir;
|
|
|
|
if (!node || !rpl_list)
|
|
return false;
|
|
|
|
node_path = node_get_storage_dir(node);
|
|
|
|
len = strlen(node_path) + strlen(rpl_dir) + 14;
|
|
|
|
if (len > PATH_MAX)
|
|
return false;
|
|
|
|
rpl_path = l_malloc(len);
|
|
snprintf(rpl_path, len, "%s%s", node_path, rpl_dir);
|
|
|
|
dir = opendir(rpl_path);
|
|
|
|
if (!dir) {
|
|
l_error("Failed to read RPL dir: %s", rpl_path);
|
|
l_free(rpl_path);
|
|
return false;
|
|
}
|
|
|
|
while ((entry = readdir(dir)) != NULL) {
|
|
/* RPL sequences are stored in files under iv_indexs */
|
|
if (entry->d_type == DT_DIR && entry->d_name[0] != '.') {
|
|
snprintf(rpl_path, len, "%s%s/%s",
|
|
node_path, rpl_dir, entry->d_name);
|
|
get_entries(rpl_path, rpl_list);
|
|
}
|
|
}
|
|
|
|
l_free(rpl_path);
|
|
closedir(dir);
|
|
|
|
return true;
|
|
}
|
|
|
|
void rpl_init(struct mesh_node *node, uint32_t cur)
|
|
{
|
|
uint32_t old = cur - 1;
|
|
const char *node_path;
|
|
struct dirent *entry;
|
|
char path[PATH_MAX];
|
|
DIR *dir;
|
|
|
|
if (!node)
|
|
return;
|
|
|
|
node_path = node_get_storage_dir(node);
|
|
|
|
if (strlen(node_path) + strlen(rpl_dir) + 10 >= PATH_MAX)
|
|
return;
|
|
|
|
/* Make sure path exists */
|
|
snprintf(path, PATH_MAX, "%s%s", node_path, rpl_dir);
|
|
mkdir(path, 0755);
|
|
|
|
dir = opendir(path);
|
|
if (!dir)
|
|
return;
|
|
|
|
/* Cleanup any stale or malformed trees */
|
|
while ((entry = readdir(dir)) != NULL) {
|
|
if (entry->d_type == DT_DIR && entry->d_name[0] != '.') {
|
|
uint32_t val;
|
|
bool del = false;
|
|
|
|
if (strlen(entry->d_name) != 8)
|
|
del = true;
|
|
else if (sscanf(entry->d_name, "%08x", &val) != 1)
|
|
del = true;
|
|
|
|
/* Delete all invalid iv_index trees */
|
|
if (del || (val != cur && val != old)) {
|
|
snprintf(path, PATH_MAX, "%s%s/%s",
|
|
node_path, rpl_dir, entry->d_name);
|
|
del_path(path);
|
|
}
|
|
}
|
|
}
|
|
|
|
closedir(dir);
|
|
}
|