blob: 792c52ad67cfecf0c56a3e93bb7029ad65fff147 [file] [log] [blame]
/*
*
* 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 (!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 (!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 (!rpl_list)
return false;
node_path = node_get_storage_dir(node);
len = strlen(node_path) + strlen(rpl_dir) + 15;
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_update(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;
node_path = node_get_storage_dir(node);
if (!node_path)
return;
if (strlen(node_path) + strlen(rpl_dir) + 15 >= 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);
}
bool rpl_init(const char *node_path)
{
char path[PATH_MAX];
if (strlen(node_path) + strlen(rpl_dir) + 15 >= PATH_MAX)
return false;
snprintf(path, PATH_MAX, "%s%s", node_path, rpl_dir);
mkdir(path, 0755);
return true;
}