├── CMakeLists.txt ├── README.md └── src ├── CMakeLists.txt ├── alloc.h ├── arp.c ├── arp.h ├── clientmgr.c ├── clientmgr.h ├── config.c ├── config.h ├── error.h ├── genl.c ├── genl.h ├── hash.h ├── icmp6.c ├── icmp6.h ├── intercom.c ├── intercom.h ├── ipmgr.c ├── ipmgr.h ├── l3roamd.h ├── main.c ├── routemgr.c ├── routemgr.h ├── taskqueue.c ├── taskqueue.h ├── timespec.c ├── timespec.h ├── vector.c ├── vector.h ├── wifistations.c └── wifistations.h /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8.3) 2 | project(L3ROAMD C) 3 | 4 | INCLUDE(FindPkgConfig) 5 | IF(NOT PKG_CONFIG_FOUND) 6 | MESSAGE(FATAL_ERROR "Error: pkg-config not found on this system") 7 | ENDIF(NOT PKG_CONFIG_FOUND) 8 | 9 | MESSAGE(STATUS "") 10 | MESSAGE(STATUS "Configuring libnl ...") 11 | 12 | PKG_SEARCH_MODULE(LIBNL-TINY libnl-tiny) 13 | IF(LIBNL-TINY_FOUND) 14 | INCLUDE_DIRECTORIES(${LIBNL-TINY_INCLUDE_DIRS}) 15 | LINK_DIRECTORIES(${LIBNL-TINY_LIBRARY_DIRS}) 16 | SET(LIBNL_LIBRARIES ${LIBNL-TINY_LIBRARIES}) 17 | ELSE(LIBNL-TINY_FOUND) 18 | PKG_SEARCH_MODULE(LIBNL libnl>=2.0 libnl-2.0 libnl-2) 19 | IF(LIBNL_FOUND) 20 | INCLUDE_DIRECTORIES(${LIBNL_INCLUDE_DIRS}) 21 | LINK_DIRECTORIES(${LIBNL_LIB_DIRS}) 22 | LIST(APPEND LIBNL_LIBRARIES 23 | nl-genl 24 | ) 25 | ELSE(LIBNL_FOUND) 26 | PKG_SEARCH_MODULE(LIBNL3 libnl-3 libnl-3.0 libnl-3.1) 27 | IF(LIBNL3_FOUND) 28 | INCLUDE_DIRECTORIES(${LIBNL3_INCLUDE_DIRS}) 29 | LINK_DIRECTORIES(${LIBNL3_LIB_DIRS}) 30 | SET(LIBNL_LIBRARIES ${LIBNL3_LIBRARIES}) 31 | 32 | ############################################################# 33 | MESSAGE(STATUS "") 34 | MESSAGE(STATUS "Configuring libnl-genl ...") 35 | 36 | PKG_SEARCH_MODULE(LIBNL_GENL libnl-genl>=2.0 libnl-genl-3 libnl-genl-3.0 libnl-genl-3.1) 37 | IF(LIBNL_GENL_FOUND) 38 | INCLUDE_DIRECTORIES(${LIBNL_GENL_INCLUDE_DIRS}) 39 | LINK_DIRECTORIES(${LIBNL_GENL_LIB_DIRS}) 40 | ENDIF(LIBNL_GENL_FOUND) 41 | ELSE(LIBNL3_FOUND) 42 | MESSAGE(FATAL_ERROR "Error: libnl and libnl-genl not found") 43 | ENDIF(LIBNL3_FOUND) 44 | ENDIF(LIBNL_FOUND) 45 | ENDIF(LIBNL-TINY_FOUND) 46 | 47 | set(CMAKE_MODULE_PATH ${L3ROAMD_SOURCE_DIR}) 48 | 49 | add_subdirectory(src) 50 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # l3roamd 2 | 3 | l3roamd, pronunced *ɛl θriː ɹoʊm diː*, is supposed to become a core part of future layer 3 mesh networks. 4 | At first it will be built to work with [babeld](https://github.com/jech/babeld). 5 | 6 | l3roamd will be doing quite a few things: 7 | 8 | - manage a lot of host routes 9 | - integrate tightly with the mac80211 layer for monitor the presence of clients 10 | - manage a distributed database in tandem with all l3roamd nodes within the mesh [DB] 11 | - manage distribution of prefixes across the mesh (for router advertisements) [RA] 12 | - proxy neighbour discovery across the mesh 13 | - monitor babeld for duplicate host routes that this node also announces 14 | 15 | Ideally, I'd like to split this up into multiple daemons communicating using standardized protocols: 16 | 17 | ## [DB] Distributed Database 18 | 19 | The database could become its own daemon. It's basically a key-value database that can answer a single question: 20 | 21 | *Does anyone know a client with this $MAC and if so, what IPs did it use and who has served this client recently?* 22 | 23 | This information does not need to be present on all nodes (though that's probably the naïve approach we'll take first). 24 | It just needs to be reasonable certain that a node noticing a client previously unknown to it can figure out whether 25 | that client has been known by some other node within the last, say, 15 minutes or so. 26 | In fact, it may suffice to share a client's information with just the nodes that are sending data to the client. 27 | This timeout depends primarily on the lifecycle of host routes within l3roamd. 28 | 29 | The most important outcome of querying the database is actually: 30 | Notify any node that thinks it serves client to let go of it and instruct it to release all host routes. 31 | Taking over those routes is a welcome side effect as it improves roaming. 32 | In the worst case neighbour discovery will be able to re-establish these routes, though. 33 | 34 | ### Data stored about clients 35 | 36 | - MAC 37 | - a set of nodes that have served this client before 38 | - a set of IPs 39 | - (optional) a set of multicast groups 40 | 41 | This dataset needs some timeouts and so on. 42 | 43 | ### Set of routeable client prefixes 44 | 45 | A secondary usage of the database will probably be managing a set of prefixes used within the mesh. 46 | There will be some set of prefixes clients may use. 47 | This set may change at runtime depending on which prefixes may be used within the network. 48 | l3roamd needs to track these. 49 | 50 | ## [RA] Router Advertisements 51 | 52 | Any node should be able to announce a prefix (a /64) to be used by clients. 53 | This must be announced both within l3roamd and as a default route with a source prefix (set to the announced prefix!) 54 | through babeld. 55 | A metric (e.g. uplink bandwidth, reliability, ...) should be included, too. 56 | Nodes should announce a small subset of prefixes from nearby uplinks (actually, metric based) to clients via normal 57 | router advertisements. 58 | Lifetime of these prefixes should be managed such that clients always use the best uplink available. 59 | This is where early loadbalancing can reasonably take place. 60 | Clients are expected to cope with changing prefixes. 61 | Clients are also expected to hold onto deprecated prefixes as long as active connections require it. 62 | Routing for all, even deprecated, prefixes will be maintained as long as reasonably possible to avoid breaking a clients (TCP) connection. 63 | Multiple default routes for prefixes may be common (think multi homed AS), in this case loadbalancing is delegated to babeld. 64 | This means, that multiple nodes will announce the same set of prefixes with possibly different metrics. 65 | l3roamd will manages the prefixes it announces to a client on a per-client basis, if possibly. 66 | I.e. it will actively deprecate prefixes of clients it deems unreliably. 67 | This is likely to happen during roaming longer distances when a completely different set of uplinks should be used. 68 | As stated before, this will not break active connections. 69 | 70 | ## Managing clients 71 | 72 | l3roamd will directly monitor a set of wireless interfaces for clients. 73 | On the mac80211 layer it will monitor clients and act whenever a new client appears (query the database or create an entry) 74 | or when a client disappears (it should use its own timeout instead relying on the mac80211 internal timeout). 75 | In case of a disappearing client a node should remove all host routes for that client but 76 | not yet forget about them completely (in case the client re-appears). 77 | The routes presence in the routing table is controlled by the presence of the client (subject to some timeout). 78 | The routes presence in the database is subject to the timeout of the IPs lifetime (as was announced by the RA). 79 | Getting these mechanics right is crucial for sane roaming behaviour during bad network conditions. 80 | In worst case two nodes may have to switch routes fast and repeatably due to a client having bad connectivity 81 | to either node. 82 | These nodes may not be connected directly. 83 | 84 | ## The host route lifecycle 85 | 86 | Host routes need to have some kind of timeout. 87 | This directly correlates with management traffic overhead. 88 | It also affects the worst case amount of time a client will be unreachable in case of severe network conditions. 89 | 90 | ## IPv4? 91 | 92 | Well, not really. This is IPv6 only. 93 | We may, however, once everything works nicely, define a way for mapping IPv4 prefixes within IPv6 and rely on 94 | some other translation mechanism (SIIT, NIIT, whatever) to carry the payload. 95 | We may also extend the neighbour discovery proxy code to work with ARP. 96 | Ideally, this will be a seperate daemon. 97 | 98 | ## Improvements welcome! 99 | 100 | If you can improve this specifications (typos, better wording, restructering, ...) or even new important aspects, feel free to open 101 | a pull request. Please prefix your commits with "README: $my message" and try to summarize the changes in the commit 102 | message even if the commit message turns out to be longer than the change. Say, if you change a singel word, write a message like 103 | 104 | README: corrected singel to single 105 | 106 | This corrects a typo in the "Improvements welcome!" section. 107 | 108 | This approach makes reviewing and reasoning about changes a lot easier. 109 | 110 | # Intercom Packets 111 | 112 | There are currently three packet types used by l3roamd: 113 | 114 | - SEEK, 115 | - QUERY and 116 | - INFO 117 | 118 | All packets can be sent either as unicast or multicast. 119 | 120 | ## Header 121 | 122 | 123 | 124 | ## SEEK 125 | 126 | This is a multicast packet. SEEK is used to establish a route to an IPv6 127 | address. The packet contains 128 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | ADD_DEFINITIONS(-D_GNU_SOURCE) 2 | 3 | list(APPEND CMAKE_REQUIRED_DEFINITIONS '-D_GNU_SOURCE') 4 | 5 | include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${L3ROAMD_BINARY_DIR} ${CMAKE_CURRENT_BINARY_DIR}) 6 | 7 | add_executable(l3roamd main.c config.c intercom.c arp.c ipmgr.c icmp6.c routemgr.c vector.c wifistations.c genl.c clientmgr.c taskqueue.c timespec.c) 8 | 9 | target_link_libraries(l3roamd ${LIBNL_LIBRARIES} ${LIBNL_GENL_LIBRARIES}) 10 | 11 | set_target_properties(l3roamd PROPERTIES COMPILE_FLAGS "-std=gnu11 -Wall" LINK_FLAGS "") 12 | 13 | install(TARGETS l3roamd RUNTIME DESTINATION bin) 14 | -------------------------------------------------------------------------------- /src/alloc.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012-2015, Matthias Schiffer 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 | 1. Redistributions of source code must retain the above copyright notice, 9 | this list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | */ 25 | 26 | /** 27 | \file 28 | 29 | \em memory allocation functions 30 | */ 31 | 32 | 33 | #pragma once 34 | 35 | #include "error.h" 36 | 37 | 38 | /** 39 | Allocates a block of uninitialized memory on the heap 40 | 41 | Terminates the process on failure. 42 | */ 43 | static inline void * l3roamd_alloc(size_t size) { 44 | void *ret = malloc(size); 45 | if (!ret) 46 | exit_errno("malloc"); 47 | 48 | return ret; 49 | } 50 | 51 | /** 52 | Allocates a block of uninitialized memory on the heap, aligned to 16 bytes 53 | 54 | Terminates the process on failure. 55 | */ 56 | static inline void * l3roamd_alloc_aligned(size_t size, size_t align) { 57 | void *ret; 58 | int err = posix_memalign(&ret, align, size); 59 | if (err) 60 | exit_error("posix_memalign: %s", strerror(err)); 61 | 62 | return ret; 63 | } 64 | 65 | /** 66 | Allocates a block of memory set to zero for an array on the heap 67 | 68 | Terminates the process on failure. 69 | */ 70 | static inline void * l3roamd_alloc0_array(size_t members, size_t size) { 71 | void *ret = calloc(members, size); 72 | if (!ret) 73 | exit_errno("calloc"); 74 | 75 | return ret; 76 | } 77 | 78 | /** 79 | Allocates a block of memory set to zero on the heap 80 | 81 | Terminates the process on failure. 82 | */ 83 | static inline void * l3roamd_alloc0(size_t size) { 84 | return l3roamd_alloc0_array(1, size); 85 | } 86 | 87 | /** 88 | Reallocates a block of memory on the heap 89 | 90 | Terminates the process on failure. 91 | */ 92 | static inline void * l3roamd_realloc(void *ptr, size_t size) { 93 | void *ret = realloc(ptr, size); 94 | if (!ret) 95 | exit_errno("realloc"); 96 | 97 | return ret; 98 | } 99 | 100 | 101 | /** Allocates a block of uninitialized memory in the size of a given type */ 102 | #define l3roamd_new(type) ((type *)l3roamd_alloc(sizeof(type))) 103 | 104 | /** Allocates a block of uninitialized memory in the size of a given type, aligned to 16 bytes */ 105 | #define l3roamd_new_aligned(type, align) ((type *)l3roamd_alloc_aligned(sizeof(type), align)) 106 | 107 | /** Allocates a block of memory set to zero in the size of a given type */ 108 | #define l3roamd_new0(type) ((type *)l3roamd_alloc0(sizeof(type))) 109 | 110 | /** Allocates a block of undefined memory for an array of elements of a given type */ 111 | #define l3roamd_new_array(members, type) ((type *)l3roamd_alloc(members * sizeof(type))) 112 | 113 | /** Allocates a block of memory set to zero for an array of elements of a given type */ 114 | #define l3roamd_new0_array(members, type) ((type *)l3roamd_alloc0_array(members, sizeof(type))) 115 | 116 | 117 | /** Duplicates a string (string may be NULL) */ 118 | static inline char * l3roamd_strdup(const char *s) { 119 | if (!s) 120 | return NULL; 121 | 122 | char *ret = strdup(s); 123 | if (!ret) 124 | exit_errno("strdup"); 125 | 126 | return ret; 127 | } 128 | 129 | /** Duplicates a string up to a maximum length (string may be NULL) */ 130 | static inline char * l3roamd_strndup(const char *s, size_t n) { 131 | if (!s) 132 | return NULL; 133 | 134 | char *ret = strndup(s, n); 135 | if (!ret) 136 | exit_errno("strndup"); 137 | 138 | return ret; 139 | } 140 | -------------------------------------------------------------------------------- /src/arp.c: -------------------------------------------------------------------------------- 1 | #include "arp.h" 2 | #include "l3roamd.h" 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | void arp_handle_in(arp_ctx *ctx, int fd) { 11 | struct msghdr msghdr; 12 | memset (&msghdr, 0, sizeof (msghdr)); 13 | 14 | char cbuf[CMSG_SPACE (sizeof (int))]; 15 | 16 | struct arp_packet packet; 17 | 18 | struct iovec iov = { 19 | .iov_base = &packet, 20 | .iov_len = sizeof(packet) 21 | }; 22 | 23 | struct sockaddr_ll lladdr; 24 | 25 | struct msghdr hdr = { 26 | .msg_name = &lladdr, 27 | .msg_namelen = sizeof(lladdr), 28 | .msg_iov = &iov, 29 | .msg_iovlen = 1, 30 | .msg_control = cbuf, 31 | .msg_controllen = sizeof (cbuf) 32 | }; 33 | 34 | ssize_t rc = recvmsg(ctx->fd, &hdr, 0); 35 | 36 | if (rc == -1) 37 | return; 38 | 39 | if (packet.op != htons(ARP_REPLY)) 40 | return; 41 | 42 | uint8_t *mac = lladdr.sll_addr; 43 | 44 | struct in6_addr address = ctx->prefix; 45 | 46 | memcpy(&address.s6_addr[12], packet.spa, 4); 47 | 48 | char str[INET6_ADDRSTRLEN]; 49 | inet_ntop(AF_INET6, &address, str, INET6_ADDRSTRLEN); 50 | printf("ARP Response from %s (MAC %02x:%02x:%02x:%02x:%02x:%02x)\n", str, mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); 51 | 52 | clientmgr_add_address(CTX(clientmgr), &address, packet.sha, ctx->ifindex); 53 | } 54 | 55 | void arp_send_request(arp_ctx *ctx, const struct in6_addr *addr) { 56 | struct arp_packet packet = { 57 | .hd = htons(1), 58 | .pr = htons(0x800), 59 | .hdl = 6, 60 | .prl = 4, 61 | .op = htons(ARP_REQUEST) 62 | }; 63 | 64 | memcpy(&packet.sha, ctx->mac, 6); 65 | memcpy(&packet.spa, "\x00\x00\x00\x00", 4); 66 | memcpy(&packet.dha, "\xff\xff\xff\xff\xff\xff", 6); 67 | memcpy(&packet.dpa, &addr->s6_addr[12], 4); 68 | 69 | char str[INET6_ADDRSTRLEN]; 70 | inet_ntop(AF_INET6, &addr->s6_addr, str, sizeof str); 71 | printf("Send ARP to %s\n", str); 72 | 73 | struct sockaddr_ll dst = { 74 | .sll_ifindex = ctx->ifindex, 75 | .sll_protocol = htons(ETH_P_ARP), 76 | .sll_family = PF_PACKET, 77 | .sll_halen = 6, 78 | }; 79 | 80 | memcpy(&dst.sll_addr, "\xff\xff\xff\xff\xff\xff", 6); 81 | 82 | sendto(ctx->fd, &packet, sizeof(packet), 0, (struct sockaddr *)&dst, sizeof(dst)); 83 | } 84 | 85 | void arp_init(arp_ctx *ctx) { 86 | ctx->fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_ARP)); 87 | if (ctx->fd < 0) 88 | perror("Can't create socket(PF_PACKET)"); 89 | 90 | arp_setup_interface(ctx); 91 | }; 92 | 93 | 94 | void arp_setup_interface(arp_ctx *ctx) { 95 | ctx->ok = false; 96 | 97 | struct ifreq req = {}; 98 | strncpy(req.ifr_name, ctx->clientif, IFNAMSIZ-1); 99 | ioctl(ctx->fd, SIOCGIFHWADDR, &req); 100 | memcpy(ctx->mac, req.ifr_hwaddr.sa_data, 6); 101 | 102 | strncpy(req.ifr_name, ctx->clientif, IFNAMSIZ-1); 103 | ioctl(ctx->fd, SIOCGIFINDEX, &req); 104 | 105 | struct sockaddr_ll lladdr = { 106 | .sll_family = PF_PACKET, 107 | .sll_protocol = htons(ETH_P_ARP), 108 | .sll_ifindex = req.ifr_ifindex, 109 | .sll_hatype = 0, 110 | .sll_pkttype = PACKET_BROADCAST, 111 | .sll_halen = ETH_ALEN, 112 | }; 113 | 114 | bind(ctx->fd, (struct sockaddr *)&lladdr, sizeof(lladdr)); 115 | 116 | ctx->ifindex = req.ifr_ifindex; 117 | 118 | ctx->ok = true; 119 | } 120 | 121 | void arp_interface_changed(arp_ctx *ctx, int type, const struct ifinfomsg *msg) { 122 | char ifname[IFNAMSIZ]; 123 | 124 | if (if_indextoname(msg->ifi_index, ifname) == NULL) 125 | return; 126 | 127 | if (strcmp(ifname, ctx->clientif) != 0) 128 | return; 129 | 130 | printf("arp interface change detected\n"); 131 | 132 | ctx->ifindex = msg->ifi_index; 133 | 134 | switch (type) { 135 | case RTM_NEWLINK: 136 | case RTM_SETLINK: 137 | arp_setup_interface(ctx); 138 | break; 139 | 140 | case RTM_DELLINK: 141 | ctx->ok = false; 142 | break; 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /src/arp.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #define ARP_REQUEST 1 9 | #define ARP_REPLY 2 10 | 11 | struct __attribute__((packed)) arp_packet { 12 | uint16_t hd; 13 | uint16_t pr; 14 | uint8_t hdl; 15 | uint8_t prl; 16 | uint16_t op; 17 | uint8_t sha[6]; 18 | uint8_t spa[4]; 19 | uint8_t dha[6]; 20 | uint8_t dpa[4]; 21 | }; 22 | 23 | typedef struct { 24 | struct l3ctx *l3ctx; 25 | int fd; 26 | bool ok; 27 | struct in6_addr prefix; 28 | uint8_t mac[6]; 29 | const char *clientif; 30 | unsigned int ifindex; 31 | } arp_ctx; 32 | 33 | void arp_handle_in(arp_ctx *ctx, int fd); 34 | void arp_send_request(arp_ctx *ctx, const struct in6_addr *addr); 35 | void arp_init(arp_ctx *ctx); 36 | void arp_interface_changed(arp_ctx *ctx, int type, const struct ifinfomsg *msg); 37 | void arp_setup_interface(arp_ctx *ctx); 38 | -------------------------------------------------------------------------------- /src/clientmgr.c: -------------------------------------------------------------------------------- 1 | #include "clientmgr.h" 2 | #include "routemgr.h" 3 | #include "icmp6.h" 4 | #include "timespec.h" 5 | #include "error.h" 6 | #include "l3roamd.h" 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | /* Static functions used only in this file. */ 13 | static bool client_is_active(const struct client *client); 14 | static void checkclient_task(void *d); 15 | static void checkclient(clientmgr_ctx *ctx, uint8_t mac[6]); 16 | static const char *state_str(enum ip_state state); 17 | 18 | struct in6_addr node_client_ip_from_mac(uint8_t mac[6]) { 19 | struct in6_addr address = {}; 20 | inet_pton(AF_INET6, NODE_CLIENT_PREFIX, &address); 21 | 22 | address.s6_addr[8] = mac[0] ^ 0x02; 23 | address.s6_addr[9] = mac[1]; 24 | address.s6_addr[10] = mac[2]; 25 | address.s6_addr[11] = 0xff; 26 | address.s6_addr[12] = 0xfe; 27 | address.s6_addr[13] = mac[3]; 28 | address.s6_addr[14] = mac[4]; 29 | address.s6_addr[15] = mac[5]; 30 | 31 | return address; 32 | } 33 | 34 | bool prefix_contains(const struct prefix *prefix, struct in6_addr *addr) { 35 | int plen = prefix->plen; 36 | 37 | for (int i = 0; i < 16; i++) { 38 | int mask = ~((1<<(8 - (plen > 8 ? 8 : plen))) - 1); 39 | 40 | if ((addr->s6_addr[i] & mask) != prefix->prefix.s6_addr[i]) 41 | return false; 42 | 43 | plen -= 8; 44 | 45 | if (plen < 0) 46 | plen = 0; 47 | } 48 | return true; 49 | } 50 | 51 | void print_client(struct client *client) { 52 | char ifname[IFNAMSIZ]; 53 | 54 | printf("Client %02x:%02x:%02x:%02x:%02x:%02x", client->mac[0], client->mac[1], 55 | client->mac[2], client->mac[3], 56 | client->mac[4], client->mac[5]); 57 | 58 | if (client_is_active(client)) 59 | printf(" (active"); 60 | else 61 | printf(" (______"); 62 | 63 | if (client->ifindex != 0) { 64 | if_indextoname(client->ifindex, ifname); 65 | printf(", %s/%i)\n", ifname, client->ifindex); 66 | } else { 67 | printf(")\n"); 68 | } 69 | 70 | for (int i = 0; i < VECTOR_LEN(client->addresses); i++) { 71 | struct client_ip *e = &VECTOR_INDEX(client->addresses, i); 72 | 73 | char str[INET6_ADDRSTRLEN]; 74 | inet_ntop(AF_INET6, &e->address, str, INET6_ADDRSTRLEN); 75 | 76 | switch (e->state) { 77 | case IP_INACTIVE: 78 | printf(" %s %s\n", state_str(e->state), str); 79 | break; 80 | case IP_ACTIVE: 81 | printf(" %s %s (%ld.%.9ld)\n", state_str(e->state), str, e->timestamp.tv_sec, e->timestamp.tv_nsec); 82 | break; 83 | case IP_TENTATIVE: 84 | printf(" %s %s (tries left: %d)\n", state_str(e->state), str, e->tentative_cnt); 85 | break; 86 | default: 87 | exit_error("Invalid IP state"); 88 | } 89 | } 90 | } 91 | 92 | /** Check whether a client is currently active. 93 | A client is considered active when at least one of its IP addresses is 94 | currently active or tentative. 95 | */ 96 | bool client_is_active(const struct client *client) { 97 | struct timespec now; 98 | clock_gettime(CLOCK_MONOTONIC, &now); 99 | 100 | if (timespec_cmp(client->timeout, now) > 0) 101 | return true; 102 | 103 | for (int i = 0; i < VECTOR_LEN(client->addresses); i++) { 104 | struct client_ip *ip = &VECTOR_INDEX(client->addresses, i); 105 | 106 | if (ip->state == IP_ACTIVE || ip->state == IP_TENTATIVE) 107 | return true; 108 | } 109 | 110 | return false; 111 | } 112 | 113 | /** Adds the special node client IP address. 114 | */ 115 | void add_special_ip(clientmgr_ctx *ctx, struct client *client) { 116 | struct in6_addr address = node_client_ip_from_mac(client->mac); 117 | printf("Adding special address\n"); 118 | rtnl_add_address(CTX(routemgr), &address); 119 | } 120 | 121 | /** Removes the special node client IP address. 122 | */ 123 | void remove_special_ip(clientmgr_ctx *ctx, struct client *client) { 124 | struct in6_addr address = node_client_ip_from_mac(client->mac); 125 | printf("Removing special address\n"); 126 | rtnl_remove_address(CTX(routemgr), &address); 127 | } 128 | 129 | /** Given an IP address returns the IP object of a client. 130 | Returns NULL if no object is found. 131 | */ 132 | struct client_ip *get_client_ip(struct client *client, const struct in6_addr *address) { 133 | for (int i = 0; i < VECTOR_LEN(client->addresses); i++) { 134 | struct client_ip *e = &VECTOR_INDEX(client->addresses, i); 135 | 136 | if (memcmp(address, &e->address, sizeof(struct in6_addr)) == 0) 137 | return e; 138 | } 139 | 140 | return NULL; 141 | } 142 | 143 | /** Removes an IP address from a client. Safe to call if the IP is not 144 | currently present in the clients list. 145 | */ 146 | void delete_client_ip(struct client *client, const struct in6_addr *address) { 147 | for (int i = 0; i < VECTOR_LEN(client->addresses); i++) { 148 | struct client_ip *e = &VECTOR_INDEX(client->addresses, i); 149 | 150 | if (memcmp(address, &e->address, sizeof(struct in6_addr)) == 0) { 151 | VECTOR_DELETE(client->addresses, i); 152 | break; 153 | } 154 | } 155 | 156 | printf("\x1b[31mDeleting\x1b[0m "); 157 | print_client(client); 158 | } 159 | 160 | /** Adds a route. 161 | */ 162 | void client_add_route(clientmgr_ctx *ctx, struct client *client, struct client_ip *ip) { 163 | if (clientmgr_is_ipv4(ctx, &ip->address)) { 164 | struct in_addr ip4 = { 165 | .s_addr = ip->address.s6_addr[12] << 24 | ip->address.s6_addr[13] << 16 | ip->address.s6_addr[14] << 8 | ip->address.s6_addr[15] 166 | }; 167 | 168 | routemgr_insert_route(CTX(routemgr), ctx->export_table, ctx->nat46ifindex, &ip->address); 169 | routemgr_insert_route4(CTX(routemgr), ctx->export_table, client->ifindex, &ip4); 170 | routemgr_insert_neighbor4(CTX(routemgr), client->ifindex, &ip4, client->mac); 171 | } else { 172 | routemgr_insert_route(CTX(routemgr), ctx->export_table, client->ifindex, &ip->address); 173 | routemgr_insert_neighbor(CTX(routemgr), client->ifindex, &ip->address, client->mac); 174 | } 175 | } 176 | 177 | /** Remove a route. 178 | */ 179 | void client_remove_route(clientmgr_ctx *ctx, struct client *client, struct client_ip *ip) { 180 | if (clientmgr_is_ipv4(ctx, &ip->address)) { 181 | struct in_addr ip4 = { 182 | .s_addr = ip->address.s6_addr[12] << 24 | ip->address.s6_addr[13] << 16 | ip->address.s6_addr[14] << 8 | ip->address.s6_addr[15] 183 | }; 184 | 185 | routemgr_remove_route(CTX(routemgr), ctx->export_table, &ip->address); 186 | routemgr_remove_route4(CTX(routemgr), ctx->export_table, &ip4); 187 | routemgr_remove_neighbor4(CTX(routemgr), client->ifindex, &ip4, client->mac); 188 | } else { 189 | routemgr_remove_route(CTX(routemgr), ctx->export_table, &ip->address); 190 | routemgr_remove_neighbor(CTX(routemgr), client->ifindex, &ip->address, client->mac); 191 | } 192 | } 193 | 194 | /** Given a MAC address returns a client object. 195 | Returns NULL if the client is not known. 196 | */ 197 | struct client *get_client(clientmgr_ctx *ctx, const uint8_t mac[6]) { 198 | for (int i = 0; i < VECTOR_LEN(ctx->clients); i++) { 199 | struct client *e = &VECTOR_INDEX(ctx->clients, i); 200 | 201 | if (memcmp(mac, e->mac, sizeof(uint8_t) * 6) == 0) 202 | return e; 203 | } 204 | 205 | return NULL; 206 | } 207 | 208 | /** Get a client or create a new, empty one. 209 | */ 210 | struct client *get_or_create_client(clientmgr_ctx *ctx, const uint8_t mac[6]) { 211 | struct client *client = get_client(ctx, mac); 212 | 213 | if (client == NULL) { 214 | struct client _client = {}; 215 | memcpy(_client.mac, mac, sizeof(uint8_t) * 6); 216 | VECTOR_ADD(ctx->clients, _client); 217 | client = &VECTOR_INDEX(ctx->clients, VECTOR_LEN(ctx->clients) - 1); 218 | } 219 | 220 | return client; 221 | } 222 | 223 | /** Given a MAC address deletes a client. Safe to call if the client is not 224 | known. 225 | */ 226 | void delete_client(clientmgr_ctx *ctx, const uint8_t mac[6]) { 227 | // TODO free addresses vector here? 228 | for (int i = 0; i < VECTOR_LEN(ctx->clients); i++) { 229 | struct client *client = &VECTOR_INDEX(ctx->clients, i); 230 | 231 | if (memcmp(mac, client->mac, sizeof(uint8_t) * 6) == 0) { 232 | VECTOR_FREE(client->addresses); 233 | VECTOR_DELETE(ctx->clients, i); 234 | break; 235 | } 236 | } 237 | } 238 | 239 | const char *state_str(enum ip_state state) { 240 | switch (state) { 241 | case IP_INACTIVE: 242 | return "\x1b[31mINACTIVE\x1b[0m"; 243 | case IP_ACTIVE: 244 | return "\x1b[32mACTIVE\x1b[0m"; 245 | case IP_TENTATIVE: 246 | return "\x1b[33mTENTATIVE\x1b[0m"; 247 | default: 248 | return "\x1b[35m(INVALID)\x1b[0m"; 249 | } 250 | } 251 | 252 | /** Change state of an IP address. Trigger all side effects like resetting 253 | counters, timestamps and route changes. 254 | */ 255 | void client_ip_set_state(clientmgr_ctx *ctx, struct client *client, struct client_ip *ip, enum ip_state state) { 256 | struct timespec now; 257 | clock_gettime(CLOCK_MONOTONIC, &now); 258 | 259 | switch (ip->state) { 260 | case IP_INACTIVE: 261 | switch (state) { 262 | case IP_INACTIVE: 263 | // ignore 264 | break; 265 | case IP_ACTIVE: 266 | client_add_route(ctx, client, ip); 267 | ip->timestamp = now; 268 | break; 269 | case IP_TENTATIVE: 270 | ip->timestamp = now; 271 | ip->tentative_cnt = TENTATIVE_TRIES; 272 | break; 273 | } 274 | break; 275 | case IP_ACTIVE: 276 | switch (state) { 277 | case IP_INACTIVE: 278 | ip->timestamp = now; 279 | client_remove_route(ctx, client, ip); 280 | break; 281 | case IP_ACTIVE: 282 | ip->timestamp = now; 283 | // TODO update route 284 | break; 285 | case IP_TENTATIVE: 286 | ip->timestamp = now; 287 | ip->tentative_cnt = TENTATIVE_TRIES; 288 | client_remove_route(ctx, client, ip); 289 | break; 290 | } 291 | break; 292 | case IP_TENTATIVE: 293 | switch (state) { 294 | case IP_INACTIVE: 295 | ip->timestamp = now; 296 | break; 297 | case IP_ACTIVE: 298 | ip->timestamp = now; 299 | client_add_route(ctx, client, ip); 300 | break; 301 | case IP_TENTATIVE: 302 | ip->timestamp = now; 303 | ip->tentative_cnt = TENTATIVE_TRIES; 304 | break; 305 | } 306 | break; 307 | } 308 | 309 | char ip_str[INET6_ADDRSTRLEN]; 310 | inet_ntop(AF_INET6, &ip->address, ip_str, INET6_ADDRSTRLEN); 311 | 312 | printf("%s changes from %s to %s\n", ip_str, state_str(ip->state), 313 | state_str(state)); 314 | 315 | ip->state = state; 316 | } 317 | 318 | /** Schedule a client check. Set timeout to 0 in order to check the client at 319 | the next iteration. 320 | */ 321 | void schedule_clientcheck(clientmgr_ctx *ctx, struct client *client, unsigned int timeout) { 322 | struct client_task *data = calloc(1, sizeof(struct client_task)); 323 | 324 | data->ctx = ctx; 325 | memcpy(data->mac, client->mac, 6); 326 | 327 | if (!reschedule_task(CTX(taskqueue), client->check_task, timeout)) 328 | client->check_task = post_task(CTX(taskqueue), timeout, checkclient_task, free, data); 329 | } 330 | 331 | /** Wrapper for checkclient to be used in post_task. 332 | */ 333 | void checkclient_task(void *d) { 334 | struct client_task *data = d; 335 | checkclient(data->ctx, data->mac); 336 | } 337 | 338 | /** Check a client. 339 | */ 340 | void checkclient(clientmgr_ctx *ctx, uint8_t mac[6]) { 341 | struct client *client = get_client(ctx, mac); 342 | 343 | // The client may have vanished. 344 | if (client == NULL) 345 | return; 346 | 347 | printf("Checking "); 348 | print_client(client); 349 | 350 | struct timespec now; 351 | clock_gettime(CLOCK_MONOTONIC, &now); 352 | 353 | struct timespec na_timeout = now; 354 | struct timespec client_timeout = now; 355 | 356 | na_timeout.tv_sec -= NA_TIMEOUT; 357 | client_timeout.tv_sec -= CLIENT_TIMEOUT; 358 | 359 | for (int i = 0; i < VECTOR_LEN(client->addresses); i++) { 360 | struct client_ip *ip = &VECTOR_INDEX(client->addresses, i); 361 | 362 | switch (ip->state) { 363 | case IP_ACTIVE: 364 | if (timespec_cmp(ip->timestamp, na_timeout) <= 0) 365 | client_ip_set_state(ctx, client, ip, IP_INACTIVE); 366 | 367 | if (clientmgr_is_ipv4(ctx, &ip->address)) 368 | arp_send_request(CTX(arp), &ip->address); 369 | else 370 | icmp6_send_solicitation(CTX(icmp6), &ip->address); 371 | break; 372 | case IP_INACTIVE: 373 | if (timespec_cmp(ip->timestamp, client_timeout) <= 0) { 374 | VECTOR_DELETE(client->addresses, i); 375 | i--; 376 | } 377 | break; 378 | case IP_TENTATIVE: 379 | if (clientmgr_is_ipv4(ctx, &ip->address)) 380 | arp_send_request(CTX(arp), &ip->address); 381 | else 382 | icmp6_send_solicitation(CTX(icmp6), &ip->address); 383 | 384 | ip->tentative_cnt--; 385 | if (ip->tentative_cnt <= 0) 386 | client_ip_set_state(ctx, client, ip, IP_INACTIVE); 387 | break; 388 | } 389 | } 390 | 391 | // If the client has no IP addresses associated and has timed out (after a 392 | // roaming event), delete it. 393 | if (VECTOR_LEN(client->addresses) == 0 && timespec_cmp(client->timeout, now) <= 0) { 394 | delete_client(ctx, client->mac); 395 | return; 396 | } 397 | 398 | // TODO schedule at earliest IP timeout 399 | schedule_clientcheck(ctx, client, IP_CHECKCLIENT_TIMEOUT); 400 | } 401 | 402 | /** Check whether an IP address is contained in a client prefix. 403 | */ 404 | bool clientmgr_valid_address(clientmgr_ctx *ctx, struct in6_addr *address) { 405 | return prefix_contains(&ctx->prefix, address) | clientmgr_is_ipv4(ctx, address); 406 | } 407 | 408 | /** Check whether an IP address is contained in the IPv4 prefix. 409 | */ 410 | bool clientmgr_is_ipv4(clientmgr_ctx *ctx, struct in6_addr *address) { 411 | return prefix_contains(&ctx->v4prefix, address); 412 | } 413 | 414 | /** Add a new address to a client identified by its MAC. 415 | */ 416 | void clientmgr_add_address(clientmgr_ctx *ctx, struct in6_addr *address, uint8_t *mac, unsigned int ifindex) { 417 | // TODO sicherstellen, dass IPs nur jeweils einem Client zugeordnet sind 418 | 419 | char str[INET6_ADDRSTRLEN]; 420 | inet_ntop(AF_INET6, address, str, INET6_ADDRSTRLEN); 421 | printf("Add Address: %s (MAC %02x:%02x:%02x:%02x:%02x:%02x)\n", str, mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); 422 | 423 | if (!clientmgr_valid_address(ctx, address)) 424 | return; 425 | 426 | struct client *client = get_or_create_client(ctx, mac); 427 | struct client_ip *ip = get_client_ip(client, address); 428 | 429 | bool was_active = client_is_active(client); 430 | bool ip_is_new = ip == NULL; 431 | 432 | if (ip == NULL) { 433 | struct client_ip _ip = {}; 434 | memcpy(&_ip.address, address, sizeof(struct in6_addr)); 435 | VECTOR_ADD(client->addresses, _ip); 436 | ip = &VECTOR_INDEX(client->addresses, VECTOR_LEN(client->addresses) - 1); 437 | } 438 | 439 | client->ifindex = ifindex; 440 | 441 | client_ip_set_state(ctx, client, ip, IP_ACTIVE); 442 | 443 | if (!was_active) { 444 | struct in6_addr address = node_client_ip_from_mac(client->mac); 445 | if (!intercom_claim(CTX(intercom), &address, client)) 446 | add_special_ip(ctx, client); 447 | } 448 | 449 | // If the IP address is new, add to distributed database (neighbor's for now). 450 | if (ip_is_new) 451 | intercom_info(CTX(intercom), NULL, client, false); 452 | 453 | schedule_clientcheck(ctx, client, IP_CHECKCLIENT_TIMEOUT); 454 | } 455 | 456 | /** Notify the client manager about a new MAC (e.g. a new wifi client). 457 | */ 458 | void clientmgr_notify_mac(clientmgr_ctx *ctx, uint8_t *mac, unsigned int ifindex) { 459 | struct client *client = get_or_create_client(ctx, mac); 460 | 461 | char ifname[IFNAMSIZ]; 462 | if_indextoname(ifindex, ifname); 463 | 464 | printf("\033[34mnew client %02x:%02x:%02x:%02x:%02x:%02x on %s\033[0m\n", 465 | mac[0], mac[1], mac[2], mac[3], mac[4], mac[5], ifname); 466 | 467 | struct timespec now; 468 | clock_gettime(CLOCK_MONOTONIC, &now); 469 | 470 | client->timeout = now; 471 | client->timeout.tv_sec += CLIENT_TIMEOUT; 472 | client->ifindex = ifindex; 473 | 474 | struct in6_addr address = node_client_ip_from_mac(client->mac); 475 | if (!intercom_claim(CTX(intercom), &address, client)) { 476 | printf("Claim failed.\n"); 477 | add_special_ip(ctx, client); 478 | } 479 | 480 | // Mark all inactive IP addresses tentative and schedule a check to establish 481 | // whether the client is using them now. 482 | for (int i = 0; i < VECTOR_LEN(client->addresses); i++) { 483 | struct client_ip *ip = &VECTOR_INDEX(client->addresses, i); 484 | 485 | if (ip->state == IP_TENTATIVE || ip->state == IP_INACTIVE) 486 | client_ip_set_state(ctx, client, ip, IP_TENTATIVE); 487 | } 488 | 489 | schedule_clientcheck(ctx, client, 0); 490 | } 491 | 492 | /** Handle info request. 493 | */ 494 | void clientmgr_handle_claim(clientmgr_ctx *ctx, const struct in6_addr *sender, uint8_t mac[6]) { 495 | struct client *client = get_client(ctx, mac); 496 | 497 | if (client == NULL) 498 | return; 499 | 500 | bool active = client_is_active(client); 501 | 502 | if (active) 503 | remove_special_ip(ctx, client); 504 | 505 | intercom_info(CTX(intercom), sender, client, active); 506 | 507 | if (!client_is_active(client)) 508 | return; 509 | 510 | printf("Dropping client in response to claim\n"); 511 | 512 | for (int i = 0; i < VECTOR_LEN(client->addresses); i++) { 513 | struct client_ip *ip = &VECTOR_INDEX(client->addresses, i); 514 | 515 | if (ip->state == IP_ACTIVE || ip->state == IP_TENTATIVE) 516 | client_ip_set_state(ctx, client, ip, IP_TENTATIVE); 517 | } 518 | 519 | schedule_clientcheck(ctx, client, 0); 520 | } 521 | 522 | /** Handle incoming client info. 523 | */ 524 | void clientmgr_handle_info(clientmgr_ctx *ctx, struct client *foreign_client, bool relinquished) { 525 | struct client *client = get_client(ctx, foreign_client->mac); 526 | 527 | if (client == NULL || !client_is_active(client)) 528 | return; 529 | 530 | for (int i = 0; i < VECTOR_LEN(foreign_client->addresses); i++) { 531 | struct client_ip *foreign_ip = &VECTOR_INDEX(foreign_client->addresses, i); 532 | struct client_ip *ip = get_client_ip(client, &foreign_ip->address); 533 | 534 | // Skip if we know this IP address 535 | if (ip != NULL) 536 | continue; 537 | 538 | VECTOR_ADD(client->addresses, *foreign_ip); 539 | ip = &VECTOR_INDEX(client->addresses, VECTOR_LEN(client->addresses) - 1); 540 | 541 | client_ip_set_state(ctx, client, ip, IP_TENTATIVE); 542 | } 543 | 544 | if (relinquished) 545 | add_special_ip(ctx, client); 546 | 547 | printf("Merged "); 548 | print_client(client); 549 | 550 | schedule_clientcheck(ctx, client, 0); 551 | } 552 | -------------------------------------------------------------------------------- /src/clientmgr.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "vector.h" 4 | #include "taskqueue.h" 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #define IP_CHECKCLIENT_TIMEOUT 5 11 | #define NA_TIMEOUT 10 12 | #define CLIENT_TIMEOUT 300 13 | #define TENTATIVE_TRIES 5 14 | #define NODE_CLIENT_PREFIX "fec0::" 15 | 16 | enum ip_state { 17 | IP_INACTIVE = 0, 18 | IP_ACTIVE, 19 | IP_TENTATIVE 20 | }; 21 | 22 | struct prefix { 23 | struct in6_addr prefix; 24 | int plen; 25 | }; 26 | 27 | struct client_ip { 28 | enum ip_state state; 29 | int tentative_cnt; 30 | struct in6_addr address; 31 | struct timespec timestamp; 32 | }; 33 | 34 | struct client { 35 | unsigned int ifindex; 36 | taskqueue_t *check_task; 37 | struct timespec timeout; 38 | uint8_t mac[6]; 39 | VECTOR(struct client_ip) addresses; 40 | }; 41 | 42 | typedef struct { 43 | struct l3ctx *l3ctx; 44 | struct prefix prefix; 45 | struct prefix v4prefix; 46 | unsigned int export_table; 47 | int nat46ifindex; 48 | VECTOR(struct client) clients; 49 | } clientmgr_ctx; 50 | 51 | struct client_task { 52 | clientmgr_ctx *ctx; 53 | uint8_t mac[6]; 54 | }; 55 | 56 | bool clientmgr_valid_address(clientmgr_ctx *ctx, struct in6_addr *address); 57 | bool clientmgr_is_ipv4(clientmgr_ctx *ctx, struct in6_addr *address); 58 | void clientmgr_add_address(clientmgr_ctx *ctx, struct in6_addr *address, uint8_t *mac, unsigned int ifindex); 59 | void clientmgr_notify_mac(clientmgr_ctx *ctx, uint8_t *mac, unsigned int ifindex); 60 | void clientmgr_handle_claim(clientmgr_ctx *ctx, const struct in6_addr *sender, uint8_t mac[6]); 61 | void clientmgr_handle_info(clientmgr_ctx *ctx, struct client *foreign_client, bool relinquished); 62 | -------------------------------------------------------------------------------- /src/config.c: -------------------------------------------------------------------------------- 1 | #include "config.h" 2 | void parse_line(char *line) { 3 | char delimiter[] = " "; 4 | char *ptr; 5 | char *saveptr; 6 | ptr = strtok_r(line, delimiter, &saveptr); 7 | printf("key: %s\n", ptr); 8 | ptr = strtok_r(NULL, delimiter, &saveptr); 9 | printf("value: %s", ptr); 10 | 11 | // TODO: how to turn key and value into 12 | // parameters that can actually be used by 13 | // l3roamd? 14 | // 15 | // attach-mesh-interface 16 | // detach-mesh-interface 17 | // attach-client-interface 18 | // detach-client-interface 19 | // add-prefix 20 | // remove-prefix 21 | // set-export-table 22 | } 23 | 24 | bool parse_config(const char *filename) { 25 | FILE * fp; 26 | char * line = NULL; 27 | size_t len = 0; 28 | ssize_t read; 29 | 30 | fp = fopen(filename, "r"); 31 | if (fp == NULL) 32 | return false; 33 | 34 | while ((read = getline(&line, &len, fp)) != -1) 35 | parse_line(line); 36 | 37 | fclose(fp); 38 | free(line); 39 | 40 | return true; 41 | } 42 | -------------------------------------------------------------------------------- /src/config.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | void parse_line(char *line); 9 | bool parse_config(const char *filename); 10 | -------------------------------------------------------------------------------- /src/error.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015, Matthias Schiffer 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 | 1. Redistributions of source code must retain the above copyright notice, 9 | this list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | */ 25 | #pragma once 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | #define exit_errno(message) _exit_error(1, errno, "%s", message) 34 | 35 | static inline void _exit_error(int status, int errnum, const char *format, ...) { 36 | va_list ap; 37 | va_start(ap, format); 38 | vfprintf(stderr, format, ap); 39 | va_end(ap); 40 | 41 | if (errnum) 42 | fprintf(stderr, ": %s\n", strerror(errnum)); 43 | else 44 | fprintf(stderr, "\n"); 45 | 46 | exit(status); 47 | } 48 | 49 | static inline void exit_error(const char *format, ...) { 50 | va_list ap; 51 | va_start(ap, format); 52 | _exit_error(1, 0, format, ap); 53 | va_end(ap); 54 | } 55 | 56 | static inline void exit_bug(const char *format, ...) { 57 | va_list ap; 58 | va_start(ap, format); 59 | _exit_error(1, 0, format, ap); 60 | va_end(ap); 61 | } 62 | -------------------------------------------------------------------------------- /src/genl.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This ought to be provided by libnl 3 | */ 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "genl.h" 14 | 15 | static int error_handler(struct sockaddr_nl *nla, struct nlmsgerr *err, void *arg) { 16 | int *ret = arg; 17 | *ret = err->error; 18 | return NL_STOP; 19 | } 20 | 21 | static int ack_handler(struct nl_msg *msg, void *arg) { 22 | int *ret = arg; 23 | *ret = 0; 24 | return NL_STOP; 25 | } 26 | 27 | struct handler_args { 28 | const char *group; 29 | int id; 30 | }; 31 | 32 | static int family_handler(struct nl_msg *msg, void *arg) { 33 | struct handler_args *grp = arg; 34 | struct nlattr *tb[CTRL_ATTR_MAX + 1]; 35 | struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); 36 | struct nlattr *mcgrp; 37 | int rem_mcgrp; 38 | 39 | nla_parse(tb, CTRL_ATTR_MAX, genlmsg_attrdata(gnlh, 0), 40 | genlmsg_attrlen(gnlh, 0), NULL); 41 | 42 | if (!tb[CTRL_ATTR_MCAST_GROUPS]) 43 | return NL_SKIP; 44 | 45 | nla_for_each_nested(mcgrp, tb[CTRL_ATTR_MCAST_GROUPS], rem_mcgrp) { 46 | struct nlattr *tb_mcgrp[CTRL_ATTR_MCAST_GRP_MAX + 1]; 47 | 48 | nla_parse(tb_mcgrp, CTRL_ATTR_MCAST_GRP_MAX, 49 | nla_data(mcgrp), nla_len(mcgrp), NULL); 50 | 51 | if (!tb_mcgrp[CTRL_ATTR_MCAST_GRP_NAME] || 52 | !tb_mcgrp[CTRL_ATTR_MCAST_GRP_ID]) 53 | continue; 54 | if (strncmp(nla_data(tb_mcgrp[CTRL_ATTR_MCAST_GRP_NAME]), 55 | grp->group, nla_len(tb_mcgrp[CTRL_ATTR_MCAST_GRP_NAME]))) 56 | continue; 57 | grp->id = nla_get_u32(tb_mcgrp[CTRL_ATTR_MCAST_GRP_ID]); 58 | break; 59 | } 60 | 61 | return NL_SKIP; 62 | } 63 | 64 | int nl_get_multicast_id(struct nl_sock *sock, const char *family, const char *group) { 65 | struct nl_msg *msg; 66 | struct nl_cb *cb; 67 | int ret, ctrlid; 68 | struct handler_args grp = { 69 | .group = group, 70 | .id = -ENOENT, 71 | }; 72 | 73 | msg = nlmsg_alloc(); 74 | if (!msg) 75 | return -ENOMEM; 76 | 77 | cb = nl_cb_alloc(NL_CB_DEFAULT); 78 | if (!cb) { 79 | ret = -ENOMEM; 80 | goto out_fail_cb; 81 | } 82 | 83 | ctrlid = genl_ctrl_resolve(sock, "nlctrl"); 84 | 85 | genlmsg_put(msg, 0, 0, ctrlid, 0, 86 | 0, CTRL_CMD_GETFAMILY, 0); 87 | 88 | ret = -ENOBUFS; 89 | NLA_PUT_STRING(msg, CTRL_ATTR_FAMILY_NAME, family); 90 | 91 | ret = nl_send_auto_complete(sock, msg); 92 | if (ret < 0) 93 | goto out; 94 | 95 | ret = 1; 96 | 97 | nl_cb_err(cb, NL_CB_CUSTOM, error_handler, &ret); 98 | nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, ack_handler, &ret); 99 | nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, family_handler, &grp); 100 | 101 | while (ret > 0) 102 | nl_recvmsgs(sock, cb); 103 | 104 | if (ret == 0) 105 | ret = grp.id; 106 | nla_put_failure: 107 | out: 108 | nl_cb_put(cb); 109 | out_fail_cb: 110 | nlmsg_free(msg); 111 | return ret; 112 | } 113 | -------------------------------------------------------------------------------- /src/genl.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | int nl_get_multicast_id(struct nl_sock *sock, const char *family, const char *group); 6 | -------------------------------------------------------------------------------- /src/hash.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012-2015, Matthias Schiffer 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 | 1. Redistributions of source code must retain the above copyright notice, 9 | this list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | */ 25 | 26 | /** 27 | \file 28 | 29 | An implementation of the Jenkins hash function 30 | 31 | \sa https://en.wikipedia.org/wiki/Jenkins_hash_function 32 | */ 33 | 34 | 35 | #pragma once 36 | 37 | 38 | #include 39 | #include 40 | 41 | 42 | /** Adds data bytes to the 32bit hash value */ 43 | static inline void hash(uint32_t *hash, const void *data, size_t len) { 44 | size_t i; 45 | for (i = 0; i < len; ++i) { 46 | *hash += ((uint8_t *)data)[i]; 47 | *hash += (*hash << 10); 48 | *hash ^= (*hash >> 6); 49 | } 50 | } 51 | 52 | /** Finalizes a hash value */ 53 | static inline void hash_final(uint32_t *hash) { 54 | *hash += (*hash << 3); 55 | *hash ^= (*hash >> 11); 56 | *hash += (*hash << 15); 57 | } 58 | -------------------------------------------------------------------------------- /src/icmp6.c: -------------------------------------------------------------------------------- 1 | #include "icmp6.h" 2 | #include "l3roamd.h" 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | int icmp6_init_packet() { 15 | int sock, err; 16 | struct sock_fprog fprog; 17 | static const struct sock_filter filter[] = 18 | { 19 | BPF_STMT(BPF_LD|BPF_B|BPF_ABS, sizeof(struct ip6_hdr) + offsetof(struct icmp6_hdr, icmp6_type)), 20 | BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, ND_NEIGHBOR_SOLICIT, 1, 0), 21 | BPF_STMT(BPF_RET|BPF_K, 0), 22 | BPF_STMT(BPF_RET|BPF_K, 0xffffffff), 23 | }; 24 | 25 | fprog.filter = (struct sock_filter *)filter; 26 | fprog.len = sizeof filter / sizeof filter[0]; 27 | 28 | sock = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IPV6)); 29 | if (sock < 0) { 30 | perror("Can't create socket(PF_PACKET)"); 31 | } 32 | 33 | // Tie the BSD-PF filter to the socket 34 | err = setsockopt(sock, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog)); 35 | if (err < 0) { 36 | perror("setsockopt(SO_ATTACH_FILTER)"); 37 | } 38 | 39 | return sock; 40 | } 41 | 42 | static inline int setsockopt_int(int socket, int level, int option, int value) { 43 | return setsockopt(socket, level, option, &value, sizeof(value)); 44 | } 45 | 46 | void icmp6_init(icmp6_ctx *ctx) { 47 | int fd = socket(PF_INET6, SOCK_RAW | SOCK_NONBLOCK, IPPROTO_ICMPV6); 48 | setsockopt_int(fd, IPPROTO_RAW, IPV6_CHECKSUM, 2); 49 | setsockopt_int(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, 255); 50 | setsockopt_int(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, 1); 51 | setsockopt_int(fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, 1); 52 | 53 | struct icmp6_filter filter; 54 | ICMP6_FILTER_SETBLOCKALL(&filter); 55 | ICMP6_FILTER_SETPASS(ND_NEIGHBOR_ADVERT, &filter); 56 | setsockopt(fd, IPPROTO_ICMPV6, ICMP6_FILTER, &filter, sizeof(filter)); 57 | 58 | ctx->fd = fd; 59 | 60 | ctx->nsfd = icmp6_init_packet(); 61 | 62 | icmp6_setup_interface(ctx); 63 | } 64 | 65 | void icmp6_setup_interface(icmp6_ctx *ctx) { 66 | ctx->ok = false; 67 | 68 | int rc = setsockopt(ctx->fd, SOL_SOCKET, SO_BINDTODEVICE, ctx->clientif, strnlen(ctx->clientif, IFNAMSIZ-1)); 69 | 70 | printf("Setting up icmp6 interface: %i\n", rc); 71 | 72 | if (rc < 0) 73 | return; 74 | 75 | struct ifreq req = {}; 76 | strncpy(req.ifr_name, ctx->clientif, IFNAMSIZ-1); 77 | ioctl(ctx->fd, SIOCGIFHWADDR, &req); 78 | memcpy(ctx->mac, req.ifr_hwaddr.sa_data, 6); 79 | 80 | strncpy(req.ifr_name, ctx->clientif, IFNAMSIZ-1); 81 | ioctl(ctx->fd, SIOCGIFINDEX, &req); 82 | 83 | struct sockaddr_ll lladdr; 84 | 85 | // Bind the socket to the interface we're interested in 86 | memset(&lladdr, 0, sizeof(lladdr)); 87 | lladdr.sll_family = PF_PACKET; 88 | lladdr.sll_protocol = htons(ETH_P_IPV6); 89 | lladdr.sll_ifindex = req.ifr_ifindex; 90 | lladdr.sll_hatype = 0; 91 | lladdr.sll_pkttype = 0; 92 | lladdr.sll_halen = ETH_ALEN; 93 | bind(ctx->nsfd, (struct sockaddr *)&lladdr, sizeof(lladdr)); 94 | 95 | ctx->ifindex = req.ifr_ifindex; 96 | 97 | ctx->ok = true; 98 | } 99 | 100 | void icmp6_interface_changed(icmp6_ctx *ctx, int type, const struct ifinfomsg *msg) { 101 | char ifname[IFNAMSIZ]; 102 | 103 | if (if_indextoname(msg->ifi_index, ifname) == NULL) 104 | return; 105 | 106 | if (strcmp(ifname, ctx->clientif) != 0) 107 | return; 108 | 109 | printf("icmp6 interface change detected\n"); 110 | 111 | ctx->ifindex = msg->ifi_index; 112 | 113 | switch (type) { 114 | case RTM_NEWLINK: 115 | case RTM_SETLINK: 116 | icmp6_setup_interface(ctx); 117 | break; 118 | 119 | case RTM_DELLINK: 120 | ctx->ok = false; 121 | break; 122 | } 123 | } 124 | 125 | struct __attribute__((__packed__)) sol_packet { 126 | struct nd_neighbor_solicit hdr; 127 | struct nd_opt_hdr opt; 128 | uint8_t hw_addr[6]; 129 | }; 130 | 131 | struct __attribute__((__packed__)) adv_packet { 132 | struct nd_neighbor_advert hdr; 133 | struct nd_opt_hdr opt; 134 | uint8_t hw_addr[6]; 135 | }; 136 | 137 | void icmp6_handle_ns_in(icmp6_ctx *ctx, int fd) { 138 | char str[INET6_ADDRSTRLEN]; 139 | struct msghdr msghdr; 140 | memset (&msghdr, 0, sizeof (msghdr)); 141 | 142 | char cbuf[CMSG_SPACE (sizeof (int))]; 143 | 144 | struct __attribute__((__packed__)) { 145 | struct ip6_hdr hdr; 146 | struct sol_packet sol; 147 | } packet; 148 | 149 | struct iovec iov = { 150 | .iov_base = &packet, 151 | .iov_len = sizeof(packet) 152 | }; 153 | 154 | struct sockaddr_ll lladdr; 155 | 156 | struct msghdr hdr = { 157 | .msg_name = &lladdr, 158 | .msg_namelen = sizeof(lladdr), 159 | .msg_iov = &iov, 160 | .msg_iovlen = 1, 161 | .msg_control = cbuf, 162 | .msg_controllen = sizeof (cbuf) 163 | }; 164 | 165 | ssize_t rc = recvmsg(ctx->nsfd, &hdr, 0); 166 | 167 | if (rc == -1) 168 | return; 169 | 170 | uint8_t *mac = lladdr.sll_addr; 171 | 172 | if (packet.sol.hdr.nd_ns_hdr.icmp6_type == ND_NEIGHBOR_SOLICIT) { 173 | 174 | if (memcmp(&packet.hdr.ip6_src, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 16) != 0) 175 | return; 176 | 177 | inet_ntop(AF_INET6, &packet.hdr.ip6_src, str, INET6_ADDRSTRLEN); 178 | printf("Neighbor Solicitation from %s (MAC %02x:%02x:%02x:%02x:%02x:%02x)\n", str, mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); 179 | inet_ntop(AF_INET6, &packet.sol.hdr.nd_ns_target, str, INET6_ADDRSTRLEN); 180 | printf(" Target: %s\n", str); 181 | 182 | clientmgr_add_address(CTX(clientmgr), &packet.sol.hdr.nd_ns_target, mac, ctx->ifindex); 183 | } 184 | } 185 | 186 | void icmp6_handle_in(icmp6_ctx *ctx, int fd) { 187 | char str[INET6_ADDRSTRLEN]; 188 | struct msghdr msghdr; 189 | memset (&msghdr, 0, sizeof (msghdr)); 190 | 191 | struct adv_packet packet; 192 | char cbuf[CMSG_SPACE (sizeof (int))]; 193 | 194 | struct iovec iov = { 195 | .iov_base = &packet, 196 | .iov_len = sizeof(packet) 197 | }; 198 | 199 | struct sockaddr_in6 peeraddr; 200 | 201 | struct msghdr hdr = { 202 | .msg_name = &peeraddr, 203 | .msg_namelen = sizeof(peeraddr), 204 | .msg_iov = &iov, 205 | .msg_iovlen = 1, 206 | .msg_control = cbuf, 207 | .msg_controllen = sizeof (cbuf) 208 | }; 209 | 210 | ssize_t rc = recvmsg(ctx->fd, &hdr, 0); 211 | 212 | if (rc == -1) 213 | return; 214 | 215 | if (packet.hdr.nd_na_hdr.icmp6_type != ND_NEIGHBOR_ADVERT) 216 | return; 217 | 218 | if (packet.hdr.nd_na_hdr.icmp6_code != 0) 219 | return; 220 | 221 | // only handle when it is a response to a solicitation 222 | // and override bit is set (i.e. a MAC is supplied) 223 | if ((packet.hdr.nd_na_hdr.icmp6_dataun.icmp6_un_data8[0] & 0x60) != 0x60) 224 | return; 225 | 226 | inet_ntop(AF_INET6, &packet.hdr.nd_na_target, str, INET6_ADDRSTRLEN); 227 | 228 | clientmgr_add_address(CTX(clientmgr), &packet.hdr.nd_na_target, packet.hw_addr, ctx->ifindex); 229 | } 230 | 231 | void icmp6_send_solicitation(icmp6_ctx *ctx, const struct in6_addr *addr) { 232 | struct sol_packet packet; 233 | 234 | packet.hdr.nd_ns_hdr.icmp6_type = ND_NEIGHBOR_SOLICIT; 235 | packet.hdr.nd_ns_hdr.icmp6_code = 0; 236 | packet.hdr.nd_ns_hdr.icmp6_cksum = 0; 237 | packet.hdr.nd_ns_reserved = 0; 238 | memcpy(&packet.hdr.nd_ns_target, addr, 16); 239 | 240 | packet.opt.nd_opt_type = ND_OPT_SOURCE_LINKADDR; 241 | packet.opt.nd_opt_len = 1; 242 | memcpy(packet.hw_addr, ctx->mac, 6); 243 | 244 | struct sockaddr_in6 dst = {}; 245 | dst.sin6_family = AF_INET6; 246 | memcpy(&dst.sin6_addr, addr, 16); 247 | memcpy(&dst.sin6_addr, "\xff\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xff", 13); 248 | 249 | char str[INET6_ADDRSTRLEN]; 250 | inet_ntop(AF_INET6, &dst.sin6_addr, str, sizeof str); 251 | printf("Send NS to %s\n", str); 252 | 253 | sendto(ctx->fd, &packet, sizeof(packet), 0, &dst, sizeof(dst)); 254 | } 255 | -------------------------------------------------------------------------------- /src/icmp6.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | typedef struct { 9 | struct l3ctx *l3ctx; 10 | int fd; 11 | int nsfd; 12 | bool ok; 13 | uint8_t mac[6]; 14 | const char *clientif; 15 | unsigned int ifindex; 16 | } icmp6_ctx; 17 | 18 | void icmp6_handle_in(icmp6_ctx *ctx, int fd); 19 | void icmp6_handle_ns_in(icmp6_ctx *ctx, int fd); 20 | void icmp6_send_solicitation(icmp6_ctx *ctx, const struct in6_addr *addr); 21 | void icmp6_init(icmp6_ctx *ctx); 22 | void icmp6_interface_changed(icmp6_ctx *ctx, int type, const struct ifinfomsg *msg); 23 | void icmp6_setup_interface(icmp6_ctx *ctx); 24 | -------------------------------------------------------------------------------- /src/intercom.c: -------------------------------------------------------------------------------- 1 | #include "intercom.h" 2 | #include "error.h" 3 | #include "l3roamd.h" 4 | #include "icmp6.h" 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #define INTERCOM_PORT 5523 16 | #define INTERCOM_GROUP "ff02::5523" 17 | #define INTERCOM_MAX_RECENT 100 18 | 19 | bool join_mcast(const int sock, const struct in6_addr addr, intercom_if *iface) { 20 | struct ipv6_mreq mreq; 21 | 22 | mreq.ipv6mr_multiaddr = addr; 23 | mreq.ipv6mr_interface = iface->ifindex; 24 | 25 | if (mreq.ipv6mr_interface == 0) 26 | goto error; 27 | 28 | if (setsockopt(sock, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq)) == 0) 29 | return true; 30 | else if (errno == EADDRINUSE) 31 | return true; 32 | 33 | error: 34 | fprintf(stderr, "Could not join multicast group on %s: ", iface->ifname); 35 | perror(NULL); 36 | return false; 37 | } 38 | 39 | void intercom_update_interfaces(intercom_ctx *ctx) { 40 | for (int i = 0; i < VECTOR_LEN(ctx->interfaces); i++) { 41 | intercom_if *iface = &VECTOR_INDEX(ctx->interfaces, i); 42 | 43 | iface->ifindex = if_nametoindex(iface->ifname); 44 | 45 | if (!iface->ifindex) 46 | continue; 47 | 48 | if (join_mcast(ctx->fd, ctx->groupaddr.sin6_addr, iface)) 49 | iface->ok = true; 50 | } 51 | } 52 | 53 | bool intercom_has_ifname(intercom_ctx *ctx, char *ifname) { 54 | for (int i = 0; i < VECTOR_LEN(ctx->interfaces); i++) { 55 | intercom_if *iface = &VECTOR_INDEX(ctx->interfaces, i); 56 | 57 | if (strcmp(ifname, iface->ifname) == 0) 58 | return true; 59 | } 60 | 61 | return false; 62 | } 63 | 64 | void intercom_add_interface(intercom_ctx *ctx, char *ifname) { 65 | if (intercom_has_ifname(ctx, ifname)) 66 | return; 67 | 68 | intercom_if iface = { 69 | .ok = false, 70 | .ifname = ifname 71 | }; 72 | 73 | VECTOR_ADD(ctx->interfaces, iface); 74 | 75 | intercom_update_interfaces(ctx); 76 | } 77 | 78 | void intercom_init(intercom_ctx *ctx) { 79 | 80 | struct in6_addr mgroup_addr; 81 | inet_pton(AF_INET6, INTERCOM_GROUP, &mgroup_addr); // TODO Fehler abfangen 82 | 83 | ctx->groupaddr = (struct sockaddr_in6) { 84 | .sin6_family = AF_INET6, 85 | .sin6_addr = mgroup_addr, 86 | .sin6_port = htons(INTERCOM_PORT), 87 | }; 88 | 89 | ctx->fd = socket(PF_INET6, SOCK_DGRAM | SOCK_NONBLOCK, 0); 90 | 91 | if (ctx->fd < 0) 92 | exit_error("creating socket"); 93 | 94 | struct sockaddr_in6 server_addr = {}; 95 | 96 | server_addr.sin6_family = AF_INET6; 97 | server_addr.sin6_addr = in6addr_any; 98 | server_addr.sin6_port = htons(INTERCOM_PORT); 99 | 100 | if (bind(ctx->fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) { 101 | perror("bind failed"); 102 | exit(EXIT_FAILURE); 103 | } 104 | } 105 | 106 | void intercom_seek(intercom_ctx *ctx, const struct in6_addr *address) { 107 | intercom_packet_seek packet; 108 | 109 | uint32_t nonce = rand(); 110 | 111 | packet.hdr = (intercom_packet_hdr) { 112 | .type = INTERCOM_SEEK, 113 | .nonce = nonce, 114 | .ttl = 255, 115 | }; 116 | 117 | memcpy(&packet.hdr.sender, ctx->ip.s6_addr, sizeof(uint8_t) * 16); 118 | 119 | memcpy(&packet.address, address, 16); 120 | 121 | intercom_recently_seen_add(ctx, &packet.hdr); 122 | 123 | intercom_send_packet(ctx, (uint8_t*)&packet, sizeof(packet)); 124 | } 125 | 126 | bool intercom_send_packet_unicast(intercom_ctx *ctx, const struct in6_addr *recipient, uint8_t *packet, ssize_t packet_len) { 127 | struct sockaddr_in6 addr = (struct sockaddr_in6) { 128 | .sin6_family = AF_INET6, 129 | .sin6_port = htons(INTERCOM_PORT), 130 | .sin6_addr = *recipient 131 | }; 132 | 133 | ssize_t rc = sendto(ctx->fd, packet, packet_len, 0, &addr, sizeof(addr)); 134 | 135 | if (rc < 0) 136 | perror("sendto failed"); 137 | 138 | return rc >= 0; 139 | } 140 | 141 | void intercom_send_packet(intercom_ctx *ctx, uint8_t *packet, ssize_t packet_len) { 142 | for (int i = 0; i < VECTOR_LEN(ctx->interfaces); i++) { 143 | intercom_if *iface = &VECTOR_INDEX(ctx->interfaces, i); 144 | 145 | if (!iface->ok) 146 | continue; 147 | 148 | struct sockaddr_in6 groupaddr = ctx->groupaddr; 149 | 150 | groupaddr.sin6_scope_id = iface->ifindex; 151 | 152 | ssize_t rc = sendto(ctx->fd, packet, packet_len, 0, &groupaddr, sizeof(groupaddr)); 153 | 154 | if (rc < 0) 155 | iface->ok = false; 156 | } 157 | } 158 | 159 | bool intercom_recently_seen(intercom_ctx *ctx, intercom_packet_hdr *hdr) { 160 | for (int i = 0; i < VECTOR_LEN(ctx->recent_packets); i++) { 161 | intercom_packet_hdr *ref_hdr = &VECTOR_INDEX(ctx->recent_packets, i); 162 | 163 | if (ref_hdr->nonce == hdr->nonce && ref_hdr->type == hdr->type) 164 | return true; 165 | } 166 | return false; 167 | } 168 | 169 | void intercom_recently_seen_add(intercom_ctx *ctx, intercom_packet_hdr *hdr) { 170 | if (VECTOR_LEN(ctx->recent_packets) > INTERCOM_MAX_RECENT) 171 | VECTOR_DELETE(ctx->recent_packets, 0); 172 | 173 | VECTOR_ADD(ctx->recent_packets, *hdr); 174 | } 175 | 176 | void intercom_handle_seek(intercom_ctx *ctx, intercom_packet_seek *packet) { 177 | if (clientmgr_is_ipv4(CTX(clientmgr), (struct in6_addr *)packet->address)) 178 | arp_send_request(CTX(arp), (struct in6_addr *)packet->address); 179 | else 180 | icmp6_send_solicitation(CTX(icmp6), (struct in6_addr *)packet->address); 181 | } 182 | 183 | void intercom_handle_claim(intercom_ctx *ctx, intercom_packet_claim *packet) { 184 | struct in6_addr sender; 185 | 186 | memcpy(&sender.s6_addr, &packet->hdr.sender, sizeof(uint8_t) * 16); 187 | 188 | clientmgr_handle_claim(CTX(clientmgr), &sender, packet->mac); 189 | } 190 | 191 | void intercom_handle_info(intercom_ctx *ctx, intercom_packet_info *packet) { 192 | struct timespec now; 193 | clock_gettime(CLOCK_MONOTONIC, &now); 194 | 195 | struct client client = {}; 196 | 197 | memcpy(client.mac, &packet->mac, sizeof(uint8_t) * 6); 198 | 199 | struct client_ip ip = { 200 | .state = IP_INACTIVE 201 | }; 202 | 203 | intercom_packet_info_entry *entry = (intercom_packet_info_entry*)((uint8_t*)(packet) + sizeof(intercom_packet_info)); 204 | 205 | for (int i = 0; i < packet->num_addresses; i++) { 206 | memcpy(&ip.address.s6_addr, &entry->address, sizeof(uint8_t) * 16); 207 | VECTOR_ADD(client.addresses, ip); 208 | entry++; 209 | } 210 | 211 | clientmgr_handle_info(CTX(clientmgr), &client, packet->relinquished); 212 | } 213 | 214 | void intercom_handle_packet(intercom_ctx *ctx, uint8_t *packet, ssize_t packet_len) { 215 | intercom_packet_hdr *hdr = (intercom_packet_hdr*) packet; 216 | 217 | if (intercom_recently_seen(ctx, hdr)) 218 | return; 219 | 220 | intercom_recently_seen_add(ctx, hdr); 221 | 222 | if (hdr->type == INTERCOM_SEEK) 223 | intercom_handle_seek(ctx, (intercom_packet_seek*)packet); 224 | 225 | if (hdr->type == INTERCOM_CLAIM) 226 | intercom_handle_claim(ctx, (intercom_packet_claim*)packet); 227 | 228 | if (hdr->type == INTERCOM_INFO) 229 | intercom_handle_info(ctx, (intercom_packet_info*)packet); 230 | 231 | hdr->ttl--; 232 | 233 | if (hdr->ttl > 0) 234 | intercom_send_packet(ctx, packet, packet_len); 235 | } 236 | 237 | void intercom_handle_in(intercom_ctx *ctx, int fd) { 238 | ssize_t count; 239 | uint8_t buf[1500]; 240 | 241 | while (1) { 242 | count = read(fd, buf, sizeof buf); 243 | 244 | if (count == -1) { 245 | /* If errno == EAGAIN, that means we have read all 246 | data. So go back to the main loop. */ 247 | if (errno != EAGAIN) { 248 | perror("read"); 249 | } 250 | break; 251 | } else if (count == 0) { 252 | /* End of file. The remote has closed the 253 | connection. */ 254 | break; 255 | } 256 | 257 | intercom_handle_packet(ctx, buf, count); 258 | } 259 | } 260 | 261 | // Announce at most 32 addresses per client 262 | #define INFO_MAX 32 263 | 264 | /* recipient = NULL -> send to neighbours */ 265 | void intercom_info(intercom_ctx *ctx, const struct in6_addr *recipient, struct client *client, bool relinquished) { 266 | intercom_packet_info *packet = malloc(sizeof(intercom_packet_info) + INFO_MAX * sizeof(intercom_packet_info_entry)); 267 | int i; 268 | 269 | uint32_t nonce = rand(); 270 | 271 | packet->hdr = (intercom_packet_hdr) { 272 | .type = INTERCOM_INFO, 273 | .nonce = nonce, 274 | .ttl = 1, 275 | }; 276 | 277 | memcpy(&packet->hdr.sender, ctx->ip.s6_addr, sizeof(uint8_t) * 16); 278 | 279 | memcpy(&packet->mac, client->mac, sizeof(uint8_t) * 6); 280 | packet->relinquished = relinquished; 281 | 282 | intercom_packet_info_entry *entry = (intercom_packet_info_entry*)((uint8_t*)(packet) + sizeof(intercom_packet_info)); 283 | 284 | for (i = 0; i < VECTOR_LEN(client->addresses) && i < INFO_MAX; i++) { 285 | struct client_ip *ip = &VECTOR_INDEX(client->addresses, i); 286 | memcpy(&entry->address, ip->address.s6_addr, sizeof(uint8_t) * 16); 287 | entry++; 288 | } 289 | 290 | packet->num_addresses = i; 291 | 292 | ssize_t packet_len = sizeof(intercom_packet_info) + i * sizeof(intercom_packet_info_entry); 293 | 294 | if (recipient != NULL) 295 | intercom_send_packet_unicast(ctx, recipient, (uint8_t*)packet, packet_len); 296 | else { 297 | intercom_recently_seen_add(ctx, &packet->hdr); 298 | 299 | intercom_send_packet(ctx, (uint8_t*)packet, packet_len); 300 | } 301 | free(packet); 302 | } 303 | 304 | bool intercom_claim(intercom_ctx *ctx, const struct in6_addr *recipient, struct client *client) { 305 | intercom_packet_claim packet; 306 | 307 | uint32_t nonce = rand(); 308 | 309 | packet.hdr = (intercom_packet_hdr) { 310 | .type = INTERCOM_CLAIM, 311 | .nonce = nonce, 312 | .ttl = 255, 313 | }; 314 | 315 | memcpy(&packet.hdr.sender, ctx->ip.s6_addr, sizeof(uint8_t) * 16); 316 | 317 | memcpy(&packet.mac, client->mac, 6); 318 | 319 | intercom_recently_seen_add(ctx, &packet.hdr); 320 | 321 | if (recipient != NULL) 322 | return intercom_send_packet_unicast(ctx, recipient, (uint8_t*)&packet, sizeof(packet)); 323 | else { 324 | intercom_send_packet(ctx, (uint8_t*)&packet, sizeof(packet)); 325 | return true; 326 | } 327 | } 328 | -------------------------------------------------------------------------------- /src/intercom.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "vector.h" 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | enum {INTERCOM_SEEK, INTERCOM_CLAIM, INTERCOM_INFO}; 14 | 15 | typedef struct __attribute__((__packed__)) { 16 | uint8_t ttl; 17 | uint32_t nonce; 18 | uint8_t type; 19 | uint8_t sender[16]; 20 | } intercom_packet_hdr; 21 | 22 | typedef struct __attribute__((__packed__)) { 23 | intercom_packet_hdr hdr; 24 | uint8_t address[16]; 25 | } intercom_packet_seek; 26 | 27 | typedef struct __attribute__((__packed__)) { 28 | intercom_packet_hdr hdr; 29 | uint8_t mac[6]; 30 | } intercom_packet_claim; 31 | 32 | typedef struct __attribute__((__packed__)) { 33 | intercom_packet_hdr hdr; 34 | uint8_t relinquished; 35 | uint8_t mac[6]; 36 | uint8_t num_addresses; 37 | } intercom_packet_info; 38 | 39 | typedef struct __attribute__((__packed__)) { 40 | uint8_t address[16]; 41 | } intercom_packet_info_entry; 42 | 43 | typedef struct { 44 | bool ok; 45 | unsigned int ifindex; 46 | char *ifname; 47 | } intercom_if; 48 | 49 | typedef struct { 50 | struct l3ctx *l3ctx; 51 | int fd; 52 | struct sockaddr_in6 groupaddr; 53 | struct in6_addr ip; 54 | VECTOR(intercom_packet_hdr) recent_packets; 55 | VECTOR(intercom_if) interfaces; 56 | } intercom_ctx; 57 | 58 | struct client; 59 | 60 | void intercom_recently_seen_add(intercom_ctx *ctx, intercom_packet_hdr *hdr); 61 | void intercom_send_packet(intercom_ctx *ctx, uint8_t *packet, ssize_t packet_len); 62 | void intercom_seek(intercom_ctx *ctx, const struct in6_addr *address); 63 | void intercom_init(intercom_ctx *ctx); 64 | void intercom_handle_in(intercom_ctx *ctx, int fd); 65 | void intercom_add_interface(intercom_ctx *ctx, char *ifname); 66 | void intercom_update_interfaces(intercom_ctx *ctx); 67 | void intercom_info(intercom_ctx *ctx, const struct in6_addr *recipient, struct client *client, bool relinquished); 68 | bool intercom_claim(intercom_ctx *ctx, const struct in6_addr *recipient, struct client *client); 69 | -------------------------------------------------------------------------------- /src/ipmgr.c: -------------------------------------------------------------------------------- 1 | #include "error.h" 2 | #include "ipmgr.h" 3 | #include "l3roamd.h" 4 | #include "timespec.h" 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | static void tun_open(ipmgr_ctx *ctx, const char *ifname, uint16_t mtu, const char *dev_name); 15 | static void schedule_ipcheck(ipmgr_ctx *ctx, struct entry *e); 16 | static void ipcheck_task(void *d); 17 | static bool ipcheck(ipmgr_ctx *ctx, struct entry *e); 18 | 19 | void tun_open(ipmgr_ctx *ctx, const char *ifname, uint16_t mtu, const char *dev_name) { 20 | int ctl_sock = -1; 21 | struct ifreq ifr = {}; 22 | 23 | ctx->fd = open(dev_name, O_RDWR|O_NONBLOCK); 24 | if (ctx->fd < 0) 25 | exit_errno("could not open TUN/TAP device file"); 26 | 27 | if (ifname) 28 | strncpy(ifr.ifr_name, ifname, IFNAMSIZ-1); 29 | 30 | ifr.ifr_flags = IFF_TUN | IFF_NO_PI; 31 | 32 | if (ioctl(ctx->fd, TUNSETIFF, &ifr) < 0) { 33 | puts("unable to open TUN/TAP interface: TUNSETIFF ioctl failed"); 34 | goto error; 35 | } 36 | 37 | // TODO this must be freed eventually 38 | ctx->ifname = strndup(ifr.ifr_name, IFNAMSIZ-1); 39 | 40 | ctl_sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); 41 | if (ctl_sock < 0) 42 | exit_errno("socket"); 43 | 44 | if (ioctl(ctl_sock, SIOCGIFMTU, &ifr) < 0) 45 | exit_errno("SIOCGIFMTU ioctl failed"); 46 | 47 | if (ifr.ifr_mtu != mtu) { 48 | ifr.ifr_mtu = mtu; 49 | if (ioctl(ctl_sock, SIOCSIFMTU, &ifr) < 0) { 50 | puts("unable to set TUN/TAP interface MTU: SIOCSIFMTU ioctl failed"); 51 | goto error; 52 | } 53 | } 54 | 55 | if (close(ctl_sock)) 56 | puts("close"); 57 | 58 | return; 59 | 60 | error: 61 | if (ctl_sock >= 0) { 62 | if (close(ctl_sock)) 63 | puts("close"); 64 | } 65 | 66 | close(ctx->fd); 67 | ctx->fd = -1; 68 | } 69 | 70 | struct entry *find_entry(ipmgr_ctx *ctx, const struct in6_addr *k) { 71 | for (int i = 0; i < VECTOR_LEN(ctx->addrs); i++) { 72 | struct entry *e = &VECTOR_INDEX(ctx->addrs, i); 73 | 74 | if (memcmp(k, &(e->address), sizeof(struct in6_addr)) == 0) 75 | return e; 76 | } 77 | 78 | return NULL; 79 | } 80 | 81 | void delete_entry(ipmgr_ctx *ctx, const struct in6_addr *k) { 82 | for (int i = 0; i < VECTOR_LEN(ctx->addrs); i++) { 83 | struct entry *e = &VECTOR_INDEX(ctx->addrs, i); 84 | 85 | if (memcmp(k, &(e->address), sizeof(struct in6_addr)) == 0) { 86 | VECTOR_DELETE(ctx->addrs, i); 87 | break; 88 | } 89 | } 90 | } 91 | 92 | void seek_address(ipmgr_ctx *ctx, struct in6_addr *addr) { 93 | char str[INET6_ADDRSTRLEN]; 94 | inet_ntop(AF_INET6, addr, str, sizeof str); 95 | 96 | printf("\x1b[36mLooking for %s\x1b[0m\n", str); 97 | 98 | if (clientmgr_is_ipv4(CTX(clientmgr), addr)) 99 | arp_send_request(CTX(arp), addr); 100 | else 101 | icmp6_send_solicitation(CTX(icmp6), addr); 102 | 103 | intercom_seek(CTX(intercom), addr); 104 | } 105 | 106 | void handle_packet(ipmgr_ctx *ctx, uint8_t packet[], ssize_t packet_len) { 107 | struct in6_addr dst; 108 | memcpy(&dst, packet + 24, 16); 109 | 110 | uint8_t a0 = dst.s6_addr[0]; 111 | 112 | // Ignore multicast 113 | if (a0 == 0xff) 114 | return; 115 | 116 | char str[INET6_ADDRSTRLEN]; 117 | inet_ntop(AF_INET6, &dst, str, sizeof str); 118 | printf("Got packet to %s\n", str); 119 | 120 | if (!clientmgr_valid_address(CTX(clientmgr), &dst)) { 121 | printf("Ignoring packet\n"); 122 | return; 123 | } 124 | 125 | struct timespec now; 126 | clock_gettime(CLOCK_MONOTONIC, &now); 127 | 128 | struct entry *e = find_entry(ctx, &dst); 129 | 130 | bool new = !e; 131 | 132 | if (!e) { 133 | struct entry entry = { 134 | .address = dst, 135 | .timestamp = now, 136 | }; 137 | 138 | VECTOR_ADD(ctx->addrs, entry); 139 | e = &VECTOR_INDEX(ctx->addrs, VECTOR_LEN(ctx->addrs) - 1); 140 | } 141 | 142 | struct packet *p = malloc(sizeof(struct packet)); 143 | 144 | p->timestamp = now; 145 | p->len = packet_len; 146 | p->data = malloc(packet_len); 147 | 148 | memcpy(p->data, packet, packet_len); 149 | 150 | VECTOR_ADD(e->packets, p); 151 | 152 | struct timespec then = now; 153 | then.tv_sec -= SEEK_TIMEOUT; 154 | 155 | if (timespec_cmp(e->timestamp, then) <= 0 || new) { 156 | seek_address(ctx, &dst); 157 | e->timestamp = now; 158 | } 159 | 160 | schedule_ipcheck(ctx, e); 161 | } 162 | 163 | void schedule_ipcheck(ipmgr_ctx *ctx, struct entry *e) { 164 | struct ip_task *data = calloc(1, sizeof(struct ip_task)); 165 | 166 | data->ctx = ctx; 167 | data->address = e->address; 168 | 169 | if (e->check_task == NULL) 170 | e->check_task = post_task(CTX(taskqueue), IPCHECK_INTERVAL, ipcheck_task, free, data); 171 | } 172 | 173 | void ipcheck_task(void *d) { 174 | struct ip_task *data = d; 175 | 176 | struct entry *e = find_entry(data->ctx, &data->address); 177 | 178 | if (!e) 179 | return; 180 | 181 | e->check_task = NULL; 182 | 183 | if (ipcheck(data->ctx, e)) 184 | schedule_ipcheck(data->ctx, e); 185 | } 186 | 187 | bool ipcheck(ipmgr_ctx *ctx, struct entry *e) { 188 | struct timespec now; 189 | clock_gettime(CLOCK_MONOTONIC, &now); 190 | 191 | struct timespec then = now; 192 | then.tv_sec -= PACKET_TIMEOUT; 193 | 194 | for (int i = 0; i < VECTOR_LEN(e->packets); i++) { 195 | struct packet *p = VECTOR_INDEX(e->packets, i); 196 | 197 | if (timespec_cmp(p->timestamp, then) <= 0) { 198 | free(p->data); 199 | VECTOR_DELETE(e->packets, i); 200 | i--; 201 | } 202 | } 203 | 204 | then = now; 205 | then.tv_sec -= SEEK_TIMEOUT; 206 | 207 | if (VECTOR_LEN(e->packets) == 0 && timespec_cmp(e->timestamp, then) <= 0) { 208 | VECTOR_FREE(e->packets); 209 | delete_entry(ctx, &e->address); 210 | return false; 211 | } 212 | 213 | return true; 214 | } 215 | 216 | void ipmgr_handle_in(ipmgr_ctx *ctx, int fd) { 217 | ssize_t count; 218 | uint8_t buf[1500]; 219 | 220 | while (1) { 221 | count = read(fd, buf, sizeof buf); 222 | 223 | if (count == -1) { 224 | /* If errno == EAGAIN, that means we have read all 225 | data. So go back to the main loop. */ 226 | if (errno != EAGAIN) { 227 | perror("read"); 228 | } 229 | break; 230 | } else if (count == 0) { 231 | /* End of file. The remote has closed the 232 | connection. */ 233 | break; 234 | } 235 | 236 | if (count < 40) 237 | continue; 238 | 239 | // We're only interested in ip6 packets 240 | if ((buf[0] & 0xf0) != 0x60) 241 | continue; 242 | 243 | handle_packet(ctx, buf, count); 244 | } 245 | } 246 | 247 | void ipmgr_handle_out(ipmgr_ctx *ctx, int fd) { 248 | ssize_t count; 249 | while (1) { 250 | if (VECTOR_LEN(ctx->output_queue) == 0) 251 | break; 252 | 253 | struct packet *packet = &VECTOR_INDEX(ctx->output_queue, 0); 254 | count = write(fd, packet->data, packet->len); 255 | 256 | // TODO refactor to use epoll. do we have to put the packet back in case of EAGAIN? 257 | free(packet->data); 258 | VECTOR_DELETE(ctx->output_queue, 0); 259 | 260 | if (count == -1) { 261 | if (errno != EAGAIN) 262 | perror("write"); 263 | 264 | break; 265 | } 266 | } 267 | } 268 | 269 | void ipmgr_route_appeared(ipmgr_ctx *ctx, const struct in6_addr *destination) { 270 | struct entry *e = find_entry(ctx, destination); 271 | 272 | if (!e) 273 | return; 274 | 275 | for (int i = 0; i < VECTOR_LEN(e->packets); i++) { 276 | struct packet *p = VECTOR_INDEX(e->packets, i); 277 | VECTOR_ADD(ctx->output_queue, *p); 278 | } 279 | 280 | VECTOR_FREE(e->packets); 281 | 282 | delete_entry(ctx, destination); 283 | 284 | ipmgr_handle_out(ctx, ctx->fd); 285 | } 286 | 287 | 288 | void ipmgr_init(ipmgr_ctx *ctx, char *tun_name, unsigned int mtu) { 289 | tun_open(ctx, tun_name, mtu, "/dev/net/tun"); 290 | } 291 | -------------------------------------------------------------------------------- /src/ipmgr.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015, Nils Schneider 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 | 1. Redistributions of source code must retain the above copyright notice, 9 | this list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | */ 25 | #pragma once 26 | 27 | #include "vector.h" 28 | #include "taskqueue.h" 29 | 30 | #include 31 | #include 32 | 33 | #define IPCHECK_INTERVAL 2 34 | #define PACKET_TIMEOUT 2 35 | #define SEEK_TIMEOUT 10 36 | 37 | struct packet { 38 | struct timespec timestamp; 39 | ssize_t len; 40 | uint8_t *data; 41 | }; 42 | 43 | struct entry { 44 | struct in6_addr address; 45 | struct timespec timestamp; 46 | taskqueue_t *check_task; 47 | VECTOR(struct packet*) packets; 48 | }; 49 | 50 | typedef struct { 51 | int fd; 52 | char *ifname; 53 | struct l3ctx *l3ctx; 54 | VECTOR(struct entry) addrs; 55 | VECTOR(struct packet) output_queue; 56 | } ipmgr_ctx; 57 | 58 | struct ip_task { 59 | ipmgr_ctx *ctx; 60 | struct in6_addr address; 61 | }; 62 | 63 | void ipmgr_init(ipmgr_ctx *ctx, char *tun_name, unsigned int mtu); 64 | void ipmgr_route_appeared(ipmgr_ctx *ctx, const struct in6_addr *destination); 65 | void ipmgr_handle_in(ipmgr_ctx *ctx, int fd); 66 | void ipmgr_handle_out(ipmgr_ctx *ctx, int fd); 67 | -------------------------------------------------------------------------------- /src/l3roamd.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "vector.h" 4 | #include "intercom.h" 5 | #include "wifistations.h" 6 | #include "clientmgr.h" 7 | #include "taskqueue.h" 8 | #include "icmp6.h" 9 | #include "ipmgr.h" 10 | #include "arp.h" 11 | #include "routemgr.h" 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | struct l3ctx { 21 | taskqueue_ctx taskqueue_ctx; 22 | intercom_ctx intercom_ctx; 23 | wifistations_ctx wifistations_ctx; 24 | clientmgr_ctx clientmgr_ctx; 25 | icmp6_ctx icmp6_ctx; 26 | ipmgr_ctx ipmgr_ctx; 27 | arp_ctx arp_ctx; 28 | routemgr_ctx routemgr_ctx; 29 | }; 30 | 31 | void interfaces_changed(struct l3ctx *ctx, int type, const struct ifinfomsg *msg); 32 | 33 | #define CTX(tgt) (&ctx->l3ctx->tgt ## _ctx) 34 | -------------------------------------------------------------------------------- /src/main.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015, Nils Schneider 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 | 1. Redistributions of source code must retain the above copyright notice, 9 | this list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | */ 25 | 26 | // TODO EPOLLOUT beim schreiben auf den tunfd 27 | 28 | #include "ipmgr.h" 29 | #include "l3roamd.h" 30 | #include "error.h" 31 | #include "icmp6.h" 32 | #include "routemgr.h" 33 | #include "intercom.h" 34 | #include "config.h" 35 | 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | 46 | void add_fd(int efd, int fd, uint32_t events) { 47 | struct epoll_event event = {}; 48 | event.data.fd = fd; 49 | event.events = events; 50 | 51 | int s = epoll_ctl(efd, EPOLL_CTL_ADD, fd, &event); 52 | if (s == -1) 53 | exit_error("epoll_ctl"); 54 | } 55 | 56 | void loop(struct l3ctx *ctx) { 57 | int efd; 58 | int maxevents = 64; 59 | struct epoll_event *events; 60 | 61 | efd = epoll_create1(0); 62 | if (efd == -1) { 63 | perror("epoll_create"); 64 | abort(); 65 | } 66 | 67 | add_fd(efd, ctx->ipmgr_ctx.fd, EPOLLIN | EPOLLET); 68 | add_fd(efd, ctx->routemgr_ctx.fd, EPOLLIN | EPOLLET); 69 | add_fd(efd, ctx->taskqueue_ctx.fd, EPOLLIN); 70 | add_fd(efd, ctx->icmp6_ctx.fd, EPOLLIN); 71 | add_fd(efd, ctx->icmp6_ctx.nsfd, EPOLLIN); 72 | add_fd(efd, ctx->arp_ctx.fd, EPOLLIN); 73 | add_fd(efd, ctx->intercom_ctx.fd, EPOLLIN | EPOLLET); 74 | add_fd(efd, ctx->wifistations_ctx.fd, EPOLLIN); 75 | 76 | /* Buffer where events are returned */ 77 | events = calloc(maxevents, sizeof(struct epoll_event)); 78 | 79 | /* The event loop */ 80 | while (1) { 81 | int n; 82 | n = epoll_wait(efd, events, maxevents, -1); 83 | 84 | for(int i = 0; i < n; i++) { 85 | if ((events[i].events & EPOLLERR) || (events[i].events & EPOLLHUP)) { 86 | fprintf(stderr, "epoll error\n"); 87 | close(events[i].data.fd); 88 | } else if (ctx->routemgr_ctx.fd == events[i].data.fd) { 89 | if (events[i].events & EPOLLIN) 90 | routemgr_handle_in(&ctx->routemgr_ctx, events[i].data.fd); 91 | } else if (ctx->ipmgr_ctx.fd == events[i].data.fd) { 92 | if (events[i].events & EPOLLIN) 93 | ipmgr_handle_in(&ctx->ipmgr_ctx, events[i].data.fd); 94 | } else if (ctx->icmp6_ctx.fd == events[i].data.fd) { 95 | if (events[i].events & EPOLLIN) 96 | icmp6_handle_in(&ctx->icmp6_ctx, events[i].data.fd); 97 | } else if (ctx->icmp6_ctx.nsfd == events[i].data.fd) { 98 | if (events[i].events & EPOLLIN) 99 | icmp6_handle_ns_in(&ctx->icmp6_ctx, events[i].data.fd); 100 | } else if (ctx->arp_ctx.fd == events[i].data.fd) { 101 | if (events[i].events & EPOLLIN) 102 | arp_handle_in(&ctx->arp_ctx, events[i].data.fd); 103 | } else if (ctx->intercom_ctx.fd == events[i].data.fd) { 104 | if (events[i].events & EPOLLIN) 105 | intercom_handle_in(&ctx->intercom_ctx, events[i].data.fd); 106 | } else if (ctx->taskqueue_ctx.fd == events[i].data.fd) { 107 | taskqueue_run(&ctx->taskqueue_ctx); 108 | } else if (ctx->wifistations_ctx.fd == events[i].data.fd) { 109 | wifistations_handle_in(&ctx->wifistations_ctx); 110 | } 111 | } 112 | } 113 | 114 | free(events); 115 | } 116 | 117 | void usage() { 118 | puts("Usage: l3roamd [-h] -a -p -i -m ... -t -4 [prefix] -t "); 119 | puts(" -a ip address of this node"); 120 | puts(" -c configuration file"); 121 | puts(" -p clientprefix"); // TODO mehrfache angabe sollte möglich sein 122 | puts(" -i client interface"); 123 | puts(" -m mesh interface. may be specified multiple times"); 124 | puts(" -t export routes to this table"); 125 | puts(" -4 IPv4 translation prefix"); 126 | puts(" -t interface for nat46"); 127 | puts(" -h this help\n"); 128 | } 129 | 130 | bool parse_prefix(struct prefix *prefix, const char *str) { 131 | char *saveptr; 132 | char *tmp = strdupa(str); 133 | char *ptr = strtok_r(tmp, "/", &saveptr); 134 | 135 | if (ptr == NULL) 136 | return false; 137 | 138 | int rc = inet_pton(AF_INET6, ptr, &prefix->prefix); 139 | 140 | if (rc != 1) 141 | return false; 142 | 143 | ptr = strtok_r(NULL, "/", &saveptr); 144 | 145 | if (ptr == NULL) 146 | return false; 147 | 148 | prefix->plen = atoi(ptr); 149 | 150 | if (prefix->plen < 0 || prefix->plen > 128) 151 | return false; 152 | 153 | return true; 154 | } 155 | 156 | void interfaces_changed(struct l3ctx *ctx, int type, const struct ifinfomsg *msg) { 157 | printf("interfaces changed\n"); 158 | intercom_update_interfaces(&ctx->intercom_ctx); 159 | icmp6_interface_changed(&ctx->icmp6_ctx, type, msg); 160 | arp_interface_changed(&ctx->arp_ctx, type, msg); 161 | } 162 | 163 | int main(int argc, char *argv[]) { 164 | struct l3ctx ctx = {}; 165 | 166 | ctx.taskqueue_ctx.l3ctx = &ctx; 167 | ctx.wifistations_ctx.l3ctx = &ctx; 168 | ctx.clientmgr_ctx.l3ctx = &ctx; 169 | ctx.intercom_ctx.l3ctx = &ctx; 170 | ctx.icmp6_ctx.l3ctx = &ctx; 171 | ctx.ipmgr_ctx.l3ctx = &ctx; 172 | ctx.arp_ctx.l3ctx = &ctx; 173 | ctx.routemgr_ctx.l3ctx = &ctx; 174 | 175 | intercom_init(&ctx.intercom_ctx); 176 | 177 | int c; 178 | while ((c = getopt(argc, argv, "ha:p:i:m:t:c:4:n:")) != -1) 179 | switch (c) { 180 | case 'h': 181 | usage(); 182 | exit(EXIT_SUCCESS); 183 | case 'a': 184 | if(inet_pton(AF_INET6, optarg, &ctx.intercom_ctx.ip) != 1) 185 | exit_error("Can not parse IP address"); 186 | 187 | break; 188 | case 'c': 189 | parse_config(optarg); 190 | break; 191 | case 'p': 192 | if (!parse_prefix(&ctx.clientmgr_ctx.prefix, optarg)) 193 | exit_error("Can not parse prefix"); 194 | 195 | printf("plen %i\n", ctx.clientmgr_ctx.prefix.plen); 196 | break; 197 | case 'i': 198 | ctx.icmp6_ctx.clientif = strdupa(optarg); 199 | ctx.arp_ctx.clientif = strdupa(optarg); 200 | break; 201 | case 'm': 202 | intercom_add_interface(&ctx.intercom_ctx, strdupa(optarg)); 203 | break; 204 | case 't': 205 | ctx.clientmgr_ctx.export_table = atoi(optarg); 206 | break; 207 | case '4': 208 | if (!parse_prefix(&ctx.clientmgr_ctx.v4prefix, optarg)) 209 | exit_error("Can not parse IPv4 prefix"); 210 | 211 | if (ctx.clientmgr_ctx.v4prefix.plen != 96) 212 | exit_error("IPv4 prefix must be /96"); 213 | 214 | ctx.arp_ctx.prefix = ctx.clientmgr_ctx.v4prefix.prefix; 215 | 216 | break; 217 | case 'n': 218 | ctx.clientmgr_ctx.nat46ifindex = if_nametoindex(optarg); 219 | break; 220 | default: 221 | fprintf(stderr, "Invalid parameter %c ignored.\n", c); 222 | } 223 | 224 | routemgr_init(&ctx.routemgr_ctx); 225 | ipmgr_init(&ctx.ipmgr_ctx, "l3roam0", 9000); 226 | icmp6_init(&ctx.icmp6_ctx); 227 | arp_init(&ctx.arp_ctx); 228 | taskqueue_init(&ctx.taskqueue_ctx); 229 | wifistations_init(&ctx.wifistations_ctx); 230 | 231 | loop(&ctx); 232 | 233 | return 0; 234 | } 235 | -------------------------------------------------------------------------------- /src/routemgr.c: -------------------------------------------------------------------------------- 1 | #include "routemgr.h" 2 | #include "error.h" 3 | #include "l3roamd.h" 4 | 5 | static void rtnl_change_address(routemgr_ctx *ctx, struct in6_addr *address, int type, int flags); 6 | static void rtnl_handle_link(routemgr_ctx *ctx, const struct nlmsghdr *nh); 7 | static int rtnl_addattr(struct nlmsghdr *n, int maxlen, int type, void *data, int datalen); 8 | static void rtnl_talk(routemgr_ctx *ctx, struct nlmsghdr *req); 9 | 10 | void rtnl_handle_link(routemgr_ctx *ctx, const struct nlmsghdr *nh) { 11 | const struct ifinfomsg *msg = NLMSG_DATA(nh); 12 | switch (nh->nlmsg_type) { 13 | case RTM_NEWLINK: 14 | printf("new link %i\n", msg->ifi_index); 15 | break; 16 | 17 | case RTM_SETLINK: 18 | printf("set link %i\n", msg->ifi_index); 19 | break; 20 | 21 | case RTM_DELLINK: 22 | printf("del link %i\n", msg->ifi_index); 23 | break; 24 | } 25 | 26 | interfaces_changed(ctx->l3ctx, nh->nlmsg_type, msg); 27 | } 28 | 29 | void filter_kernel_routes(routemgr_ctx *ctx, const struct nlmsghdr *nh) { 30 | int rc; 31 | 32 | struct kernel_route route; 33 | 34 | int len; 35 | 36 | struct rtmsg *rtm; 37 | 38 | len = nh->nlmsg_len; 39 | 40 | if (nh->nlmsg_type != RTM_NEWROUTE) 41 | return; 42 | 43 | rtm = (struct rtmsg*)NLMSG_DATA(nh); 44 | len -= NLMSG_LENGTH(0); 45 | 46 | /* Ignore cached routes, advertised by some kernels (linux 3.x). */ 47 | if (rtm->rtm_flags & RTM_F_CLONED) 48 | return; 49 | 50 | rc = parse_kernel_route_rta(rtm, len, &route); 51 | if (rc < 0) 52 | return; 53 | 54 | /* Ignore default unreachable routes; no idea where they come from. */ 55 | if (route.plen == 0 && route.metric >= KERNEL_INFINITY) 56 | return; 57 | 58 | /* only interested in host routes */ 59 | if (route.plen != 128) 60 | return; 61 | 62 | ipmgr_route_appeared(CTX(ipmgr), &route.prefix); 63 | } 64 | 65 | void rtnl_handle_msg(routemgr_ctx *ctx, const struct nlmsghdr *nh) { 66 | switch (nh->nlmsg_type) { 67 | case RTM_NEWROUTE: 68 | case RTM_DELROUTE: 69 | filter_kernel_routes(ctx, nh); 70 | break; 71 | case RTM_NEWLINK: 72 | case RTM_DELLINK: 73 | case RTM_SETLINK: 74 | rtnl_handle_link(ctx, nh); 75 | break; 76 | default: 77 | return; 78 | } 79 | } 80 | 81 | void routemgr_init(routemgr_ctx *ctx) { 82 | ctx->fd = socket(AF_NETLINK, SOCK_RAW|SOCK_NONBLOCK, NETLINK_ROUTE); 83 | if (ctx->fd < 0) 84 | exit_error("can't open RTNL socket"); 85 | 86 | struct sockaddr_nl snl = { 87 | .nl_family = AF_NETLINK, 88 | .nl_groups = RTMGRP_IPV6_ROUTE | RTMGRP_LINK, 89 | }; 90 | 91 | if (bind(ctx->fd, (struct sockaddr *)&snl, sizeof(snl)) < 0) 92 | exit_error("can't bind RTNL socket"); 93 | } 94 | 95 | 96 | int parse_kernel_route_rta(struct rtmsg *rtm, int len, struct kernel_route *route) { 97 | len -= NLMSG_ALIGN(sizeof(*rtm)); 98 | 99 | memset(route, 0, sizeof(struct kernel_route)); 100 | route->proto = rtm->rtm_protocol; 101 | 102 | for (struct rtattr *rta = RTM_RTA(rtm); RTA_OK(rta, len); rta = RTA_NEXT(rta, len)) { 103 | switch(rta->rta_type) { 104 | case RTA_DST: 105 | route->plen = rtm->rtm_dst_len; 106 | memcpy(route->prefix.s6_addr, RTA_DATA(rta), 16); 107 | break; 108 | case RTA_SRC: 109 | route->src_plen = rtm->rtm_src_len; 110 | memcpy(route->src_prefix.s6_addr, RTA_DATA(rta), 16); 111 | break; 112 | case RTA_GATEWAY: 113 | memcpy(route->gw.s6_addr, RTA_DATA(rta), 16); 114 | break; 115 | case RTA_OIF: 116 | route->ifindex = *(int*)RTA_DATA(rta); 117 | break; 118 | case RTA_PRIORITY: 119 | route->metric = *(int*)RTA_DATA(rta); 120 | if(route->metric < 0 || route->metric > KERNEL_INFINITY) 121 | route->metric = KERNEL_INFINITY; 122 | break; 123 | default: 124 | break; 125 | } 126 | } 127 | 128 | return 1; 129 | } 130 | 131 | void routemgr_handle_in(routemgr_ctx *ctx, int fd) { 132 | while (1) { 133 | ssize_t count; 134 | uint8_t buf[8192]; 135 | 136 | count = recv(fd, buf, sizeof buf, 0); 137 | if (count == -1) { 138 | if (errno != EAGAIN) 139 | perror("read"); 140 | break; 141 | } else if (count == 0) 142 | break; 143 | 144 | const struct nlmsghdr *nh; 145 | for (nh = (struct nlmsghdr *)buf; NLMSG_OK(nh, count); nh = NLMSG_NEXT(nh, count)) { 146 | switch (nh->nlmsg_type) { 147 | case NLMSG_DONE: 148 | return; 149 | case NLMSG_ERROR: 150 | perror("error: netlink error"); 151 | default: 152 | rtnl_handle_msg(ctx, nh); 153 | } 154 | } 155 | } 156 | } 157 | 158 | int rtnl_addattr(struct nlmsghdr *n, int maxlen, int type, void *data, int datalen) { 159 | int len = RTA_LENGTH(datalen); 160 | struct rtattr *rta; 161 | if (NLMSG_ALIGN(n->nlmsg_len) + len > maxlen) 162 | return -1; 163 | rta = (struct rtattr*)(((char*)n) + NLMSG_ALIGN(n->nlmsg_len)); 164 | rta->rta_type = type; 165 | rta->rta_len = len; 166 | memcpy(RTA_DATA(rta), data, datalen); 167 | n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + len; 168 | return 0; 169 | } 170 | 171 | void rtnl_add_address(routemgr_ctx *ctx, struct in6_addr *address) { 172 | rtnl_change_address(ctx, address, RTM_NEWADDR, NLM_F_CREATE | NLM_F_EXCL | NLM_F_REQUEST); 173 | } 174 | 175 | void rtnl_remove_address(routemgr_ctx *ctx, struct in6_addr *address) { 176 | rtnl_change_address(ctx, address, RTM_DELADDR, NLM_F_REQUEST); 177 | } 178 | 179 | void rtnl_change_address(routemgr_ctx *ctx, struct in6_addr *address, int type, int flags) { 180 | struct { 181 | struct nlmsghdr nl; 182 | struct ifaddrmsg ifa; 183 | char buf[1024]; 184 | } req = { 185 | .nl = { 186 | .nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg)), 187 | .nlmsg_type = type, 188 | .nlmsg_flags = flags, 189 | }, 190 | .ifa = { 191 | .ifa_family = AF_INET6, 192 | .ifa_prefixlen = 128, 193 | .ifa_index = 1, // get the loopback index 194 | .ifa_scope = 0, 195 | } 196 | }; 197 | 198 | rtnl_addattr(&req.nl, sizeof(req), IFA_LOCAL, address, sizeof(struct in6_addr)); 199 | 200 | rtnl_talk(ctx, (struct nlmsghdr*)&req); 201 | } 202 | 203 | void routemgr_insert_neighbor(routemgr_ctx *ctx, const int ifindex, struct in6_addr *address, uint8_t mac[6]) { 204 | struct nlneighreq req = { 205 | .nl = { 206 | .nlmsg_type = RTM_NEWNEIGH, 207 | .nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_REPLACE, 208 | .nlmsg_len = NLMSG_LENGTH(sizeof(struct ndmsg)), 209 | }, 210 | .nd = { 211 | .ndm_family = AF_INET6, 212 | .ndm_state = NUD_REACHABLE, 213 | .ndm_ifindex = ifindex, 214 | }, 215 | }; 216 | 217 | rtnl_addattr(&req.nl, sizeof(req), NDA_DST, (void*)address, sizeof(struct in6_addr)); 218 | rtnl_addattr(&req.nl, sizeof(req), NDA_LLADDR, mac, sizeof(uint8_t) * 6); 219 | 220 | rtnl_talk(ctx, (struct nlmsghdr*)&req); 221 | } 222 | 223 | void routemgr_remove_neighbor(routemgr_ctx *ctx, const int ifindex, struct in6_addr *address, uint8_t mac[6]) { 224 | struct nlneighreq req = { 225 | .nl = { 226 | .nlmsg_type = RTM_NEWNEIGH, 227 | .nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_REPLACE, 228 | .nlmsg_len = NLMSG_LENGTH(sizeof(struct ndmsg)), 229 | }, 230 | .nd = { 231 | .ndm_family = AF_INET6, 232 | .ndm_state = NUD_NONE, 233 | .ndm_ifindex = ifindex, 234 | }, 235 | }; 236 | 237 | rtnl_addattr(&req.nl, sizeof(req), NDA_DST, (void*)address, sizeof(struct in6_addr)); 238 | rtnl_addattr(&req.nl, sizeof(req), NDA_LLADDR, mac, sizeof(uint8_t) * 6); 239 | 240 | rtnl_talk(ctx, (struct nlmsghdr*)&req); 241 | } 242 | 243 | void routemgr_insert_route(routemgr_ctx *ctx, const int table, const int ifindex, struct in6_addr *address) { 244 | struct nlrtreq req = { 245 | .nl = { 246 | .nlmsg_type = RTM_NEWROUTE, 247 | .nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_REPLACE, 248 | .nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)), 249 | }, 250 | .rt = { 251 | .rtm_family = AF_INET6, 252 | .rtm_table = table, 253 | .rtm_protocol = ROUTE_PROTO, 254 | .rtm_scope = RT_SCOPE_UNIVERSE, 255 | .rtm_type = RTN_UNICAST, 256 | .rtm_dst_len = 128 257 | }, 258 | }; 259 | 260 | rtnl_addattr(&req.nl, sizeof(req), RTA_DST, (void*)address, sizeof(struct in6_addr)); 261 | rtnl_addattr(&req.nl, sizeof(req), RTA_OIF, (void*)&ifindex, sizeof(ifindex)); 262 | 263 | rtnl_talk(ctx, (struct nlmsghdr *)&req); 264 | } 265 | 266 | void routemgr_remove_route(routemgr_ctx *ctx, const int table, struct in6_addr *address) { 267 | struct nlrtreq req1 = { 268 | .nl = { 269 | .nlmsg_type = RTM_NEWROUTE, 270 | .nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_REPLACE, 271 | .nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)), 272 | }, 273 | .rt = { 274 | .rtm_family = AF_INET6, 275 | .rtm_table = table, 276 | .rtm_type = RTN_THROW, 277 | .rtm_dst_len = 128 278 | } 279 | }; 280 | 281 | rtnl_addattr(&req1.nl, sizeof(req1), RTA_DST, (void*)address, sizeof(struct in6_addr)); 282 | rtnl_talk(ctx, (struct nlmsghdr *)&req1); 283 | 284 | struct nlrtreq req2 = { 285 | .nl = { 286 | .nlmsg_type = RTM_DELROUTE, 287 | .nlmsg_flags = NLM_F_REQUEST, 288 | .nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)), 289 | }, 290 | .rt = { 291 | .rtm_family = AF_INET6, 292 | .rtm_table = table, 293 | .rtm_dst_len = 128 294 | } 295 | }; 296 | 297 | rtnl_addattr(&req2.nl, sizeof(req2), RTA_DST, (void*)address, sizeof(struct in6_addr)); 298 | rtnl_talk(ctx, (struct nlmsghdr *)&req2); 299 | } 300 | 301 | void rtnl_talk(routemgr_ctx *ctx, struct nlmsghdr *req) { 302 | struct sockaddr_nl nladdr = { 303 | .nl_family = AF_NETLINK 304 | }; 305 | 306 | struct iovec iov = {req, 0}; 307 | struct msghdr msg = {&nladdr, sizeof(nladdr), &iov, 1, NULL, 0, 0}; 308 | 309 | iov.iov_len = req->nlmsg_len; 310 | 311 | if (sendmsg(ctx->fd, &msg, 0) < 0) 312 | perror("nl_sendmsg"); 313 | } 314 | 315 | void routemgr_insert_neighbor4(routemgr_ctx *ctx, const int ifindex, struct in_addr *address, uint8_t mac[6]) { 316 | struct nlneighreq req = { 317 | .nl = { 318 | .nlmsg_type = RTM_NEWNEIGH, 319 | .nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_REPLACE, 320 | .nlmsg_len = NLMSG_LENGTH(sizeof(struct ndmsg)), 321 | }, 322 | .nd = { 323 | .ndm_family = AF_INET, 324 | .ndm_state = NUD_REACHABLE, 325 | .ndm_ifindex = ifindex, 326 | }, 327 | }; 328 | 329 | rtnl_addattr(&req.nl, sizeof(req), NDA_DST, (void*)address, sizeof(struct in_addr)); 330 | rtnl_addattr(&req.nl, sizeof(req), NDA_LLADDR, mac, sizeof(uint8_t) * 6); 331 | 332 | rtnl_talk(ctx, (struct nlmsghdr*)&req); 333 | } 334 | 335 | void routemgr_remove_neighbor4(routemgr_ctx *ctx, const int ifindex, struct in_addr *address, uint8_t mac[6]) { 336 | struct nlneighreq req = { 337 | .nl = { 338 | .nlmsg_type = RTM_NEWNEIGH, 339 | .nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_REPLACE, 340 | .nlmsg_len = NLMSG_LENGTH(sizeof(struct ndmsg)), 341 | }, 342 | .nd = { 343 | .ndm_family = AF_INET, 344 | .ndm_state = NUD_NONE, 345 | .ndm_ifindex = ifindex, 346 | }, 347 | }; 348 | 349 | rtnl_addattr(&req.nl, sizeof(req), NDA_DST, (void*)address, sizeof(struct in_addr)); 350 | rtnl_addattr(&req.nl, sizeof(req), NDA_LLADDR, mac, sizeof(uint8_t) * 6); 351 | 352 | rtnl_talk(ctx, (struct nlmsghdr*)&req); 353 | } 354 | 355 | void routemgr_insert_route4(routemgr_ctx *ctx, const int table, const int ifindex, struct in_addr *address) { 356 | struct nlrtreq req = { 357 | .nl = { 358 | .nlmsg_type = RTM_NEWROUTE, 359 | .nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_REPLACE, 360 | .nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)), 361 | }, 362 | .rt = { 363 | .rtm_family = AF_INET, 364 | .rtm_table = table, 365 | .rtm_protocol = ROUTE_PROTO, 366 | .rtm_scope = RT_SCOPE_UNIVERSE, 367 | .rtm_type = RTN_UNICAST, 368 | .rtm_dst_len = 32 369 | }, 370 | }; 371 | 372 | rtnl_addattr(&req.nl, sizeof(req), RTA_DST, (void*)address, sizeof(struct in_addr)); 373 | rtnl_addattr(&req.nl, sizeof(req), RTA_OIF, (void*)&ifindex, sizeof(ifindex)); 374 | 375 | rtnl_talk(ctx, (struct nlmsghdr *)&req); 376 | } 377 | 378 | void routemgr_remove_route4(routemgr_ctx *ctx, const int table, struct in_addr *address) { 379 | struct nlrtreq req1 = { 380 | .nl = { 381 | .nlmsg_type = RTM_NEWROUTE, 382 | .nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_REPLACE, 383 | .nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)), 384 | }, 385 | .rt = { 386 | .rtm_family = AF_INET, 387 | .rtm_table = table, 388 | .rtm_type = RTN_THROW, 389 | .rtm_dst_len = 32 390 | } 391 | }; 392 | 393 | rtnl_addattr(&req1.nl, sizeof(req1), RTA_DST, (void*)address, sizeof(struct in_addr)); 394 | rtnl_talk(ctx, (struct nlmsghdr *)&req1); 395 | 396 | struct nlrtreq req2 = { 397 | .nl = { 398 | .nlmsg_type = RTM_DELROUTE, 399 | .nlmsg_flags = NLM_F_REQUEST, 400 | .nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)), 401 | }, 402 | .rt = { 403 | .rtm_family = AF_INET, 404 | .rtm_table = table, 405 | .rtm_dst_len = 32 406 | } 407 | }; 408 | 409 | rtnl_addattr(&req2.nl, sizeof(req2), RTA_DST, (void*)address, sizeof(struct in_addr)); 410 | rtnl_talk(ctx, (struct nlmsghdr *)&req2); 411 | } 412 | -------------------------------------------------------------------------------- /src/routemgr.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #define KERNEL_INFINITY 0xffff 8 | #define ROUTE_PROTO 158 9 | 10 | struct nlrtreq { 11 | struct nlmsghdr nl; 12 | struct rtmsg rt; 13 | char buf[1024]; 14 | }; 15 | 16 | struct nlneighreq { 17 | struct nlmsghdr nl; 18 | struct ndmsg nd; 19 | char buf[1024]; 20 | }; 21 | 22 | struct kernel_route { 23 | struct in6_addr prefix; 24 | int plen; 25 | struct in6_addr src_prefix; 26 | int src_plen; /* no source prefix <=> src_plen == 0 */ 27 | int metric; 28 | unsigned int ifindex; 29 | int proto; 30 | struct in6_addr gw; 31 | unsigned int table; 32 | }; 33 | 34 | typedef struct { 35 | struct l3ctx *l3ctx; 36 | int fd; 37 | } routemgr_ctx; 38 | 39 | void handle_route(routemgr_ctx *ctx, struct kernel_route *route); 40 | int parse_kernel_route_rta(struct rtmsg *rtm, int len, struct kernel_route *route); 41 | void routemgr_handle_in(routemgr_ctx *ctx, int fd); 42 | void routemgr_init(routemgr_ctx *ctx); 43 | void routemgr_insert_neighbor(routemgr_ctx *ctx, const int ifindex, struct in6_addr *address, uint8_t mac[6]); 44 | void routemgr_remove_neighbor(routemgr_ctx *ctx, const int ifindex, struct in6_addr *address, uint8_t mac[6]); 45 | void routemgr_insert_route(routemgr_ctx *ctx, const int table, const int ifindex, struct in6_addr *address); 46 | void routemgr_remove_route(routemgr_ctx *ctx, const int table, struct in6_addr *address); 47 | void routemgr_insert_neighbor4(routemgr_ctx *ctx, const int ifindex, struct in_addr *address, uint8_t mac[6]); 48 | void routemgr_remove_neighbor4(routemgr_ctx *ctx, const int ifindex, struct in_addr *address, uint8_t mac[6]); 49 | void routemgr_insert_route4(routemgr_ctx *ctx, const int table, const int ifindex, struct in_addr *address); 50 | void routemgr_remove_route4(routemgr_ctx *ctx, const int table, struct in_addr *address); 51 | void rtnl_add_address(routemgr_ctx *ctx, struct in6_addr *address); 52 | void rtnl_remove_address(routemgr_ctx *ctx, struct in6_addr *address); 53 | -------------------------------------------------------------------------------- /src/taskqueue.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012-2016, Matthias Schiffer 3 | Copyright (c) 2016, Nils Schneider 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, 10 | this list of conditions and the following disclaimer. 11 | 2. Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 19 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 22 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | */ 26 | 27 | #include 28 | #include 29 | #include 30 | 31 | #include "taskqueue.h" 32 | #include "error.h" 33 | #include "timespec.h" 34 | 35 | void taskqueue_init(taskqueue_ctx *ctx) { 36 | ctx->fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK); 37 | ctx->queue = NULL; 38 | } 39 | 40 | /** Enqueues a new task. A task with a timeout of zero is scheduled immediately. */ 41 | taskqueue_t * post_task(taskqueue_ctx *ctx, unsigned int timeout, void (*function)(void*), void (*cleanup)(void*), void *data) { 42 | taskqueue_t *task = calloc(1, sizeof(taskqueue_t)); 43 | 44 | clock_gettime(CLOCK_MONOTONIC, &task->due); 45 | 46 | task->due.tv_sec += timeout; 47 | task->function = function; 48 | task->cleanup = cleanup; 49 | task->data = data; 50 | 51 | taskqueue_insert(&ctx->queue, task); 52 | taskqueue_schedule(ctx); 53 | 54 | return task; 55 | } 56 | 57 | /** Changes the timeout of a task. 58 | */ 59 | bool reschedule_task(taskqueue_ctx *ctx, taskqueue_t *task, unsigned int timeout) { 60 | if (task == NULL || !taskqueue_linked(task)) 61 | return false; 62 | 63 | struct timespec due; 64 | clock_gettime(CLOCK_MONOTONIC, &due); 65 | due.tv_sec += timeout; 66 | 67 | if (timespec_cmp(due, task->due)) { 68 | task->due = due; 69 | taskqueue_remove(task); 70 | taskqueue_insert(&ctx->queue, task); 71 | taskqueue_schedule(ctx); 72 | } 73 | 74 | return true; 75 | } 76 | 77 | void taskqueue_schedule(taskqueue_ctx *ctx) { 78 | if (ctx->queue == NULL) 79 | return; 80 | 81 | struct itimerspec t = { 82 | .it_value = ctx->queue->due 83 | }; 84 | 85 | timerfd_settime(ctx->fd, TFD_TIMER_ABSTIME, &t, NULL); 86 | } 87 | 88 | void taskqueue_run(taskqueue_ctx *ctx) { 89 | unsigned long long nEvents; 90 | 91 | struct timespec now; 92 | clock_gettime(CLOCK_MONOTONIC, &now); 93 | 94 | read(ctx->fd, &nEvents, sizeof(nEvents)); 95 | 96 | if (ctx->queue == NULL) 97 | return; 98 | 99 | taskqueue_t *task = ctx->queue; 100 | 101 | if (timespec_cmp(task->due, now) <= 0) { 102 | taskqueue_remove(task); 103 | task->function(task->data); 104 | 105 | if (task->cleanup != NULL) 106 | task->cleanup(task->data); 107 | 108 | free(task); 109 | } 110 | 111 | taskqueue_schedule(ctx); 112 | } 113 | 114 | /** Links an element at the position specified by \e queue */ 115 | static inline void taskqueue_link(taskqueue_t **queue, taskqueue_t *elem) { 116 | if (elem->next) 117 | exit_bug("taskqueue_link: element already linked"); 118 | 119 | elem->pprev = queue; 120 | elem->next = *queue; 121 | if (elem->next) 122 | elem->next->pprev = &elem->next; 123 | 124 | *queue = elem; 125 | } 126 | 127 | /** Unlinks an element */ 128 | static inline void taskqueue_unlink(taskqueue_t *elem) { 129 | *elem->pprev = elem->next; 130 | if (elem->next) 131 | elem->next->pprev = elem->pprev; 132 | 133 | elem->next = NULL; 134 | } 135 | 136 | /** 137 | Merges two priority queues 138 | 139 | \e queue2 may be empty (NULL) 140 | */ 141 | static taskqueue_t * taskqueue_merge(taskqueue_t *queue1, taskqueue_t *queue2) { 142 | if (!queue1) 143 | exit_bug("taskqueue_merge: queue1 unset"); 144 | if (queue1->next) 145 | exit_bug("taskqueue_merge: queue2 has successor"); 146 | if (!queue2) 147 | return queue1; 148 | if (queue2->next) 149 | exit_bug("taskqueue_merge: queue2 has successor"); 150 | 151 | taskqueue_t *lo, *hi; 152 | 153 | if (timespec_cmp(queue1->due, queue2->due) < 0) { 154 | lo = queue1; 155 | hi = queue2; 156 | } 157 | else { 158 | lo = queue2; 159 | hi = queue1; 160 | } 161 | 162 | taskqueue_link(&lo->children, hi); 163 | 164 | return lo; 165 | } 166 | 167 | /** Merges a list of priority queues */ 168 | static taskqueue_t * taskqueue_merge_pairs(taskqueue_t *queue0) { 169 | if (!queue0) 170 | return NULL; 171 | 172 | if (!queue0->pprev) 173 | exit_bug("taskqueue_merge_pairs: unlinked queue"); 174 | 175 | taskqueue_t *queue1 = queue0->next; 176 | 177 | if (!queue1) 178 | return queue0; 179 | 180 | taskqueue_t *queue2 = queue1->next; 181 | 182 | queue0->next = queue1->next = NULL; 183 | 184 | return taskqueue_merge(taskqueue_merge(queue0, queue1), taskqueue_merge_pairs(queue2)); 185 | } 186 | 187 | /** Inserts a new element into a priority queue */ 188 | void taskqueue_insert(taskqueue_t **queue, taskqueue_t *elem) { 189 | if (elem->pprev || elem->next || elem->children) 190 | exit_bug("taskqueue_insert: tried to insert linked queue element"); 191 | 192 | *queue = taskqueue_merge(elem, *queue); 193 | (*queue)->pprev = queue; 194 | } 195 | 196 | /** Removes an element from a priority queue */ 197 | void taskqueue_remove(taskqueue_t *elem) { 198 | if (!taskqueue_linked(elem)) { 199 | if (elem->children || elem->next) 200 | exit_bug("taskqueue_remove: corrupted queue item"); 201 | 202 | return; 203 | } 204 | 205 | taskqueue_t **pprev = elem->pprev; 206 | 207 | taskqueue_unlink(elem); 208 | 209 | taskqueue_t *merged = taskqueue_merge_pairs(elem->children); 210 | if (merged) 211 | taskqueue_link(pprev, merged); 212 | 213 | elem->pprev = NULL; 214 | elem->children = NULL; 215 | } 216 | -------------------------------------------------------------------------------- /src/taskqueue.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | typedef struct taskqueue taskqueue_t; 7 | 8 | typedef struct { 9 | struct l3ctx *l3ctx; 10 | int fd; 11 | taskqueue_t *queue; 12 | } taskqueue_ctx; 13 | 14 | /** Element of a priority queue */ 15 | struct taskqueue { 16 | taskqueue_t **pprev; /**< \e next element of the previous element (or \e children of the parent) */ 17 | taskqueue_t *next; /**< Next sibling in the heap */ 18 | 19 | taskqueue_t *children; /**< Heap children */ 20 | 21 | struct timespec due; /**< The priority */ 22 | 23 | void (*function)(void*); 24 | void (*cleanup)(void*); 25 | void *data; 26 | }; 27 | 28 | /** Checks if an element is currently part of a priority queue */ 29 | static inline bool taskqueue_linked(taskqueue_t *elem) { 30 | return elem->pprev; 31 | } 32 | 33 | void taskqueue_insert(taskqueue_t **queue, taskqueue_t *elem); 34 | void taskqueue_remove(taskqueue_t *elem); 35 | 36 | void taskqueue_init(taskqueue_ctx *ctx); 37 | void taskqueue_run(taskqueue_ctx *ctx); 38 | void taskqueue_schedule(taskqueue_ctx *ctx); 39 | taskqueue_t * post_task(taskqueue_ctx *ctx, unsigned int timeout, void (*function)(void*), void (*cleanup)(void*), void *data); 40 | bool reschedule_task(taskqueue_ctx *ctx, taskqueue_t *task, unsigned int timeout); 41 | -------------------------------------------------------------------------------- /src/timespec.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int timespec_cmp(struct timespec a, struct timespec b) { 4 | if (a.tv_sec < b.tv_sec ) return -1; 5 | else if (a.tv_sec > b.tv_sec ) return +1; 6 | else if (a.tv_nsec < b.tv_nsec) return -1; 7 | else if (a.tv_nsec > b.tv_nsec) return +1; 8 | 9 | return 0 ; 10 | } 11 | -------------------------------------------------------------------------------- /src/timespec.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | int timespec_cmp(struct timespec a, struct timespec b); 4 | -------------------------------------------------------------------------------- /src/vector.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012-2015, Matthias Schiffer 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 | 1. Redistributions of source code must retain the above copyright notice, 9 | this list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | */ 25 | 26 | /** 27 | \file 28 | 29 | Typesafe dynamically sized arrays 30 | */ 31 | 32 | 33 | #include "vector.h" 34 | #include "alloc.h" 35 | 36 | #include 37 | 38 | 39 | /** The minimum number of elements to allocate even when less elements are used */ 40 | #define MIN_VECTOR_ALLOC 4 41 | 42 | 43 | /** 44 | Resizes a vector 45 | 46 | Vector allocations are always powers of 2. 47 | 48 | Internal function, use VECTOR_RESIZE() instead. 49 | */ 50 | void _l3roamd_vector_resize(l3roamd_vector_desc_t *desc, void **data, size_t n, size_t elemsize) { 51 | desc->length = n; 52 | 53 | size_t alloc = desc->allocated; 54 | 55 | if (!alloc) { 56 | alloc = MIN_VECTOR_ALLOC; 57 | n = n*3/2; 58 | } 59 | 60 | while (alloc < n) 61 | alloc <<= 1; 62 | 63 | if (alloc != desc->allocated) { 64 | desc->allocated = alloc; 65 | *data = l3roamd_realloc(*data, alloc * elemsize); 66 | } 67 | } 68 | 69 | /** 70 | Inserts an element into a vector 71 | 72 | Internal function, use VECTOR_INSERT() and VECTOR_ADD() instead. 73 | */ 74 | void _l3roamd_vector_insert(l3roamd_vector_desc_t *desc, void **data, void *element, size_t pos, size_t elemsize) { 75 | _l3roamd_vector_resize(desc, data, desc->length+1, elemsize); 76 | 77 | void *p = *data + pos*elemsize; 78 | 79 | memmove(p+elemsize, p, (desc->length-pos-1)*elemsize); 80 | memcpy(p, element, elemsize); 81 | } 82 | 83 | /** 84 | Deletes an element from a vector 85 | 86 | Internal function, use VECTOR_DELETE() instead. 87 | */ 88 | void _l3roamd_vector_delete(l3roamd_vector_desc_t *desc, void **data, size_t pos, size_t elemsize) { 89 | void *p = *data + pos*elemsize; 90 | memmove(p, p+elemsize, (desc->length-pos-1)*elemsize); 91 | 92 | _l3roamd_vector_resize(desc, data, desc->length-1, elemsize); 93 | } 94 | -------------------------------------------------------------------------------- /src/vector.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012-2015, Matthias Schiffer 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 | 1. Redistributions of source code must retain the above copyright notice, 9 | this list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | */ 25 | 26 | /** 27 | \file 28 | 29 | Typesafe dynamically sized arrays 30 | */ 31 | 32 | 33 | #pragma once 34 | 35 | #include 36 | 37 | 38 | /** A vector descriptor */ 39 | typedef struct l3roamd_vector_desc { 40 | size_t allocated; /**< The number of elements currently allocated */ 41 | size_t length; /**< The actual number of elements in the vector */ 42 | } l3roamd_vector_desc_t; 43 | 44 | 45 | /** 46 | A type for a vector of \e type elements 47 | 48 | \hideinitializer 49 | */ 50 | #define VECTOR(type) \ 51 | struct { \ 52 | l3roamd_vector_desc_t desc; \ 53 | type *data; \ 54 | } 55 | 56 | 57 | 58 | void _l3roamd_vector_resize(l3roamd_vector_desc_t *desc, void **data, size_t n, size_t elemsize); 59 | void _l3roamd_vector_insert(l3roamd_vector_desc_t *desc, void **data, void *element, size_t pos, size_t elemsize); 60 | void _l3roamd_vector_delete(l3roamd_vector_desc_t *desc, void **data, size_t pos, size_t elemsize); 61 | 62 | 63 | /** 64 | Resizes the vector \e a to \e n elements 65 | 66 | \hideinitializer 67 | */ 68 | #define VECTOR_RESIZE(v, n) ({ \ 69 | __typeof__(v) *_v = &(v); \ 70 | _l3roamd_vector_resize(&_v->desc, (void **)&_v->data, (n), sizeof(*_v->data)); \ 71 | }) 72 | 73 | /** 74 | Frees all resources used by the vector \e v 75 | 76 | \hideinitializer 77 | */ 78 | #define VECTOR_FREE(v) free((v).data) 79 | 80 | /** 81 | Returns the number of elements in the vector \e v 82 | 83 | \hideinitializer 84 | */ 85 | #define VECTOR_LEN(v) ((v).desc.length) 86 | 87 | /** 88 | Returns the element with index \e i in the vector \e v 89 | 90 | \hideinitializer 91 | */ 92 | #define VECTOR_INDEX(v, i) ((v).data[i]) 93 | 94 | /** 95 | Returns a pointer to the vector elements of \e v 96 | 97 | \hideinitializer 98 | */ 99 | #define VECTOR_DATA(v) ((v).data) 100 | 101 | /** 102 | Inserts the element \e elem at index \e pos of vector \e v 103 | 104 | \hideinitializer 105 | */ 106 | #define VECTOR_INSERT(v, elem, pos) ({ \ 107 | __typeof__(v) *_v = &(v); \ 108 | __typeof__(*_v->data) _e = (elem); \ 109 | _l3roamd_vector_insert(&_v->desc, (void **)&_v->data, &_e, (pos), sizeof(_e)); \ 110 | }) 111 | 112 | /** 113 | Adds the element \e elem at the end of vector \e v 114 | 115 | \hideinitializer 116 | */ 117 | #define VECTOR_ADD(v, elem) ({ \ 118 | __typeof__(v) *_v = &(v); \ 119 | __typeof__(*_v->data) _e = (elem); \ 120 | _l3roamd_vector_insert(&_v->desc, (void **)&_v->data, &_e, _v->desc.length, sizeof(_e)); \ 121 | }) 122 | 123 | /** 124 | Deletes the element at index \e pos of vector \e v 125 | 126 | \hideinitializer 127 | */ 128 | #define VECTOR_DELETE(v, pos) ({ \ 129 | __typeof__(v) *_v = &(v); \ 130 | _l3roamd_vector_delete(&_v->desc, (void **)&_v->data, (pos), sizeof(*_v->data)); \ 131 | }) 132 | 133 | /** 134 | Performs a binary search on the vector \e v, returning a pointer to a matching vector element 135 | 136 | \hideinitializer 137 | */ 138 | #define VECTOR_BSEARCH(key, v, cmp) ({ \ 139 | __typeof__(v) *_v = &(v); \ 140 | const __typeof__(*_v->data) *_key = (key); \ 141 | int (*_cmp)(__typeof__(_key), __typeof__(_key)) = (cmp); \ 142 | (__typeof__(_v->data))bsearch(_key, _v->data, _v->desc.length, sizeof(*_v->data), (int (*)(const void *, const void *))_cmp); \ 143 | }) 144 | -------------------------------------------------------------------------------- /src/wifistations.c: -------------------------------------------------------------------------------- 1 | #include "genl.h" 2 | #include "error.h" 3 | #include "wifistations.h" 4 | #include "clientmgr.h" 5 | #include "l3roamd.h" 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #define NL80211_CMD_NEW_STATION 19 24 | #define NL80211_CMD_DEL_STATION 20 25 | #define NL80211_ATTR_IFINDEX 3 26 | #define NL80211_ATTR_MAC 6 27 | 28 | static int no_seq_check(struct nl_msg *msg, void *arg) { 29 | return NL_OK; 30 | } 31 | 32 | void mac_addr_n2a(char *mac_addr, unsigned char *arg) { 33 | int i, l; 34 | 35 | l = 0; 36 | for (i = 0; i < 6; i++) { 37 | if (i == 0) { 38 | sprintf(mac_addr+l, "%02x", arg[i]); 39 | l += 2; 40 | } else { 41 | sprintf(mac_addr+l, ":%02x", arg[i]); 42 | l += 3; 43 | } 44 | } 45 | } 46 | 47 | void wifistations_handle_in(wifistations_ctx *ctx) { 48 | nl_recvmsgs(ctx->nl_sock, ctx->cb); 49 | } 50 | 51 | int wifistations_handle_event(struct nl_msg *msg, void *arg) { 52 | wifistations_ctx *ctx = arg; 53 | struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); 54 | struct nlattr *tb[8]; 55 | char macbuf[6*3]; 56 | 57 | 58 | // TODO filtern auf interfaces, die uns interessieren 59 | // TODO liste von interfaces pflegen (netlink) 60 | 61 | printf("event %i\n", gnlh->cmd); 62 | 63 | char ifname[100]; 64 | 65 | nla_parse(tb, 8, genlmsg_attrdata(gnlh, 0), 66 | genlmsg_attrlen(gnlh, 0), NULL); 67 | 68 | unsigned int ifindex = nla_get_u32(tb[NL80211_ATTR_IFINDEX]); 69 | 70 | if_indextoname(ifindex, ifname); 71 | printf("%s: ", ifname); 72 | 73 | // TODO warum kann das NULL sein? 74 | if (gnlh == NULL) 75 | return 0; 76 | 77 | switch (gnlh->cmd) { 78 | case NL80211_CMD_NEW_STATION: 79 | mac_addr_n2a(macbuf, nla_data(tb[NL80211_ATTR_MAC])); 80 | 81 | printf("new station %s\n", macbuf); 82 | 83 | // TODO Hack for br-client 84 | ifindex = ctx->l3ctx->icmp6_ctx.ifindex; 85 | clientmgr_notify_mac(CTX(clientmgr), nla_data(tb[NL80211_ATTR_MAC]), ifindex); 86 | break; 87 | case NL80211_CMD_DEL_STATION: 88 | break; 89 | } 90 | 91 | return 0; 92 | } 93 | 94 | void wifistations_init(wifistations_ctx *ctx) { 95 | ctx->nl_sock = nl_socket_alloc(); 96 | if (!ctx->nl_sock) 97 | exit_error("Failed to allocate netlink socket.\n"); 98 | 99 | nl_socket_set_buffer_size(ctx->nl_sock, 8192, 8192); 100 | 101 | if (genl_connect(ctx->nl_sock)) { 102 | fprintf(stderr, "Failed to connect to generic netlink.\n"); 103 | goto fail; 104 | } 105 | 106 | int nl80211_id = genl_ctrl_resolve(ctx->nl_sock, "nl80211"); 107 | if (nl80211_id < 0) { 108 | fprintf(stderr, "nl80211 not found.\n"); 109 | goto fail; 110 | } 111 | 112 | /* MLME multicast group */ 113 | int mcid = nl_get_multicast_id(ctx->nl_sock, "nl80211", "mlme"); 114 | if (mcid >= 0) { 115 | int ret = nl_socket_add_membership(ctx->nl_sock, mcid); 116 | if (ret) 117 | goto fail; 118 | } 119 | 120 | ctx->cb = nl_cb_alloc(NL_CB_DEFAULT); 121 | 122 | if (!ctx->cb) 123 | exit_error("failed to allocate netlink callbacks\n"); 124 | 125 | /* no sequence checking for multicast messages */ 126 | nl_cb_set(ctx->cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, no_seq_check, NULL); 127 | nl_cb_set(ctx->cb, NL_CB_VALID, NL_CB_CUSTOM, wifistations_handle_event, ctx); 128 | 129 | ctx->fd = nl_socket_get_fd(ctx->nl_sock); 130 | 131 | return; 132 | 133 | fail: 134 | nl_socket_free(ctx->nl_sock); 135 | exit_error("Could not open nl80211 socket"); 136 | } 137 | -------------------------------------------------------------------------------- /src/wifistations.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "vector.h" 4 | #include 5 | 6 | typedef struct { 7 | char *ifname; 8 | unsigned int ifindex; 9 | bool ok; 10 | } wifistations_if; 11 | 12 | typedef struct { 13 | struct l3ctx *l3ctx; 14 | int fd; 15 | struct nl_sock *nl_sock; 16 | struct nl_cb *cb; 17 | VECTOR(wifistations_if) interfaces; 18 | } wifistations_ctx; 19 | 20 | void wifistations_handle_in(wifistations_ctx *ctx); 21 | void wifistations_init(wifistations_ctx *ctx); 22 | --------------------------------------------------------------------------------