├── .gitignore ├── test ├── invoke_rpld ├── lowpan1.conf ├── lowpan2.conf ├── lowpan3.conf ├── lowpan4.conf ├── lowpan5.conf ├── start ├── start_non_storing ├── stop ├── ns_setup_simple ├── lowpan0.conf ├── lowpan0_non_storing.conf └── ns_setup ├── socket.h ├── send.h ├── include └── linux │ ├── rpl_iptunnel.h │ ├── rpl.h │ └── lwtunnel.h ├── recv.h ├── process.h ├── tree.h ├── buffer.h ├── examples └── rpld.conf ├── netlink.h ├── log.h ├── meson.build ├── config.h ├── .travis.yml ├── COPYRIGHT ├── list.h ├── testprog.c ├── helpers.h ├── buffer.c ├── recv.c ├── dag.h ├── tree.c ├── helpers.c ├── README.md ├── send.c ├── log.c ├── socket.c ├── rpl.h ├── rpld.c ├── process.c ├── netlink.c ├── dag.c ├── config.c └── utlist.h /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | *.swp 3 | -------------------------------------------------------------------------------- /test/invoke_rpld: -------------------------------------------------------------------------------- 1 | ip netns exec ns$1 ./../build/rpld -d 1 -C $2 2 | -------------------------------------------------------------------------------- /test/lowpan1.conf: -------------------------------------------------------------------------------- 1 | ifaces = { { 2 | -- the interface to run on! 3 | ifname = "lowpan1", 4 | -- we are not the dodag_root! 5 | dodag_root = false, 6 | }, } 7 | 8 | -- vim: syntax=lua 9 | -------------------------------------------------------------------------------- /test/lowpan2.conf: -------------------------------------------------------------------------------- 1 | ifaces = { { 2 | -- the interface to run on! 3 | ifname = "lowpan2", 4 | -- we are not the dodag_root! 5 | dodag_root = false, 6 | }, } 7 | 8 | -- vim: syntax=lua 9 | -------------------------------------------------------------------------------- /test/lowpan3.conf: -------------------------------------------------------------------------------- 1 | ifaces = { { 2 | -- the interface to run on! 3 | ifname = "lowpan3", 4 | -- we are not the dodag_root! 5 | dodag_root = false, 6 | }, } 7 | 8 | -- vim: syntax=lua 9 | -------------------------------------------------------------------------------- /test/lowpan4.conf: -------------------------------------------------------------------------------- 1 | ifaces = { { 2 | -- the interface to run on! 3 | ifname = "lowpan4", 4 | -- we are not the dodag_root! 5 | dodag_root = false, 6 | }, } 7 | 8 | -- vim: syntax=lua 9 | -------------------------------------------------------------------------------- /test/lowpan5.conf: -------------------------------------------------------------------------------- 1 | ifaces = { { 2 | -- the interface to run on! 3 | ifname = "lowpan5", 4 | -- we are not the dodag_root! 5 | dodag_root = false, 6 | }, } 7 | 8 | -- vim: syntax=lua 9 | -------------------------------------------------------------------------------- /test/start: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ "$EUID" -ne 0 ] 4 | then 5 | echo "Please run as root" 6 | exit 7 | fi 8 | 9 | ./invoke_rpld 0 "lowpan0.conf" & 10 | 11 | for i in `seq 1 5` 12 | do 13 | sleep 1 14 | ./invoke_rpld $i & 15 | done 16 | -------------------------------------------------------------------------------- /test/start_non_storing: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ "$EUID" -ne 0 ] 4 | then 5 | echo "Please run as root" 6 | exit 7 | fi 8 | 9 | ./invoke_rpld 0 "lowpan0_non_storing.conf" & 10 | 11 | for i in `seq 1 5` 12 | do 13 | sleep 1 14 | ./invoke_rpld $i "lowpan$i.conf" & 15 | done 16 | -------------------------------------------------------------------------------- /test/stop: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ "$EUID" -ne 0 ] 4 | then 5 | echo "Please run as root" 6 | exit 7 | fi 8 | 9 | killall rpld 10 | 11 | rmmod mac802154_hwsim 12 | rmmod mac802154 13 | rmmod ieee802154_6lowpan 14 | rmmod ieee802154 15 | 16 | for i in `seq 0 4` 17 | do 18 | ip netns del ns$i 19 | done 20 | -------------------------------------------------------------------------------- /test/ns_setup_simple: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | modprobe mac802154_hwsim 4 | ip netns add foo 5 | ip netns add bar 6 | 7 | iwpan phy phy0 set netns name foo 8 | iwpan phy phy1 set netns name bar 9 | 10 | ip netns exec foo iwpan dev wpan0 set pan_id 0xbeef 11 | ip netns exec bar iwpan dev wpan1 set pan_id 0xbeef 12 | 13 | ip netns exec foo ip link add link wpan0 name lowpan0 type lowpan 14 | ip netns exec bar ip link add link wpan1 name lowpan1 type lowpan 15 | 16 | ip netns exec foo ip link set wpan0 up 17 | ip netns exec bar ip link set wpan1 up 18 | 19 | ip netns exec foo ip link set lowpan0 up 20 | ip netns exec bar ip link set lowpan1 up 21 | -------------------------------------------------------------------------------- /socket.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Authors: 3 | * Alexander Aring 4 | * 5 | * Original Authors: 6 | * Lars Fenneberg 7 | * 8 | * This software is Copyright 1996,1997,2019 by the above mentioned author(s), 9 | * All Rights Reserved. 10 | * 11 | * The license which is distributed with this software in the file COPYRIGHT 12 | * applies to this software. If your distribution is missing this file, you 13 | * may request it from . 14 | */ 15 | 16 | #ifndef __RPLD_SOCKET_H__ 17 | #define __RPLD_SOCKET_H__ 18 | 19 | #include "list.h" 20 | 21 | int open_icmpv6_socket(const struct list_head *ifaces); 22 | void close_icmpv6_socket(int sock, const struct list_head *ifaces); 23 | 24 | #endif /* __RPLD_SOCKET_H__ */ 25 | -------------------------------------------------------------------------------- /send.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Authors: 3 | * Alexander Aring 4 | * 5 | * This software is Copyright 2019 by the above mentioned author(s), 6 | * All Rights Reserved. 7 | * 8 | * The license which is distributed with this software in the file COPYRIGHT 9 | * applies to this software. If your distribution is missing this file, you 10 | * may request it from . 11 | */ 12 | 13 | #ifndef __RPLD_SEND_H__ 14 | #define __RPLD_SEND_H__ 15 | 16 | #include "dag.h" 17 | 18 | void send_dio(int sock, struct dag *dag); 19 | void send_dao(int sock, const struct in6_addr *to, struct dag *dag); 20 | void send_dao_ack(int sock, const struct in6_addr *to, struct dag *dag); 21 | void send_dis(int sock, struct iface *iface); 22 | 23 | #endif /* __RPLD_SEND_H__ */ 24 | -------------------------------------------------------------------------------- /include/linux/rpl_iptunnel.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ 2 | /* 3 | * IPv6 RPL-SR implementation 4 | * 5 | * Author: 6 | * Alexander Aring 7 | * 8 | * 9 | * This program is free software; you can redistribute it and/or 10 | * modify it under the terms of the GNU General Public License 11 | * as published by the Free Software Foundation; either version 12 | * 2 of the License, or (at your option) any later version. 13 | */ 14 | 15 | #ifndef _UAPI_LINUX_RPL_IPTUNNEL_H 16 | #define _UAPI_LINUX_RPL_IPTUNNEL_H 17 | 18 | enum { 19 | RPL_IPTUNNEL_UNSPEC, 20 | RPL_IPTUNNEL_SRH, 21 | __RPL_IPTUNNEL_MAX, 22 | }; 23 | #define RPL_IPTUNNEL_MAX (__RPL_IPTUNNEL_MAX - 1) 24 | 25 | #define RPL_IPTUNNEL_SRH_SIZE(srh) (((srh)->hdrlen + 1) << 3) 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /test/lowpan0.conf: -------------------------------------------------------------------------------- 1 | ifaces = { { 2 | -- the interface to run on! 3 | ifname = "lowpan0", 4 | -- we are the dodag_root! 5 | dodag_root = true, 6 | -- root init rpl instance 7 | rpls = { { 8 | -- the rpl instance to use - global scope only for now! 9 | instance = 1, 10 | -- the dags per rpl instance 11 | dags = { { 12 | -- versioning stuff, to make old deprecated? 13 | -- hey can be useful when we doing SIGHUP signal 14 | -- version = 1, 15 | -- stupid name, it's just a simple timer to send dio's 16 | -- trickle_t = 1, 17 | -- destination prefix, similar like RA PIO just reinvented 18 | dest_prefix = "fd3c:be8a:173f:8e80::/64", 19 | -- The DODAGID MUST be a routable IPv6 20 | -- address belonging to the DODAG root. 21 | -- dodagid = "fd3c:be8a:173f:8e80::1", 22 | }, } 23 | }, } 24 | }, } 25 | 26 | -- vim: syntax=lua 27 | -------------------------------------------------------------------------------- /recv.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Authors: 3 | * Alexander Aring 4 | * 5 | * Original Authors: 6 | * Lars Fenneberg 7 | * 8 | * This software is Copyright 1996,1997,2019 by the above mentioned author(s), 9 | * All Rights Reserved. 10 | * 11 | * The license which is distributed with this software in the file COPYRIGHT 12 | * applies to this software. If your distribution is missing this file, you 13 | * may request it from . 14 | */ 15 | 16 | #ifndef __RPLD_RECV_H__ 17 | #define __RPLD_RECV_H__ 18 | 19 | #include 20 | #include 21 | 22 | #define MSG_SIZE_RECV 1500 23 | 24 | int recv_rs_ra(int sock, unsigned char *msg, struct sockaddr_in6 *addr, 25 | struct in6_pktinfo **pkt_info, int *hoplimit, 26 | unsigned char *chdr); 27 | 28 | #endif /* __RPLD_RECV_H__ */ 29 | -------------------------------------------------------------------------------- /process.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Authors: 3 | * Alexander Aring 4 | * 5 | * Original Authors: 6 | * Lars Fenneberg 7 | * 8 | * This software is Copyright 1996,1997,2019 by the above mentioned author(s), 9 | * All Rights Reserved. 10 | * 11 | * The license which is distributed with this software in the file COPYRIGHT 12 | * applies to this software. If your distribution is missing this file, you 13 | * may request it from . 14 | */ 15 | 16 | #ifndef __RPLD_PROCESS_H__ 17 | #define __RPLD_PROCESS_H__ 18 | 19 | #include 20 | #include 21 | 22 | #include "config.h" 23 | 24 | void process(int sock, struct list_head *ifaces, unsigned char *msg, 25 | int len, struct sockaddr_in6 *addr, struct in6_pktinfo *pkt_info, 26 | int hoplimit); 27 | 28 | #endif /* __RPLD_PROCESS_H__ */ 29 | -------------------------------------------------------------------------------- /test/lowpan0_non_storing.conf: -------------------------------------------------------------------------------- 1 | ifaces = { { 2 | -- the interface to run on! 3 | ifname = "lowpan0", 4 | -- we are the dodag_root! 5 | dodag_root = true, 6 | -- root init rpl instance 7 | rpls = { { 8 | -- the rpl instance to use - global scope only for now! 9 | instance = 1, 10 | -- the dags per rpl instance 11 | dags = { { 12 | -- 2 (default) or 1 13 | mode_of_operation = 1, 14 | -- versioning stuff, to make old deprecated? 15 | -- hey can be useful when we doing SIGHUP signal 16 | -- version = 1, 17 | -- stupid name, it's just a simple timer to send dio's 18 | -- trickle_t = 1, 19 | -- destination prefix, similar like RA PIO just reinvented 20 | dest_prefix = "fd3c:be8a:173f:8e80::/64", 21 | -- The DODAGID MUST be a routable IPv6 22 | -- address belonging to the DODAG root. 23 | -- dodagid = "fd3c:be8a:173f:8e80::1", 24 | }, } 25 | }, } 26 | }, } 27 | 28 | -- vim: syntax=lua 29 | -------------------------------------------------------------------------------- /tree.h: -------------------------------------------------------------------------------- 1 | #ifndef __RPL_TREE_H__ 2 | #define __RPL_TREE_H__ 3 | 4 | #include 5 | 6 | #include "list.h" 7 | 8 | struct t_path { 9 | struct in6_addr addr; 10 | struct in6_addr target; 11 | 12 | struct list list; 13 | }; 14 | 15 | struct t_node { 16 | struct in6_addr addr; 17 | struct in6_addr target; 18 | 19 | struct t_node *parent; 20 | struct list_head childs; 21 | 22 | struct list list; 23 | }; 24 | 25 | struct t_root { 26 | struct t_node *n; 27 | }; 28 | 29 | void t_init(struct t_root *root, const struct in6_addr *addr, 30 | const struct in6_addr *target); 31 | struct t_node *t_insert(struct t_root *root, const struct in6_addr *parent, 32 | const struct in6_addr *addr, 33 | const struct in6_addr *target); 34 | void t_free(struct t_root *root); 35 | 36 | int t_path(const struct t_node *node, struct list_head *result); 37 | void t_path_free(struct list_head *head); 38 | 39 | #endif /* __RPL_TREE_H__ */ 40 | -------------------------------------------------------------------------------- /test/ns_setup: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | rmmod mac802154_hwsim 4 | rmmod mac802154 5 | rmmod ieee802154_6lowpan 6 | rmmod ieee802154 7 | modprobe mac802154_hwsim 8 | wpan-hwsim add 9 | wpan-hwsim add 10 | wpan-hwsim add 11 | wpan-hwsim add 12 | 13 | # BUILD TOPOLOGY 14 | wpan-hwsim edge add 0 2 15 | wpan-hwsim edge add 2 0 16 | 17 | wpan-hwsim edge add 1 3 18 | wpan-hwsim edge add 3 1 19 | 20 | wpan-hwsim edge add 4 3 21 | wpan-hwsim edge add 3 4 22 | 23 | wpan-hwsim edge add 2 3 24 | wpan-hwsim edge add 3 2 25 | 26 | wpan-hwsim edge add 4 1 27 | wpan-hwsim edge add 1 4 28 | 29 | wpan-hwsim edge add 4 5 30 | wpan-hwsim edge add 5 4 31 | 32 | for i in `seq 0 5` 33 | do 34 | ip netns add ns$i 35 | iwpan phy phy$i set netns name ns$i 36 | ip netns exec ns$i iwpan dev wpan$i set pan_id 0xbeef 37 | ip netns exec ns$i ip link add link wpan$i name lowpan$i type lowpan 38 | ip netns exec ns$i ip link set wpan$i up 39 | ip netns exec ns$i ip link set lowpan$i up 40 | done 41 | -------------------------------------------------------------------------------- /buffer.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Authors: 3 | * Alexander Aring 4 | * 5 | * Original Authors: 6 | * Lars Fenneberg 7 | * 8 | * This software is Copyright 1996,1997,2019 by the above mentioned author(s), 9 | * All Rights Reserved. 10 | * 11 | * The license which is distributed with this software in the file COPYRIGHT 12 | * applies to this software. If your distribution is missing this file, you 13 | * may request it from . 14 | */ 15 | 16 | #ifndef __RPLD_BUFFER_H__ 17 | #define __RPLD_BUFFER_H__ 18 | 19 | struct safe_buffer *safe_buffer_new(void); 20 | void safe_buffer_free(struct safe_buffer *sb); 21 | size_t safe_buffer_pad(struct safe_buffer *sb, size_t count); 22 | size_t safe_buffer_append(struct safe_buffer *sb, const void *v, size_t count); 23 | 24 | struct safe_buffer { 25 | int should_free; 26 | size_t allocated; 27 | size_t used; 28 | unsigned char *buffer; 29 | }; 30 | 31 | #endif /* __RPLD_BUFFER_H__ */ 32 | -------------------------------------------------------------------------------- /examples/rpld.conf: -------------------------------------------------------------------------------- 1 | ifaces = { { 2 | -- the interface to run on! 3 | ifname = "lowpan0", 4 | -- if we are dodag_root or not, floating is not supported 5 | dodag_root = true, 6 | -- default trickle timer, for now simple timer 7 | trickle_t = 1, 8 | -- rpl instances 9 | rpls = { { 10 | -- the rpl instance to use - global scope only for now! 11 | instance = 1, 12 | -- the dags per rpl instance 13 | dags = { { 14 | -- 2 (default) or 1 TODO some string stuff 15 | mode_of_operation = 2, 16 | -- versioning stuff, to make old deprecated? 17 | -- hey can be useful when we doing SIGHUP signal 18 | version = 1, 19 | -- stupid name, it's just a simple timer to send dio's 20 | trickle_t = 1, 21 | -- destination prefix, similar like RA PIO just reinvented 22 | dest_prefix = "fd3c:be8a:173f:8e80::/64", 23 | -- The DODAGID MUST be a routable IPv6 24 | -- address belonging to the DODAG root. 25 | dodagid = "fd3c:be8a:173f:8e80::1", 26 | }, } 27 | }, } 28 | }, } 29 | 30 | -- vim: syntax=lua 31 | -------------------------------------------------------------------------------- /include/linux/rpl.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ 2 | /* 3 | * IPv6 RPL-SR implementation 4 | * 5 | * Author: 6 | * Alexander Aring 7 | */ 8 | 9 | #ifndef _UAPI_LINUX_RPL_H 10 | #define _UAPI_LINUX_RPL_H 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | /* 17 | * RPL SR Header 18 | */ 19 | struct ipv6_rpl_sr_hdr { 20 | __u8 nexthdr; 21 | __u8 hdrlen; 22 | __u8 type; 23 | __u8 segments_left; 24 | #if defined(__LITTLE_ENDIAN_BITFIELD) 25 | __u32 cmpre:4, 26 | cmpri:4, 27 | reserved:4, 28 | pad:4, 29 | reserved1:16; 30 | #elif defined(__BIG_ENDIAN_BITFIELD) 31 | __u32 reserved:20, 32 | pad:4, 33 | cmpri:4, 34 | cmpre:4; 35 | #else 36 | #error "Please fix " 37 | #endif 38 | 39 | union { 40 | struct in6_addr addr[0]; 41 | __u8 data[0]; 42 | } segments; 43 | } __attribute__((packed)); 44 | 45 | #define rpl_segaddr segments.addr 46 | #define rpl_segdata segments.data 47 | 48 | #endif 49 | -------------------------------------------------------------------------------- /netlink.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Authors: 3 | * Alexander Aring 4 | * 5 | * This software is Copyright 2019 by the above mentioned author(s), 6 | * All Rights Reserved. 7 | * 8 | * The license which is distributed with this software in the file COPYRIGHT 9 | * applies to this software. If your distribution is missing this file, you 10 | * may request it from . 11 | */ 12 | 13 | #ifndef __RPLD_NETLINK_H__ 14 | #define __RPLD_NETLINK_H__ 15 | 16 | #include "config.h" 17 | 18 | int nl_add_addr(uint32_t ifindex, const struct in6_addr *addr); 19 | int nl_get_llinfo(uint32_t ifindex, struct iface_llinfo *llinfo); 20 | int nl_add_route_via(uint32_t ifindex, const struct in6_addr *route, 21 | const struct in6_addr *via); 22 | int nl_add_route_default(uint32_t ifindex, const struct in6_addr *via); 23 | int nl_del_route_via(uint32_t ifindex, const struct in6_prefix *dst, 24 | struct in6_addr *via); 25 | int nl_add_source_routes(uint32_t ifindex, const struct list_head *segs); 26 | int netlink_open(void); 27 | void netlink_close(void); 28 | 29 | #endif /* __RPLD_NETLINK_H__ */ 30 | -------------------------------------------------------------------------------- /log.h: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Authors: 4 | * Lars Fenneberg 5 | * 6 | * This software is Copyright 1996,1997 by the above mentioned author(s), 7 | * All Rights Reserved. 8 | * 9 | * The license which is distributed with this software in the file 10 | * COPYRIGHT applies to this software. If your distribution is missing 11 | * this file, you may request it from . 12 | * 13 | */ 14 | 15 | #ifndef __RPLD_LOG_H__ 16 | #define __RPLD_LOG_H__ 17 | 18 | #include 19 | #include 20 | #include 21 | 22 | #pragma once 23 | 24 | #define L_NONE 0 25 | #define L_SYSLOG 1 26 | #define L_STDERR 2 27 | #define L_STDERR_SYSLOG 3 28 | #define L_LOGFILE 4 29 | #define L_STDERR_CLEAN 5 30 | #define L_UNSPEC 6 31 | 32 | #define LOG_TIME_FORMAT "%b %d %H:%M:%S" 33 | 34 | int log_open(int, char const *, char const *, int); 35 | void flog(int, char const *, ...) __attribute__((format(printf, 2, 3))); 36 | void dlog(int, int, char const *, ...) __attribute__((format(printf, 3, 4))); 37 | int log_close(void); 38 | int log_reopen(void); 39 | void set_debuglevel(int); 40 | int get_debuglevel(void); 41 | 42 | #endif /* __RPLD_LOG_H__ */ 43 | -------------------------------------------------------------------------------- /meson.build: -------------------------------------------------------------------------------- 1 | project('rpld', 'c') 2 | compiler = meson.get_compiler('c') 3 | 4 | incdir = include_directories('include') 5 | 6 | evdep = dependency('ev', required: false) 7 | if evdep.found() 8 | evdep = [ libev ] 9 | elif compiler.has_header('ev.h') and compiler.has_function('ev_time', args: ['-lev']) 10 | evdep = [ compiler.find_library('ev') ] 11 | else 12 | error('Couldn\'t find libev header / library') 13 | endif 14 | 15 | foreach name : ['lua', 'lua5.3', 'lua-5.3', 'lua53'] 16 | dep = dependency(name, version: '>=5.3', required: false) 17 | if dep.found() 18 | luadep = dep 19 | endif 20 | endforeach 21 | if not luadep.found() 22 | error('Couldn\'t find Lua.') 23 | endif 24 | 25 | mnldep = dependency('libmnl') 26 | 27 | srcs = files( 28 | 'rpld.c', 29 | 'config.c', 30 | 'socket.c', 31 | 'buffer.c', 32 | 'recv.c', 33 | 'send.c', 34 | 'helpers.c', 35 | 'process.c', 36 | 'netlink.c', 37 | 'dag.c', 38 | 'log.c', 39 | 'tree.c', 40 | ) 41 | 42 | executable('rpld', srcs, include_directories : incdir, dependencies : [ evdep, luadep, mnldep ], install: true) 43 | 44 | e = executable('prog', files('testprog.c', 'tree.c')) 45 | test('Some unit tests with asserts', e) 46 | 47 | # vim: syntax=python 48 | -------------------------------------------------------------------------------- /config.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Authors: 3 | * Alexander Aring 4 | * 5 | * This software is Copyright 2019 by the above mentioned author(s), 6 | * All Rights Reserved. 7 | * 8 | * The license which is distributed with this software in the file COPYRIGHT 9 | * applies to this software. If your distribution is missing this file, you 10 | * may request it from . 11 | */ 12 | 13 | #ifndef __RPLD_CONFIG__ 14 | #define __RPLD_CONFIG__ 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | #include "dag.h" 23 | #include "list.h" 24 | 25 | #define MAX_RPL_INSTANCEID UINT8_MAX 26 | #define DEFAULT_TICKLE_T 5 27 | #define DEFAULT_DAG_VERSION 1 28 | 29 | struct iface_llinfo { 30 | unsigned char *addr; 31 | uint8_t addr_len; 32 | uint32_t ifindex; 33 | }; 34 | 35 | struct iface { 36 | char ifname[IFNAMSIZ]; 37 | uint32_t ifindex; 38 | 39 | ev_timer dis_w; 40 | struct iface_llinfo llinfo; 41 | 42 | struct in6_addr ifaddr; 43 | struct in6_addr *ifaddrs; 44 | int ifaddrs_count; 45 | struct in6_addr *ifaddr_src; 46 | 47 | struct list_head rpls; 48 | bool dodag_root; 49 | 50 | struct list list; 51 | uint8_t mop; 52 | int tickle_t; 53 | }; 54 | 55 | int config_load(const char *filename, struct list_head *ifaces); 56 | void config_free(struct list_head *ifaces); 57 | 58 | struct iface *iface_find_by_ifindex(struct list_head *ifaces, 59 | uint32_t ifindex); 60 | 61 | #endif /* __RPLD_CONFIG__ */ 62 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | env: 2 | global: 3 | # The next declaration is the encrypted COVERITY_SCAN_TOKEN, created 4 | # via the "travis encrypt" command using the project repo's public key 5 | - secure: "H2LC/vR1TNyECl8K1MJnvBay7je0NO4hzVdxk1TanJM4Cjo66Bdf+iMsIWW9KifTWlF+I8ATC8yrweGm02b9FqKSl7gBEPpgOev3q/IXlcU5HHqP+TDrGnn4aLFVYDiTjNYI39TpouUEQBN5cbDljN0GlYFyrXQkP28rvnhGyvvF65t0yQWRXrE1vlAeXq/1/IK/DwMams5YmGAVanH9TtOfePomAGtFtpiryHSgrZugyC03W6eiIzmJ2jzIJ6Wkykv1ASy1v2MEC5xl0YYOpZM1EF8hfScqKbA5Vn1JwM6yrGmDbjwFZCEYBj8N8EPAaZFXj6VNtJoaRB9jnO9Im1GqnD6TuYVc4caXYSrgxT7nMZhFe/Jt9y2mVsy5/nngJjZ5me3hLJlWuRgWDfo9hi02U6K8GkoHNumejGzOikErqGPVp4+aEFy3qr1sElWXgVYQP0/nKHGtciLnE3g2Tbt3mNEAakv+38Rh2zu2icn6vAXD5o4tlr7B86YUaSZlWVNr3LmW/GCZazdcR+I8zAwwo7Tj66pDvdejAe4s8yotOJw1/TMnBvJTO2w+ngggSoo/Ftp6AfdvNWqNBLV8h62uHjL5rXQh/Nx2lE3pA4PyN4mJegFPvqUSAdQurk+JZbfeP9HJ1R3CvP34LNsqoRWziKvv895xxjD3vjlJ7s8=" 6 | 7 | notifications: 8 | irc: "chat.freenode.net#linux-wpan" 9 | 10 | language: c 11 | 12 | dist: bionic 13 | sudo: true 14 | 15 | compiler: 16 | - clang 17 | - gcc 18 | 19 | addons: 20 | apt: 21 | packages: 22 | - libev-dev 23 | - liblua5.3-dev 24 | - libmnl-dev 25 | - meson 26 | - ninja-build 27 | coverity_scan: 28 | project: 29 | name: "linux-wpan/rpld" 30 | description: "Build submitted via Travis CI" 31 | notification_email: stefan@datenfreihafen.org 32 | build_command_prepend: "rm -rf build && mkdir build && meson build" 33 | build_command: "ninja -C build" 34 | branch_pattern: coverity_scan 35 | 36 | script: 37 | - rm -rf build && mkdir build && meson build . 38 | - ninja -C build 39 | -------------------------------------------------------------------------------- /COPYRIGHT: -------------------------------------------------------------------------------- 1 | The author(s) grant permission for redistribution and use in source and 2 | binary forms, with or without modification, of the software and documentation 3 | provided that the following conditions are met: 4 | 5 | 0. If you receive a version of the software that is specifically labelled 6 | as not being for redistribution (check the version message and/or README), 7 | you are not permitted to redistribute that version of the software in any 8 | way or form. 9 | 1. All terms of all other applicable copyrights and licenses must be 10 | followed. 11 | 2. Redistributions of source code must retain the authors' copyright 12 | notice(s), this list of conditions, and the following disclaimer. 13 | 3. Redistributions in binary form must reproduce the authors' copyright 14 | notice(s), this list of conditions, and the following disclaimer in the 15 | documentation and/or other materials provided with the distribution. 16 | 4. All advertising materials mentioning features or use of this software 17 | must display the following acknowledgement with the name(s) of the 18 | authors as specified in the copyright notice(s) substituted where 19 | indicated: 20 | 21 | This product includes software developed by the authors which are 22 | mentioned at the start of the source files and other contributors. 23 | 24 | 5. Neither the name(s) of the author(s) nor the names of its contributors 25 | may be used to endorse or promote products derived from this software 26 | without specific prior written permission. 27 | 28 | THIS SOFTWARE IS PROVIDED BY ITS AUTHORS AND CONTRIBUTORS ``AS IS'' AND ANY 29 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 30 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 31 | DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY 32 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 33 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 34 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 35 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 36 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 37 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 38 | -------------------------------------------------------------------------------- /list.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Authors: 3 | * Alexander Aring 4 | * 5 | * This software is Copyright 2019 by the above mentioned author(s), 6 | * All Rights Reserved. 7 | * 8 | * The license which is distributed with this software in the file COPYRIGHT 9 | * applies to this software. If your distribution is missing this file, you 10 | * may request it from . 11 | */ 12 | 13 | #ifndef __RPLD_LIST_H__ 14 | #define __RPLD_LIST_H__ 15 | 16 | #include "helpers.h" 17 | #include "utlist.h" 18 | 19 | /* TODO I really want to improve this wrapper API to send it upstream in utlist 20 | * as an "option" to use. I need to add more static inline wrapper here. 21 | */ 22 | 23 | /* utlist is great but the API is shit, so make it like linux 24 | * specially I HATE the head handling, which is a assing which 25 | * introduce a lot of weird behaviours when you don't pass head 26 | * as ** as function who deals with it when the list is empty 27 | * as NULL, weird is when not NULL you don't have issues. 28 | * 29 | * Sorry, but I said, it's a great implementation. :-) 30 | * This makes it like linux API which doesn't have this issue 31 | * because pointers are packed and using of container_of() 32 | */ 33 | struct list_head { 34 | struct list *head; 35 | }; 36 | 37 | struct list { 38 | struct list *prev; 39 | struct list *next; 40 | }; 41 | 42 | static inline void list_add(struct list_head *list_head, struct list *entry) 43 | { 44 | DL_APPEND(list_head->head, entry); 45 | } 46 | 47 | /* TODO Not 100% linux api may we can get rid of head arg */ 48 | static inline void list_del(struct list_head *list_head, struct list *entry) 49 | { 50 | DL_DELETE(list_head->head, entry); 51 | } 52 | 53 | static inline int list_empty(const struct list_head *list_head) 54 | { 55 | return !list_head->head; 56 | } 57 | 58 | #define list_first_entry(list_head, type, member) \ 59 | ((!(list_head)->head)?(NULL):(container_of((list_head)->head, type, member))) 60 | 61 | #define list_next_entry(pos, member) \ 62 | ((!(pos)->member.next)?(NULL):(container_of((pos)->member.next, typeof(*(pos)), member))) 63 | 64 | #define list_for_each_entry(pos, list_head, member) \ 65 | for ((pos) = list_first_entry(list_head, typeof(*(pos)), member); pos; \ 66 | (pos) = list_next_entry(pos, list)) 67 | 68 | #define list_for_each_entry_safe(pos, n, list_head, member) \ 69 | for ((pos) = list_first_entry(list_head, typeof(*(pos)), member); \ 70 | (pos) && ((n) = list_next_entry(pos, list), 1); \ 71 | (pos) = (n)) 72 | 73 | #endif /* __RPLD_LIST_H__ */ 74 | -------------------------------------------------------------------------------- /testprog.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "tree.h" 5 | 6 | void test_tree(void) 7 | { 8 | struct in6_addr dummy = { .s6_addr = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } }; 9 | struct in6_addr s0 = { .s6_addr = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 } }; 10 | struct in6_addr s1 = { .s6_addr = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2 } }; 11 | struct in6_addr s2 = { .s6_addr = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3 } }; 12 | struct in6_addr s3 = { .s6_addr = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4 } }; 13 | struct in6_addr s4 = { .s6_addr = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5 } }; 14 | struct list_head path = {}; 15 | struct t_root root = {}; 16 | struct t_node *n; 17 | struct t_path *p; 18 | 19 | t_init(&root, &s0, &dummy); 20 | assert(t_insert(&root, &s1, &s2, &dummy) == NULL); 21 | t_insert(&root, &s0, &s1, &dummy); 22 | t_insert(&root, &s1, &s3, &dummy); 23 | t_insert(&root, &s0, &s4, &dummy); 24 | n = t_insert(&root, &s3, &s2, &dummy); 25 | 26 | t_path(n, &path); 27 | 28 | list_for_each_entry(p, &path, list) 29 | printf("FFF %d\n", p->addr.s6_addr[15]); 30 | 31 | t_path_free(&path); 32 | t_free(&root); 33 | } 34 | 35 | struct test_entry { 36 | int x; 37 | struct list list; 38 | }; 39 | 40 | void test_list(void) 41 | { 42 | struct list_head head = {}; 43 | struct test_entry e = { .x = 5 }; 44 | struct test_entry y = { .x = 6 }; 45 | struct test_entry z = { .x = 7 }; 46 | struct test_entry *x, *tmp; 47 | int i; 48 | 49 | assert(list_empty(&head)); 50 | assert(list_first_entry(&head, struct test_entry, list) == NULL); 51 | 52 | list_for_each_entry(x, &head, list) 53 | assert(x->x == 5); 54 | 55 | list_add(&head, &e.list); 56 | assert(!list_empty(&head)); 57 | assert(list_first_entry(&head, struct test_entry, list) != NULL); 58 | assert(list_first_entry(&head, struct test_entry, list)->x == 5); 59 | x = list_first_entry(&head, struct test_entry, list); 60 | assert(!list_next_entry(x, list)); 61 | 62 | list_add(&head, &y.list); 63 | list_add(&head, &z.list); 64 | i = 0; 65 | list_for_each_entry(x, &head, list) { 66 | switch (i) { 67 | case 0: 68 | assert(x->x == 5); 69 | break; 70 | case 1: 71 | assert(x->x == 6); 72 | break; 73 | case 2: 74 | assert(x->x == 7); 75 | break; 76 | default: 77 | assert(0); 78 | break; 79 | } 80 | 81 | i++; 82 | } 83 | 84 | assert(i == 3); 85 | 86 | list_for_each_entry_safe(x, tmp, &head, list) 87 | list_del(&head, &x->list); 88 | 89 | assert(list_empty(&head)); 90 | } 91 | 92 | int main(int argc, const char *argv[]) 93 | { 94 | test_list(); 95 | test_tree(); 96 | return 0; 97 | } 98 | -------------------------------------------------------------------------------- /helpers.h: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Authors: 4 | * Alexander Aring 5 | * 6 | * Original Authors: 7 | * Lars Fenneberg 8 | * 9 | * This software is Copyright 1996,1997,2019 by the above mentioned author(s), 10 | * All Rights Reserved. 11 | * 12 | * The license which is distributed with this software in the file COPYRIGHT 13 | * applies to this software. If your distribution is missing this file, you 14 | * may request it from . 15 | */ 16 | 17 | #ifndef __RPLD_HELPERS__ 18 | #define __RPLD_HELPERS__ 19 | 20 | #include 21 | #include 22 | 23 | #include 24 | #include 25 | 26 | #include "log.h" 27 | 28 | #define mzalloc(size) calloc(1, size) 29 | 30 | /* thanks mcr, I stole that form unstrung */ 31 | static const struct in6_addr all_rpl_addr = { .s6_addr = { 0xff,0x02,0,0,0,0,0,0,0,0,0,0,0,0,0,0x1a } }; 32 | 33 | /* This assumes that str is not null and str_size > 0 */ 34 | static inline void addrtostr(const struct in6_addr *addr, char *str, size_t str_size) 35 | { 36 | const char *res; 37 | 38 | res = inet_ntop(AF_INET6, addr, str, str_size); 39 | 40 | if (res == NULL) { 41 | flog(LOG_ERR, "addrtostr: inet_ntop: %s", strerror(errno)); 42 | strncpy(str, "[invalid address]", str_size); 43 | str[str_size - 1] = '\0'; 44 | } 45 | } 46 | 47 | static inline uint8_t bits_to_bytes(uint8_t bits) 48 | { 49 | uint8_t o = bits >> 3; 50 | uint8_t b = bits & 0x7; 51 | 52 | return b?o+1:o; 53 | } 54 | 55 | struct in6_prefix { 56 | struct in6_addr prefix; 57 | uint8_t len; 58 | }; 59 | 60 | struct iface_llinfo; 61 | int gen_stateless_addr(const struct in6_prefix *prefix, 62 | const struct iface_llinfo *llinfo, 63 | struct in6_addr *dst); 64 | #define PROC_SYS_IP6_IFACE_FORWARDING "/proc/sys/net/ipv6/conf/%s/forwarding" 65 | #define PROC_SYS_IP6_IFACE_ACCEPT_SOURCE_ROUTE "/proc/sys/net/ipv6/conf/%s/accept_source_route" 66 | #define PROC_SYS_IP6_IFACE_RPL_SEG_ENABLED "/proc/sys/net/ipv6/conf/%s/rpl_seg_enabled" 67 | #define PROC_SYS_IP6_MAX_HBH_OPTS_NUM "/proc/sys/net/ipv6/max_hbh_opts_number" 68 | int set_interface_var(const char *iface, const char *var, const char *name, 69 | uint32_t val); 70 | int set_var(const char *var, uint32_t val); 71 | void init_random_gen(void); 72 | int gen_random_private_ula_pfx(struct in6_prefix *prefix); 73 | 74 | #undef offsetof 75 | #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) 76 | 77 | /* The Linux kernel is _not_ the inventor of that! */ 78 | #define container_of(ptr, type, member) ({ \ 79 | const typeof( ((type *)0)->member ) *__mptr = (ptr); \ 80 | (type *)( (char *)__mptr - offsetof(type,member) );}) 81 | 82 | #endif /* __RPLD_HELPERS__ */ 83 | -------------------------------------------------------------------------------- /buffer.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Authors: 3 | * Alexander Aring 4 | * 5 | * Original Authors: 6 | * Lars Fenneberg 7 | * 8 | * This software is Copyright 1996,1997,2019 by the above mentioned author(s), 9 | * All Rights Reserved. 10 | * 11 | * The license which is distributed with this software in the file COPYRIGHT 12 | * applies to this software. If your distribution is missing this file, you 13 | * may request it from . 14 | */ 15 | 16 | #include 17 | #include 18 | #include 19 | 20 | #include "buffer.h" 21 | #include "log.h" 22 | 23 | #define SAFE_BUFFER_INIT \ 24 | (struct safe_buffer) { .should_free = 0, .allocated = 0, .used = 0, .buffer = 0 } 25 | 26 | struct safe_buffer *safe_buffer_new(void) 27 | { 28 | struct safe_buffer *sb = malloc(sizeof(struct safe_buffer)); 29 | *sb = SAFE_BUFFER_INIT; 30 | sb->should_free = 1; 31 | return sb; 32 | } 33 | 34 | void safe_buffer_free(struct safe_buffer *sb) 35 | { 36 | if (sb->buffer) { 37 | free(sb->buffer); 38 | } 39 | 40 | if (sb->should_free) { 41 | free(sb); 42 | } 43 | } 44 | 45 | /** 46 | * Requests that the safe_buffer capacity be least n bytes in size. 47 | * 48 | * If n is greater than the current capacity, the function causes the container 49 | * to reallocate its storage increasing its capacity to n (or greater). 50 | * 51 | * In all other cases, the function call does not cause a reallocation and the 52 | * capacity is not affected. 53 | * 54 | * @param sb safe_buffer to enlarge 55 | * @param b Minimum capacity for the safe_buffer. 56 | */ 57 | static void safe_buffer_resize(struct safe_buffer *sb, size_t n) 58 | { 59 | const int blocksize = 1 << 6; // MUST BE POWER OF 2. 60 | if (sb->allocated < n) { 61 | if (n % blocksize > 0) { 62 | n |= (blocksize - 1); // Set all the low bits 63 | n++; 64 | } 65 | if (n > 64 * 1024) { 66 | flog(LOG_ERR, "Requested buffer too large for any possible IPv6 ND, even with jumbogram. Exiting."); 67 | exit(1); 68 | } 69 | sb->allocated = n; 70 | sb->buffer = realloc(sb->buffer, sb->allocated); 71 | } 72 | } 73 | 74 | size_t safe_buffer_pad(struct safe_buffer *sb, size_t count) 75 | { 76 | safe_buffer_resize(sb, sb->used + count); 77 | memset(&sb->buffer[sb->used], 0, count); 78 | sb->used += count; 79 | return count; 80 | } 81 | 82 | size_t safe_buffer_append(struct safe_buffer *sb, const void *v, size_t count) 83 | { 84 | if (sb) { 85 | unsigned const char *m = (unsigned const char *)v; 86 | safe_buffer_resize(sb, sb->used + count); 87 | memcpy(&sb->buffer[sb->used], m, count); 88 | sb->used += count; 89 | } 90 | 91 | return count; 92 | } 93 | -------------------------------------------------------------------------------- /include/linux/lwtunnel.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ 2 | #ifndef _UAPI_LWTUNNEL_H_ 3 | #define _UAPI_LWTUNNEL_H_ 4 | 5 | #include 6 | 7 | enum lwtunnel_encap_types { 8 | LWTUNNEL_ENCAP_NONE, 9 | LWTUNNEL_ENCAP_MPLS, 10 | LWTUNNEL_ENCAP_IP, 11 | LWTUNNEL_ENCAP_ILA, 12 | LWTUNNEL_ENCAP_IP6, 13 | LWTUNNEL_ENCAP_SEG6, 14 | LWTUNNEL_ENCAP_BPF, 15 | LWTUNNEL_ENCAP_SEG6_LOCAL, 16 | LWTUNNEL_ENCAP_RPL, 17 | __LWTUNNEL_ENCAP_MAX, 18 | }; 19 | 20 | #define LWTUNNEL_ENCAP_MAX (__LWTUNNEL_ENCAP_MAX - 1) 21 | 22 | enum lwtunnel_ip_t { 23 | LWTUNNEL_IP_UNSPEC, 24 | LWTUNNEL_IP_ID, 25 | LWTUNNEL_IP_DST, 26 | LWTUNNEL_IP_SRC, 27 | LWTUNNEL_IP_TTL, 28 | LWTUNNEL_IP_TOS, 29 | LWTUNNEL_IP_FLAGS, 30 | LWTUNNEL_IP_PAD, 31 | LWTUNNEL_IP_OPTS, 32 | __LWTUNNEL_IP_MAX, 33 | }; 34 | 35 | #define LWTUNNEL_IP_MAX (__LWTUNNEL_IP_MAX - 1) 36 | 37 | enum lwtunnel_ip6_t { 38 | LWTUNNEL_IP6_UNSPEC, 39 | LWTUNNEL_IP6_ID, 40 | LWTUNNEL_IP6_DST, 41 | LWTUNNEL_IP6_SRC, 42 | LWTUNNEL_IP6_HOPLIMIT, 43 | LWTUNNEL_IP6_TC, 44 | LWTUNNEL_IP6_FLAGS, 45 | LWTUNNEL_IP6_PAD, 46 | LWTUNNEL_IP6_OPTS, 47 | __LWTUNNEL_IP6_MAX, 48 | }; 49 | 50 | #define LWTUNNEL_IP6_MAX (__LWTUNNEL_IP6_MAX - 1) 51 | 52 | enum { 53 | LWTUNNEL_IP_OPTS_UNSPEC, 54 | LWTUNNEL_IP_OPTS_GENEVE, 55 | LWTUNNEL_IP_OPTS_VXLAN, 56 | LWTUNNEL_IP_OPTS_ERSPAN, 57 | __LWTUNNEL_IP_OPTS_MAX, 58 | }; 59 | 60 | #define LWTUNNEL_IP_OPTS_MAX (__LWTUNNEL_IP_OPTS_MAX - 1) 61 | 62 | enum { 63 | LWTUNNEL_IP_OPT_GENEVE_UNSPEC, 64 | LWTUNNEL_IP_OPT_GENEVE_CLASS, 65 | LWTUNNEL_IP_OPT_GENEVE_TYPE, 66 | LWTUNNEL_IP_OPT_GENEVE_DATA, 67 | __LWTUNNEL_IP_OPT_GENEVE_MAX, 68 | }; 69 | 70 | #define LWTUNNEL_IP_OPT_GENEVE_MAX (__LWTUNNEL_IP_OPT_GENEVE_MAX - 1) 71 | 72 | enum { 73 | LWTUNNEL_IP_OPT_VXLAN_UNSPEC, 74 | LWTUNNEL_IP_OPT_VXLAN_GBP, 75 | __LWTUNNEL_IP_OPT_VXLAN_MAX, 76 | }; 77 | 78 | #define LWTUNNEL_IP_OPT_VXLAN_MAX (__LWTUNNEL_IP_OPT_VXLAN_MAX - 1) 79 | 80 | enum { 81 | LWTUNNEL_IP_OPT_ERSPAN_UNSPEC, 82 | LWTUNNEL_IP_OPT_ERSPAN_VER, 83 | LWTUNNEL_IP_OPT_ERSPAN_INDEX, 84 | LWTUNNEL_IP_OPT_ERSPAN_DIR, 85 | LWTUNNEL_IP_OPT_ERSPAN_HWID, 86 | __LWTUNNEL_IP_OPT_ERSPAN_MAX, 87 | }; 88 | 89 | #define LWTUNNEL_IP_OPT_ERSPAN_MAX (__LWTUNNEL_IP_OPT_ERSPAN_MAX - 1) 90 | 91 | enum { 92 | LWT_BPF_PROG_UNSPEC, 93 | LWT_BPF_PROG_FD, 94 | LWT_BPF_PROG_NAME, 95 | __LWT_BPF_PROG_MAX, 96 | }; 97 | 98 | #define LWT_BPF_PROG_MAX (__LWT_BPF_PROG_MAX - 1) 99 | 100 | enum { 101 | LWT_BPF_UNSPEC, 102 | LWT_BPF_IN, 103 | LWT_BPF_OUT, 104 | LWT_BPF_XMIT, 105 | LWT_BPF_XMIT_HEADROOM, 106 | __LWT_BPF_MAX, 107 | }; 108 | 109 | #define LWT_BPF_MAX (__LWT_BPF_MAX - 1) 110 | 111 | #define LWT_BPF_MAX_HEADROOM 256 112 | 113 | #endif /* _UAPI_LWTUNNEL_H_ */ 114 | -------------------------------------------------------------------------------- /recv.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Authors: 3 | * Alexander Aring 4 | * 5 | * Original Authors: 6 | * Lars Fenneberg 7 | * 8 | * This software is Copyright 1996,1997,2019 by the above mentioned author(s), 9 | * All Rights Reserved. 10 | * 11 | * The license which is distributed with this software in the file COPYRIGHT 12 | * applies to this software. If your distribution is missing this file, you 13 | * may request it from . 14 | */ 15 | 16 | #include "recv.h" 17 | #include "log.h" 18 | 19 | int recv_rs_ra(int sock, unsigned char *msg, struct sockaddr_in6 *addr, 20 | struct in6_pktinfo **pkt_info, int *hoplimit, 21 | unsigned char *chdr) 22 | { 23 | struct iovec iov; 24 | iov.iov_len = MSG_SIZE_RECV; 25 | iov.iov_base = (caddr_t)msg; 26 | 27 | struct msghdr mhdr; 28 | memset(&mhdr, 0, sizeof(mhdr)); 29 | mhdr.msg_name = (caddr_t)addr; 30 | mhdr.msg_namelen = sizeof(*addr); 31 | mhdr.msg_iov = &iov; 32 | mhdr.msg_iovlen = 1; 33 | mhdr.msg_control = (void *)chdr; 34 | mhdr.msg_controllen = CMSG_SPACE(sizeof(struct in6_pktinfo)) + CMSG_SPACE(sizeof(int)); 35 | 36 | int len = recvmsg(sock, &mhdr, 0); 37 | 38 | if (len < 0) { 39 | if (errno != EINTR) 40 | flog(LOG_ERR, "recvmsg: %s", strerror(errno)); 41 | 42 | return len; 43 | } 44 | 45 | *hoplimit = 255; 46 | 47 | for (struct cmsghdr *cmsg = CMSG_FIRSTHDR(&mhdr); cmsg != NULL; cmsg = CMSG_NXTHDR(&mhdr, cmsg)) { 48 | if (cmsg->cmsg_level != IPPROTO_IPV6) 49 | continue; 50 | 51 | switch (cmsg->cmsg_type) { 52 | #ifdef IPV6_HOPLIMIT 53 | case IPV6_HOPLIMIT: 54 | if ((cmsg->cmsg_len == CMSG_LEN(sizeof(int))) && (*(int *)CMSG_DATA(cmsg) >= 0) && 55 | (*(int *)CMSG_DATA(cmsg) < 256)) { 56 | *hoplimit = *(int *)CMSG_DATA(cmsg); 57 | } else { 58 | flog(LOG_ERR, "received a bogus IPV6_HOPLIMIT from the kernel! len=%d, data=%d", 59 | (int)cmsg->cmsg_len, *(int *)CMSG_DATA(cmsg)); 60 | return -1; 61 | } 62 | break; 63 | #endif /* IPV6_HOPLIMIT */ 64 | case IPV6_PKTINFO: 65 | if ((cmsg->cmsg_len == CMSG_LEN(sizeof(struct in6_pktinfo))) && 66 | ((struct in6_pktinfo *)CMSG_DATA(cmsg))->ipi6_ifindex) { 67 | *pkt_info = (struct in6_pktinfo *)CMSG_DATA(cmsg); 68 | } else { 69 | flog(LOG_ERR, "received a bogus IPV6_PKTINFO from the kernel! len=%d, index=%d", 70 | (int)cmsg->cmsg_len, ((struct in6_pktinfo *)CMSG_DATA(cmsg))->ipi6_ifindex); 71 | return -1; 72 | } 73 | break; 74 | } 75 | } 76 | 77 | #if 0 78 | char if_namebuf[IF_NAMESIZE] = {""}; 79 | char *if_name = 0; 80 | if (pkt_info && *pkt_info) { 81 | if_name = if_indextoname((*pkt_info)->ipi6_ifindex, if_namebuf); 82 | } 83 | if (!if_name) { 84 | if_name = "unknown interface"; 85 | } 86 | dlog(LOG_DEBUG, 5, "%s recvmsg len=%d", if_name, len); 87 | #endif 88 | 89 | return len; 90 | } 91 | -------------------------------------------------------------------------------- /dag.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Authors: 3 | * Alexander Aring 4 | * 5 | * This software is Copyright 2019 by the above mentioned author(s), 6 | * All Rights Reserved. 7 | * 8 | * The license which is distributed with this software in the file COPYRIGHT 9 | * applies to this software. If your distribution is missing this file, you 10 | * may request it from . 11 | */ 12 | 13 | #ifndef __RPLD_DAG_H__ 14 | #define __RPLD_DAG_H__ 15 | 16 | #include 17 | #include 18 | #include 19 | 20 | #include "buffer.h" 21 | #include "list.h" 22 | #include "tree.h" 23 | 24 | struct peer { 25 | struct in6_addr addr; 26 | uint16_t rank; 27 | 28 | struct list list; 29 | }; 30 | 31 | struct child { 32 | struct in6_addr addr; 33 | struct in6_addr from; 34 | 35 | struct list list; 36 | }; 37 | 38 | struct dag_daoack { 39 | uint8_t dsn; 40 | 41 | struct list list; 42 | }; 43 | 44 | struct dag { 45 | uint8_t version; 46 | /* trigger */ 47 | uint8_t dtsn; 48 | /* if changed */ 49 | uint8_t dsn; 50 | struct in6_addr dodagid; 51 | uint8_t mop; 52 | 53 | struct t_root root; 54 | struct in6_prefix dest; 55 | 56 | uint16_t my_rank; 57 | struct peer *parent; 58 | 59 | /* routable self address */ 60 | struct in6_addr self; 61 | /* routable childs, if .head NULL -> leaf */ 62 | struct list_head childs; 63 | 64 | ev_tstamp trickle_t; 65 | ev_timer trickle_w; 66 | 67 | /* iface which dag belongs to */ 68 | const struct iface *iface; 69 | /* rpl instance which dag belongs to */ 70 | const struct rpl *rpl; 71 | 72 | /* TODO dodagid is optional so move it to instance? 73 | * 74 | * No idea how it works when it's not given. We don't 75 | * support it. 76 | */ 77 | struct list_head pending_acks; 78 | 79 | struct list list; 80 | }; 81 | 82 | struct rpl { 83 | uint8_t instance_id; 84 | 85 | /* set of dags, unique key is dodagid */ 86 | struct list_head dags; 87 | 88 | struct list list; 89 | }; 90 | 91 | struct dag *dag_create(struct iface *iface, uint8_t instanceid, 92 | const struct in6_addr *dodagid, 93 | uint16_t my_rank, uint8_t version, 94 | uint8_t mop, const struct in6_prefix *dest); 95 | void dag_free(struct dag *dag); 96 | void dag_build_dio(struct dag *dag, struct safe_buffer *sb); 97 | struct dag *dag_lookup(const struct iface *iface, uint8_t instance_id, 98 | const struct in6_addr *dodagid); 99 | void dag_process_dio(struct dag *dag); 100 | struct peer *dag_peer_create(const struct in6_addr *addr); 101 | void dag_build_dao(struct dag *dag, struct safe_buffer *sb); 102 | void dag_build_dao_ack(struct dag *dag, struct safe_buffer *sb); 103 | void dag_build_dis(struct safe_buffer *sb); 104 | struct child *dag_lookup_child_or_create(struct dag *dag, 105 | const struct in6_addr *addr, 106 | const struct in6_addr *from); 107 | bool dag_is_peer(const struct peer *peer, const struct in6_addr *addr); 108 | 109 | #endif /* __RPLD_DAG_H__ */ 110 | -------------------------------------------------------------------------------- /tree.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Authors: 3 | * Alexander Aring 4 | * 5 | * This software is Copyright 2020 by the above mentioned author(s), 6 | * All Rights Reserved. 7 | * 8 | * The license which is distributed with this software in the file COPYRIGHT 9 | * applies to this software. If your distribution is missing this file, you 10 | * may request it from . 11 | */ 12 | 13 | #include "tree.h" 14 | 15 | struct t_node *t_node_alloc(const struct in6_addr *addr, 16 | const struct in6_addr *target) 17 | { 18 | struct t_node *n; 19 | 20 | n = mzalloc(sizeof(*n)); 21 | if (!n) 22 | return NULL; 23 | 24 | n->addr = *addr; 25 | n->target = *target; 26 | return n; 27 | } 28 | 29 | static struct t_node *t_insert_tail(struct t_node *node, const struct in6_addr *parent, 30 | const struct in6_addr *addr, 31 | const struct in6_addr *target) 32 | { 33 | struct t_node *n, *res = NULL; 34 | 35 | if (!memcmp(&node->addr, parent, sizeof(node->addr))) { 36 | n = t_node_alloc(addr, target); 37 | if (!n) 38 | return NULL; 39 | 40 | n->parent = node; 41 | list_add(&node->childs, &n->list); 42 | return n; 43 | } 44 | 45 | list_for_each_entry(n, &node->childs, list) { 46 | res = t_insert_tail(n, parent, addr, target); 47 | if (res) 48 | break; 49 | } 50 | 51 | return res; 52 | } 53 | 54 | struct t_node *t_insert(struct t_root *root, const struct in6_addr *parent, 55 | const struct in6_addr *addr, 56 | const struct in6_addr *target) 57 | { 58 | return t_insert_tail(root->n, parent, addr, target); 59 | } 60 | 61 | void t_init(struct t_root *root, const struct in6_addr *addr, 62 | const struct in6_addr *dodagid) 63 | { 64 | root->n = t_node_alloc(addr, dodagid); 65 | } 66 | 67 | static void t_free_tail(struct t_node *node) 68 | { 69 | struct t_node *n, *tmp; 70 | 71 | list_for_each_entry_safe(n, tmp, &node->childs, list) { 72 | list_del(&node->childs, &n->list); 73 | t_free_tail(n); 74 | } 75 | 76 | free(node); 77 | } 78 | 79 | void t_free(struct t_root *root) 80 | { 81 | t_free_tail(root->n); 82 | } 83 | 84 | static struct t_path *t_path_alloc(const struct in6_addr *addr, 85 | const struct in6_addr *target) 86 | { 87 | struct t_path *path; 88 | 89 | path = mzalloc(sizeof(*path)); 90 | if (!path) 91 | return NULL; 92 | 93 | path->addr = *addr; 94 | path->target = *target; 95 | 96 | return path; 97 | } 98 | 99 | int t_path(const struct t_node *node, struct list_head *result) 100 | { 101 | struct t_path *p; 102 | 103 | if (!node) 104 | return 0; 105 | 106 | p = t_path_alloc(&node->addr, &node->target); 107 | if (!p) { 108 | /* TODO memleak */ 109 | return -ENOMEM; 110 | } 111 | 112 | list_add(result, &p->list); 113 | 114 | return t_path(node->parent, result); 115 | } 116 | 117 | void t_path_free(struct list_head *head) 118 | { 119 | struct t_path *p, *tmp; 120 | 121 | list_for_each_entry_safe(p, tmp, head, list) { 122 | list_del(head, &p->list); 123 | free(p); 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /helpers.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Authors: 3 | * Alexander Aring 4 | * 5 | * This software is Copyright 2019 by the above mentioned author(s), 6 | * All Rights Reserved. 7 | * 8 | * The license which is distributed with this software in the file COPYRIGHT 9 | * applies to this software. If your distribution is missing this file, you 10 | * may request it from . 11 | */ 12 | 13 | #define _GNU_SOURCE 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #include "helpers.h" 21 | #include "config.h" 22 | #include "log.h" 23 | 24 | int gen_stateless_addr(const struct in6_prefix *prefix, 25 | const struct iface_llinfo *llinfo, 26 | struct in6_addr *dst) 27 | { 28 | uint8_t len = bits_to_bytes(prefix->len); 29 | int i; 30 | 31 | memset(dst, 0, sizeof(*dst)); 32 | 33 | /* TODO only supported right now, gets tricky with bluetooth */ 34 | if (prefix->len != 64 || llinfo->addr_len != 8) 35 | return -1; 36 | 37 | memcpy(dst, &prefix->prefix, len); 38 | 39 | for (i = 0; i < llinfo->addr_len; i++) 40 | dst->s6_addr[len + i] = llinfo->addr[i]; 41 | 42 | /* U/L */ 43 | dst->s6_addr[8] ^= 0x02; 44 | 45 | return 0; 46 | } 47 | 48 | __attribute__((format(printf, 1, 2))) static char *strdupf(char const *format, ...) 49 | { 50 | va_list va; 51 | va_start(va, format); 52 | char *strp = 0; 53 | int rc = vasprintf(&strp, format, va); 54 | if (rc == -1 || !strp) { 55 | flog(LOG_ERR, "vasprintf failed: %s", strerror(errno)); 56 | exit(-1); 57 | } 58 | va_end(va); 59 | 60 | return strp; 61 | } 62 | 63 | /* note: also called from the root context */ 64 | int set_var(const char *var, uint32_t val) 65 | { 66 | int retval = -1; 67 | FILE *fp = 0; 68 | 69 | if (access(var, F_OK) != 0) 70 | goto cleanup; 71 | 72 | fp = fopen(var, "w"); 73 | if (!fp) { 74 | flog(LOG_ERR, "failed to set %s: %s", var, strerror(errno)); 75 | goto cleanup; 76 | } 77 | 78 | if (0 > fprintf(fp, "%u", val)) { 79 | goto cleanup; 80 | } 81 | 82 | retval = 0; 83 | 84 | cleanup: 85 | if (fp) 86 | fclose(fp); 87 | 88 | return retval; 89 | } 90 | 91 | int set_interface_var(const char *iface, const char *var, const char *name, uint32_t val) 92 | { 93 | int retval = -1; 94 | FILE *fp = 0; 95 | char *spath = strdupf(var, iface); 96 | 97 | /* No path traversal */ 98 | if (!iface[0] || !strcmp(iface, ".") || !strcmp(iface, "..") || strchr(iface, '/')) 99 | goto cleanup; 100 | 101 | if (access(spath, F_OK) != 0) 102 | goto cleanup; 103 | 104 | fp = fopen(spath, "w"); 105 | if (!fp) { 106 | if (name) 107 | flog(LOG_ERR, "failed to set %s (%u) for %s: %s", name, val, iface, strerror(errno)); 108 | goto cleanup; 109 | } 110 | 111 | if (0 > fprintf(fp, "%u", val)) { 112 | goto cleanup; 113 | } 114 | 115 | retval = 0; 116 | 117 | cleanup: 118 | if (fp) 119 | fclose(fp); 120 | 121 | free(spath); 122 | 123 | return retval; 124 | } 125 | 126 | void init_random_gen() 127 | { 128 | srandom(time(NULL)); 129 | } 130 | 131 | int gen_random_private_ula_pfx(struct in6_prefix *prefix) 132 | { 133 | int i; 134 | 135 | prefix->len = 64; 136 | memset(&prefix->prefix, 0, sizeof(prefix->prefix)); 137 | prefix->prefix.s6_addr[0] = 0xfd; 138 | for (i = 1; i < 6; i++) 139 | prefix->prefix.s6_addr[i] = random() & 0xff; 140 | 141 | return 0; 142 | } 143 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This is rpld, as it currently is a dirty hack between radvd (log handling, 2 | socket, etc) and unstrung (rpl.h). 3 | 4 | As radvd requires only libc dependencies, we have more fancy stuff to make 5 | high level handling easier, so we need: 6 | 7 | - lua (version >= XXX) for config parsing 8 | - ev (version >= XXX) for high level timer, loop handling 9 | - mnl (version >= XXX) for high level netlink handling 10 | 11 | As this version is a hack and shows my experiments... it will do something 12 | to have a routable thing afterwards in a Linux IEEE 802.15.4 6LoWPAN network. 13 | 14 | I have 1001 ideas to make the handling better, but this comes all afterwards. 15 | 16 | Build: 17 | 18 | $ mkdir build 19 | 20 | $ meson build 21 | 22 | $ ninja -C build 23 | 24 | Testing: 25 | 26 | If you want to test the implementation just invoke tests/start script. 27 | 28 | Topology (default example, can be changed by changing the script): 29 | 30 | 0 31 | / \ 32 | 2 1 33 | \ | \ 34 | 3 | 35 | | / 36 | 4 37 | | 38 | 5 39 | 40 | The numbers are the corresponding namespace ns#. 41 | 42 | RPL ranks and resulting DODAG is: 43 | 44 | 1 <--- root 45 | / \ 46 | 2 2 47 | | \ 48 | 3 | 49 | / 50 | 3 51 | | 52 | 4 53 | 54 | OR (depends on first come first serve) 55 | 56 | 1 <--- root 57 | / \ 58 | 2 2 59 | \ \ 60 | 3 | 61 | / 62 | 3 63 | | 64 | 4 65 | 66 | I did here mostly the same behaviour as unstrung implementation 67 | by Michael Richardson https://github.com/AnimaGUS-minerva/unstrung 68 | 69 | However you can do a: 70 | 71 | $ ip -n ns5 a 72 | 73 | remember the IPv6 address of lowpan interface. 74 | 75 | Then try a: 76 | 77 | $ ip netns exec ns0 traceroute -6 $IPV6_ADDRESS 78 | 79 | Then you see some hops to reach the node of ns4 from the root. 80 | 81 | HOW IT WORKS? 82 | 83 | It uses link-local address for as nexthop addresses. It setups a prefix 84 | carried by DIO and setups necessary routing tables. 85 | 86 | TODO 87 | 88 | This stuff is all early state, it has no clever logic of avoiding netlink 89 | messages. I have 1001 ideas to make some timeout handling so repairing 90 | will work, but that's all future stuff. Sorry, this implementation was 91 | only made to figure out how this routing protocol works... 92 | 93 | Several memory leaks and some bufferoverflows, the RPL messages need to 94 | be very tight and setting the right flags with the right additional data 95 | otherwise it will not work. Everything can be added but this works for 96 | now. 97 | 98 | Parent Acknowledge? I tried to implement it, I stopped working because 99 | it begun to finally working... sure there is a lot of security stuff 100 | it's one of these routing protocol implementation which just blindly 101 | accept router discovery messages. 102 | 103 | The fact that we only use stateless configuration we could use stateful 104 | compression for the used global prefix, but this will only work for 105 | Linux to Linux (not sure about that ... this implementation is only 106 | tested between Linux systems so... maybe future work but needs a 107 | netlink interface for stateful compression inside the Linux kernel) 108 | -------------------------------------------------------------------------------- /send.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Authors: 3 | * Alexander Aring 4 | * 5 | * Original Authors: 6 | * Lars Fenneberg 7 | * 8 | * This software is Copyright 1996,1997,2019 by the above mentioned author(s), 9 | * All Rights Reserved. 10 | * 11 | * The license which is distributed with this software in the file COPYRIGHT 12 | * applies to this software. If your distribution is missing this file, you 13 | * may request it from . 14 | */ 15 | 16 | #include 17 | #include 18 | 19 | #include 20 | #include 21 | 22 | #include "helpers.h" 23 | #include "buffer.h" 24 | #include "config.h" 25 | #include "config.h" 26 | #include "send.h" 27 | #include "log.h" 28 | #include "rpl.h" 29 | 30 | static int really_send(int sock, const struct iface *iface, 31 | const struct in6_addr *dest, 32 | struct safe_buffer *sb) 33 | { 34 | struct sockaddr_in6 addr; 35 | int rc; 36 | memset((void *)&addr, 0, sizeof(addr)); 37 | addr.sin6_family = AF_INET6; 38 | addr.sin6_port = htons(IPPROTO_ICMPV6); 39 | memcpy(&addr.sin6_addr, dest, sizeof(struct in6_addr)); 40 | 41 | struct iovec iov; 42 | iov.iov_len = sb->used; 43 | iov.iov_base = (caddr_t)sb->buffer; 44 | 45 | char __attribute__((aligned(8))) chdr[CMSG_SPACE(sizeof(struct in6_pktinfo))]; 46 | memset(chdr, 0, sizeof(chdr)); 47 | struct cmsghdr *cmsg = (struct cmsghdr *)chdr; 48 | 49 | cmsg->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo)); 50 | cmsg->cmsg_level = IPPROTO_IPV6; 51 | cmsg->cmsg_type = IPV6_PKTINFO; 52 | 53 | struct in6_pktinfo *pkt_info = (struct in6_pktinfo *)CMSG_DATA(cmsg); 54 | pkt_info->ipi6_ifindex = iface->ifindex; 55 | memcpy(&pkt_info->ipi6_addr, iface->ifaddr_src, sizeof(struct in6_addr)); 56 | 57 | #ifdef HAVE_SIN6_SCOPE_ID 58 | if (IN6_IS_ADDR_LINKLOCAL(&addr.sin6_addr) || IN6_IS_ADDR_MC_LINKLOCAL(&addr.sin6_addr)) 59 | addr.sin6_scope_id = iface->ifindex; 60 | #endif 61 | 62 | struct msghdr mhdr; 63 | memset(&mhdr, 0, sizeof(mhdr)); 64 | mhdr.msg_name = (caddr_t)&addr; 65 | mhdr.msg_namelen = sizeof(struct sockaddr_in6); 66 | mhdr.msg_iov = &iov; 67 | mhdr.msg_iovlen = 1; 68 | mhdr.msg_control = (void *)cmsg; 69 | mhdr.msg_controllen = sizeof(chdr); 70 | 71 | rc = sendmsg(sock, &mhdr, 0); 72 | safe_buffer_free(sb); 73 | 74 | return rc; 75 | } 76 | void send_dio(int sock, struct dag *dag) 77 | { 78 | struct safe_buffer *sb; 79 | int rc; 80 | 81 | sb = safe_buffer_new(); 82 | if (!sb) 83 | return; 84 | 85 | dag_build_dio(dag, sb); 86 | rc = really_send(sock, dag->iface, &all_rpl_addr, sb); 87 | flog(LOG_INFO, "foo! %s %d %s", dag->iface->ifname, rc ,strerror(errno)); 88 | } 89 | 90 | void send_dao(int sock, const struct in6_addr *to, struct dag *dag) 91 | { 92 | struct safe_buffer *sb; 93 | int rc; 94 | 95 | sb = safe_buffer_new(); 96 | if (!sb) 97 | return; 98 | 99 | dag_build_dao(dag, sb); 100 | rc = really_send(sock, dag->iface, to, sb); 101 | flog(LOG_INFO, "send_dao! %d", rc); 102 | } 103 | 104 | void send_dao_ack(int sock, const struct in6_addr *to, struct dag *dag) 105 | { 106 | struct safe_buffer *sb; 107 | int rc; 108 | 109 | sb = safe_buffer_new(); 110 | if (!sb) 111 | return; 112 | 113 | dag_build_dao_ack(dag, sb); 114 | rc = really_send(sock, dag->iface, to, sb); 115 | flog(LOG_INFO, "send_dao_ack! %d", rc); 116 | } 117 | 118 | void send_dis(int sock, struct iface *iface) 119 | { 120 | struct safe_buffer *sb; 121 | int rc; 122 | 123 | sb = safe_buffer_new(); 124 | if (!sb) 125 | return; 126 | 127 | dag_build_dis(sb); 128 | rc = really_send(sock, iface, &all_rpl_addr, sb); 129 | flog(LOG_INFO, "send_dis! %d", rc); 130 | } 131 | -------------------------------------------------------------------------------- /log.c: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Authors: 4 | * Lars Fenneberg 5 | * 6 | * This software is Copyright 1996,1997 by the above mentioned author(s), 7 | * All Rights Reserved. 8 | * 9 | * The license which is distributed with this software in the file 10 | * COPYRIGHT applies to this software. If your distribution is missing 11 | * this file, you may request it from . 12 | * 13 | */ 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #include "log.h" 22 | 23 | __attribute__((format(printf, 2, 0))) static int vlog(int prio, char const *format, va_list ap); 24 | 25 | static int log_method = L_NONE; 26 | static char const *log_ident; 27 | static char const *log_file; 28 | static FILE *log_file_fd; 29 | static int log_facility; 30 | static int debug_level = 0; 31 | 32 | int log_open(int method, char const *ident, char const *log, int facility) 33 | { 34 | log_method = method; 35 | log_ident = ident; 36 | 37 | switch (log_method) { 38 | case L_NONE: 39 | case L_UNSPEC: 40 | case L_STDERR: 41 | break; 42 | case L_STDERR_CLEAN: 43 | /* fallthrough */ 44 | case L_STDERR_SYSLOG: 45 | /* fallthrough */ 46 | case L_SYSLOG: 47 | if (facility == -1) 48 | log_facility = LOG_DAEMON; 49 | else 50 | log_facility = facility; 51 | 52 | openlog(log_ident, LOG_PID, log_facility); 53 | break; 54 | case L_LOGFILE: 55 | if (!log) { 56 | fprintf(stderr, "%s (%d): no logfile specified\n", log_ident, getpid()); 57 | return -1; 58 | } 59 | log_file = log; 60 | if ((log_file_fd = fopen(log_file, "a")) == NULL) { 61 | fprintf(stderr, "%s (%d): can't open %s: %s\n", log_ident, getpid(), log_file, strerror(errno)); 62 | return -1; 63 | } 64 | break; 65 | default: 66 | fprintf(stderr, "%s (%d): unknown logging method: %d\n", log_ident, getpid(), log_method); 67 | log_method = L_NONE; 68 | return -1; 69 | } 70 | return 0; 71 | } 72 | 73 | /* note: [dfv]log() is also called from root context */ 74 | __attribute__((format(printf, 2, 0))) static int vlog(int prio, char const *format, va_list ap) 75 | { 76 | char tstamp[64], buff[1024]; 77 | struct tm *tm; 78 | time_t current; 79 | 80 | vsnprintf(buff, sizeof(buff), format, ap); 81 | 82 | switch (log_method) { 83 | case L_NONE: 84 | case L_UNSPEC: 85 | break; 86 | case L_SYSLOG: 87 | syslog(prio, "%s", buff); 88 | break; 89 | case L_STDERR_SYSLOG: 90 | syslog(prio, "%s", buff); 91 | if (debug_level < prio) /* fall through for messages with high priority */ 92 | break; 93 | case L_STDERR: 94 | current = time(NULL); 95 | tm = localtime(¤t); 96 | (void)strftime(tstamp, sizeof(tstamp), LOG_TIME_FORMAT, tm); 97 | 98 | fprintf(stderr, "[%s] %s (%d): %s\n", tstamp, log_ident, getpid(), buff); 99 | fflush(stderr); 100 | break; 101 | case L_STDERR_CLEAN: 102 | fprintf(stderr, "%s\n", buff); 103 | fflush(stderr); 104 | break; 105 | case L_LOGFILE: 106 | current = time(NULL); 107 | tm = localtime(¤t); 108 | (void)strftime(tstamp, sizeof(tstamp), LOG_TIME_FORMAT, tm); 109 | 110 | fprintf(log_file_fd, "[%s] %s (%d): %s\n", tstamp, log_ident, getpid(), buff); 111 | fflush(log_file_fd); 112 | break; 113 | default: 114 | fprintf(stderr, "%s (%d): unknown logging method: %d\n", log_ident, getpid(), log_method); 115 | log_method = L_NONE; 116 | return -1; 117 | } 118 | return 0; 119 | } 120 | 121 | void dlog(int prio, int level, char const *format, ...) 122 | { 123 | if (debug_level < level) 124 | return; 125 | 126 | va_list ap; 127 | va_start(ap, format); 128 | vlog(prio, format, ap); 129 | va_end(ap); 130 | } 131 | 132 | void flog(int prio, char const *format, ...) 133 | { 134 | va_list ap; 135 | 136 | va_start(ap, format); 137 | vlog(prio, format, ap); 138 | va_end(ap); 139 | } 140 | 141 | int log_close(void) 142 | { 143 | switch (log_method) { 144 | case L_NONE: 145 | case L_UNSPEC: 146 | case L_STDERR: 147 | break; 148 | case L_STDERR_SYSLOG: 149 | case L_SYSLOG: 150 | closelog(); 151 | break; 152 | case L_LOGFILE: 153 | fclose(log_file_fd); 154 | break; 155 | default: 156 | fprintf(stderr, "%s (%d): unknown logging method: %d\n", log_ident, getpid(), log_method); 157 | log_method = L_NONE; 158 | return -1; 159 | } 160 | return 0; 161 | } 162 | 163 | void set_debuglevel(int level) { debug_level = level; } 164 | 165 | int get_debuglevel(void) { return debug_level; } 166 | -------------------------------------------------------------------------------- /socket.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Authors: 3 | * Alexander Aring 4 | * 5 | * Original Authors: 6 | * Pedro Roque 7 | * Lars Fenneberg 8 | * 9 | * This software is Copyright 1996,1997,2019 by the above mentioned author(s), 10 | * All Rights Reserved. 11 | * 12 | * The license which is distributed with this software in the file COPYRIGHT 13 | * applies to this software. If your distribution is missing this file, you 14 | * may request it from . 15 | * 16 | */ 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #include "helpers.h" 25 | #include "socket.h" 26 | #include "config.h" 27 | #include "log.h" 28 | #include "rpl.h" 29 | 30 | /* Note: these are applicable to receiving sockopts only */ 31 | #if defined IPV6_HOPLIMIT && !defined IPV6_RECVHOPLIMIT 32 | #define IPV6_RECVHOPLIMIT IPV6_HOPLIMIT 33 | #endif 34 | 35 | #if defined IPV6_PKTINFO && !defined IPV6_RECVPKTINFO 36 | #define IPV6_RECVPKTINFO IPV6_PKTINFO 37 | #endif 38 | 39 | static int rpl_multicast_handler(int sock, const struct list_head *ifaces, 40 | int optname) 41 | { 42 | struct ipv6_mreq mreq; 43 | struct iface *iface; 44 | int rc; 45 | 46 | list_for_each_entry(iface, ifaces, list) { 47 | memset(&mreq, 0, sizeof(mreq)); 48 | 49 | mreq.ipv6mr_interface = iface->ifindex; 50 | /* all-rpl-nodes: ff02::1a */ 51 | memcpy(&mreq.ipv6mr_multiaddr.s6_addr[0], &all_rpl_addr, 52 | sizeof(mreq.ipv6mr_multiaddr)); 53 | 54 | rc = setsockopt(sock, SOL_IPV6, optname, &mreq, 55 | sizeof(mreq)); 56 | if (rc < 0) 57 | return -1; 58 | 59 | flog(LOG_INFO, "interface %s %s to rpl multicast", 60 | iface->ifname, (optname == IPV6_ADD_MEMBERSHIP)? 61 | "subscribe":"unsubscribe"); 62 | } 63 | 64 | return 0; 65 | } 66 | 67 | static int subscribe_rpl_multicast(int sock, const struct list_head *ifaces) 68 | { 69 | return rpl_multicast_handler(sock, ifaces, IPV6_ADD_MEMBERSHIP); 70 | } 71 | 72 | static void unsubscribe_rpl_multicast(int sock, const struct list_head *ifaces) 73 | { 74 | rpl_multicast_handler(sock, ifaces, IPV6_DROP_MEMBERSHIP); 75 | } 76 | 77 | int open_icmpv6_socket(const struct list_head *ifaces) 78 | { 79 | struct icmp6_filter filter; 80 | int sock; 81 | int err; 82 | 83 | sock = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6); 84 | if (sock < 0) { 85 | flog(LOG_ERR, "can't create socket(AF_INET6): %s", strerror(errno)); 86 | return -1; 87 | } 88 | 89 | err = setsockopt(sock, IPPROTO_IPV6, IPV6_RECVPKTINFO, (int[]){1}, sizeof(int)); 90 | if (err < 0) { 91 | flog(LOG_ERR, "setsockopt(IPV6_RECVPKTINFO): %s", strerror(errno)); 92 | close(sock); 93 | return -1; 94 | } 95 | 96 | err = setsockopt(sock, IPPROTO_RAW, IPV6_CHECKSUM, (int[]){2}, sizeof(int)); 97 | if (err < 0) { 98 | flog(LOG_ERR, "setsockopt(IPV6_CHECKSUM): %s", strerror(errno)); 99 | close(sock); 100 | return -1; 101 | } 102 | 103 | err = setsockopt(sock, IPPROTO_IPV6, IPV6_UNICAST_HOPS, (int[]){255}, sizeof(int)); 104 | if (err < 0) { 105 | flog(LOG_ERR, "setsockopt(IPV6_UNICAST_HOPS): %s", strerror(errno)); 106 | close(sock); 107 | return -1; 108 | } 109 | 110 | err = setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, (int[]){1}, sizeof(int)); 111 | if (err < 0) { 112 | flog(LOG_ERR, "setsockopt(IPV6_MULTICAST_HOPS): %s", strerror(errno)); 113 | close(sock); 114 | return -1; 115 | } 116 | 117 | err = setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, (int[]){0}, sizeof(int)); 118 | if (err < 0) { 119 | flog(LOG_ERR, "setsockopt(IPV6_MULTICAST_LOOP): %s", strerror(errno)); 120 | close(sock); 121 | return -1; 122 | } 123 | 124 | #ifdef IPV6_RECVHOPLIMIT 125 | err = setsockopt(sock, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, (int[]){1}, sizeof(int)); 126 | if (err < 0) { 127 | flog(LOG_ERR, "setsockopt(IPV6_RECVHOPLIMIT): %s", strerror(errno)); 128 | close(sock); 129 | return -1; 130 | } 131 | #endif 132 | 133 | err = subscribe_rpl_multicast(sock, ifaces); 134 | if (err < 0) { 135 | flog(LOG_ERR, "Failed to subscribe rpl multicast: %s", strerror(errno)); 136 | close(sock); 137 | return -1; 138 | } 139 | 140 | ICMP6_FILTER_SETBLOCKALL(&filter); 141 | ICMP6_FILTER_SETPASS(ND_RPL_MESSAGE, &filter); 142 | #if 0 143 | ICMP6_FILTER_SETPASS(ND_ROUTER_SOLICIT, &filter); 144 | ICMP6_FILTER_SETPASS(ND_ROUTER_ADVERT, &filter); 145 | ICMP6_FILTER_SETPASS(ND_NEIGHBOR_SOLICIT, &filter); 146 | ICMP6_FILTER_SETPASS(ND_NEIGHBOR_ADVERT, &filter); 147 | #endif 148 | 149 | return sock; 150 | } 151 | 152 | void close_icmpv6_socket(int sock, const struct list_head *ifaces) 153 | { 154 | unsubscribe_rpl_multicast(sock, ifaces); 155 | close(sock); 156 | } 157 | -------------------------------------------------------------------------------- /rpl.h: -------------------------------------------------------------------------------- 1 | #ifndef _RPL_H_ 2 | 3 | /* 4 | * NOTE: the contents of this file are an interpretation of RFC6550. 5 | * no copyright is asserted on this file, as it transcribes 6 | * a public specification. 7 | * It comes from https://github.com/mcr/unstrung/blob/master/include/rpl.h 8 | */ 9 | 10 | #define PACKED __attribute__((packed)) 11 | 12 | /* 13 | * DIO: Updated to RFC6550, as published in 2012: section 6. (page 30) 14 | */ 15 | 16 | #define ND_RPL_MESSAGE 155 /* 0x9B */ 17 | 18 | enum ND_RPL_CODE { 19 | ND_RPL_DAG_IS=0x00, /* DIS */ 20 | ND_RPL_DAG_IO=0x01, /* DIO */ 21 | ND_RPL_DAO =0x02, 22 | ND_RPL_DAO_ACK=0x03, 23 | ND_RPL_SEC_DAG_IS = 0x80, 24 | ND_RPL_SEC_DAG_IO = 0x81, 25 | ND_RPL_SEC_DAG = 0x82, 26 | ND_RPL_SEC_DAG_ACK= 0x83, 27 | ND_RPL_SEC_CONSIST= 0x84, 28 | }; 29 | 30 | enum ND_RPL_DIO_FLAGS { 31 | ND_RPL_DIO_GROUNDED = 0x80, 32 | ND_RPL_DIO_DATRIG = 0x40, 33 | ND_RPL_DIO_DASUPPORT= 0x20, 34 | ND_RPL_DIO_RES4 = 0x10, 35 | ND_RPL_DIO_RES3 = 0x08, 36 | ND_RPL_DIO_PRF_MASK = 0x07, /* 3-bit preference */ 37 | }; 38 | 39 | #define DAGID_LEN 16 40 | 41 | /* section 6 of draft-ietf-roll-rpl-19 */ 42 | struct nd_rpl_security { 43 | u_int8_t rpl_sec_t_reserved; /* bit 7 is T-bit */ 44 | u_int8_t rpl_sec_algo; 45 | u_int16_t rpl_sec_kim_lvl_flags; /* bit 15/14, KIM */ 46 | /* bit 10-8, LVL, bit 7-0 flags */ 47 | u_int32_t rpl_sec_counter; 48 | u_int8_t rpl_sec_ki[0]; /* depends upon kim */ 49 | } PACKED; 50 | 51 | struct nd_rpl_opt { 52 | u_int8_t type; 53 | u_int8_t len; 54 | } PACKED; 55 | 56 | /* section 6.2.1, DODAG Information Solication (DIS) */ 57 | struct nd_rpl_dis { 58 | u_int8_t rpl_dis_flags; 59 | u_int8_t rpl_dis_reserved; 60 | //u_int8_t rpl_dis_options[0]; 61 | } PACKED; 62 | 63 | /* section 6.7.9, DIS - Solicited Information */ 64 | struct rpl_dis_solicitedinfo { 65 | u_int8_t rpl_dis_type; 66 | u_int8_t rpl_dis_len; 67 | u_int8_t rpl_dis_instanceid; 68 | u_int8_t rpl_dis_flags; /* flags, V,I,D */ 69 | u_int8_t rpl_dis_dagid[DAGID_LEN]; 70 | u_int8_t rpl_dis_versionnum; 71 | } PACKED; 72 | #define RPL_DIS_SI_V (1 << 7) 73 | #define RPL_DIS_SI_I (1 << 6) 74 | #define RPL_DIS_SI_D (1 << 5) 75 | #define RPL_DIS_SI_FLAGS ((1 << 5)-1) 76 | 77 | 78 | 79 | /* section 6.3.1, DODAG Information Object (DIO) */ 80 | struct nd_rpl_dio { 81 | u_int8_t rpl_instanceid; 82 | u_int8_t rpl_version; 83 | u_int16_t rpl_dagrank; 84 | u_int8_t rpl_mopprf; /* bit 7=G, 5-3=MOP, 2-0=PRF */ 85 | u_int8_t rpl_dtsn; /* Dest. Advertisement Trigger Sequence Number */ 86 | u_int8_t rpl_flags; /* no flags defined yet */ 87 | u_int8_t rpl_resv1; 88 | struct in6_addr rpl_dagid; 89 | //unsigned char u_data[0]; 90 | } PACKED; 91 | #define RPL_DIO_GROUND_FLAG 0x80 92 | #define RPL_DIO_MOP_SHIFT 3 93 | #define RPL_DIO_MOP_MASK (7 << RPL_DIO_MOP_SHIFT) 94 | #define RPL_DIO_PRF_SHIFT 0 95 | #define RPL_DIO_PRF_MASK (7 << RPL_DIO_PRF_SHIFT) 96 | #define RPL_DIO_GROUNDED(X) ((X)&RPL_DIO_GROUND_FLAG) 97 | #define RPL_DIO_MOP(X) (enum RPL_DIO_MOP)(((X)&RPL_DIO_MOP_MASK) >> RPL_DIO_MOP_SHIFT) 98 | #define RPL_DIO_PRF(X) (((X)&RPL_DIO_PRF_MASK) >> RPL_DIO_PRF_SHIFT) 99 | 100 | enum RPL_DIO_MOP { 101 | RPL_DIO_NO_DOWNWARD_ROUTES_MAINT = 0x0, 102 | RPL_DIO_NONSTORING = 0x1, 103 | RPL_DIO_STORING_NO_MULTICAST = 0x2, 104 | RPL_DIO_STORING_MULTICAST = 0x3, 105 | }; 106 | 107 | enum RPL_SUBOPT { 108 | RPL_OPT_PAD0 = 0, 109 | RPL_OPT_PADN = 1, 110 | RPL_DIO_METRICS = 2, 111 | RPL_DIO_ROUTINGINFO = 3, 112 | RPL_DIO_CONFIG = 4, 113 | RPL_DAO_RPLTARGET = 5, 114 | RPL_DAO_TRANSITINFO = 6, 115 | RPL_DIS_SOLICITEDINFO=7, 116 | RPL_DIO_DESTPREFIX = 8, 117 | RPL_DAO_RPLTARGET_DESC=9, 118 | }; 119 | 120 | struct rpl_dio_genoption { 121 | u_int8_t rpl_dio_type; 122 | u_int8_t rpl_dio_len; /* suboption length, not including type/len */ 123 | u_int8_t rpl_dio_data[0]; 124 | } PACKED; 125 | 126 | #define RPL_DIO_LIFETIME_INFINITE 0xffffffff 127 | #define RPL_DIO_LIFETIME_DISCONNECT 0 128 | 129 | #define RPL_DIO_PREFIX_AUTONOMOUS_ADDR_CONFIG_FLAG 0x40 130 | #define RPL_DIO_PREFIX_AUTONOMOUS_ADDR_CONFIG_SHIFT 6 131 | #define RPL_DIO_PREFIX_AUTONOMOUS_ADDR_CONFIG_MASK (1 << RPL_DIO_PREFIX_AUTONOMOUS_ADDR_CONFIG_SHIFT) 132 | 133 | struct rpl_dio_destprefix { 134 | u_int8_t rpl_dio_type; 135 | u_int8_t rpl_dio_len; 136 | u_int8_t rpl_dio_prefixlen; /* in bits */ 137 | u_int8_t rpl_dio_prf; /* flags, including Route Preference */ 138 | u_int32_t rpl_dio_route_lifetime; /* in seconds */ 139 | struct in6_addr rpl_dio_prefix; 140 | // u_int32_t rpl_dio_preferred_lifetime; /* in seconds */ 141 | // u_int32_t reserved2; /* RFC6550 section 6.7.10 says to always*/ 142 | // u_int8_t rpl_dio_prefix[16]; /* send 16 bytes,even if fewer are used*/ 143 | } PACKED; 144 | 145 | /* section 6.4.1, DODAG Information Object (DIO) */ 146 | struct nd_rpl_dao { 147 | u_int8_t rpl_instanceid; 148 | u_int8_t rpl_flags; /* bit 7=K, 6=D */ 149 | u_int8_t rpl_resv; 150 | u_int8_t rpl_daoseq; /* a sequence number, to be echoed in ACK */ 151 | struct in6_addr rpl_dagid; /* [DAGID_LEN] present when D set. */ 152 | } PACKED; 153 | 154 | /* indicates if this DAO is to be acK'ed */ 155 | #define RPL_DAO_K_SHIFT 7 156 | #define RPL_DAO_K_MASK (1 << RPL_DAO_K_SHIFT) 157 | #define RPL_DAO_K(X) (((X)&RPL_DAO_K_MASK) >> RPL_DAO_K_SHIFT) 158 | 159 | /* indicates if the DAGID is present */ 160 | #define RPL_DAO_D_SHIFT 6 161 | #define RPL_DAO_D_MASK (1 << RPL_DAO_D_SHIFT) 162 | #define RPL_DAO_D(X) (((X)&RPL_DAO_D_MASK) >> RPL_DAO_D_SHIFT) 163 | 164 | /* TODO make rpl option with type and len */ 165 | 166 | struct rpl_dao_target { 167 | u_int8_t rpl_dao_type; 168 | u_int8_t rpl_dao_len; 169 | u_int8_t rpl_dao_flags; /* unused */ 170 | u_int8_t rpl_dao_prefixlen; /* in bits */ 171 | struct in6_addr rpl_dao_prefix; /* variables number of bytes */ 172 | } PACKED; 173 | 174 | struct rpl_dao_transit { 175 | u_int8_t type; 176 | u_int8_t len; 177 | u_int8_t flags; 178 | u_int8_t path_ctrl; 179 | u_int8_t seq; 180 | u_int8_t lifetime; 181 | struct in6_addr parent; 182 | } PACKED; 183 | 184 | /* section 6.5.1, Destination Advertisement Object Acknowledgement (DAO-ACK) */ 185 | struct nd_rpl_daoack { 186 | u_int8_t rpl_instanceid; 187 | u_int8_t rpl_flags; /* bit 7=D */ 188 | u_int8_t rpl_daoseq; 189 | u_int8_t rpl_status; 190 | struct in6_addr rpl_dagid; /* [DAGID_LEN] present when D set. */ 191 | } PACKED; 192 | /* indicates if the DAGID is present */ 193 | #define RPL_DAOACK_D_SHIFT 7 194 | #define RPL_DAOACK_D_MASK (1 << RPL_DAOACK_D_SHIFT) 195 | #define RPL_DAOACK_D(X) (((X)&RPL_DAOACK_D_MASK) >> RPL_DAOACK_D_SHIFT) 196 | 197 | 198 | 199 | #define _RPL_H_ 200 | #endif /* _RPL_H_ */ 201 | 202 | /* 203 | * Local Variables: 204 | * c-basic-offset:4 205 | * c-style: whitesmith 206 | * End: 207 | */ 208 | 209 | -------------------------------------------------------------------------------- /rpld.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Authors: 3 | * Alexander Aring 4 | * 5 | * This software is Copyright 2019 by the above mentioned author(s), 6 | * All Rights Reserved. 7 | * 8 | * The license which is distributed with this software in the file COPYRIGHT 9 | * applies to this software. If your distribution is missing this file, you 10 | * may request it from . 11 | */ 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include 19 | 20 | #include "process.h" 21 | #include "netlink.h" 22 | #include "helpers.h" 23 | #include "socket.h" 24 | #include "config.h" 25 | #include "send.h" 26 | #include "recv.h" 27 | #include "log.h" 28 | 29 | #define VERSION "0.0.1" 30 | #define PATH_RPLD_LOG "/var/log/rpld.log" 31 | #define PATH_RPLD_CONF "/etc/rpld.conf" 32 | #define LOG_FACILITY LOG_DAEMON 33 | 34 | //ICMPV6_PLD_MAXLEN 35 | 36 | static struct list_head ifaces; 37 | static int sock; 38 | 39 | /* TODO overwrite root setting */ 40 | static char usage_str[] = { 41 | "\n" 42 | " -C, --config=PATH Set the config file. Default is /etc/rpld.conf\n" 43 | " -d, --debug=NUM Set the debug level. Values can be 1, 2, 3, 4 or 5.\n" 44 | " -h, --help Show this help screen.\n" 45 | " -f, --facility=NUM Set the logging facility.\n" 46 | " -l, --logfile=PATH Set the log file.\n" 47 | " -m, --logmethod=X Set method to: syslog, stderr, stderr_syslog, logfile,\n" 48 | " -v, --version Print the version and quit.\n" 49 | }; 50 | 51 | static void usage(FILE *o, const char *pname) 52 | { 53 | fprintf(o, "usage: %s %s\n", pname, usage_str); 54 | } 55 | 56 | static void icmpv6_cb(EV_P_ ev_io *w, int revents) 57 | { 58 | int len, hoplimit; 59 | struct sockaddr_in6 rcv_addr; 60 | struct in6_pktinfo *pkt_info = NULL; 61 | unsigned char msg[MSG_SIZE_RECV]; 62 | unsigned char chdr[CMSG_SPACE(sizeof(struct in6_pktinfo)) + CMSG_SPACE(sizeof(int))]; 63 | 64 | len = recv_rs_ra(sock, msg, &rcv_addr, &pkt_info, &hoplimit, chdr); 65 | if (len > 0 && pkt_info) { 66 | process(sock, &ifaces, msg, len, &rcv_addr, pkt_info, hoplimit); 67 | } else if (!pkt_info) { 68 | dlog(LOG_INFO, 4, "recv_rs_ra returned null pkt_info"); 69 | } else if (len <= 0) { 70 | dlog(LOG_INFO, 4, "recv_rs_ra returned len <= 0: %d", len); 71 | } 72 | } 73 | 74 | static void trickle_cb(EV_P_ ev_timer *w, int revents) 75 | { 76 | struct dag *dag = container_of(w, struct dag, trickle_w); 77 | 78 | flog(LOG_INFO, "send dio %p", dag->parent); 79 | send_dio(sock, dag); 80 | } 81 | 82 | static void sigint_cb(struct ev_loop *loop, ev_signal *w, int revents) 83 | { 84 | ev_break(loop, EVBREAK_ALL); 85 | } 86 | 87 | static void send_dis_cb(EV_P_ ev_timer *w, int revents) 88 | { 89 | struct iface *iface = container_of(w, struct iface, dis_w); 90 | 91 | ev_timer_stop(loop, w); 92 | send_dis(sock, iface); 93 | } 94 | 95 | /* TODO move somewhere else */ 96 | struct ev_loop *foo; 97 | void dag_init_timer(struct dag *dag) 98 | { 99 | ev_timer_init(&dag->trickle_w, trickle_cb, 100 | dag->trickle_t, dag->trickle_t); 101 | ev_timer_start(foo, &dag->trickle_w); 102 | } 103 | 104 | static int rpld_setup(struct ev_loop *loop, struct list_head *ifaces) 105 | { 106 | struct iface *iface; 107 | struct rpl *rpl; 108 | struct dag *dag; 109 | 110 | list_for_each_entry(iface, ifaces, list) { 111 | ev_timer_init(&iface->dis_w, send_dis_cb, 1, 1); 112 | /* schedule a dis at statup */ 113 | ev_timer_start(loop, &iface->dis_w); 114 | 115 | list_for_each_entry(rpl, &iface->rpls, list) { 116 | list_for_each_entry(dag, &rpl->dags, list) { 117 | ev_timer_init(&dag->trickle_w, trickle_cb, 118 | dag->trickle_t, dag->trickle_t); 119 | ev_timer_start(loop, &dag->trickle_w); 120 | 121 | /* TODO wrong here */ 122 | if (iface->dodag_root) 123 | nl_add_addr(iface->ifindex, &dag->dodagid); 124 | } 125 | } 126 | } 127 | 128 | return 0; 129 | } 130 | 131 | /* read a new trickle interval (seconds) from file, return >0 on success */ 132 | static double read_trickle_config(const char *path) 133 | { 134 | FILE *f; 135 | double val = -1.0; 136 | 137 | f = fopen(path, "r"); 138 | if (!f) 139 | return -1.0; 140 | 141 | /* expected line: trickle_t=1.5 (seconds) */ 142 | if (fscanf(f, "trickle_t=%lf", &val) != 1) { 143 | /* try an alternative: just a number in file */ 144 | rewind(f); 145 | if (fscanf(f, "%lf", &val) != 1) 146 | val = -1.0; 147 | } 148 | 149 | fclose(f); 150 | return val; 151 | } 152 | 153 | /* reload handler for SIGUSR1: update trickle_t for all dags and restart timers */ 154 | static void reload_cb(struct ev_loop *loop, ev_signal *w, int revents) 155 | { 156 | struct iface *iface; 157 | struct rpl *rpl; 158 | struct dag *dag; 159 | double new_trickle_t; 160 | const char *conf = "/tmp/rpld_trickle.conf"; 161 | 162 | flog(LOG_INFO, "Received SIGUSR1: reloading trickle configuration from %s", conf); 163 | 164 | new_trickle_t = read_trickle_config(conf); 165 | if (new_trickle_t <= 0.0) { 166 | flog(LOG_WARNING, "Invalid trickle value in %s; ignoring", conf); 167 | return; 168 | } 169 | 170 | /* iterate over all interfaces/rpls/dags and update trickle_t */ 171 | list_for_each_entry(iface, &ifaces, list) { 172 | list_for_each_entry(rpl, &iface->rpls, list) { 173 | list_for_each_entry(dag, &rpl->dags, list) { 174 | /* set new interval value in dag */ 175 | dag->trickle_t = new_trickle_t; 176 | 177 | /* restart the libev timer for this dag: 178 | stop -> set new values -> start 179 | */ 180 | ev_timer_stop(loop, &dag->trickle_w); 181 | ev_timer_set(&dag->trickle_w, dag->trickle_t, dag->trickle_t); 182 | ev_timer_start(loop, &dag->trickle_w); 183 | 184 | flog(LOG_INFO, "Updated dag %p trickle_t to %f", dag->parent, dag->trickle_t); 185 | } 186 | } 187 | } 188 | 189 | flog(LOG_INFO, "Trickle reload complete"); 190 | } 191 | 192 | 193 | int main(int argc, char *argv[]) 194 | { 195 | char const *conf_path = PATH_RPLD_CONF; 196 | struct ev_loop *loop = EV_DEFAULT; 197 | char *logfile = PATH_RPLD_LOG; 198 | const char *pname = argv[0]; 199 | int facility = LOG_FACILITY; 200 | int log_method = L_UNSPEC; 201 | ev_io sock_watcher; 202 | ev_signal exitsig; 203 | int opt; 204 | int rc; 205 | 206 | /* TODO this is a hack need to rework some parts */ 207 | foo = loop; 208 | 209 | /* TODO add longopt as the help says it */ 210 | while ((opt = getopt(argc, argv, "C:m:f:l:d:h")) != -1) { 211 | switch (opt) { 212 | case 'C': 213 | conf_path = optarg; 214 | break; 215 | case 'm': 216 | if (!strcmp(optarg, "syslog")) { 217 | log_method = L_SYSLOG; 218 | } else if (!strcmp(optarg, "stderr_syslog")) { 219 | log_method = L_STDERR_SYSLOG; 220 | } else if (!strcmp(optarg, "stderr")) { 221 | log_method = L_STDERR; 222 | } else if (!strcmp(optarg, "stderr_clean")) { 223 | log_method = L_STDERR_CLEAN; 224 | } else if (!strcmp(optarg, "logfile")) { 225 | log_method = L_LOGFILE; 226 | } else if (!strcmp(optarg, "none")) { 227 | log_method = L_NONE; 228 | } else { 229 | fprintf(stderr, "%s: unknown log method: %s\n", 230 | pname, optarg); 231 | exit(1); 232 | } 233 | break; 234 | case 'f': 235 | facility = atoi(optarg); 236 | break; 237 | case 'l': 238 | logfile = optarg; 239 | break; 240 | case 'd': 241 | /* TODO I hate atoi() ? */ 242 | set_debuglevel(atoi(optarg)); 243 | break; 244 | case 'h': 245 | usage(stdout, argv[0]); 246 | exit(0); 247 | default: 248 | usage(stderr, argv[0]); 249 | exit(1); 250 | } 251 | } 252 | 253 | init_random_gen(); 254 | ev_signal_init(&exitsig, sigint_cb, SIGINT); 255 | ev_signal_start(loop, &exitsig); 256 | 257 | /* register SIGUSR1 to allow dynamic trickle reloads */ 258 | static ev_signal reloadsig; 259 | ev_signal_init(&reloadsig, reload_cb, SIGUSR1); 260 | ev_signal_start(loop, &reloadsig); 261 | 262 | if (log_method == L_UNSPEC) 263 | log_method = L_STDERR; 264 | if (log_open(log_method, pname, logfile, facility) < 0) { 265 | perror("log_open"); 266 | exit(1); 267 | } 268 | 269 | flog(LOG_INFO, "version %s started", VERSION); 270 | 271 | rc = netlink_open(); 272 | if (rc == -1) { 273 | perror("mnl_socket_open"); 274 | exit(1); 275 | } 276 | 277 | rc = config_load(conf_path, &ifaces); 278 | if (rc < 0) { 279 | netlink_close(); 280 | flog(LOG_ERR, "Failed to parse config: %s", conf_path); 281 | exit(1); 282 | } 283 | 284 | rc = rpld_setup(loop, &ifaces); 285 | if (rc != 0) { 286 | netlink_close(); 287 | config_free(&ifaces); 288 | exit(1); 289 | } 290 | 291 | rc = set_var(PROC_SYS_IP6_MAX_HBH_OPTS_NUM, 99); 292 | if (rc == -1) { 293 | flog(LOG_ERR, "Failed to set hbh max value"); 294 | return -1; 295 | } 296 | 297 | sock = open_icmpv6_socket(&ifaces); 298 | if (sock < 0) { 299 | perror("open_icmpv6_socket"); 300 | netlink_close(); 301 | config_free(&ifaces); 302 | exit(1); 303 | } 304 | 305 | ev_io_init(&sock_watcher, icmpv6_cb, sock, EV_READ); 306 | ev_io_start(loop, &sock_watcher); 307 | 308 | ev_run(loop, 0); 309 | 310 | netlink_close(); 311 | close_icmpv6_socket(sock, &ifaces); 312 | config_free(&ifaces); 313 | log_close(); 314 | 315 | flog(LOG_INFO, "exited"); 316 | 317 | return 0; 318 | } 319 | -------------------------------------------------------------------------------- /process.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Authors: 3 | * Alexander Aring 4 | * 5 | * Original Authors: 6 | * Lars Fenneberg 7 | * 8 | * This software is Copyright 1996,1997,2019 by the above mentioned author(s), 9 | * All Rights Reserved. 10 | * 11 | * The license which is distributed with this software in the file COPYRIGHT 12 | * applies to this software. If your distribution is missing this file, you 13 | * may request it from . 14 | */ 15 | 16 | #include 17 | #include 18 | 19 | #include "process.h" 20 | #include "netlink.h" 21 | #include "send.h" 22 | #include "dag.h" 23 | #include "log.h" 24 | #include "rpl.h" 25 | 26 | static void process_dio(int sock, struct iface *iface, const void *msg, 27 | size_t len, struct sockaddr_in6 *addr) 28 | { 29 | const struct nd_rpl_dio *dio = msg; 30 | const struct rpl_dio_destprefix *diodp; 31 | char addr_str[INET6_ADDRSTRLEN]; 32 | struct in6_prefix pfx; 33 | struct dag *dag; 34 | uint16_t rank; 35 | int rc; 36 | 37 | if (len < sizeof(*dio)) { 38 | flog(LOG_INFO, "dio length mismatch, drop"); 39 | return; 40 | } 41 | len -= sizeof(*dio); 42 | 43 | addrtostr(&addr->sin6_addr, addr_str, sizeof(addr_str)); 44 | flog(LOG_INFO, "received dio %s", addr_str); 45 | 46 | dag = dag_lookup(iface, dio->rpl_instanceid, 47 | &dio->rpl_dagid); 48 | if (dag) { 49 | if (dag->my_rank == 1) 50 | return; 51 | } else { 52 | diodp = (struct rpl_dio_destprefix *) 53 | (((unsigned char *)msg) + sizeof(*dio)); 54 | 55 | if (len < sizeof(*diodp) - 16) { 56 | flog(LOG_INFO, "diodp length mismatch, drop"); 57 | return; 58 | } 59 | len -= sizeof(*diodp) - 16; 60 | 61 | if (diodp->rpl_dio_type != 0x3) { 62 | flog(LOG_INFO, "we assume diodp - not supported, drop"); 63 | return; 64 | } 65 | 66 | if (len < bits_to_bytes(diodp->rpl_dio_prefixlen)) { 67 | flog(LOG_INFO, "diodp prefix length mismatch, drop"); 68 | return; 69 | } 70 | len -= bits_to_bytes(diodp->rpl_dio_prefixlen); 71 | 72 | pfx.len = diodp->rpl_dio_prefixlen; 73 | memcpy(&pfx.prefix, &diodp->rpl_dio_prefix, 74 | bits_to_bytes(pfx.len)); 75 | 76 | flog(LOG_INFO, "received but no dag found %s", addr_str); 77 | dag = dag_create(iface, dio->rpl_instanceid, 78 | &dio->rpl_dagid, UINT16_MAX, dio->rpl_version, 79 | RPL_DIO_MOP(dio->rpl_mopprf), &pfx); 80 | if (!dag) 81 | return; 82 | 83 | addrtostr(&dio->rpl_dagid, addr_str, sizeof(addr_str)); 84 | flog(LOG_INFO, "created dag %s", addr_str); 85 | } 86 | 87 | flog(LOG_INFO, "process dio %s", addr_str); 88 | 89 | rank = ntohs(dio->rpl_dagrank); 90 | if (!dag->parent) { 91 | dag->parent = dag_peer_create(&addr->sin6_addr); 92 | if (!dag->parent) 93 | return; 94 | } 95 | 96 | if (rank > dag->parent->rank) 97 | return; 98 | 99 | /* Tell Linux about our choosen parent */ 100 | rc = nl_add_route_default(dag->iface->ifindex, &dag->parent->addr); 101 | flog(LOG_INFO, "default route %d %s", rc, strerror(errno)); 102 | 103 | dag->parent->rank = rank; 104 | dag->my_rank = rank + 1; 105 | 106 | dag_process_dio(dag); 107 | 108 | switch (dag->mop) { 109 | case RPL_DIO_STORING_NO_MULTICAST: 110 | /* fall-through */ 111 | case RPL_DIO_STORING_MULTICAST: 112 | send_dao(sock, &dag->parent->addr, dag); 113 | break; 114 | case RPL_DIO_NONSTORING: 115 | send_dao(sock, &dag->dodagid, dag); 116 | default: 117 | break; 118 | } 119 | } 120 | 121 | 122 | static void dag_insert_source_routes(uint32_t ifindex, const struct t_node *node) 123 | { 124 | char addr_str[INET6_ADDRSTRLEN]; 125 | struct list_head path = {}; 126 | struct t_path *p; 127 | 128 | t_path(node, &path); 129 | 130 | nl_add_source_routes(ifindex, &path); 131 | list_for_each_entry(p, &path, list) { 132 | addrtostr(&p->addr, addr_str, sizeof(addr_str)); 133 | flog(LOG_INFO, "XXXXXXXXXXXXXXXXXX %s", addr_str); 134 | addrtostr(&p->target, addr_str, sizeof(addr_str)); 135 | flog(LOG_INFO, "XXXXXXXXXXXXXXXXXX %s", addr_str); 136 | } 137 | 138 | t_path_free(&path); 139 | } 140 | 141 | static void process_dao(int sock, struct iface *iface, const void *msg, 142 | size_t len, struct sockaddr_in6 *addr) 143 | { 144 | const struct rpl_dao_transit *transit = NULL; 145 | const struct rpl_dao_target *target = NULL; 146 | const struct nd_rpl_dao *dao = msg; 147 | char addr_str[INET6_ADDRSTRLEN]; 148 | const struct nd_rpl_opt *opt; 149 | const unsigned char *p; 150 | struct child *child; 151 | struct t_node *n; 152 | struct dag *dag; 153 | int optlen; 154 | int rc; 155 | 156 | if (len < sizeof(*dao)) { 157 | flog(LOG_INFO, "dao length mismatch, drop"); 158 | return; 159 | } 160 | len -= sizeof(*dao); 161 | 162 | addrtostr(&addr->sin6_addr, addr_str, sizeof(addr_str)); 163 | flog(LOG_INFO, "received dao %s", addr_str); 164 | 165 | dag = dag_lookup(iface, dao->rpl_instanceid, 166 | &dao->rpl_dagid); 167 | if (!dag) { 168 | addrtostr(&dao->rpl_dagid, addr_str, sizeof(addr_str)); 169 | flog(LOG_INFO, "can't find dag %s", addr_str); 170 | return; 171 | } 172 | 173 | p = msg; 174 | p += sizeof(*dao); 175 | optlen = len; 176 | flog(LOG_INFO, "dao optlen %d", optlen); 177 | while (optlen > 0) { 178 | opt = (const struct nd_rpl_opt *)p; 179 | 180 | if (optlen < sizeof(*opt)) { 181 | flog(LOG_INFO, "rpl opt length mismatch, drop"); 182 | return; 183 | } 184 | 185 | flog(LOG_INFO, "dao opt %d", opt->type); 186 | switch (opt->type) { 187 | case RPL_DAO_TRANSITINFO: 188 | transit = (const struct rpl_dao_transit *)p; 189 | if (optlen < sizeof(*opt)) { 190 | flog(LOG_INFO, "rpl transit length mismatch, drop"); 191 | return; 192 | } 193 | 194 | addrtostr(&transit->parent, addr_str, sizeof(addr_str)); 195 | flog(LOG_INFO, "dao transit %s", addr_str); 196 | break; 197 | case RPL_DAO_RPLTARGET: 198 | target = (const struct rpl_dao_target *)p; 199 | if (optlen < sizeof(*opt)) { 200 | flog(LOG_INFO, "rpl target length mismatch, drop"); 201 | return; 202 | } 203 | 204 | addrtostr(&target->rpl_dao_prefix, addr_str, sizeof(addr_str)); 205 | flog(LOG_INFO, "dao target %s", addr_str); 206 | dag_lookup_child_or_create(dag, 207 | &target->rpl_dao_prefix, 208 | &addr->sin6_addr); 209 | break; 210 | default: 211 | /* IGNORE NOT SUPPORTED */ 212 | break; 213 | } 214 | 215 | /* TODO critical, we trust opt->len here... which is wire data */ 216 | optlen -= (2 + opt->len); 217 | p += (2 + opt->len); 218 | flog(LOG_INFO, "dao optlen %d", optlen); 219 | } 220 | 221 | switch (dag->mop) { 222 | case RPL_DIO_STORING_NO_MULTICAST: 223 | /* fall-through */ 224 | case RPL_DIO_STORING_MULTICAST: 225 | list_for_each_entry(child, &dag->childs, list) { 226 | rc = nl_add_route_via(dag->iface->ifindex, &child->addr, 227 | &child->from); 228 | flog(LOG_INFO, "via route %d %s", rc, strerror(errno)); 229 | } 230 | 231 | break; 232 | case RPL_DIO_NONSTORING: 233 | if (transit && target) { 234 | n = t_insert(&dag->root, &transit->parent, 235 | &addr->sin6_addr, &target->rpl_dao_prefix); 236 | if (n) 237 | dag_insert_source_routes(dag->iface->ifindex, n); 238 | } 239 | break; 240 | } 241 | 242 | flog(LOG_INFO, "process dao %s", addr_str); 243 | send_dao_ack(sock, &addr->sin6_addr, dag); 244 | } 245 | 246 | static void process_daoack(int sock, struct iface *iface, const void *msg, 247 | size_t len, struct sockaddr_in6 *addr) 248 | { 249 | const struct nd_rpl_daoack *daoack = msg; 250 | char addr_str[INET6_ADDRSTRLEN]; 251 | struct dag *dag; 252 | 253 | if (len < sizeof(*daoack)) { 254 | flog(LOG_INFO, "rpl daoack length mismatch, drop"); 255 | return; 256 | } 257 | 258 | addrtostr(&addr->sin6_addr, addr_str, sizeof(addr_str)); 259 | flog(LOG_INFO, "received daoack %s", addr_str); 260 | 261 | dag = dag_lookup(iface, daoack->rpl_instanceid, 262 | &daoack->rpl_dagid); 263 | if (!dag) { 264 | addrtostr(&daoack->rpl_dagid, addr_str, sizeof(addr_str)); 265 | flog(LOG_INFO, "can't find dag %s", addr_str); 266 | return; 267 | } 268 | 269 | /* TODO implement ack handling */ 270 | } 271 | 272 | static void process_dis(int sock, struct iface *iface, const void *msg, 273 | size_t len, struct sockaddr_in6 *addr) 274 | { 275 | char addr_str[INET6_ADDRSTRLEN]; 276 | struct rpl *rpl; 277 | struct dag *dag; 278 | 279 | addrtostr(&addr->sin6_addr, addr_str, sizeof(addr_str)); 280 | flog(LOG_INFO, "received dis %s", addr_str); 281 | 282 | list_for_each_entry(rpl, &iface->rpls, list) { 283 | list_for_each_entry(dag, &rpl->dags, list) 284 | send_dio(sock, dag); 285 | } 286 | } 287 | 288 | void process(int sock, struct list_head *ifaces, unsigned char *msg, 289 | int len, struct sockaddr_in6 *addr, struct in6_pktinfo *pkt_info, 290 | int hoplimit) 291 | { 292 | char addr_str[INET6_ADDRSTRLEN]; 293 | char if_namebuf[IFNAMSIZ] = {""}; 294 | char *if_name = if_indextoname(pkt_info->ipi6_ifindex, if_namebuf); 295 | if (!if_name) { 296 | if_name = "unknown interface"; 297 | } 298 | dlog(LOG_DEBUG, 4, "%s received a packet", if_name); 299 | 300 | addrtostr(&addr->sin6_addr, addr_str, sizeof(addr_str)); 301 | 302 | if (!pkt_info) { 303 | flog(LOG_WARNING, "%s received packet with no pkt_info from %s!", if_name, addr_str); 304 | return; 305 | } 306 | 307 | /* 308 | * can this happen? 309 | */ 310 | 311 | if (len < 4) { 312 | flog(LOG_WARNING, "%s received icmpv6 packet with invalid length (%d) from %s", if_name, len, addr_str); 313 | return; 314 | } 315 | len -= 4; 316 | 317 | struct icmp6_hdr *icmph = (struct icmp6_hdr *)msg; 318 | struct iface *iface = iface_find_by_ifindex(ifaces, pkt_info->ipi6_ifindex); 319 | if (!iface) { 320 | dlog(LOG_WARNING, 4, "%s received icmpv6 RS/RA packet on an unknown interface with index %d", if_name, 321 | pkt_info->ipi6_ifindex); 322 | return; 323 | } 324 | 325 | if (icmph->icmp6_type != ND_RPL_MESSAGE) { 326 | /* 327 | * We just want to listen to RPL 328 | */ 329 | 330 | flog(LOG_ERR, "%s icmpv6 filter failed", if_name); 331 | return; 332 | } 333 | 334 | switch (icmph->icmp6_code) { 335 | case ND_RPL_DAG_IS: 336 | process_dis(sock, iface, &icmph->icmp6_dataun, len, addr); 337 | break; 338 | case ND_RPL_DAG_IO: 339 | process_dio(sock, iface, &icmph->icmp6_dataun, len, addr); 340 | break; 341 | case ND_RPL_DAO: 342 | process_dao(sock, iface, &icmph->icmp6_dataun, len, addr); 343 | break; 344 | case ND_RPL_DAO_ACK: 345 | process_daoack(sock, iface, &icmph->icmp6_dataun, len, addr); 346 | break; 347 | default: 348 | flog(LOG_ERR, "%s received unsupported RPL code 0x%02x", 349 | if_name, icmph->icmp6_code); 350 | break; 351 | } 352 | } 353 | -------------------------------------------------------------------------------- /netlink.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Authors: 3 | * Alexander Aring 4 | * 5 | * This software is Copyright 2019 by the above mentioned author(s), 6 | * All Rights Reserved. 7 | * 8 | * The license which is distributed with this software in the file COPYRIGHT 9 | * applies to this software. If your distribution is missing this file, you 10 | * may request it from . 11 | */ 12 | 13 | #include 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #include "netlink.h" 22 | /* TODO move this out again */ 23 | #include "tree.h" 24 | #include "log.h" 25 | 26 | static struct mnl_socket *nl; 27 | static unsigned int portid; 28 | 29 | int netlink_open() 30 | { 31 | int rc; 32 | 33 | nl = mnl_socket_open(NETLINK_ROUTE); 34 | if (!nl) { 35 | perror("mnl_socket_open"); 36 | return -1; 37 | } 38 | 39 | rc = mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID); 40 | if (rc < 0) { 41 | mnl_socket_close(nl); 42 | perror("mnl_socket_bind"); 43 | return -1; 44 | } 45 | portid = mnl_socket_get_portid(nl); 46 | 47 | return 0; 48 | } 49 | 50 | void netlink_close() 51 | { 52 | mnl_socket_close(nl); 53 | } 54 | 55 | static int data_attr_cb(const struct nlattr *attr, void *data) 56 | { 57 | int type = mnl_attr_get_type(attr); 58 | const struct nlattr **tb = data; 59 | 60 | if (mnl_attr_type_valid(attr, IFLA_MAX) < 0) 61 | return MNL_CB_OK; 62 | 63 | switch(type) { 64 | case IFLA_LINK: 65 | if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) { 66 | perror("mnl_attr_validate"); 67 | return MNL_CB_ERROR; 68 | } 69 | break; 70 | case IFLA_ADDRESS: 71 | if (mnl_attr_validate(attr, MNL_TYPE_BINARY) < 0) { 72 | perror("mnl_attr_validate"); 73 | return MNL_CB_ERROR; 74 | } 75 | break; 76 | default: 77 | break; 78 | } 79 | 80 | tb[type] = attr; 81 | return MNL_CB_OK; 82 | } 83 | 84 | static int data_cb(const struct nlmsghdr *nlh, void *data) 85 | { 86 | struct ifinfomsg *ifm = mnl_nlmsg_get_payload(nlh); 87 | struct nlattr *tb[IFLA_MAX + 1] = {}; 88 | struct iface_llinfo *llinfo = data; 89 | 90 | /* TODO this will not available on bluetooth */ 91 | mnl_attr_parse(nlh, sizeof(*ifm), data_attr_cb, tb); 92 | if (tb[IFLA_LINK]) 93 | llinfo->ifindex = mnl_attr_get_u32(tb[IFLA_LINK]); 94 | 95 | if (tb[IFLA_ADDRESS]) { 96 | llinfo->addr_len = mnl_attr_get_payload_len(tb[IFLA_ADDRESS]); 97 | llinfo->addr = mzalloc(llinfo->addr_len); 98 | if (!llinfo->addr) 99 | return MNL_CB_ERROR; 100 | 101 | memcpy(llinfo->addr, mnl_attr_get_payload(tb[IFLA_ADDRESS]), 102 | llinfo->addr_len); 103 | } 104 | 105 | return MNL_CB_OK; 106 | } 107 | 108 | int nl_get_llinfo(uint32_t ifindex, struct iface_llinfo *llinfo) 109 | { 110 | unsigned char buf[MNL_SOCKET_BUFFER_SIZE]; 111 | struct ifinfomsg *ifm; 112 | struct nlmsghdr *nlh; 113 | unsigned int seq; 114 | int ret; 115 | 116 | nlh = mnl_nlmsg_put_header(buf); 117 | nlh->nlmsg_type = RTM_GETLINK; 118 | nlh->nlmsg_flags = NLM_F_REQUEST; 119 | nlh->nlmsg_seq = seq = time(NULL); 120 | ifm = mnl_nlmsg_put_extra_header(nlh, sizeof(*ifm)); 121 | ifm->ifi_family = AF_UNSPEC; 122 | ifm->ifi_index = ifindex; 123 | 124 | if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { 125 | perror("mnl_socket_send"); 126 | return -1; 127 | } 128 | 129 | ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); 130 | if (ret == -1) 131 | return -1; 132 | 133 | return mnl_cb_run(buf, ret, seq, portid, data_cb, llinfo); 134 | } 135 | 136 | int nl_add_addr(uint32_t ifindex, const struct in6_addr *addr) 137 | { 138 | unsigned char buf[MNL_SOCKET_BUFFER_SIZE]; 139 | struct ifaddrmsg *ifm; 140 | struct nlmsghdr *nlh; 141 | unsigned int seq; 142 | int ret; 143 | 144 | nlh = mnl_nlmsg_put_header(buf); 145 | nlh->nlmsg_type = RTM_NEWADDR; 146 | nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_ACK; 147 | nlh->nlmsg_seq = seq = time(NULL); 148 | ifm = mnl_nlmsg_put_extra_header(nlh, sizeof(*ifm)); 149 | 150 | ifm->ifa_index = ifindex; 151 | ifm->ifa_family = AF_INET6; 152 | ifm->ifa_prefixlen = 64; 153 | ifm->ifa_flags = IFA_F_PERMANENT; 154 | ifm->ifa_scope = RT_SCOPE_UNIVERSE; 155 | 156 | mnl_attr_put(nlh, IFA_ADDRESS, sizeof(*addr), addr); 157 | 158 | if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { 159 | perror("mnl_socket_send"); 160 | return -1; 161 | } 162 | 163 | ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); 164 | if (ret == -1) { 165 | perror("mnl_socket_recvfrom"); 166 | return -1; 167 | } 168 | 169 | return mnl_cb_run(buf, ret, seq, portid, NULL, NULL); 170 | } 171 | 172 | int nl_add_source_routes(uint32_t ifindex, const struct list_head *segs) 173 | { 174 | unsigned char buf[MNL_SOCKET_BUFFER_SIZE]; 175 | struct ipv6_rpl_sr_hdr *srh; 176 | int ret, i, nsegs, srhlen; 177 | const struct t_path *p; 178 | struct nlmsghdr *nlh; 179 | struct nlattr *type; 180 | struct rtmsg *rtm; 181 | unsigned int seq; 182 | struct list *c; 183 | 184 | nlh = mnl_nlmsg_put_header(buf); 185 | nlh->nlmsg_type = RTM_NEWROUTE; 186 | nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_ACK; 187 | nlh->nlmsg_seq = seq = time(NULL); 188 | rtm = mnl_nlmsg_put_extra_header(nlh, sizeof(*rtm)); 189 | 190 | rtm->rtm_family = AF_INET6; 191 | rtm->rtm_dst_len = 128; 192 | rtm->rtm_src_len = 0; 193 | rtm->rtm_tos = 0; 194 | rtm->rtm_protocol = RTPROT_STATIC; 195 | rtm->rtm_table = RT_TABLE_MAIN; 196 | rtm->rtm_type = RTN_UNICAST; 197 | /* is there any gateway? */ 198 | rtm->rtm_scope = RT_SCOPE_UNIVERSE; 199 | rtm->rtm_flags = 0; 200 | 201 | mnl_attr_put_u32(nlh, RTA_OIF, ifindex); 202 | 203 | DL_COUNT(segs->head, c, nsegs); 204 | nsegs--; 205 | 206 | srhlen = 8 + (16 * nsegs); 207 | srh = calloc(1, srhlen); 208 | 209 | srh->hdrlen = (srhlen >> 3) - 1; 210 | srh->type = 3; 211 | srh->segments_left = nsegs; 212 | 213 | i = 0; 214 | list_for_each_entry(p, segs, list) { 215 | if (i == 0) { 216 | srh->rpl_segaddr[nsegs - i - 1] = p->addr; 217 | mnl_attr_put(nlh, RTA_DST, sizeof(p->target), &p->target); 218 | i++; 219 | continue; 220 | } 221 | if (i == nsegs) 222 | break; 223 | 224 | srh->rpl_segaddr[nsegs - i - 1] = p->addr; 225 | i++; 226 | } 227 | 228 | mnl_attr_put_u16(nlh, RTA_ENCAP_TYPE, LWTUNNEL_ENCAP_RPL); 229 | 230 | type = mnl_attr_nest_start(nlh, RTA_ENCAP); 231 | mnl_attr_put(nlh, RPL_IPTUNNEL_SRH, srhlen, srh); 232 | mnl_attr_nest_end(nlh, type); 233 | 234 | if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { 235 | perror("mnl_socket_send"); 236 | return -1; 237 | } 238 | 239 | ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); 240 | if (ret == -1) { 241 | perror("mnl_socket_recvfrom"); 242 | return -1; 243 | } 244 | 245 | return mnl_cb_run(buf, ret, seq, portid, NULL, NULL); 246 | } 247 | 248 | int nl_add_route_via(uint32_t ifindex, const struct in6_addr *dst, 249 | const struct in6_addr *via) 250 | { 251 | unsigned char buf[MNL_SOCKET_BUFFER_SIZE]; 252 | struct nlmsghdr *nlh; 253 | struct rtmsg *rtm; 254 | unsigned int seq; 255 | int ret; 256 | 257 | nlh = mnl_nlmsg_put_header(buf); 258 | nlh->nlmsg_type = RTM_NEWROUTE; 259 | nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_ACK; 260 | nlh->nlmsg_seq = seq = time(NULL); 261 | rtm = mnl_nlmsg_put_extra_header(nlh, sizeof(*rtm)); 262 | 263 | rtm->rtm_family = AF_INET6; 264 | rtm->rtm_dst_len = 128; 265 | rtm->rtm_src_len = 0; 266 | rtm->rtm_tos = 0; 267 | rtm->rtm_protocol = RTPROT_STATIC; 268 | rtm->rtm_table = RT_TABLE_MAIN; 269 | rtm->rtm_type = RTN_UNICAST; 270 | /* is there any gateway? */ 271 | rtm->rtm_scope = RT_SCOPE_UNIVERSE; 272 | rtm->rtm_flags = 0; 273 | 274 | mnl_attr_put(nlh, RTA_DST, sizeof(*dst), dst); 275 | mnl_attr_put(nlh, RTA_GATEWAY, sizeof(*via), via); 276 | mnl_attr_put_u32(nlh, RTA_OIF, ifindex); 277 | 278 | if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { 279 | perror("mnl_socket_send"); 280 | return -1; 281 | } 282 | 283 | ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); 284 | if (ret == -1) { 285 | perror("mnl_socket_recvfrom"); 286 | return -1; 287 | } 288 | 289 | return mnl_cb_run(buf, ret, seq, portid, NULL, NULL); 290 | } 291 | 292 | int nl_add_route_default(uint32_t ifindex, const struct in6_addr *dst) 293 | { 294 | unsigned char buf[MNL_SOCKET_BUFFER_SIZE]; 295 | struct nlmsghdr *nlh; 296 | struct rtmsg *rtm; 297 | unsigned int seq; 298 | int ret; 299 | 300 | nlh = mnl_nlmsg_put_header(buf); 301 | nlh->nlmsg_type = RTM_NEWROUTE; 302 | nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_ACK; 303 | nlh->nlmsg_seq = seq = time(NULL); 304 | rtm = mnl_nlmsg_put_extra_header(nlh, sizeof(*rtm)); 305 | 306 | rtm->rtm_family = AF_INET6; 307 | rtm->rtm_dst_len = 0; 308 | rtm->rtm_src_len = 0; 309 | rtm->rtm_tos = 0; 310 | rtm->rtm_protocol = RTPROT_STATIC; 311 | rtm->rtm_table = RT_TABLE_MAIN; 312 | rtm->rtm_type = RTN_UNICAST; 313 | /* is there any gateway? */ 314 | rtm->rtm_scope = RT_SCOPE_UNIVERSE; 315 | rtm->rtm_flags = 0; 316 | 317 | mnl_attr_put(nlh, RTA_GATEWAY, sizeof(*dst), dst); 318 | mnl_attr_put_u32(nlh, RTA_OIF, ifindex); 319 | 320 | if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { 321 | perror("mnl_socket_send"); 322 | return -1; 323 | } 324 | 325 | ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); 326 | if (ret == -1) { 327 | perror("mnl_socket_recvfrom"); 328 | return -1; 329 | } 330 | 331 | return mnl_cb_run(buf, ret, seq, portid, NULL, NULL); 332 | } 333 | 334 | int nl_del_route_via(uint32_t ifindex, const struct in6_prefix *dst, 335 | struct in6_addr *via) 336 | { 337 | unsigned char buf[MNL_SOCKET_BUFFER_SIZE]; 338 | struct nlmsghdr *nlh; 339 | struct rtmsg *rtm; 340 | unsigned int seq; 341 | int ret; 342 | 343 | nlh = mnl_nlmsg_put_header(buf); 344 | nlh->nlmsg_type = RTM_DELROUTE; 345 | nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; 346 | nlh->nlmsg_seq = seq = time(NULL); 347 | rtm = mnl_nlmsg_put_extra_header(nlh, sizeof(*rtm)); 348 | 349 | rtm->rtm_family = AF_INET6; 350 | rtm->rtm_dst_len = dst->len; 351 | rtm->rtm_src_len = 0; 352 | rtm->rtm_tos = 0; 353 | rtm->rtm_table = RT_TABLE_MAIN; 354 | /* is there any gateway? */ 355 | rtm->rtm_flags = 0; 356 | 357 | mnl_attr_put(nlh, RTA_DST, sizeof(dst->prefix), &dst->prefix); 358 | if (via) 359 | mnl_attr_put(nlh, RTA_GATEWAY, sizeof(*via), via); 360 | mnl_attr_put_u32(nlh, RTA_OIF, ifindex); 361 | 362 | if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { 363 | perror("mnl_socket_send"); 364 | return -1; 365 | } 366 | 367 | ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); 368 | if (ret == -1) { 369 | perror("mnl_socket_recvfrom"); 370 | return -1; 371 | } 372 | 373 | return mnl_cb_run(buf, ret, seq, portid, NULL, NULL); 374 | } 375 | 376 | /* TODO THIS WILL ADD A STATEFUL COMPRESSION ENTRY INTO THE KERNEL 377 | * BUT I WANT A NETLINK API BEFORE WE IMPLEMENT IT. IT MAKES SENSE 378 | * WE ONLY USE STATELESS TO CONFIGURE THE NETWORK. 379 | */ 380 | #if 0 381 | int nl_stateful_add_compression_entry(uint32_t ifindex, const struct in6_prefix *pfx) 382 | { 383 | 384 | } 385 | #endif 386 | -------------------------------------------------------------------------------- /dag.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Authors: 3 | * Alexander Aring 4 | * 5 | * Original Authors: 6 | * Lars Fenneberg 7 | * 8 | * This software is Copyright 1996,1997,2019 by the above mentioned author(s), 9 | * All Rights Reserved. 10 | * 11 | * The license which is distributed with this software in the file COPYRIGHT 12 | * applies to this software. If your distribution is missing this file, you 13 | * may request it from . 14 | */ 15 | 16 | #include 17 | #include 18 | 19 | #include "helpers.h" 20 | #include "netlink.h" 21 | #include "buffer.h" 22 | #include "rpl.h" 23 | #include "dag.h" 24 | 25 | struct peer *dag_peer_create(const struct in6_addr *addr) 26 | { 27 | struct peer *peer; 28 | 29 | peer = mzalloc(sizeof(*peer)); 30 | if (!peer) 31 | return NULL; 32 | 33 | memcpy(&peer->addr, addr, sizeof(peer->addr)); 34 | peer->rank = UINT16_MAX; 35 | 36 | return peer; 37 | } 38 | 39 | struct child *dag_child_create(const struct in6_addr *addr, 40 | const struct in6_addr *from) 41 | { 42 | struct child *peer; 43 | 44 | peer = mzalloc(sizeof(*peer)); 45 | if (!peer) 46 | return NULL; 47 | 48 | memcpy(&peer->addr, addr, sizeof(peer->addr)); 49 | memcpy(&peer->from, from, sizeof(peer->from)); 50 | 51 | return peer; 52 | } 53 | 54 | bool dag_is_peer(const struct peer *peer, const struct in6_addr *addr) 55 | { 56 | if (!peer || !addr) 57 | return false; 58 | 59 | return !memcmp(&peer->addr, addr, sizeof(peer->addr)); 60 | } 61 | 62 | bool dag_is_child(const struct child *peer, const struct in6_addr *addr) 63 | { 64 | if (!peer || !addr) 65 | return false; 66 | 67 | return !memcmp(&peer->addr, addr, sizeof(peer->addr)); 68 | } 69 | 70 | static struct child *dag_lookup_child(const struct dag *dag, 71 | const struct in6_addr *addr) 72 | { 73 | struct child *peer; 74 | 75 | list_for_each_entry(peer, &dag->childs, list) { 76 | if (dag_is_child(peer, addr)) 77 | return peer; 78 | } 79 | 80 | return NULL; 81 | } 82 | 83 | struct child *dag_lookup_child_or_create(struct dag *dag, 84 | const struct in6_addr *addr, 85 | const struct in6_addr *from) 86 | { 87 | struct child *peer; 88 | 89 | peer = dag_lookup_child(dag, addr); 90 | if (peer) 91 | return peer; 92 | 93 | peer = dag_child_create(addr, from); 94 | if (peer) 95 | DL_APPEND(dag->childs.head, &peer->list); 96 | 97 | return peer; 98 | } 99 | 100 | static struct rpl *dag_lookup_rpl(const struct iface *iface, 101 | uint8_t instance_id) 102 | { 103 | struct rpl *rpl; 104 | 105 | list_for_each_entry(rpl, &iface->rpls, list) { 106 | if (rpl->instance_id == instance_id) 107 | return rpl; 108 | } 109 | 110 | return NULL; 111 | } 112 | 113 | static struct dag *dag_lookup_dodag(const struct rpl *rpl, 114 | const struct in6_addr *dodagid) 115 | { 116 | struct dag *dag; 117 | 118 | list_for_each_entry(dag, &rpl->dags, list) { 119 | if (!memcmp(&dag->dodagid, dodagid, sizeof(dag->dodagid))) 120 | return dag; 121 | } 122 | 123 | return NULL; 124 | } 125 | 126 | struct dag *dag_lookup(const struct iface *iface, uint8_t instance_id, 127 | const struct in6_addr *dodagid) 128 | { 129 | struct rpl *rpl; 130 | 131 | rpl = dag_lookup_rpl(iface, instance_id); 132 | if (!rpl) 133 | return NULL; 134 | 135 | return dag_lookup_dodag(rpl, dodagid); 136 | } 137 | 138 | static struct rpl *dag_rpl_create(uint8_t instance_id) 139 | { 140 | struct rpl *rpl; 141 | 142 | rpl = mzalloc(sizeof(*rpl)); 143 | if (!rpl) 144 | return NULL; 145 | 146 | rpl->instance_id = instance_id; 147 | return rpl; 148 | } 149 | 150 | struct dag_daoack *dag_lookup_daoack(const struct dag *dag, uint8_t dsn) 151 | { 152 | struct dag_daoack *daoack; 153 | 154 | list_for_each_entry(daoack, &dag->pending_acks, list) { 155 | if (daoack->dsn == dsn) 156 | return daoack; 157 | } 158 | 159 | return NULL; 160 | } 161 | 162 | int dag_daoack_insert(struct dag *dag, uint8_t dsn) 163 | { 164 | struct dag_daoack *daoack; 165 | 166 | daoack = mzalloc(sizeof(*daoack)); 167 | if (!daoack) 168 | return -1; 169 | 170 | DL_APPEND(dag->pending_acks.head, &daoack->list); 171 | return 0; 172 | } 173 | 174 | void dag_init_timer(struct dag *dag); 175 | 176 | static int dag_init(struct dag *dag, const struct iface *iface, 177 | const struct rpl *rpl, const struct in6_addr *dodagid, 178 | uint16_t my_rank, uint8_t version, 179 | uint8_t mop, const struct in6_prefix *dest) 180 | { 181 | /* TODO dest is currently necessary */ 182 | if (!dag || !iface || !rpl || !dest) 183 | return -1; 184 | 185 | memset(dag, 0, sizeof(*dag)); 186 | 187 | dag->iface = iface; 188 | dag->rpl = rpl; 189 | dag->dest = *dest; 190 | dag->dodagid = *dodagid; 191 | 192 | dag->version = version; 193 | dag->my_rank = my_rank; 194 | dag->trickle_t = iface->tickle_t; 195 | dag->mop = mop; 196 | 197 | dag_init_timer(dag); 198 | t_init(&dag->root, &iface->ifaddr, dodagid); 199 | 200 | return 0; 201 | } 202 | 203 | struct dag *dag_create(struct iface *iface, uint8_t instanceid, 204 | const struct in6_addr *dodagid, 205 | uint16_t my_rank, uint8_t version, uint8_t mop, 206 | const struct in6_prefix *dest) 207 | { 208 | bool append_rpl = false; 209 | struct rpl *rpl; 210 | struct dag *dag; 211 | int rc; 212 | 213 | rpl = dag_lookup_rpl(iface, instanceid); 214 | if (!rpl) { 215 | rpl = dag_rpl_create(instanceid); 216 | if (!rpl) 217 | return NULL; 218 | 219 | append_rpl = true; 220 | } 221 | 222 | /* sanity check because it's just a list 223 | * we must avoid duplicate entries 224 | */ 225 | if (!append_rpl) { 226 | dag = dag_lookup_dodag(rpl, dodagid); 227 | if (dag) { 228 | free(rpl); 229 | return NULL; 230 | } 231 | } 232 | 233 | dag = mzalloc(sizeof(*dag)); 234 | if (!dag) { 235 | free(rpl); 236 | return NULL; 237 | } 238 | 239 | rc = dag_init(dag, iface, rpl, dodagid, 240 | my_rank, version, mop, dest); 241 | if (rc != 0) { 242 | free(dag); 243 | free(rpl); 244 | return NULL; 245 | } 246 | 247 | if (append_rpl) 248 | DL_APPEND(iface->rpls.head, &rpl->list); 249 | 250 | DL_APPEND(rpl->dags.head, &dag->list); 251 | return dag; 252 | } 253 | 254 | void dag_free(struct dag *dag) 255 | { 256 | t_free(&dag->root); 257 | free(dag); 258 | } 259 | 260 | static int append_destprefix(const struct dag *dag, struct safe_buffer *sb) 261 | { 262 | struct rpl_dio_destprefix diodp = {}; 263 | uint8_t len; 264 | 265 | len = sizeof(diodp) - sizeof(diodp.rpl_dio_prefix) + 266 | bits_to_bytes(dag->dest.len); 267 | 268 | diodp.rpl_dio_type = 0x3; 269 | // diodp.rpl_dio_prf = RPL_DIO_PREFIX_AUTONOMOUS_ADDR_CONFIG_FLAG; 270 | /* TODO crazy calculation here */ 271 | diodp.rpl_dio_len = len - 2; 272 | diodp.rpl_dio_prefixlen = dag->dest.len; 273 | diodp.rpl_dio_prefix = dag->dest.prefix; 274 | diodp.rpl_dio_route_lifetime = UINT32_MAX; 275 | safe_buffer_append(sb, &diodp, len); 276 | 277 | return 0; 278 | } 279 | 280 | static void dag_build_icmp(struct safe_buffer *sb, uint8_t code) 281 | { 282 | struct icmp6_hdr nd_rpl_hdr = { 283 | .icmp6_type = ND_RPL_MESSAGE, 284 | .icmp6_code = code, 285 | }; 286 | 287 | /* TODO 4 is a hack */ 288 | safe_buffer_append(sb, &nd_rpl_hdr, sizeof(nd_rpl_hdr) - 4); 289 | } 290 | 291 | void dag_build_dio(struct dag *dag, struct safe_buffer *sb) 292 | { 293 | struct nd_rpl_dio dio = {}; 294 | 295 | dag_build_icmp(sb, ND_RPL_DAG_IO); 296 | 297 | dio.rpl_instanceid = dag->rpl->instance_id; 298 | dio.rpl_version = dag->version; 299 | dio.rpl_dtsn = dag->dtsn++; 300 | flog(LOG_INFO, "my_rank %d", dag->my_rank); 301 | dio.rpl_dagrank = htons(dag->my_rank); 302 | dio.rpl_mopprf = ND_RPL_DIO_GROUNDED | (dag->mop << 3); 303 | dio.rpl_dagid = dag->dodagid; 304 | 305 | safe_buffer_append(sb, &dio, sizeof(dio)); 306 | append_destprefix(dag, sb); 307 | } 308 | 309 | void dag_process_dio(struct dag *dag) 310 | { 311 | struct in6_addr addr; 312 | int rc; 313 | 314 | rc = gen_stateless_addr(&dag->dest, &dag->iface->llinfo, 315 | &addr); 316 | if (rc == -1) 317 | return; 318 | 319 | rc = nl_add_addr(dag->iface->ifindex, &addr); 320 | if (rc == -1 && errno != EEXIST) { 321 | flog(LOG_ERR, "error add nl %d", errno); 322 | return; 323 | } 324 | rc = nl_del_route_via(dag->iface->ifindex, &dag->dest, NULL); 325 | if (rc == -1) { 326 | flog(LOG_ERR, "error del nl %d, %s", errno, strerror(errno)); 327 | return; 328 | } 329 | 330 | memcpy(&dag->self, &addr, sizeof(dag->self)); 331 | } 332 | 333 | void dag_build_dao_ack(struct dag *dag, struct safe_buffer *sb) 334 | { 335 | struct nd_rpl_daoack dao = {}; 336 | 337 | dag_build_icmp(sb, ND_RPL_DAO_ACK); 338 | 339 | dao.rpl_instanceid = dag->rpl->instance_id; 340 | dao.rpl_flags |= RPL_DAO_K_MASK; 341 | dao.rpl_flags |= RPL_DAO_D_MASK; 342 | dao.rpl_daoseq = dag->dsn; 343 | dao.rpl_dagid = dag->dodagid; 344 | 345 | safe_buffer_append(sb, &dao, sizeof(dao)); 346 | flog(LOG_INFO, "build dao"); 347 | } 348 | 349 | static int append_target(const struct in6_prefix *prefix, 350 | struct safe_buffer *sb) 351 | { 352 | struct rpl_dao_target target = {}; 353 | uint8_t len; 354 | 355 | len = sizeof(target) - sizeof(target.rpl_dao_prefix) + 356 | bits_to_bytes(prefix->len); 357 | 358 | target.rpl_dao_type = RPL_DAO_RPLTARGET; 359 | /* TODO crazy calculation here */ 360 | target.rpl_dao_len = 18; 361 | target.rpl_dao_prefixlen = prefix->len; 362 | target.rpl_dao_prefix = prefix->prefix; 363 | safe_buffer_append(sb, &target, len); 364 | 365 | return 0; 366 | } 367 | 368 | static int append_transit(const struct in6_addr *parent, 369 | struct safe_buffer *sb) 370 | { 371 | struct rpl_dao_transit transit = {}; 372 | uint8_t len; 373 | 374 | len = sizeof(transit); 375 | transit.type = RPL_DAO_TRANSITINFO; 376 | 377 | /* TODO crazy calculation here */ 378 | transit.len = 20; 379 | transit.parent = *parent; 380 | safe_buffer_append(sb, &transit, len); 381 | 382 | return 0; 383 | } 384 | 385 | void dag_build_dao(struct dag *dag, struct safe_buffer *sb) 386 | { 387 | struct nd_rpl_daoack daoack = {}; 388 | struct in6_prefix prefix; 389 | const struct child *child; 390 | 391 | dag_build_icmp(sb, ND_RPL_DAO); 392 | 393 | daoack.rpl_instanceid = dag->rpl->instance_id; 394 | daoack.rpl_flags |= RPL_DAO_D_MASK; 395 | daoack.rpl_dagid = dag->dodagid; 396 | 397 | safe_buffer_append(sb, &daoack, sizeof(daoack)); 398 | prefix.prefix = dag->self; 399 | prefix.len = 128; 400 | append_target(&prefix, sb); 401 | append_transit(&dag->parent->addr, sb); 402 | 403 | switch (dag->mop) { 404 | case RPL_DIO_STORING_NO_MULTICAST: 405 | /* fall-through */ 406 | case RPL_DIO_STORING_MULTICAST: 407 | list_for_each_entry(child, &dag->childs, list) { 408 | prefix.prefix = child->addr; 409 | prefix.len = 128; 410 | 411 | append_target(&prefix, sb); 412 | } 413 | break; 414 | default: 415 | break; 416 | } 417 | 418 | dag_daoack_insert(dag, dag->dsn); 419 | daoack.rpl_daoseq = dag->dsn++; 420 | flog(LOG_INFO, "build dao"); 421 | } 422 | 423 | void dag_build_dis(struct safe_buffer *sb) 424 | { 425 | struct nd_rpl_dis dis = {}; 426 | 427 | dag_build_icmp(sb, ND_RPL_DAG_IS); 428 | 429 | safe_buffer_append(sb, &dis, sizeof(dis)); 430 | flog(LOG_INFO, "build dis"); 431 | } 432 | -------------------------------------------------------------------------------- /config.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Authors: 3 | * Alexander Aring 4 | * 5 | * This software is Copyright 2019 by the above mentioned author(s), 6 | * All Rights Reserved. 7 | * 8 | * The license which is distributed with this software in the file COPYRIGHT 9 | * applies to this software. If your distribution is missing this file, you 10 | * may request it from . 11 | */ 12 | 13 | #include 14 | #include 15 | 16 | #include 17 | #include 18 | #include 19 | 20 | #include "helpers.h" 21 | #include "netlink.h" 22 | #include "config.h" 23 | #include "log.h" 24 | #include "rpl.h" 25 | 26 | static inline int cmp_iface_addrs(void const *a, void const *b) 27 | { 28 | return memcmp(a, b, sizeof(struct in6_addr)); 29 | } 30 | 31 | /* 32 | * Return first IPv6 link local addr in if_addr. 33 | * Return all the IPv6 addresses in if_addrs in ascending 34 | * order. 35 | * Return value is -1 if there was no link local addr. 36 | * otherwise return value is count of addres in if_addrs 37 | * not including the all zero (unspecified) addr at the 38 | * end of the list. 39 | */ 40 | static int get_iface_addrs(char const *name, struct in6_addr *if_addr, 41 | struct in6_addr **if_addrs) 42 | { 43 | struct ifaddrs *addresses = 0; 44 | int link_local_set = 0; 45 | int i = 0; 46 | 47 | if (getifaddrs(&addresses) != 0) { 48 | flog(LOG_ERR, "getifaddrs failed on %s: %s", name, strerror(errno)); 49 | } else { 50 | for (struct ifaddrs *ifa = addresses; ifa != NULL; ifa = ifa->ifa_next) { 51 | 52 | if (!ifa->ifa_addr) 53 | continue; 54 | 55 | if (ifa->ifa_addr->sa_family != AF_INET6) 56 | continue; 57 | 58 | struct sockaddr_in6 *a6 = (struct sockaddr_in6 *)ifa->ifa_addr; 59 | 60 | /* Skip if it is not the interface we're looking for. */ 61 | if (strcmp(ifa->ifa_name, name) != 0) 62 | continue; 63 | 64 | *if_addrs = realloc(*if_addrs, (i + 1) * sizeof(struct in6_addr)); 65 | (*if_addrs)[i++] = a6->sin6_addr; 66 | 67 | /* Skip if it is not a linklocal address or link locak address already found*/ 68 | uint8_t const ll_prefix[] = {0xfe, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}; 69 | if (link_local_set || 0 != memcmp(&(a6->sin6_addr), ll_prefix, sizeof(ll_prefix))) 70 | continue; 71 | 72 | if (if_addr) 73 | memcpy(if_addr, &(a6->sin6_addr), sizeof(struct in6_addr)); 74 | 75 | link_local_set = 1; 76 | } 77 | } 78 | 79 | if (addresses) 80 | freeifaddrs(addresses); 81 | 82 | /* last item in the list is all zero (unspecified) address */ 83 | *if_addrs = realloc(*if_addrs, (i + 1) * sizeof(struct in6_addr)); 84 | memset(&(*if_addrs)[i], 0, sizeof(struct in6_addr)); 85 | 86 | /* Sort the addresses so the output is predictable. */ 87 | qsort(*if_addrs, i, sizeof(struct in6_addr), cmp_iface_addrs); 88 | 89 | if (!link_local_set) 90 | return -1; 91 | 92 | return i; 93 | } 94 | 95 | static int parse_ipv6_prefix(struct in6_prefix *prefix, const char *str) 96 | { 97 | char ip[INET6_ADDRSTRLEN] = {}; 98 | const char *tmp, *del; 99 | unsigned long p; 100 | int rc; 101 | 102 | del = strchr(str, '/'); 103 | if (!del) 104 | return -1; 105 | 106 | /* stupid check, why I am doing that */ 107 | tmp = strchr(del + 1, '/'); 108 | if (tmp) 109 | return -1; 110 | 111 | strncpy(ip, str, del - str); 112 | rc = inet_pton(AF_INET6, ip, &prefix->prefix); 113 | if (rc != 1) 114 | return -1; 115 | 116 | p = strtoul(del + 1, NULL, 10); 117 | if (p == ULONG_MAX || p > UINT8_MAX) 118 | return -1; 119 | 120 | prefix->len = p; 121 | return 0; 122 | } 123 | 124 | #if 0 125 | struct dag *iface_find_dag(const struct list_head *dags, uint16_t inst_id) 126 | { 127 | struct dag *dag; 128 | struct list *e; 129 | 130 | DL_FOREACH(dags->head, e) { 131 | dag = container_of(e, struct dag, list); 132 | 133 | if (dag->inst_id == inst_id) 134 | return dag; 135 | } 136 | 137 | return NULL; 138 | } 139 | #endif 140 | 141 | struct iface *iface_find_by_ifindex(struct list_head *ifaces, uint32_t ifindex) 142 | { 143 | struct iface *iface; 144 | 145 | list_for_each_entry(iface, ifaces, list) { 146 | if (iface->ifindex == ifindex) 147 | return iface; 148 | } 149 | 150 | return NULL; 151 | } 152 | 153 | static struct iface *iface_create() 154 | { 155 | struct iface *iface; 156 | 157 | iface = mzalloc(sizeof(*iface)); 158 | if (!iface) 159 | return NULL; 160 | 161 | return iface; 162 | } 163 | 164 | static void iface_free(struct iface *iface) 165 | { 166 | free(iface->ifaddrs); 167 | free(iface->llinfo.addr); 168 | free(iface); 169 | } 170 | 171 | static int config_load_dags(lua_State *L, struct iface *iface, 172 | uint8_t instanceid) 173 | { 174 | struct in6_addr dodagid; 175 | struct in6_prefix dest; 176 | uint8_t version, mop; 177 | struct dag *dag; 178 | int rc; 179 | 180 | lua_getfield(L, -1, "dags"); 181 | if (!lua_istable(L, -1)) 182 | return -1; 183 | 184 | lua_pushnil(L); 185 | while (lua_next(L, -2) != 0) { 186 | lua_getfield(L, -1, "dest_prefix"); 187 | if (lua_isstring(L, -1)) { 188 | rc = parse_ipv6_prefix(&dest, lua_tostring(L, -1)); 189 | if (rc == -1) 190 | return -1; 191 | } else { 192 | rc = gen_random_private_ula_pfx(&dest); 193 | if (rc == -1) 194 | return -1; 195 | } 196 | lua_pop(L, 1); 197 | 198 | lua_getfield(L, -1, "trickle_t"); 199 | if (lua_isnumber(L, -1)) { 200 | iface->tickle_t = lua_tonumber(L, -1); 201 | } else { 202 | if (!iface->tickle_t) 203 | iface->tickle_t = DEFAULT_TICKLE_T; 204 | } 205 | lua_pop(L, 1); 206 | 207 | lua_getfield(L, -1, "dodagid"); 208 | if (lua_isstring(L, -1)) { 209 | rc = inet_pton(AF_INET6, lua_tostring(L, -1), &dodagid); 210 | if (rc != 1) 211 | return -1; 212 | } else { 213 | rc = gen_stateless_addr(&dest, &iface->llinfo, 214 | &dodagid); 215 | if (rc == -1) 216 | return -1; 217 | } 218 | lua_pop(L, 1); 219 | 220 | lua_getfield(L, -1, "version"); 221 | if (lua_isnumber(L, -1)) { 222 | version = lua_tonumber(L, -1); 223 | } else { 224 | version = DEFAULT_DAG_VERSION; 225 | } 226 | lua_pop(L, 1); 227 | 228 | /* TODO also check string then to num */ 229 | lua_getfield(L, -1, "mode_of_operation"); 230 | if (lua_isnumber(L, -1)) { 231 | mop = lua_tonumber(L, -1); 232 | } else { 233 | mop = RPL_DIO_STORING_NO_MULTICAST; 234 | } 235 | lua_pop(L, 1); 236 | 237 | lua_pop(L, 1); 238 | 239 | dag = dag_create(iface, instanceid, &dodagid, 1, version, mop, &dest); 240 | if (!dag) 241 | return -1; 242 | 243 | /* we are root, self is dodagid */ 244 | memcpy(&dag->self, &dodagid, sizeof(dag->self)); 245 | } 246 | lua_pop(L, 1); 247 | 248 | return 0; 249 | } 250 | 251 | static int config_load_instances(lua_State *L, struct iface *iface) 252 | { 253 | uint8_t instanceid; 254 | int rc; 255 | 256 | lua_getfield(L, -1, "rpls"); 257 | if (!lua_istable(L, -1)) 258 | return -1; 259 | 260 | lua_pushnil(L); 261 | while (lua_next(L, -2) != 0) { 262 | lua_getfield(L, -1, "instance"); 263 | if (!lua_isnumber(L, -1)) 264 | return -1; 265 | 266 | instanceid = lua_tonumber(L, -1); 267 | lua_pop(L, 1); 268 | 269 | rc = config_load_dags(L, iface, instanceid); 270 | if (rc == -1) 271 | return rc; 272 | 273 | lua_pop(L, 1); 274 | } 275 | lua_pop(L, 1); 276 | 277 | return 0; 278 | } 279 | 280 | static int config_set_sysfs(const struct iface *iface) 281 | { 282 | int rc; 283 | 284 | #if 1 285 | /* TODO check why we need that to all? */ 286 | rc = set_interface_var("all", 287 | PROC_SYS_IP6_IFACE_FORWARDING, 288 | "forwarding", 1); 289 | #else 290 | rc = set_interface_var(iface->ifname, 291 | PROC_SYS_IP6_IFACE_FORWARDING, 292 | "forwarding", 1); 293 | #endif 294 | if (rc == -1) { 295 | flog(LOG_ERR, "Failed to set forwarding"); 296 | goto out; 297 | } 298 | 299 | rc = set_interface_var("all", 300 | PROC_SYS_IP6_IFACE_ACCEPT_SOURCE_ROUTE, 301 | "accept_source_route", 1); 302 | if (rc == -1) 303 | goto warn; 304 | 305 | rc = set_interface_var(iface->ifname, 306 | PROC_SYS_IP6_IFACE_ACCEPT_SOURCE_ROUTE, 307 | "accept_source_route", 1); 308 | if (rc == -1) 309 | goto warn; 310 | 311 | rc = set_interface_var(iface->ifname, 312 | PROC_SYS_IP6_IFACE_RPL_SEG_ENABLED, 313 | "rpl_seg_enabled", 1); 314 | if (rc == -1) 315 | goto warn; 316 | 317 | rc = set_interface_var("all", 318 | PROC_SYS_IP6_IFACE_RPL_SEG_ENABLED, 319 | "rpl_seg_enabled", 1); 320 | warn: 321 | if (rc == -1) 322 | flog(LOG_ERR, "Failed to set segmentation route settings"); 323 | 324 | rc = 0; 325 | out: 326 | return rc; 327 | } 328 | 329 | int config_load(const char *filename, struct list_head *ifaces) 330 | { 331 | struct iface *iface; 332 | lua_State *L; 333 | int rc; 334 | 335 | L = luaL_newstate(); 336 | if (!L) 337 | return -1; 338 | 339 | luaopen_base(L); 340 | luaopen_io(L); 341 | luaopen_string(L); 342 | luaopen_math(L); 343 | 344 | if (luaL_loadfile(L, filename) || lua_pcall(L, 0, 0, 0)) { 345 | flog(LOG_ERR, "cannot run configuration file: %s", 346 | lua_tostring(L, -1)); 347 | return -1; 348 | } 349 | 350 | lua_getglobal(L, "ifaces"); 351 | if (!lua_istable(L, -1)) 352 | return -1; 353 | 354 | lua_pushnil(L); 355 | while (lua_next(L, -2) != 0) { 356 | iface = iface_create(); 357 | if (!iface) { 358 | lua_close(L); 359 | return -1; 360 | } 361 | 362 | lua_getfield(L, -1, "ifname"); 363 | if (!lua_isstring(L, -1)) { 364 | iface_free(iface); 365 | lua_close(L); 366 | return -1; 367 | } 368 | 369 | strncpy(iface->ifname, lua_tostring(L, -1), IFNAMSIZ); 370 | lua_pop(L, 1); 371 | 372 | rc = config_set_sysfs(iface); 373 | if (rc == -1) { 374 | iface_free(iface); 375 | lua_close(L); 376 | return -1; 377 | } 378 | 379 | iface->ifindex = if_nametoindex(iface->ifname); 380 | if (iface->ifindex == 0) { 381 | flog(LOG_ERR, "%s not found: %s", iface->ifname, strerror(errno)); 382 | iface_free(iface); 383 | lua_close(L); 384 | return -1; 385 | } 386 | 387 | nl_get_llinfo(iface->ifindex, &iface->llinfo); 388 | 389 | rc = get_iface_addrs(iface->ifname, &iface->ifaddr, &iface->ifaddrs); 390 | if (rc == -1) { 391 | iface_free(iface); 392 | lua_close(L); 393 | return rc; 394 | } 395 | 396 | /* TODO because compression might be different here... */ 397 | iface->ifaddr_src = &iface->ifaddr; 398 | iface->ifaddrs_count = rc; 399 | 400 | lua_getfield(L, -1, "mode_of_operation"); 401 | iface->mop = lua_tonumber(L, -1); 402 | lua_pop(L, 1); 403 | 404 | lua_getfield(L, -1, "trickle_t"); 405 | if (lua_isnumber(L, -1)) { 406 | iface->tickle_t = lua_tonumber(L, -1); 407 | } else { 408 | iface->tickle_t = DEFAULT_TICKLE_T; 409 | } 410 | lua_pop(L, 1); 411 | 412 | lua_getfield(L, -1, "dodag_root"); 413 | if (!lua_isboolean(L, -1)) { 414 | iface_free(iface); 415 | lua_close(L); 416 | return -1; 417 | } 418 | 419 | iface->dodag_root = lua_toboolean(L, -1); 420 | lua_pop(L, 1); 421 | 422 | if (iface->dodag_root) { 423 | rc = config_load_instances(L, iface); 424 | if (rc == -1) 425 | return rc; 426 | } 427 | 428 | lua_pop(L, 1); 429 | DL_APPEND(ifaces->head, &iface->list); 430 | } 431 | 432 | lua_pop(L, 1); 433 | lua_close(L); 434 | 435 | flog(LOG_INFO, "config successful parsed"); 436 | 437 | return 0; 438 | } 439 | 440 | void config_free(struct list_head *ifaces) 441 | { 442 | struct iface *iface, *tmp; 443 | 444 | list_for_each_entry_safe(iface, tmp, ifaces, list) { 445 | iface_free(iface); 446 | list_del(ifaces, &iface->list); 447 | } 448 | } 449 | -------------------------------------------------------------------------------- /utlist.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2007-2018, Troy D. Hanson http://troydhanson.github.com/uthash/ 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 11 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 12 | IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 13 | TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 14 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 15 | OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 16 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 17 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 18 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 19 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 20 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 21 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 22 | */ 23 | 24 | #ifndef UTLIST_H 25 | #define UTLIST_H 26 | 27 | #define UTLIST_VERSION 2.1.0 28 | 29 | #include 30 | 31 | /* 32 | * This file contains macros to manipulate singly and doubly-linked lists. 33 | * 34 | * 1. LL_ macros: singly-linked lists. 35 | * 2. DL_ macros: doubly-linked lists. 36 | * 3. CDL_ macros: circular doubly-linked lists. 37 | * 38 | * To use singly-linked lists, your structure must have a "next" pointer. 39 | * To use doubly-linked lists, your structure must "prev" and "next" pointers. 40 | * Either way, the pointer to the head of the list must be initialized to NULL. 41 | * 42 | * ----------------.EXAMPLE ------------------------- 43 | * struct item { 44 | * int id; 45 | * struct item *prev, *next; 46 | * } 47 | * 48 | * struct item *list = NULL: 49 | * 50 | * int main() { 51 | * struct item *item; 52 | * ... allocate and populate item ... 53 | * DL_APPEND(list, item); 54 | * } 55 | * -------------------------------------------------- 56 | * 57 | * For doubly-linked lists, the append and delete macros are O(1) 58 | * For singly-linked lists, append and delete are O(n) but prepend is O(1) 59 | * The sort macro is O(n log(n)) for all types of single/double/circular lists. 60 | */ 61 | 62 | /* These macros use decltype or the earlier __typeof GNU extension. 63 | As decltype is only available in newer compilers (VS2010 or gcc 4.3+ 64 | when compiling c++ source) this code uses whatever method is needed 65 | or, for VS2008 where neither is available, uses casting workarounds. */ 66 | #if !defined(LDECLTYPE) && !defined(NO_DECLTYPE) 67 | #if defined(_MSC_VER) /* MS compiler */ 68 | #if _MSC_VER >= 1600 && defined(__cplusplus) /* VS2010 or newer in C++ mode */ 69 | #define LDECLTYPE(x) decltype(x) 70 | #else /* VS2008 or older (or VS2010 in C mode) */ 71 | #define NO_DECLTYPE 72 | #endif 73 | #elif defined(__BORLANDC__) || defined(__ICCARM__) || defined(__LCC__) || defined(__WATCOMC__) 74 | #define NO_DECLTYPE 75 | #else /* GNU, Sun and other compilers */ 76 | #define LDECLTYPE(x) __typeof(x) 77 | #endif 78 | #endif 79 | 80 | /* for VS2008 we use some workarounds to get around the lack of decltype, 81 | * namely, we always reassign our tmp variable to the list head if we need 82 | * to dereference its prev/next pointers, and save/restore the real head.*/ 83 | #ifdef NO_DECLTYPE 84 | #define IF_NO_DECLTYPE(x) x 85 | #define LDECLTYPE(x) char* 86 | #define UTLIST_SV(elt,list) _tmp = (char*)(list); {char **_alias = (char**)&(list); *_alias = (elt); } 87 | #define UTLIST_NEXT(elt,list,next) ((char*)((list)->next)) 88 | #define UTLIST_NEXTASGN(elt,list,to,next) { char **_alias = (char**)&((list)->next); *_alias=(char*)(to); } 89 | /* #define UTLIST_PREV(elt,list,prev) ((char*)((list)->prev)) */ 90 | #define UTLIST_PREVASGN(elt,list,to,prev) { char **_alias = (char**)&((list)->prev); *_alias=(char*)(to); } 91 | #define UTLIST_RS(list) { char **_alias = (char**)&(list); *_alias=_tmp; } 92 | #define UTLIST_CASTASGN(a,b) { char **_alias = (char**)&(a); *_alias=(char*)(b); } 93 | #else 94 | #define IF_NO_DECLTYPE(x) 95 | #define UTLIST_SV(elt,list) 96 | #define UTLIST_NEXT(elt,list,next) ((elt)->next) 97 | #define UTLIST_NEXTASGN(elt,list,to,next) ((elt)->next)=(to) 98 | /* #define UTLIST_PREV(elt,list,prev) ((elt)->prev) */ 99 | #define UTLIST_PREVASGN(elt,list,to,prev) ((elt)->prev)=(to) 100 | #define UTLIST_RS(list) 101 | #define UTLIST_CASTASGN(a,b) (a)=(b) 102 | #endif 103 | 104 | /****************************************************************************** 105 | * The sort macro is an adaptation of Simon Tatham's O(n log(n)) mergesort * 106 | * Unwieldy variable names used here to avoid shadowing passed-in variables. * 107 | *****************************************************************************/ 108 | #define LL_SORT(list, cmp) \ 109 | LL_SORT2(list, cmp, next) 110 | 111 | #define LL_SORT2(list, cmp, next) \ 112 | do { \ 113 | LDECLTYPE(list) _ls_p; \ 114 | LDECLTYPE(list) _ls_q; \ 115 | LDECLTYPE(list) _ls_e; \ 116 | LDECLTYPE(list) _ls_tail; \ 117 | IF_NO_DECLTYPE(LDECLTYPE(list) _tmp;) \ 118 | int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping; \ 119 | if (list) { \ 120 | _ls_insize = 1; \ 121 | _ls_looping = 1; \ 122 | while (_ls_looping) { \ 123 | UTLIST_CASTASGN(_ls_p,list); \ 124 | (list) = NULL; \ 125 | _ls_tail = NULL; \ 126 | _ls_nmerges = 0; \ 127 | while (_ls_p) { \ 128 | _ls_nmerges++; \ 129 | _ls_q = _ls_p; \ 130 | _ls_psize = 0; \ 131 | for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) { \ 132 | _ls_psize++; \ 133 | UTLIST_SV(_ls_q,list); _ls_q = UTLIST_NEXT(_ls_q,list,next); UTLIST_RS(list); \ 134 | if (!_ls_q) break; \ 135 | } \ 136 | _ls_qsize = _ls_insize; \ 137 | while (_ls_psize > 0 || (_ls_qsize > 0 && _ls_q)) { \ 138 | if (_ls_psize == 0) { \ 139 | _ls_e = _ls_q; UTLIST_SV(_ls_q,list); _ls_q = \ 140 | UTLIST_NEXT(_ls_q,list,next); UTLIST_RS(list); _ls_qsize--; \ 141 | } else if (_ls_qsize == 0 || !_ls_q) { \ 142 | _ls_e = _ls_p; UTLIST_SV(_ls_p,list); _ls_p = \ 143 | UTLIST_NEXT(_ls_p,list,next); UTLIST_RS(list); _ls_psize--; \ 144 | } else if (cmp(_ls_p,_ls_q) <= 0) { \ 145 | _ls_e = _ls_p; UTLIST_SV(_ls_p,list); _ls_p = \ 146 | UTLIST_NEXT(_ls_p,list,next); UTLIST_RS(list); _ls_psize--; \ 147 | } else { \ 148 | _ls_e = _ls_q; UTLIST_SV(_ls_q,list); _ls_q = \ 149 | UTLIST_NEXT(_ls_q,list,next); UTLIST_RS(list); _ls_qsize--; \ 150 | } \ 151 | if (_ls_tail) { \ 152 | UTLIST_SV(_ls_tail,list); UTLIST_NEXTASGN(_ls_tail,list,_ls_e,next); UTLIST_RS(list); \ 153 | } else { \ 154 | UTLIST_CASTASGN(list,_ls_e); \ 155 | } \ 156 | _ls_tail = _ls_e; \ 157 | } \ 158 | _ls_p = _ls_q; \ 159 | } \ 160 | if (_ls_tail) { \ 161 | UTLIST_SV(_ls_tail,list); UTLIST_NEXTASGN(_ls_tail,list,NULL,next); UTLIST_RS(list); \ 162 | } \ 163 | if (_ls_nmerges <= 1) { \ 164 | _ls_looping=0; \ 165 | } \ 166 | _ls_insize *= 2; \ 167 | } \ 168 | } \ 169 | } while (0) 170 | 171 | 172 | #define DL_SORT(list, cmp) \ 173 | DL_SORT2(list, cmp, prev, next) 174 | 175 | #define DL_SORT2(list, cmp, prev, next) \ 176 | do { \ 177 | LDECLTYPE(list) _ls_p; \ 178 | LDECLTYPE(list) _ls_q; \ 179 | LDECLTYPE(list) _ls_e; \ 180 | LDECLTYPE(list) _ls_tail; \ 181 | IF_NO_DECLTYPE(LDECLTYPE(list) _tmp;) \ 182 | int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping; \ 183 | if (list) { \ 184 | _ls_insize = 1; \ 185 | _ls_looping = 1; \ 186 | while (_ls_looping) { \ 187 | UTLIST_CASTASGN(_ls_p,list); \ 188 | (list) = NULL; \ 189 | _ls_tail = NULL; \ 190 | _ls_nmerges = 0; \ 191 | while (_ls_p) { \ 192 | _ls_nmerges++; \ 193 | _ls_q = _ls_p; \ 194 | _ls_psize = 0; \ 195 | for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) { \ 196 | _ls_psize++; \ 197 | UTLIST_SV(_ls_q,list); _ls_q = UTLIST_NEXT(_ls_q,list,next); UTLIST_RS(list); \ 198 | if (!_ls_q) break; \ 199 | } \ 200 | _ls_qsize = _ls_insize; \ 201 | while ((_ls_psize > 0) || ((_ls_qsize > 0) && _ls_q)) { \ 202 | if (_ls_psize == 0) { \ 203 | _ls_e = _ls_q; UTLIST_SV(_ls_q,list); _ls_q = \ 204 | UTLIST_NEXT(_ls_q,list,next); UTLIST_RS(list); _ls_qsize--; \ 205 | } else if ((_ls_qsize == 0) || (!_ls_q)) { \ 206 | _ls_e = _ls_p; UTLIST_SV(_ls_p,list); _ls_p = \ 207 | UTLIST_NEXT(_ls_p,list,next); UTLIST_RS(list); _ls_psize--; \ 208 | } else if (cmp(_ls_p,_ls_q) <= 0) { \ 209 | _ls_e = _ls_p; UTLIST_SV(_ls_p,list); _ls_p = \ 210 | UTLIST_NEXT(_ls_p,list,next); UTLIST_RS(list); _ls_psize--; \ 211 | } else { \ 212 | _ls_e = _ls_q; UTLIST_SV(_ls_q,list); _ls_q = \ 213 | UTLIST_NEXT(_ls_q,list,next); UTLIST_RS(list); _ls_qsize--; \ 214 | } \ 215 | if (_ls_tail) { \ 216 | UTLIST_SV(_ls_tail,list); UTLIST_NEXTASGN(_ls_tail,list,_ls_e,next); UTLIST_RS(list); \ 217 | } else { \ 218 | UTLIST_CASTASGN(list,_ls_e); \ 219 | } \ 220 | UTLIST_SV(_ls_e,list); UTLIST_PREVASGN(_ls_e,list,_ls_tail,prev); UTLIST_RS(list); \ 221 | _ls_tail = _ls_e; \ 222 | } \ 223 | _ls_p = _ls_q; \ 224 | } \ 225 | UTLIST_CASTASGN((list)->prev, _ls_tail); \ 226 | UTLIST_SV(_ls_tail,list); UTLIST_NEXTASGN(_ls_tail,list,NULL,next); UTLIST_RS(list); \ 227 | if (_ls_nmerges <= 1) { \ 228 | _ls_looping=0; \ 229 | } \ 230 | _ls_insize *= 2; \ 231 | } \ 232 | } \ 233 | } while (0) 234 | 235 | #define CDL_SORT(list, cmp) \ 236 | CDL_SORT2(list, cmp, prev, next) 237 | 238 | #define CDL_SORT2(list, cmp, prev, next) \ 239 | do { \ 240 | LDECLTYPE(list) _ls_p; \ 241 | LDECLTYPE(list) _ls_q; \ 242 | LDECLTYPE(list) _ls_e; \ 243 | LDECLTYPE(list) _ls_tail; \ 244 | LDECLTYPE(list) _ls_oldhead; \ 245 | LDECLTYPE(list) _tmp; \ 246 | int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping; \ 247 | if (list) { \ 248 | _ls_insize = 1; \ 249 | _ls_looping = 1; \ 250 | while (_ls_looping) { \ 251 | UTLIST_CASTASGN(_ls_p,list); \ 252 | UTLIST_CASTASGN(_ls_oldhead,list); \ 253 | (list) = NULL; \ 254 | _ls_tail = NULL; \ 255 | _ls_nmerges = 0; \ 256 | while (_ls_p) { \ 257 | _ls_nmerges++; \ 258 | _ls_q = _ls_p; \ 259 | _ls_psize = 0; \ 260 | for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) { \ 261 | _ls_psize++; \ 262 | UTLIST_SV(_ls_q,list); \ 263 | if (UTLIST_NEXT(_ls_q,list,next) == _ls_oldhead) { \ 264 | _ls_q = NULL; \ 265 | } else { \ 266 | _ls_q = UTLIST_NEXT(_ls_q,list,next); \ 267 | } \ 268 | UTLIST_RS(list); \ 269 | if (!_ls_q) break; \ 270 | } \ 271 | _ls_qsize = _ls_insize; \ 272 | while (_ls_psize > 0 || (_ls_qsize > 0 && _ls_q)) { \ 273 | if (_ls_psize == 0) { \ 274 | _ls_e = _ls_q; UTLIST_SV(_ls_q,list); _ls_q = \ 275 | UTLIST_NEXT(_ls_q,list,next); UTLIST_RS(list); _ls_qsize--; \ 276 | if (_ls_q == _ls_oldhead) { _ls_q = NULL; } \ 277 | } else if (_ls_qsize == 0 || !_ls_q) { \ 278 | _ls_e = _ls_p; UTLIST_SV(_ls_p,list); _ls_p = \ 279 | UTLIST_NEXT(_ls_p,list,next); UTLIST_RS(list); _ls_psize--; \ 280 | if (_ls_p == _ls_oldhead) { _ls_p = NULL; } \ 281 | } else if (cmp(_ls_p,_ls_q) <= 0) { \ 282 | _ls_e = _ls_p; UTLIST_SV(_ls_p,list); _ls_p = \ 283 | UTLIST_NEXT(_ls_p,list,next); UTLIST_RS(list); _ls_psize--; \ 284 | if (_ls_p == _ls_oldhead) { _ls_p = NULL; } \ 285 | } else { \ 286 | _ls_e = _ls_q; UTLIST_SV(_ls_q,list); _ls_q = \ 287 | UTLIST_NEXT(_ls_q,list,next); UTLIST_RS(list); _ls_qsize--; \ 288 | if (_ls_q == _ls_oldhead) { _ls_q = NULL; } \ 289 | } \ 290 | if (_ls_tail) { \ 291 | UTLIST_SV(_ls_tail,list); UTLIST_NEXTASGN(_ls_tail,list,_ls_e,next); UTLIST_RS(list); \ 292 | } else { \ 293 | UTLIST_CASTASGN(list,_ls_e); \ 294 | } \ 295 | UTLIST_SV(_ls_e,list); UTLIST_PREVASGN(_ls_e,list,_ls_tail,prev); UTLIST_RS(list); \ 296 | _ls_tail = _ls_e; \ 297 | } \ 298 | _ls_p = _ls_q; \ 299 | } \ 300 | UTLIST_CASTASGN((list)->prev,_ls_tail); \ 301 | UTLIST_CASTASGN(_tmp,list); \ 302 | UTLIST_SV(_ls_tail,list); UTLIST_NEXTASGN(_ls_tail,list,_tmp,next); UTLIST_RS(list); \ 303 | if (_ls_nmerges <= 1) { \ 304 | _ls_looping=0; \ 305 | } \ 306 | _ls_insize *= 2; \ 307 | } \ 308 | } \ 309 | } while (0) 310 | 311 | /****************************************************************************** 312 | * singly linked list macros (non-circular) * 313 | *****************************************************************************/ 314 | #define LL_PREPEND(head,add) \ 315 | LL_PREPEND2(head,add,next) 316 | 317 | #define LL_PREPEND2(head,add,next) \ 318 | do { \ 319 | (add)->next = (head); \ 320 | (head) = (add); \ 321 | } while (0) 322 | 323 | #define LL_CONCAT(head1,head2) \ 324 | LL_CONCAT2(head1,head2,next) 325 | 326 | #define LL_CONCAT2(head1,head2,next) \ 327 | do { \ 328 | LDECLTYPE(head1) _tmp; \ 329 | if (head1) { \ 330 | _tmp = (head1); \ 331 | while (_tmp->next) { _tmp = _tmp->next; } \ 332 | _tmp->next=(head2); \ 333 | } else { \ 334 | (head1)=(head2); \ 335 | } \ 336 | } while (0) 337 | 338 | #define LL_APPEND(head,add) \ 339 | LL_APPEND2(head,add,next) 340 | 341 | #define LL_APPEND2(head,add,next) \ 342 | do { \ 343 | LDECLTYPE(head) _tmp; \ 344 | (add)->next=NULL; \ 345 | if (head) { \ 346 | _tmp = (head); \ 347 | while (_tmp->next) { _tmp = _tmp->next; } \ 348 | _tmp->next=(add); \ 349 | } else { \ 350 | (head)=(add); \ 351 | } \ 352 | } while (0) 353 | 354 | #define LL_INSERT_INORDER(head,add,cmp) \ 355 | LL_INSERT_INORDER2(head,add,cmp,next) 356 | 357 | #define LL_INSERT_INORDER2(head,add,cmp,next) \ 358 | do { \ 359 | LDECLTYPE(head) _tmp; \ 360 | if (head) { \ 361 | LL_LOWER_BOUND2(head, _tmp, add, cmp, next); \ 362 | LL_APPEND_ELEM2(head, _tmp, add, next); \ 363 | } else { \ 364 | (head) = (add); \ 365 | (head)->next = NULL; \ 366 | } \ 367 | } while (0) 368 | 369 | #define LL_LOWER_BOUND(head,elt,like,cmp) \ 370 | LL_LOWER_BOUND2(head,elt,like,cmp,next) 371 | 372 | #define LL_LOWER_BOUND2(head,elt,like,cmp,next) \ 373 | do { \ 374 | if ((head) == NULL || (cmp(head, like)) >= 0) { \ 375 | (elt) = NULL; \ 376 | } else { \ 377 | for ((elt) = (head); (elt)->next != NULL; (elt) = (elt)->next) { \ 378 | if (cmp((elt)->next, like) >= 0) { \ 379 | break; \ 380 | } \ 381 | } \ 382 | } \ 383 | } while (0) 384 | 385 | #define LL_DELETE(head,del) \ 386 | LL_DELETE2(head,del,next) 387 | 388 | #define LL_DELETE2(head,del,next) \ 389 | do { \ 390 | LDECLTYPE(head) _tmp; \ 391 | if ((head) == (del)) { \ 392 | (head)=(head)->next; \ 393 | } else { \ 394 | _tmp = (head); \ 395 | while (_tmp->next && (_tmp->next != (del))) { \ 396 | _tmp = _tmp->next; \ 397 | } \ 398 | if (_tmp->next) { \ 399 | _tmp->next = (del)->next; \ 400 | } \ 401 | } \ 402 | } while (0) 403 | 404 | #define LL_COUNT(head,el,counter) \ 405 | LL_COUNT2(head,el,counter,next) \ 406 | 407 | #define LL_COUNT2(head,el,counter,next) \ 408 | do { \ 409 | (counter) = 0; \ 410 | LL_FOREACH2(head,el,next) { ++(counter); } \ 411 | } while (0) 412 | 413 | #define LL_FOREACH(head,el) \ 414 | LL_FOREACH2(head,el,next) 415 | 416 | #define LL_FOREACH2(head,el,next) \ 417 | for ((el) = (head); el; (el) = (el)->next) 418 | 419 | #define LL_FOREACH_SAFE(head,el,tmp) \ 420 | LL_FOREACH_SAFE2(head,el,tmp,next) 421 | 422 | #define LL_FOREACH_SAFE2(head,el,tmp,next) \ 423 | for ((el) = (head); (el) && ((tmp) = (el)->next, 1); (el) = (tmp)) 424 | 425 | #define LL_SEARCH_SCALAR(head,out,field,val) \ 426 | LL_SEARCH_SCALAR2(head,out,field,val,next) 427 | 428 | #define LL_SEARCH_SCALAR2(head,out,field,val,next) \ 429 | do { \ 430 | LL_FOREACH2(head,out,next) { \ 431 | if ((out)->field == (val)) break; \ 432 | } \ 433 | } while (0) 434 | 435 | #define LL_SEARCH(head,out,elt,cmp) \ 436 | LL_SEARCH2(head,out,elt,cmp,next) 437 | 438 | #define LL_SEARCH2(head,out,elt,cmp,next) \ 439 | do { \ 440 | LL_FOREACH2(head,out,next) { \ 441 | if ((cmp(out,elt))==0) break; \ 442 | } \ 443 | } while (0) 444 | 445 | #define LL_REPLACE_ELEM2(head, el, add, next) \ 446 | do { \ 447 | LDECLTYPE(head) _tmp; \ 448 | assert((head) != NULL); \ 449 | assert((el) != NULL); \ 450 | assert((add) != NULL); \ 451 | (add)->next = (el)->next; \ 452 | if ((head) == (el)) { \ 453 | (head) = (add); \ 454 | } else { \ 455 | _tmp = (head); \ 456 | while (_tmp->next && (_tmp->next != (el))) { \ 457 | _tmp = _tmp->next; \ 458 | } \ 459 | if (_tmp->next) { \ 460 | _tmp->next = (add); \ 461 | } \ 462 | } \ 463 | } while (0) 464 | 465 | #define LL_REPLACE_ELEM(head, el, add) \ 466 | LL_REPLACE_ELEM2(head, el, add, next) 467 | 468 | #define LL_PREPEND_ELEM2(head, el, add, next) \ 469 | do { \ 470 | if (el) { \ 471 | LDECLTYPE(head) _tmp; \ 472 | assert((head) != NULL); \ 473 | assert((add) != NULL); \ 474 | (add)->next = (el); \ 475 | if ((head) == (el)) { \ 476 | (head) = (add); \ 477 | } else { \ 478 | _tmp = (head); \ 479 | while (_tmp->next && (_tmp->next != (el))) { \ 480 | _tmp = _tmp->next; \ 481 | } \ 482 | if (_tmp->next) { \ 483 | _tmp->next = (add); \ 484 | } \ 485 | } \ 486 | } else { \ 487 | LL_APPEND2(head, add, next); \ 488 | } \ 489 | } while (0) \ 490 | 491 | #define LL_PREPEND_ELEM(head, el, add) \ 492 | LL_PREPEND_ELEM2(head, el, add, next) 493 | 494 | #define LL_APPEND_ELEM2(head, el, add, next) \ 495 | do { \ 496 | if (el) { \ 497 | assert((head) != NULL); \ 498 | assert((add) != NULL); \ 499 | (add)->next = (el)->next; \ 500 | (el)->next = (add); \ 501 | } else { \ 502 | LL_PREPEND2(head, add, next); \ 503 | } \ 504 | } while (0) \ 505 | 506 | #define LL_APPEND_ELEM(head, el, add) \ 507 | LL_APPEND_ELEM2(head, el, add, next) 508 | 509 | #ifdef NO_DECLTYPE 510 | /* Here are VS2008 / NO_DECLTYPE replacements for a few functions */ 511 | 512 | #undef LL_CONCAT2 513 | #define LL_CONCAT2(head1,head2,next) \ 514 | do { \ 515 | char *_tmp; \ 516 | if (head1) { \ 517 | _tmp = (char*)(head1); \ 518 | while ((head1)->next) { (head1) = (head1)->next; } \ 519 | (head1)->next = (head2); \ 520 | UTLIST_RS(head1); \ 521 | } else { \ 522 | (head1)=(head2); \ 523 | } \ 524 | } while (0) 525 | 526 | #undef LL_APPEND2 527 | #define LL_APPEND2(head,add,next) \ 528 | do { \ 529 | if (head) { \ 530 | (add)->next = head; /* use add->next as a temp variable */ \ 531 | while ((add)->next->next) { (add)->next = (add)->next->next; } \ 532 | (add)->next->next=(add); \ 533 | } else { \ 534 | (head)=(add); \ 535 | } \ 536 | (add)->next=NULL; \ 537 | } while (0) 538 | 539 | #undef LL_INSERT_INORDER2 540 | #define LL_INSERT_INORDER2(head,add,cmp,next) \ 541 | do { \ 542 | if ((head) == NULL || (cmp(head, add)) >= 0) { \ 543 | (add)->next = (head); \ 544 | (head) = (add); \ 545 | } else { \ 546 | char *_tmp = (char*)(head); \ 547 | while ((head)->next != NULL && (cmp((head)->next, add)) < 0) { \ 548 | (head) = (head)->next; \ 549 | } \ 550 | (add)->next = (head)->next; \ 551 | (head)->next = (add); \ 552 | UTLIST_RS(head); \ 553 | } \ 554 | } while (0) 555 | 556 | #undef LL_DELETE2 557 | #define LL_DELETE2(head,del,next) \ 558 | do { \ 559 | if ((head) == (del)) { \ 560 | (head)=(head)->next; \ 561 | } else { \ 562 | char *_tmp = (char*)(head); \ 563 | while ((head)->next && ((head)->next != (del))) { \ 564 | (head) = (head)->next; \ 565 | } \ 566 | if ((head)->next) { \ 567 | (head)->next = ((del)->next); \ 568 | } \ 569 | UTLIST_RS(head); \ 570 | } \ 571 | } while (0) 572 | 573 | #undef LL_REPLACE_ELEM2 574 | #define LL_REPLACE_ELEM2(head, el, add, next) \ 575 | do { \ 576 | assert((head) != NULL); \ 577 | assert((el) != NULL); \ 578 | assert((add) != NULL); \ 579 | if ((head) == (el)) { \ 580 | (head) = (add); \ 581 | } else { \ 582 | (add)->next = head; \ 583 | while ((add)->next->next && ((add)->next->next != (el))) { \ 584 | (add)->next = (add)->next->next; \ 585 | } \ 586 | if ((add)->next->next) { \ 587 | (add)->next->next = (add); \ 588 | } \ 589 | } \ 590 | (add)->next = (el)->next; \ 591 | } while (0) 592 | 593 | #undef LL_PREPEND_ELEM2 594 | #define LL_PREPEND_ELEM2(head, el, add, next) \ 595 | do { \ 596 | if (el) { \ 597 | assert((head) != NULL); \ 598 | assert((add) != NULL); \ 599 | if ((head) == (el)) { \ 600 | (head) = (add); \ 601 | } else { \ 602 | (add)->next = (head); \ 603 | while ((add)->next->next && ((add)->next->next != (el))) { \ 604 | (add)->next = (add)->next->next; \ 605 | } \ 606 | if ((add)->next->next) { \ 607 | (add)->next->next = (add); \ 608 | } \ 609 | } \ 610 | (add)->next = (el); \ 611 | } else { \ 612 | LL_APPEND2(head, add, next); \ 613 | } \ 614 | } while (0) \ 615 | 616 | #endif /* NO_DECLTYPE */ 617 | 618 | /****************************************************************************** 619 | * doubly linked list macros (non-circular) * 620 | *****************************************************************************/ 621 | #define DL_PREPEND(head,add) \ 622 | DL_PREPEND2(head,add,prev,next) 623 | 624 | #define DL_PREPEND2(head,add,prev,next) \ 625 | do { \ 626 | (add)->next = (head); \ 627 | if (head) { \ 628 | (add)->prev = (head)->prev; \ 629 | (head)->prev = (add); \ 630 | } else { \ 631 | (add)->prev = (add); \ 632 | } \ 633 | (head) = (add); \ 634 | } while (0) 635 | 636 | #define DL_APPEND(head,add) \ 637 | DL_APPEND2(head,add,prev,next) 638 | 639 | #define DL_APPEND2(head,add,prev,next) \ 640 | do { \ 641 | if (head) { \ 642 | (add)->prev = (head)->prev; \ 643 | (head)->prev->next = (add); \ 644 | (head)->prev = (add); \ 645 | (add)->next = NULL; \ 646 | } else { \ 647 | (head)=(add); \ 648 | (head)->prev = (head); \ 649 | (head)->next = NULL; \ 650 | } \ 651 | } while (0) 652 | 653 | #define DL_INSERT_INORDER(head,add,cmp) \ 654 | DL_INSERT_INORDER2(head,add,cmp,prev,next) 655 | 656 | #define DL_INSERT_INORDER2(head,add,cmp,prev,next) \ 657 | do { \ 658 | LDECLTYPE(head) _tmp; \ 659 | if (head) { \ 660 | DL_LOWER_BOUND2(head, _tmp, add, cmp, next); \ 661 | DL_APPEND_ELEM2(head, _tmp, add, prev, next); \ 662 | } else { \ 663 | (head) = (add); \ 664 | (head)->prev = (head); \ 665 | (head)->next = NULL; \ 666 | } \ 667 | } while (0) 668 | 669 | #define DL_LOWER_BOUND(head,elt,like,cmp) \ 670 | DL_LOWER_BOUND2(head,elt,like,cmp,next) 671 | 672 | #define DL_LOWER_BOUND2(head,elt,like,cmp,next) \ 673 | do { \ 674 | if ((head) == NULL || (cmp(head, like)) >= 0) { \ 675 | (elt) = NULL; \ 676 | } else { \ 677 | for ((elt) = (head); (elt)->next != NULL; (elt) = (elt)->next) { \ 678 | if ((cmp((elt)->next, like)) >= 0) { \ 679 | break; \ 680 | } \ 681 | } \ 682 | } \ 683 | } while (0) 684 | 685 | #define DL_CONCAT(head1,head2) \ 686 | DL_CONCAT2(head1,head2,prev,next) 687 | 688 | #define DL_CONCAT2(head1,head2,prev,next) \ 689 | do { \ 690 | LDECLTYPE(head1) _tmp; \ 691 | if (head2) { \ 692 | if (head1) { \ 693 | UTLIST_CASTASGN(_tmp, (head2)->prev); \ 694 | (head2)->prev = (head1)->prev; \ 695 | (head1)->prev->next = (head2); \ 696 | UTLIST_CASTASGN((head1)->prev, _tmp); \ 697 | } else { \ 698 | (head1)=(head2); \ 699 | } \ 700 | } \ 701 | } while (0) 702 | 703 | #define DL_DELETE(head,del) \ 704 | DL_DELETE2(head,del,prev,next) 705 | 706 | #define DL_DELETE2(head,del,prev,next) \ 707 | do { \ 708 | assert((head) != NULL); \ 709 | assert((del)->prev != NULL); \ 710 | if ((del)->prev == (del)) { \ 711 | (head)=NULL; \ 712 | } else if ((del)==(head)) { \ 713 | (del)->next->prev = (del)->prev; \ 714 | (head) = (del)->next; \ 715 | } else { \ 716 | (del)->prev->next = (del)->next; \ 717 | if ((del)->next) { \ 718 | (del)->next->prev = (del)->prev; \ 719 | } else { \ 720 | (head)->prev = (del)->prev; \ 721 | } \ 722 | } \ 723 | } while (0) 724 | 725 | #define DL_COUNT(head,el,counter) \ 726 | DL_COUNT2(head,el,counter,next) \ 727 | 728 | #define DL_COUNT2(head,el,counter,next) \ 729 | do { \ 730 | (counter) = 0; \ 731 | DL_FOREACH2(head,el,next) { ++(counter); } \ 732 | } while (0) 733 | 734 | #define DL_FOREACH(head,el) \ 735 | DL_FOREACH2(head,el,next) 736 | 737 | #define DL_FOREACH2(head,el,next) \ 738 | for ((el) = (head); el; (el) = (el)->next) 739 | 740 | /* this version is safe for deleting the elements during iteration */ 741 | #define DL_FOREACH_SAFE(head,el,tmp) \ 742 | DL_FOREACH_SAFE2(head,el,tmp,next) 743 | 744 | #define DL_FOREACH_SAFE2(head,el,tmp,next) \ 745 | for ((el) = (head); (el) && ((tmp) = (el)->next, 1); (el) = (tmp)) 746 | 747 | /* these are identical to their singly-linked list counterparts */ 748 | #define DL_SEARCH_SCALAR LL_SEARCH_SCALAR 749 | #define DL_SEARCH LL_SEARCH 750 | #define DL_SEARCH_SCALAR2 LL_SEARCH_SCALAR2 751 | #define DL_SEARCH2 LL_SEARCH2 752 | 753 | #define DL_REPLACE_ELEM2(head, el, add, prev, next) \ 754 | do { \ 755 | assert((head) != NULL); \ 756 | assert((el) != NULL); \ 757 | assert((add) != NULL); \ 758 | if ((head) == (el)) { \ 759 | (head) = (add); \ 760 | (add)->next = (el)->next; \ 761 | if ((el)->next == NULL) { \ 762 | (add)->prev = (add); \ 763 | } else { \ 764 | (add)->prev = (el)->prev; \ 765 | (add)->next->prev = (add); \ 766 | } \ 767 | } else { \ 768 | (add)->next = (el)->next; \ 769 | (add)->prev = (el)->prev; \ 770 | (add)->prev->next = (add); \ 771 | if ((el)->next == NULL) { \ 772 | (head)->prev = (add); \ 773 | } else { \ 774 | (add)->next->prev = (add); \ 775 | } \ 776 | } \ 777 | } while (0) 778 | 779 | #define DL_REPLACE_ELEM(head, el, add) \ 780 | DL_REPLACE_ELEM2(head, el, add, prev, next) 781 | 782 | #define DL_PREPEND_ELEM2(head, el, add, prev, next) \ 783 | do { \ 784 | if (el) { \ 785 | assert((head) != NULL); \ 786 | assert((add) != NULL); \ 787 | (add)->next = (el); \ 788 | (add)->prev = (el)->prev; \ 789 | (el)->prev = (add); \ 790 | if ((head) == (el)) { \ 791 | (head) = (add); \ 792 | } else { \ 793 | (add)->prev->next = (add); \ 794 | } \ 795 | } else { \ 796 | DL_APPEND2(head, add, prev, next); \ 797 | } \ 798 | } while (0) \ 799 | 800 | #define DL_PREPEND_ELEM(head, el, add) \ 801 | DL_PREPEND_ELEM2(head, el, add, prev, next) 802 | 803 | #define DL_APPEND_ELEM2(head, el, add, prev, next) \ 804 | do { \ 805 | if (el) { \ 806 | assert((head) != NULL); \ 807 | assert((add) != NULL); \ 808 | (add)->next = (el)->next; \ 809 | (add)->prev = (el); \ 810 | (el)->next = (add); \ 811 | if ((add)->next) { \ 812 | (add)->next->prev = (add); \ 813 | } else { \ 814 | (head)->prev = (add); \ 815 | } \ 816 | } else { \ 817 | DL_PREPEND2(head, add, prev, next); \ 818 | } \ 819 | } while (0) \ 820 | 821 | #define DL_APPEND_ELEM(head, el, add) \ 822 | DL_APPEND_ELEM2(head, el, add, prev, next) 823 | 824 | #ifdef NO_DECLTYPE 825 | /* Here are VS2008 / NO_DECLTYPE replacements for a few functions */ 826 | 827 | #undef DL_INSERT_INORDER2 828 | #define DL_INSERT_INORDER2(head,add,cmp,prev,next) \ 829 | do { \ 830 | if ((head) == NULL) { \ 831 | (add)->prev = (add); \ 832 | (add)->next = NULL; \ 833 | (head) = (add); \ 834 | } else if ((cmp(head, add)) >= 0) { \ 835 | (add)->prev = (head)->prev; \ 836 | (add)->next = (head); \ 837 | (head)->prev = (add); \ 838 | (head) = (add); \ 839 | } else { \ 840 | char *_tmp = (char*)(head); \ 841 | while ((head)->next && (cmp((head)->next, add)) < 0) { \ 842 | (head) = (head)->next; \ 843 | } \ 844 | (add)->prev = (head); \ 845 | (add)->next = (head)->next; \ 846 | (head)->next = (add); \ 847 | UTLIST_RS(head); \ 848 | if ((add)->next) { \ 849 | (add)->next->prev = (add); \ 850 | } else { \ 851 | (head)->prev = (add); \ 852 | } \ 853 | } \ 854 | } while (0) 855 | #endif /* NO_DECLTYPE */ 856 | 857 | /****************************************************************************** 858 | * circular doubly linked list macros * 859 | *****************************************************************************/ 860 | #define CDL_APPEND(head,add) \ 861 | CDL_APPEND2(head,add,prev,next) 862 | 863 | #define CDL_APPEND2(head,add,prev,next) \ 864 | do { \ 865 | if (head) { \ 866 | (add)->prev = (head)->prev; \ 867 | (add)->next = (head); \ 868 | (head)->prev = (add); \ 869 | (add)->prev->next = (add); \ 870 | } else { \ 871 | (add)->prev = (add); \ 872 | (add)->next = (add); \ 873 | (head) = (add); \ 874 | } \ 875 | } while (0) 876 | 877 | #define CDL_PREPEND(head,add) \ 878 | CDL_PREPEND2(head,add,prev,next) 879 | 880 | #define CDL_PREPEND2(head,add,prev,next) \ 881 | do { \ 882 | if (head) { \ 883 | (add)->prev = (head)->prev; \ 884 | (add)->next = (head); \ 885 | (head)->prev = (add); \ 886 | (add)->prev->next = (add); \ 887 | } else { \ 888 | (add)->prev = (add); \ 889 | (add)->next = (add); \ 890 | } \ 891 | (head) = (add); \ 892 | } while (0) 893 | 894 | #define CDL_INSERT_INORDER(head,add,cmp) \ 895 | CDL_INSERT_INORDER2(head,add,cmp,prev,next) 896 | 897 | #define CDL_INSERT_INORDER2(head,add,cmp,prev,next) \ 898 | do { \ 899 | LDECLTYPE(head) _tmp; \ 900 | if (head) { \ 901 | CDL_LOWER_BOUND2(head, _tmp, add, cmp, next); \ 902 | CDL_APPEND_ELEM2(head, _tmp, add, prev, next); \ 903 | } else { \ 904 | (head) = (add); \ 905 | (head)->next = (head); \ 906 | (head)->prev = (head); \ 907 | } \ 908 | } while (0) 909 | 910 | #define CDL_LOWER_BOUND(head,elt,like,cmp) \ 911 | CDL_LOWER_BOUND2(head,elt,like,cmp,next) 912 | 913 | #define CDL_LOWER_BOUND2(head,elt,like,cmp,next) \ 914 | do { \ 915 | if ((head) == NULL || (cmp(head, like)) >= 0) { \ 916 | (elt) = NULL; \ 917 | } else { \ 918 | for ((elt) = (head); (elt)->next != (head); (elt) = (elt)->next) { \ 919 | if ((cmp((elt)->next, like)) >= 0) { \ 920 | break; \ 921 | } \ 922 | } \ 923 | } \ 924 | } while (0) 925 | 926 | #define CDL_DELETE(head,del) \ 927 | CDL_DELETE2(head,del,prev,next) 928 | 929 | #define CDL_DELETE2(head,del,prev,next) \ 930 | do { \ 931 | if (((head)==(del)) && ((head)->next == (head))) { \ 932 | (head) = NULL; \ 933 | } else { \ 934 | (del)->next->prev = (del)->prev; \ 935 | (del)->prev->next = (del)->next; \ 936 | if ((del) == (head)) (head)=(del)->next; \ 937 | } \ 938 | } while (0) 939 | 940 | #define CDL_COUNT(head,el,counter) \ 941 | CDL_COUNT2(head,el,counter,next) \ 942 | 943 | #define CDL_COUNT2(head, el, counter,next) \ 944 | do { \ 945 | (counter) = 0; \ 946 | CDL_FOREACH2(head,el,next) { ++(counter); } \ 947 | } while (0) 948 | 949 | #define CDL_FOREACH(head,el) \ 950 | CDL_FOREACH2(head,el,next) 951 | 952 | #define CDL_FOREACH2(head,el,next) \ 953 | for ((el)=(head);el;(el)=(((el)->next==(head)) ? NULL : (el)->next)) 954 | 955 | #define CDL_FOREACH_SAFE(head,el,tmp1,tmp2) \ 956 | CDL_FOREACH_SAFE2(head,el,tmp1,tmp2,prev,next) 957 | 958 | #define CDL_FOREACH_SAFE2(head,el,tmp1,tmp2,prev,next) \ 959 | for ((el) = (head), (tmp1) = (head) ? (head)->prev : NULL; \ 960 | (el) && ((tmp2) = (el)->next, 1); \ 961 | (el) = ((el) == (tmp1) ? NULL : (tmp2))) 962 | 963 | #define CDL_SEARCH_SCALAR(head,out,field,val) \ 964 | CDL_SEARCH_SCALAR2(head,out,field,val,next) 965 | 966 | #define CDL_SEARCH_SCALAR2(head,out,field,val,next) \ 967 | do { \ 968 | CDL_FOREACH2(head,out,next) { \ 969 | if ((out)->field == (val)) break; \ 970 | } \ 971 | } while (0) 972 | 973 | #define CDL_SEARCH(head,out,elt,cmp) \ 974 | CDL_SEARCH2(head,out,elt,cmp,next) 975 | 976 | #define CDL_SEARCH2(head,out,elt,cmp,next) \ 977 | do { \ 978 | CDL_FOREACH2(head,out,next) { \ 979 | if ((cmp(out,elt))==0) break; \ 980 | } \ 981 | } while (0) 982 | 983 | #define CDL_REPLACE_ELEM2(head, el, add, prev, next) \ 984 | do { \ 985 | assert((head) != NULL); \ 986 | assert((el) != NULL); \ 987 | assert((add) != NULL); \ 988 | if ((el)->next == (el)) { \ 989 | (add)->next = (add); \ 990 | (add)->prev = (add); \ 991 | (head) = (add); \ 992 | } else { \ 993 | (add)->next = (el)->next; \ 994 | (add)->prev = (el)->prev; \ 995 | (add)->next->prev = (add); \ 996 | (add)->prev->next = (add); \ 997 | if ((head) == (el)) { \ 998 | (head) = (add); \ 999 | } \ 1000 | } \ 1001 | } while (0) 1002 | 1003 | #define CDL_REPLACE_ELEM(head, el, add) \ 1004 | CDL_REPLACE_ELEM2(head, el, add, prev, next) 1005 | 1006 | #define CDL_PREPEND_ELEM2(head, el, add, prev, next) \ 1007 | do { \ 1008 | if (el) { \ 1009 | assert((head) != NULL); \ 1010 | assert((add) != NULL); \ 1011 | (add)->next = (el); \ 1012 | (add)->prev = (el)->prev; \ 1013 | (el)->prev = (add); \ 1014 | (add)->prev->next = (add); \ 1015 | if ((head) == (el)) { \ 1016 | (head) = (add); \ 1017 | } \ 1018 | } else { \ 1019 | CDL_APPEND2(head, add, prev, next); \ 1020 | } \ 1021 | } while (0) 1022 | 1023 | #define CDL_PREPEND_ELEM(head, el, add) \ 1024 | CDL_PREPEND_ELEM2(head, el, add, prev, next) 1025 | 1026 | #define CDL_APPEND_ELEM2(head, el, add, prev, next) \ 1027 | do { \ 1028 | if (el) { \ 1029 | assert((head) != NULL); \ 1030 | assert((add) != NULL); \ 1031 | (add)->next = (el)->next; \ 1032 | (add)->prev = (el); \ 1033 | (el)->next = (add); \ 1034 | (add)->next->prev = (add); \ 1035 | } else { \ 1036 | CDL_PREPEND2(head, add, prev, next); \ 1037 | } \ 1038 | } while (0) 1039 | 1040 | #define CDL_APPEND_ELEM(head, el, add) \ 1041 | CDL_APPEND_ELEM2(head, el, add, prev, next) 1042 | 1043 | #ifdef NO_DECLTYPE 1044 | /* Here are VS2008 / NO_DECLTYPE replacements for a few functions */ 1045 | 1046 | #undef CDL_INSERT_INORDER2 1047 | #define CDL_INSERT_INORDER2(head,add,cmp,prev,next) \ 1048 | do { \ 1049 | if ((head) == NULL) { \ 1050 | (add)->prev = (add); \ 1051 | (add)->next = (add); \ 1052 | (head) = (add); \ 1053 | } else if ((cmp(head, add)) >= 0) { \ 1054 | (add)->prev = (head)->prev; \ 1055 | (add)->next = (head); \ 1056 | (add)->prev->next = (add); \ 1057 | (head)->prev = (add); \ 1058 | (head) = (add); \ 1059 | } else { \ 1060 | char *_tmp = (char*)(head); \ 1061 | while ((char*)(head)->next != _tmp && (cmp((head)->next, add)) < 0) { \ 1062 | (head) = (head)->next; \ 1063 | } \ 1064 | (add)->prev = (head); \ 1065 | (add)->next = (head)->next; \ 1066 | (add)->next->prev = (add); \ 1067 | (head)->next = (add); \ 1068 | UTLIST_RS(head); \ 1069 | } \ 1070 | } while (0) 1071 | #endif /* NO_DECLTYPE */ 1072 | 1073 | #endif /* UTLIST_H */ 1074 | --------------------------------------------------------------------------------