├── README.md ├── config └── dnsmasq │ └── dnsmasq-custom.conf ├── interfaces-example ├── eth1-conf-dhcp └── eth1-conf-static ├── scripts ├── dhclient │ └── multihomed-routing.sh └── interfaces-static │ └── multihomed-routing-ifup ├── table_allocator ├── CMakeLists.txt ├── LICENSE ├── README.md ├── client │ ├── CMakeLists.txt │ ├── include │ │ ├── table_allocator_client.h │ │ └── table_allocator_client_netlink.h │ └── src │ │ ├── table_allocator_client.c │ │ └── table_allocator_client_netlink.c ├── server │ ├── CMakeLists.txt │ ├── deb-files │ │ ├── changelog │ │ └── conffiles │ ├── files │ │ ├── server_config.json │ │ └── systemd │ │ │ ├── postinst │ │ │ ├── prerm │ │ │ └── table-allocator-server.service │ ├── include │ │ ├── table_allocator_server.h │ │ ├── table_allocator_server_clients.h │ │ ├── table_allocator_server_sockets.h │ │ └── table_allocator_server_sqlite.h │ └── src │ │ ├── table_allocator_server.c │ │ ├── table_allocator_server_clients.c │ │ ├── table_allocator_server_sockets.c │ │ └── table_allocator_server_sqlite.c └── shared │ ├── CMakeLists.txt │ ├── include │ ├── table_allocator_shared_json.h │ ├── table_allocator_shared_libuv_helpers.h │ ├── table_allocator_shared_log.h │ └── table_allocator_shared_socket_helpers.h │ └── src │ ├── table_allocator_shared_json.c │ ├── table_allocator_shared_libuv_helpers.c │ └── table_allocator_shared_socket_helpers.c └── udev ├── 99-network-cleanup.rules └── network-cleanup.sh /README.md: -------------------------------------------------------------------------------- 1 | # Automatic routing configuration on multihomed Linux-hosts 2 | 3 | Linux supports multihoming out of the box. However, when a Linux‐device is 4 | connected to multiple networks simultaneously, the kernel will often be unable 5 | to make use of all the different networks due to overlapping routes (typically 6 | the default routes). Configuring routing on multihomed hosts requires special 7 | care, and is in most cases not handled properly by the default set of 8 | networking/routing tools installed on a host. In this document, I will present 9 | a solution that can be used to automatically configure routing correctly on 10 | multihomed Linux hosts. The solution integrates with existing tools like 11 | NetworkManager and ifupdown, so you will in most cases be able to use the tools 12 | of your choice to configure the interface. 13 | 14 | Comments, questions and feedback are always welcome! 15 | 16 | ## TL;DR 17 | 18 | * Install the table allocator client and server, potentially update server 19 | configuration. 20 | 21 | * Decide how you want to configure your interface (ifupdown, NetworkManager, or 22 | something else). 23 | 24 | * If you use ifupdown or NetworkManager, install either if-up-script of 25 | dhclient-hook. If you use another tool for configuring the interfaces, you need to write a 26 | script (contributions are welcome!). You can probably use the existing scripts as inspiration. 27 | 28 | * If you use ifupdown, create stanzas for your interfaces. 29 | 30 | * Read the section on DNS if you want fully functional, multihomed name 31 | resolving. 32 | 33 | * Start the interface(s). 34 | 35 | ## Motivation 36 | 37 | The work presented here was supported by the EU-funded research-project 38 | [MONROE](https://www.monroe-project.eu/), which is building a testbed 39 | consisting of multihomed test nodes connected to different mobile broadband 40 | provides, WAN and WIFI. Up until now, a tool called 41 | [multi](https://github.com/kristrev/multi) has been used to configure routing 42 | on the hosts. While multi has done its job well and been an invaluable tool in 43 | getting MONROE up and running, it is starting to show its age and some of the 44 | choices I made when I wrote the tool is starting to be a pain to deal with. The 45 | tool was written by me during my PhD-work and since I was young(-ish), naive 46 | and over-ambitious, multi became a hybrid between a network manager and routing 47 | daemon. This resulted in stuff like implementing a small DHCP client inside the 48 | tool. 49 | 50 | In order to support more advanced network configuration, IPv6, etc., we decided 51 | on designing and building a new routing solution for the nodes. Instead of 52 | trying to duplicate functionality available elsewhere, the new multihomed 53 | routing solution builds on as many existing components as possible. Interfaces 54 | will be configured by updating for example /etc/network/interfaces, and instead 55 | of using multi for DHCP, etc., we will instead create routing configuration 56 | scripts that will be run when a lease is acquired or interface goes down. 57 | 58 | ## Routing overview 59 | 60 | When configuring routing on multihomed hosts there are two approaches to choose 61 | from. One is to keep all routes in the same table and assign the routes 62 | belonging to different interfaces different metrics. The second is to assign 63 | each interface (address) a separate routing table, and then have a set of rules 64 | for which traffic should result in lookups in which tables. This approach is 65 | known as [policy based 66 | routing](https://en.wikipedia.org/wiki/Policy-based_routing) and is what we 67 | have based our configuration solution on. In addition to providing a degree of 68 | separation between the different interfaces, policy based routing supports, as 69 | the name implies, implementing more advanced routing policies. One can for 70 | example configure the routing so that traffic from a container or application 71 | (on recent kernels) only trigger lookups in one routing table. In MONROE, the 72 | experiments are deployed as Docker-containers, and routing policies can be used 73 | to "bind" one container to one operator. 74 | 75 | With our solution, three rules are created per interface (address): 76 | 77 | * The first rule (with priority 10000) is for traffic bound to a specific 78 | address/interfaces (a bound socket for example). Each route contains both 79 | interface and source address, so that routing will work even when there are 80 | multiple interfaces with the same address present. The rules are on the format `from X/Y lookup 100`. The reason for including the netmask is so that routing 81 | traffic through the node will work out of the box. 82 | 83 | * The second rule (with priority 20000) is for traffic destined to a network. 84 | These rules are required for routing traffic to the different networks 85 | correctly, for example required when communicating with some other equipment 86 | like a DNS server or router. 87 | 88 | * The third rule (with priority 91000) is the match all rule, which is used to 89 | route unbound traffic. The order of the 91000-rules depend on the order in 90 | which interfaces came up, so there is no guarantee that the current route for 91 | unbound traffic has internet connectivity. A separate tool which checks the 92 | connections and maintains an unbound traffic rule (with a higher priority than 91000) is required if this guarantee is desirable. 93 | 94 | On my machine, the rules looks as follows: 95 | 96 | ``` 97 | kristrev@kristrev-ThinkPad-X1-Carbon-2nd:~$ ip rule 98 | 0: from all lookup local 99 | 220: from all lookup 220 100 | 10000: from 172.16.5.187 lookup 10001 101 | 10000: from 192.168.5.113 lookup 10000 102 | 20000: from all to 172.16.5.187/22 lookup 10001 103 | 20000: from all to 192.168.5.113/24 lookup 10000 104 | 32766: from all lookup main 105 | 32767: from all lookup default 106 | 91000: from all iif lo lookup 10001 107 | 91000: from all iif lo lookup 10000 108 | ``` 109 | 110 | And a routing table: 111 | 112 | ``` 113 | kristrev@kristrev-ThinkPad-X1-Carbon-2nd:~$ ip ro show table 10000 114 | default via 192.168.5.1 dev eth1 src 192.168.5.113 115 | 192.168.5.0/24 dev eth1 scope link src 192.168.5.113 116 | ``` 117 | 118 | ## Configuring interfaces 119 | 120 | Interfaces can in most cases be configured using your tool of choice (for 121 | example ifupdown or through NetworkManager). If you already know how to 122 | configure your interfaces, or just trust for example NetworkManager, you can 123 | skip the rest of this section. 124 | 125 | In order to save space on the MONROE-nodes we do not have NetworkManager 126 | installed (and we don't need it), so we chose the ifupdown-approach. Static 127 | interfaces are easy to handle, but on our hosts network interfaces are added 128 | and removed while the system is running (for example USB modems). Fortunately, 129 | /etc/network/interfaces supports "source" and "source-directory" stanzas (see 130 | [man 5 131 | interfaces](https://manpages.debian.org/jessie/ifupdown/interfaces.5.en.html)), 132 | enabling the interface configuration to be split across multiple files and 133 | directories. Dynamically adding and removing files works, any changes are 134 | picked up by the ifup/ifdown tools. Our source-directory stanza is as follows: 135 | 136 | `source-directory /tmp/network-conf` 137 | 138 | Where to keep the additional configuration files and how and when to 139 | create/remove them depends on the user-case and system configuration. In 140 | MONROE, we store the configuration files in /var/run/network-conf and these 141 | files are generated/removed when interfaces are connect/disconnect. We assume 142 | that all dynamic interfaces will be configured using DHCP, so all files have 143 | the following format: 144 | 145 | `iface inet dhcp` 146 | 147 | ## Table Allocator 148 | 149 | Routing tables must be unique to each interface (address), and the role of the 150 | Table Allocator is to distribute these tables. The Allocator consists of a 151 | server and a client, and the code can be found 152 | [here](https://github.com/kristrev/multihomed-routing/tree/master/table_allocator). 153 | A client requests a table lease for a given address family (support for v4, v6 154 | and unspec), interface and address. If there are tables available, the server 155 | will reply with the routing table allocated to this client and the client will 156 | refresh the lease at a given interval (currently half the lease time). If a 157 | lease is not renewed or explicitly removed, then the server will automatically 158 | removed it. 159 | 160 | There is nothing really special going on inside these applications. 161 | Communication goes over domain sockets (datagram) using abstract naming for 162 | addresses (authentication is on the todo-list) and the messages are JSON 163 | objects. The server uses sqlite3 for persistent lease storage and a bitmap for 164 | quick allocation/release, and can as of now only handle one client at a time. A 165 | single client at the time is fine for now, but is the first thing that should 166 | be fixed if (when) scalability becomes an issue. 167 | 168 | The client will set up the three routing rules mentioned above when a table has 169 | been allocated for a v4 address (v6 is coming), and remove them (and release 170 | the lease + exit) if either address or interface is removed. If the client 171 | fails to get a lease, it will try five times (with a two second timeout between 172 | retries) before giving up. When the client gets a table lease, it will by 173 | default move to the background. 174 | 175 | If no lease is acquired, the client will output 0 (as opposed to the allocated 176 | routing table). All the configuration scripts (next section) will interpret 177 | this as that routes should be added to the main routing table, with the 178 | interface index as metric. This is a fallback to ensure that the device has a 179 | working (albeit incorrect) routing configuration, and you should have a 180 | watchdog or something checking for default routes in the main table and take 181 | action (for example restart server). 182 | 183 | ## Configuring routing 184 | 185 | Routing is configured using shell-scripts that are run when an address is 186 | acquired/lost. Which script(s) to use depends on how you have chosen to 187 | configure your network interfaces. We currently support ifupdown and 188 | NetworkManager and our scripts are limited to IPv4 so far, but IPv6 is coming 189 | very soon (contributions are always welcome). 190 | 191 | ### ifupdown 192 | 193 | #### Static address 194 | 195 | In order to configure routing for interfaces with a static address, we have 196 | created an ifup-script (found 197 | [here](https://github.com/kristrev/multihomed-routing/blob/master/scripts/interfaces-static/multihomed-routing-ifup)). 198 | This script must be installed in `/etc/network/if-up.d/` (at least on 199 | Debian/Ubuntu) and will be run every time an interface with a static IP has 200 | been set as up. Technically it will run for every interface that is set as up, 201 | but guards are in place so that unless the script is called with a static 202 | address or from NetworkManager (more about that later) it will exit immediately. 203 | 204 | When the script is run, addresses, etc. is already assigned to the interface and 205 | routes are configured. Thus, we need to request a table from the Table 206 | Allocator and move the already added routes to the correct table. This is done 207 | in four commands, two `ip -4 ro del` and two `ip -4 ro add`. Since the Table 208 | Allocator Client is responsible for managing the rules and routes are deleted 209 | automatically when an interface is removed/we run ifdown, no 210 | if-down.d-script is needed. 211 | 212 | #### DHCP 213 | 214 | Our script for use when DHCP (using dhclient) is used is implemented as a dhclient enter hook. The script can be found 215 | [here](https://github.com/kristrev/multihomed-routing/blob/master/scripts/dhclient/multihomed-routing.sh) 216 | and must be installed to /etc/dhcp/dhclient-enter-hooks.d/multihomed-routing 217 | (note the .sh has to be removed). We handle the *BOUND*, *RENEW*, *REBIND*, 218 | *REBOOT*, *TIMEOUT*, *EXPIRE*, *FAIL*, *RELEASE* and *STOP*-states. In the five 219 | first states, we configure routing much the same way as in the script used for 220 | static addresses (most of the complexity comes from handling the different DHCP 221 | states) and update our resolv-file (more about that later). In the last three 222 | states, we clean up the resolv-file. 223 | 224 | ### NetworkManager 225 | 226 | NetworkManager will, via. one of its dispatcher scripts 227 | (/etc/NetworkManager/dispatcher.d/01ifupdown on my Ubuntu-machine), run the 228 | scripts in if-up.d (and if-post-down.d). We have added a NetworkManager-path to 229 | the script mentioned under "ifupdown - Static address", so if you install this 230 | script then routing will be configured correctly when NetworkManager is used. 231 | 232 | Note that NetworkManager has its own algorithm(s) for determining which 233 | interface should be the default interface, by setting different metrics on the 234 | default routes (and other routes) added to the main routing table. We respect 235 | these metrics and do not delete any routes from the main routing table. The 236 | 91000-rules mentioned has in other words no effect when NetworkManager is used. 237 | 238 | ## DNS 239 | 240 | Using the default resolver and code for creating/updating resolv-conf, DNS 241 | requests will either be sent to one or all servers, and you have no control 242 | over which interface/address is used to communicate with a server. If for 243 | example a server has an address outisde the network of the interface, then the 244 | default route will be used. If the default route is over a different interface 245 | than the one that acquired the server, then requests will in many cases be 246 | dropped since they have a different source address than what the server 247 | expects. 248 | 249 | Fortunately, [dnsmasq](http://www.thekelleys.org.uk/dnsmasq/doc.html) supports 250 | specifying which interface and address to be used to communicate with a server. 251 | If you want to set up DNS to work correctly, you can do 252 | the following: 253 | 254 | * Install dnsmasq and configure it to be the default resolver. The steps for 255 | Debian can be found [here](https://wiki.debian.org/HowTo/dnsmasq). 256 | 257 | * We will keep our config in a separate config file. Uncomment the last line in 258 | /etc/dnsmasq.conf, so that /etc/dnsmasq.d/\*.conf is included when dnsmasq 259 | starts. 260 | 261 | * Copy the 262 | [following](https://github.com/kristrev/multihomed-routing/blob/master/config/dnsmasq/dnsmasq-custom.conf) 263 | file into /etc/dnsmasq.d/. You can of course call it something else. 264 | 265 | * We will write the DNS servers to a special dnsmasq-file, known as the servers 266 | file. Update the desired path to this file in the configuration file you just 267 | copied (*servers-file*). 268 | 269 | * So far, we only write servers that we acquire when dhclient is used. If you 270 | change the path of the servers file, update the dhclient-hook 271 | (*DNSMASQ_SERVER_PATH*-variable). 272 | 273 | When an interface comes up or goes down, the servers-file is update. We also 274 | signal dnsmasq (*SIGHUP*), which causes the application to reload the list of 275 | available DNS servers. 276 | 277 | ## Misc. 278 | 279 | ### ifupdown and hotplug 280 | 281 | ifupdown does not deal very well with hotplugging. ifup is easy to solve, you for 282 | example have a tool which listens for USB connect events and does ifup or a 283 | udev rule. ifdown, on the other hand, is harder. If you try to do ifdown on a 284 | non-existent interface, you will just be told that ifdown will ignore the 285 | interface (it is unknown). However, for example dhclient and proper clean-up 286 | will not be performed. In order to work around this issue, we created a udev 287 | rule which runs a script when a *remove* event happens. The script stops 288 | dhclient for that interface and removes any entry from the dnsmasq 289 | servers-file. Script and rule can be found 290 | [here](https://github.com/kristrev/multihomed-routing/tree/master/udev). 291 | 292 | ## Summary 293 | 294 | By following this document, installing the tools, scripts and configuring them 295 | when needed, you should have a system which will automatically configure 296 | multihomed routing correctly. If you have any comments, questions or 297 | contributions, you can either send me an 298 | [email](mailto:kristian.evensen@gmail.com) or create an issue/PR. 299 | 300 | This work was was funded by the H2020-project [MONROE](https://www.monroe-project.eu/). 301 | -------------------------------------------------------------------------------- /config/dnsmasq/dnsmasq-custom.conf: -------------------------------------------------------------------------------- 1 | listen-address=127.0.0.1 2 | server=8.8.8.8 3 | server=8.8.4.4 4 | servers-file=/tmp/dnsmasq-servers.conf 5 | -------------------------------------------------------------------------------- /interfaces-example/eth1-conf-dhcp: -------------------------------------------------------------------------------- 1 | auto eth1 2 | iface eth1 inet dhcp 3 | -------------------------------------------------------------------------------- /interfaces-example/eth1-conf-static: -------------------------------------------------------------------------------- 1 | auto eth1 2 | iface eth1 inet static 3 | address 192.168.5.2 4 | netmask 255.255.255.0 5 | gateway 192.168.5.1 6 | dns-nameservers 192.168.5.1 7 | -------------------------------------------------------------------------------- /scripts/dhclient/multihomed-routing.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | #Install this script by copying it to: 4 | #/etc/dhcp/dhclient-enter-hooks.d/multihomed-routing (note that .sh is removed) 5 | 6 | ADDR_RULE_PREF=10000 7 | NW_RULE_PREF=20000 8 | LO_RULE_PREF=91000 9 | DNSMASQ_SERVER_PATH="/tmp/dnsmasq-servers.conf" 10 | 11 | make_resolv_conf() 12 | { 13 | if [ -z "$new_domain_name_servers" ]; then 14 | return; 15 | fi 16 | 17 | for nameserver in $new_domain_name_servers; do 18 | server_str="server=$nameserver@$new_ip_address@$interface" 19 | 20 | grep "$server_str" "$DNSMASQ_SERVER_PATH" 21 | 22 | if [ $? -ne 0 ]; then 23 | echo "$server_str" >> "$DNSMASQ_SERVER_PATH" 24 | fi 25 | done 26 | 27 | kill -s HUP $(pgrep dnsmasq) 28 | } 29 | 30 | delete_from_resolv_conf() 31 | { 32 | match="$old_ip_address@$interface" 33 | 34 | sed -i "/$match/d" "$DNSMASQ_SERVER_PATH" 35 | 36 | kill -s HUP $(pgrep dnsmasq) 37 | } 38 | 39 | update_addr_route() 40 | { 41 | rt_table=$1 42 | if_metric=$2 43 | 44 | ip -4 ro delete \ 45 | ${new_network_number}${new_subnet_mask:+/$new_subnet_mask} \ 46 | dev ${interface} 47 | ip -4 ro add \ 48 | ${new_network_number}${new_subnet_mask:+/$new_subnet_mask} \ 49 | dev ${interface} src ${new_ip_address} ${if_metric:+metric $if_metric} \ 50 | table ${rt_table} 51 | } 52 | 53 | handle_bound_renew_rebind_reboot() 54 | { 55 | set_hostname 56 | 57 | #we ignore alias for now 58 | if [ -n "$old_ip_address" ] && [ -n "$alias_ip_address" ] && 59 | [ "$alias_ip_address" != "$old_ip_address" ]; then 60 | # alias IP may have changed => flush it 61 | ip -4 addr flush dev ${interface} label ${interface}:0 62 | fi 63 | 64 | if [ -n "$old_ip_address" ] && 65 | [ "$old_ip_address" != "$new_ip_address" ]; then 66 | # leased IP has changed => flush it 67 | ip -4 addr flush dev ${interface} label ${interface} 68 | fi 69 | 70 | if [ -z "$old_ip_address" ] || 71 | [ "$old_ip_address" != "$new_ip_address" ] || 72 | [ "$reason" = "BOUND" ] || [ "$reason" = "REBOOT" ]; then 73 | # new IP has been leased or leased IP changed => set it 74 | ip -4 addr add ${new_ip_address}${new_subnet_mask:+/$new_subnet_mask} \ 75 | ${new_broadcast_address:+broadcast $new_broadcast_address} \ 76 | dev ${interface} label ${interface} 77 | 78 | rt_table=$(/usr/sbin/table_allocator_client -4 -s -a "$new_ip_address" -n "$new_subnet_mask" -i "$interface" -d tas_socket) 79 | 80 | #Use default table and if_idx as metric if table_allocator_client can't 81 | #get a lease. This is not ideal, but will ensure that the device has 82 | #working routing 83 | if [ "$rt_table" -eq 0 ]; 84 | then 85 | rt_table=254; 86 | if_idx=$(/sbin/ip link show dev "$interface" | head -1 | cut -d " " -f 1 | cut -d ":" -f 1) 87 | fi 88 | 89 | #remove default address route and add it to the correct table 90 | update_addr_route $rt_table $if_idx 91 | 92 | if [ -n "$new_interface_mtu" ]; then 93 | # set MTU 94 | ip link set dev ${interface} mtu ${new_interface_mtu} 95 | fi 96 | 97 | # if we have $new_rfc3442_classless_static_routes then we have to 98 | # ignore $new_routers entirely 99 | if [ ! "$new_rfc3442_classless_static_routes" ]; then 100 | # set if_metric if IF_METRIC is set or there's more than one router 101 | if_metric="$IF_METRIC" 102 | if [ "${new_routers%% *}" != "${new_routers}" ]; then 103 | if_metric=${if_metric:-1} 104 | fi 105 | 106 | for router in $new_routers; do 107 | if [ "$new_subnet_mask" = "255.255.255.255" ]; then 108 | # point-to-point connection => set explicit route 109 | ip -4 ro add ${router} dev $interface src ${new_ip_address} \ 110 | table ${rt_rable} >/dev/null 2>&1 111 | fi 112 | 113 | #do not override default IF_METRIC 114 | if [ "$rt_table" -eq 254 -a -z "$IF_METRIC"]; 115 | then 116 | if_metric="$if_idx" 117 | fi 118 | 119 | # set default route 120 | ip -4 ro add default via ${router} dev ${interface} \ 121 | ${if_metric:+metric $if_metric} src ${new_ip_address} \ 122 | table ${rt_table} >/dev/null 2>&1 123 | 124 | if [ -n "$if_metric" ]; then 125 | if_metric=$((if_metric+1)) 126 | fi 127 | done 128 | fi 129 | fi 130 | 131 | #we currently don't do anything with alias addresses 132 | if [ -n "$alias_ip_address" ] && 133 | [ "$new_ip_address" != "$alias_ip_address" ]; then 134 | # separate alias IP given, which may have changed 135 | # => flush it, set it & add host route to it 136 | ip -4 addr flush dev ${interface} label ${interface}:0 137 | ip -4 addr add ${alias_ip_address}${alias_subnet_mask:+/$alias_subnet_mask} \ 138 | dev ${interface} label ${interface}:0 139 | ip -4 route add ${alias_ip_address} dev ${interface} >/dev/null 2>&1 140 | fi 141 | 142 | # update /etc/resolv.conf 143 | make_resolv_conf 144 | 145 | #This function replaces the handling in the main script. Thus, we need to 146 | #prevent the main script from handling these four states 147 | unset reason 148 | } 149 | 150 | handle_timeout() 151 | { 152 | #algorithm for timeout handling should be as follows: 153 | # * Send an allocation request for this interface, address and address 154 | # family. 155 | # * Try to configure everything as usual. If adress etc. is already 156 | # configured, the different steps will just fail. 157 | #There is no chance that the routing table for one interface + address will 158 | #move, since we control which table to use 159 | 160 | #todo: need to allocate/read table here 161 | rt_table=$(/usr/sbin/table_allocator_client -4 -s -a "$new_ip_address" -n "$new_subnet_mask" -i "$interface" -d tas_socket) 162 | if [ "$rt_table" -eq 0 ]; 163 | then 164 | rt_table=254; 165 | if_idx=$(/sbin/ip link show dev eth0 | head -1 | cut -d " " -f 1 | cut -d ":" -f 1) 166 | fi 167 | 168 | #as always, we don't care about alias 169 | if [ -n "$alias_ip_address" ]; then 170 | # flush alias IP 171 | ip -4 addr flush dev ${interface} label ${interface}:0 172 | fi 173 | 174 | # set IP from recorded lease 175 | ip -4 addr add ${new_ip_address}${new_subnet_mask:+/$new_subnet_mask} \ 176 | ${new_broadcast_address:+broadcast $new_broadcast_address} \ 177 | dev ${interface} label ${interface} 178 | 179 | # move adress route to correct table 180 | update_addr_route $rt_table $if_idx 181 | 182 | if [ -n "$new_interface_mtu" ]; then 183 | # set MTU 184 | ip link set dev ${interface} mtu ${new_interface_mtu} 185 | fi 186 | 187 | # if there is no router recorded in the lease or the 1st router answers pings 188 | if [ -z "$new_routers" ] || ping -q -c 1 "${new_routers%% *}"; then 189 | # if we have $new_rfc3442_classless_static_routes then we have to 190 | # ignore $new_routers entirely 191 | if [ ! "$new_rfc3442_classless_static_routes" ]; then 192 | if [ -n "$alias_ip_address" ] && 193 | [ "$new_ip_address" != "$alias_ip_address" ]; then 194 | # separate alias IP given => set up the alias IP & add host route to it 195 | ip -4 addr add ${alias_ip_address}${alias_subnet_mask:+/$alias_subnet_mask} \ 196 | dev ${interface} label ${interface}:0 197 | ip -4 route add ${alias_ip_address} dev ${interface} >/dev/null 2>&1 198 | fi 199 | 200 | # set if_metric if IF_METRIC is set or there's more than one router 201 | if_metric="$IF_METRIC" 202 | if [ "${new_routers%% *}" != "${new_routers}" ]; then 203 | if_metric=${if_metric:-1} 204 | fi 205 | 206 | # set default route 207 | for router in $new_routers; do 208 | if [ "$rt_table" -eq 254 -a -z "$IF_METRIC"]; 209 | then 210 | if_metric="$if_idx" 211 | fi 212 | 213 | ip -4 ro add default via ${router} dev ${interface} \ 214 | ${if_metric:+metric $if_metric} src ${new_ip_address} \ 215 | table ${rt_table} >/dev/null 2>&1 216 | 217 | if [ -n "$if_metric" ]; then 218 | if_metric=$((if_metric+1)) 219 | fi 220 | done 221 | fi 222 | 223 | # update /etc/resolv.conf 224 | make_resolv_conf 225 | else 226 | # flush all IPs from interface 227 | ip -4 addr flush dev ${interface} 228 | exit_with_hooks 2 229 | fi 230 | 231 | unset reason 232 | } 233 | 234 | case "$reason" in 235 | BOUND|RENEW|REBIND|REBOOT) 236 | handle_bound_renew_rebind_reboot 237 | ;; 238 | TIMEOUT) 239 | handle_timeout 240 | ;; 241 | EXPIRE|FAIL|RELEASE|STOP) 242 | delete_from_resolv_conf 243 | ;; 244 | *) 245 | return; 246 | ;; 247 | esac 248 | 249 | -------------------------------------------------------------------------------- /scripts/interfaces-static/multihomed-routing-ifup: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | handle_nm_ifup() 4 | { 5 | echo "$(env)" >> /tmp/nm-env.log 6 | echo "\n" >> /tmp/nm-env.log 7 | for ((i=0; i < IP4_NUM_ADDRESSES; i++)) 8 | do 9 | varname="IP4_ADDRESS_$i" 10 | addr_str=$(echo ${!varname} | cut -d " " -f 1) 11 | network_address=$(echo "$addr_str" | cut -d "/" -f 1) 12 | 13 | if [ -n "$DHCP4_SUBNET_MASK" ]; 14 | then 15 | network_number=$DHCP4_NETWORK_NUMBER 16 | netmask=$DHCP4_SUBNET_MASK; 17 | else 18 | netmask=$(ipcalc $addr_str | grep Netmask | tr -d " " | cut -d ":" -f 2 | cut -d "=" -f 1) 19 | network_number=$(ipcalc "$addr_str" | grep "^Network" | tr -d " " | cut -d ":" -f 2 | cut -d "/" -f 1) 20 | fi 21 | 22 | #get route 23 | rt_table=$(/usr/sbin/table_allocator_client -4 -s -a "$network_address" -n "$netmask" -i "$IFACE" -d tas_socket) 24 | 25 | if [ "$rt_table" -eq 0 ]; 26 | then 27 | rt_table=254; 28 | if_metric=$(/sbin/ip link show dev "$IFACE" | head -1 | cut -d " " -f 1 | cut -d ":" -f 1) 29 | fi 30 | 31 | #move the address route created by default 32 | #ip -4 ro delete ${network_number}${netmask:+/$netmask} dev ${IFACE} 33 | ip -4 ro add ${network_number}${netmask:+/$netmask} dev ${IFACE} \ 34 | src ${network_address} table ${rt_table} ${if_metric:+metric $if_metric} 35 | 36 | #move default route to table 37 | #ip -4 ro delete default via ${IP4_GATEWAY} dev ${IFACE} 38 | ip -4 ro add default via ${IP4_GATEWAY} dev ${IFACE} src ${network_address} \ 39 | table ${rt_table} ${if_metric:+metric $if_metric} 40 | 41 | #move other provided routes 42 | for ((j=0; j < IP4_NUM_ROUTES; j++)) 43 | do 44 | varname="IP4_ROUTE_$i" 45 | route_address=$(echo ${!varname} | cut -d " " -f 1) 46 | route_next_hop=$(echo ${!varname} | cut -d " " -f 2) 47 | route_metric=$(echo ${!varname} | cut -d " " -f 3) 48 | 49 | #ip -4 ro delete ${route_address} via ${route_next_hop} dev ${IFACE} \ 50 | # ${route_metric:+metric $route_metric} 51 | ip -4 ro add ${route_address} via ${route_next_hop} dev ${IFACE} \ 52 | ${route_metric:+metric $route_metric} table ${rt_table} \ 53 | src ${network_address} 54 | done 55 | done 56 | } 57 | 58 | handle_ifup() 59 | { 60 | #get route 61 | rt_table=$(/usr/sbin/table_allocator_client -4 -s -a "$IF_ADDRESS" -n "$IF_NETMASK" -i "$IFACE" -d tas_socket) 62 | 63 | if [ "$rt_table" -eq 0 ]; 64 | then 65 | rt_table=254; 66 | if_metric=$(/sbin/ip link show dev "$IFACE" | head -1 | cut -d " " -f 1 | cut -d ":" -f 1) 67 | fi 68 | 69 | #This is not the nicest way in the world to extract network number, look into 70 | #bash bitwise operators when time 71 | network_number=$(ipcalc "$IF_ADDRESS"/"$IF_NETMASK" | grep "^Network" | tr -d " " | 72 | cut -d ":" -f 2 | cut -d "/" -f 1) 73 | 74 | #we are run from up, static configuration is already applied 75 | 76 | #move the address route created by default 77 | ip -4 ro delete ${network_number}${IF_NETMASK:+/$IF_NETMASK} dev ${IFACE} 78 | ip -4 ro add ${network_number}${IF_NETMASK:+/$IF_NETMASK} dev ${IFACE} \ 79 | src ${IF_ADDRESS} table ${rt_table} ${if_metric:+metric $if_metric} 80 | 81 | #move default route to table 82 | ip -4 ro delete default via ${IF_GATEWAY} dev ${IFACE} 83 | ip -4 ro add default via ${IF_GATEWAY} dev ${IFACE} src ${IF_ADDRESS} \ 84 | table ${rt_table} ${if_metric:+metric $if_metric} 85 | } 86 | 87 | if [ "$ADDRFAM" != "inet" ]; 88 | then 89 | exit 0; 90 | fi 91 | 92 | if [ "$METHOD" = "NetworkManager" -a "$IP4_GATEWAY" ]; 93 | then 94 | handle_nm_ifup 95 | elif [ "$METHOD" = "static" -a -n "$IF_GATEWAY" ]; 96 | then 97 | handle_ifup 98 | fi 99 | 100 | exit 0 101 | -------------------------------------------------------------------------------- /table_allocator/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.6) 2 | project(table_allocator) 3 | 4 | set (PROJECT_INCLUDE_DIR ${PROJECT_SOURCE_DIR}/shared/include) 5 | 6 | include_directories("${PROJECT_INCLUDE_DIR}") 7 | 8 | #add_subdirectory(shared) 9 | add_subdirectory(server) 10 | add_subdirectory(client) 11 | -------------------------------------------------------------------------------- /table_allocator/LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | 635 | Copyright (C) 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | Copyright (C) 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | -------------------------------------------------------------------------------- /table_allocator/README.md: -------------------------------------------------------------------------------- 1 | # Table Allocator 2 | 3 | Table Allocator consists of a client and a server, and is a tool for allocating 4 | and distributing unique values based on some identifier. It is intended for use 5 | when configuring routing on multihomed hosts 6 | ([see](https://github.com/kristrev/multihomed-routing/blob/master/README.md)), 7 | as each interface (address) requires a separate table. The server will be 8 | started at boot and run as a daemon, while the clients are started on-demand 9 | (for example when a lease is acquired using DHCP). By default, the client will 10 | move to the background once a lease (from the allocator server) is acquired. If 11 | no lease is acquire, the client will exit. 12 | 13 | In both cases, a value will be written to stdout. If a lease has been acquired, 14 | we will write the value received from the server. If no lease has been acquired, 15 | we will write 0. The latter can be used as a signal to write multiple routes to 16 | the main routing table (with different metrics). Each lease has a lifetime and 17 | have to be refreshed, which is handled automatically by the client. Also, for 18 | IPv4, the client creates and manages three routing rules that are required for 19 | routing to be configured correctly. 20 | 21 | If an address or link is deleted, the client will delete the rules, release the 22 | lease and exit. If a lease has not been renewed or released, the server will 23 | automatically remove the lease when it expires. 24 | 25 | Communication between the client and server uses UNIX-domain sockets and 26 | abstract names as addresses. The messages passed back and forth are 27 | JSON-objects. For example, a request might looks like this: 28 | 29 | `{"address":"192.168.5.113","ifname":"eth1","addr_family":2,"cmd":0,"version":1}` 30 | 31 | While a reply looks like this: 32 | 33 | `{"version":1,"cmd":2,"table":10000,"lease_expires":260903}` 34 | 35 | ## Server 36 | 37 | The server is responsible for allocating and distributing the tables. It uses an 38 | sqlite3 database for persistent storage, and a bitmap for quick lookups when 39 | allocating/releasing tables. The server can be run as a normal user and supports 40 | the following command line arguments: 41 | 42 | * -c: path to configuration file, required. 43 | 44 | The configuration file must contain one JSON object with the following keys: 45 | 46 | * socket\_path: string, name of socket (required). 47 | * table\_offset: int, value of first table (required). 48 | * num\_tables: int, number of tables to allocate (required). 49 | * table\_timeout: int, validity of lease (seconds, required). 50 | * db\_path: string, path to leases database (required). 51 | * do\_syslog: bool, write to syslog (optional, default false). 52 | * log\_path": string, path to logfile (optional, default stderr) 53 | * addr\_families: object, which address families to support. Valid keys are 54 | "inet", "inet6" and "unspec" and they key is a bool. At least one must be 55 | true. 56 | 57 | See files/server\_config.json for a configuration example. 58 | 59 | ## Client 60 | 61 | The client requests and maintains a lease, as well as the routing rules (v4, 62 | soon v6). The client must be run as root in order to work correctly (or a user 63 | with permissions to update routing). We support the following command line 64 | arguments: 65 | 66 | * -4: set address family to IPv4 (default is UNSPEC). 67 | * -6: set address family to IPv6. 68 | * -s/--syslog: enable logging to syslog (default off). 69 | * -l/--log\_path: path to logfile (default stderr). 70 | * -a/--address: address to allocate table for (required, a general identifier for UNSPEC). 71 | * -n/--netmask: netmask for use with address (required with -4). 72 | * -i/--ifname: interface to allocate table for (required). 73 | * -t/--tag: optional tag to send to server. 74 | * -r/--release: set command to release instead of request. 75 | * -d/--destination: Path to server socket (required). 76 | * -f/--foreground: Run application in foreground. 77 | * -h/--help: this information. 78 | 79 | 80 | ## Building and installation 81 | 82 | Table Allocator Client and Server are built using CMake: 83 | 84 | * Enter either the client or server folder. 85 | * mkdir build. 86 | * cd build && cmake .. && make. 87 | * If you want to compile and build a deb-package, replace the last command with 88 | make package. 89 | 90 | You can also build the applications in parallel (does not work for package). 91 | Follow the same steps as above, but make the build directory in the root folder 92 | (i.e., .../table\_allocator). The client requires libuv, libmnl and json-c, 93 | while the server requires libuv, libsqlite3 and json-c. 94 | 95 | The easiest way to install the applications, is to build and use the 96 | deb-packages. If anyone wants to add support for other packet formats, then that 97 | would be great. For examples of how to use the client, you can look at the 98 | scripts located 99 | [here](https://github.com/kristrev/multihomed-routing/tree/master/scripts). 100 | 101 | ## TODO 102 | 103 | * Message authentication. Figure out if it is possible to get cmsg to play nice 104 | with libuv. 105 | -------------------------------------------------------------------------------- /table_allocator/client/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | #Standard stuff 2 | cmake_minimum_required(VERSION 2.6) 3 | project(table_allocator_client) 4 | 5 | set(CMAKE_C_FLAGS "-O1 -Wall -std=gnu99 -g") 6 | 7 | set (PROJECT_INCLUDE_DIR ${PROJECT_SOURCE_DIR}/include) 8 | set (PROJECT_SRC_DIR ${PROJECT_SOURCE_DIR}/src) 9 | 10 | find_library(LIBUV_LIBRARY uv) 11 | find_library(LIBJSON_LIBRARY json-c) 12 | find_library(LIBMNL_LIBRARY mnl) 13 | 14 | set(SOURCE 15 | ${PROJECT_SRC_DIR}/table_allocator_client.c 16 | ${PROJECT_SRC_DIR}/table_allocator_client_netlink.c 17 | ) 18 | 19 | set(CPACK_GENERATOR "DEB") 20 | set(CPACK_DEBIAN_PACKAGE_MAINTAINER "Kristian Evensen ") 21 | set(CPACK_DEBIAN_PACKAGE_NAME "table-allocator-client") 22 | set(CPACK_DEBIAN_PACKAGE_VERSION "0.1-1") 23 | set(CPACK_DEBIAN_PACKAGE_ARCHITECTURE "amd64") 24 | set(CPACK_DEBIAN_PACKAGE_DEPENDS "libuv1, libjson-c3, libmnl0") 25 | set(CPACK_DEBIAN_PACKAGE_DESCRIPTION "Table Allocator Client is a tool aimed to simplify the configuration of multihomed hosts, and must be used together with the Table Allocator Server. When started, the client will request a table lease from the Table Allocator Server. After the table lease has been acquired, the table will be written to stdout and the tool (by default) will move to the background. The reason for writing the table is so that routes easily can be added or moved to the correct table in for example an ifup script or dhclient-hook. The client tool takes care of adding and removing the required ip rules (rules removed when either address or interface is removed).") 26 | INCLUDE(CPack) 27 | 28 | if(NOT TARGET table_allocator_shared) 29 | add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../shared 30 | ${CMAKE_CURRENT_BINARY_DIR}/shared) 31 | include_directories("${CMAKE_CURRENT_SOURCE_DIR}/../shared/include") 32 | endif() 33 | 34 | include_directories("${PROJECT_INCLUDE_DIR}") 35 | add_executable(${PROJECT_NAME} ${SOURCE}) 36 | target_link_libraries(${PROJECT_NAME} table_allocator_shared) 37 | target_link_libraries(${PROJECT_NAME} ${LIBUV_LIBRARY}) 38 | target_link_libraries(${PROJECT_NAME} ${LIBJSON_LIBRARY}) 39 | target_link_libraries(${PROJECT_NAME} ${LIBMNL_LIBRARY}) 40 | install(TARGETS ${PROJECT_NAME} RUNTIME DESTINATION sbin) 41 | -------------------------------------------------------------------------------- /table_allocator/client/include/table_allocator_client.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Kristian Evensen 3 | * 4 | * This file is part of Usb Monitor. Usb Monitor is free software: you can 5 | * redistribute it and/or modify it under the terms of the Lesser GNU General 6 | * Public License as published by the Free Software Foundation, either version 3 7 | * of the License, or (at your option) any later version. 8 | * 9 | * Usb Montior is distributed in the hope that it will be useful, but WITHOUT ANY 10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 11 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU General Public License along with 14 | * Usb Monitor. If not, see http://www.gnu.org/licenses/. 15 | */ 16 | 17 | #ifndef TABLE_ALLOCATOR_CLIENT_H 18 | #define TABLE_ALLOCATOR_CLIENT_H 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #include 26 | 27 | #define REQUEST_RETRANSMISSION_MS 2000 28 | #define NUM_FAILED_LIMIT 5 29 | 30 | struct mnl_socket; 31 | 32 | //keep all buffers, values, etc. related to an address here 33 | struct tac_address { 34 | struct sockaddr_storage addr; 35 | uint32_t ifidx; 36 | 37 | //allocated variables 38 | uint32_t rt_table; 39 | uint32_t lease_expires; 40 | 41 | //will keep subnet or prefix len, depending on family 42 | uint8_t subnet_prefix_len; 43 | //read from command line 44 | uint8_t addr_family; 45 | uint8_t rules_added; 46 | char ifname[IFNAMSIZ]; 47 | char address_str[INET6_ADDRSTRLEN]; 48 | char tag[TA_SHARED_MAX_TAG_SIZE]; 49 | }; 50 | 51 | struct tac_ctx { 52 | uv_loop_t event_loop; 53 | uv_udp_t unix_socket_handle; 54 | uv_udp_t netlink_handle; 55 | uv_timer_t unix_socket_timeout_handle; 56 | uv_timer_t request_timeout_handle; 57 | uv_timer_t netlink_timeout_handle; 58 | struct mnl_socket *rt_mnl_socket; 59 | FILE *logfile; 60 | struct tac_address *address; 61 | char destination[TA_SHARED_MAX_ADDR_SIZE]; 62 | uint8_t rcv_buf[TA_SHARED_MAX_JSON_LEN]; 63 | uint8_t *mnl_recv_buf; 64 | uint8_t use_syslog; 65 | //request or release 66 | uint8_t cmd; 67 | uint8_t daemonize; 68 | uint8_t daemonized; 69 | uint8_t closing; 70 | uint8_t num_failed; 71 | }; 72 | 73 | void table_allocator_client_send_request(struct tac_ctx *ctx, uint8_t cmd); 74 | 75 | #endif 76 | -------------------------------------------------------------------------------- /table_allocator/client/include/table_allocator_client_netlink.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Kristian Evensen 3 | * 4 | * This file is part of Usb Monitor. Usb Monitor is free software: you can 5 | * redistribute it and/or modify it under the terms of the Lesser GNU General 6 | * Public License as published by the Free Software Foundation, either version 3 7 | * of the License, or (at your option) any later version. 8 | * 9 | * Usb Montior is distributed in the hope that it will be useful, but WITHOUT ANY 10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 11 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU General Public License along with 14 | * Usb Monitor. If not, see http://www.gnu.org/licenses/. 15 | */ 16 | 17 | #ifndef TABLE_ALLOCATOR_CLIENT_NETLINK_H 18 | #define TABLE_ALLOCATOR_CLIENT_NETLINK_H 19 | 20 | #define ADDR_RULE_PRIO 10000 21 | #define NW_RULE_PRIO 20000 22 | #define DEF_RULE_PRIO 91000 23 | 24 | //how long to wait until we try to add rules again 25 | #define TAC_NETLINK_TIMEOUT_MS 1000 26 | 27 | struct nlattr; 28 | struct tac_ctx; 29 | 30 | struct nlattr_storage { 31 | const struct nlattr **tb; 32 | uint32_t limit; 33 | }; 34 | 35 | //configure netlink + start listening 36 | uint8_t table_allocator_client_netlink_configure(struct tac_ctx *ctx); 37 | 38 | //stop netlink handling 39 | void table_allocator_client_netlink_stop(struct tac_ctx *ctx); 40 | 41 | //add rules. If adding rules fails, then we will start a timer 42 | void table_allocator_client_netlink_update_rules(struct tac_ctx *ctx, 43 | uint32_t msg_type); 44 | 45 | #endif 46 | -------------------------------------------------------------------------------- /table_allocator/client/src/table_allocator_client.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Kristian Evensen 3 | * 4 | * This file is part of Usb Monitor. Usb Monitor is free software: you can 5 | * redistribute it and/or modify it under the terms of the Lesser GNU General 6 | * Public License as published by the Free Software Foundation, either version 3 7 | * of the License, or (at your option) any later version. 8 | * 9 | * Usb Montior is distributed in the hope that it will be useful, but WITHOUT ANY 10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 11 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU General Public License along with 14 | * Usb Monitor. If not, see http://www.gnu.org/licenses/. 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #include "table_allocator_client.h" 32 | #include "table_allocator_client_netlink.h" 33 | 34 | #include 35 | #include 36 | #include 37 | #include 38 | 39 | static void unix_socket_timeout_cb(uv_timer_t *handle); 40 | static void client_request_timeout_handle_cb(uv_timer_t *handle); 41 | 42 | //generic close callback used then exiting application 43 | static void table_allocator_client_close_cb(uv_handle_t *handle) 44 | { 45 | //don't do anything, we have no per. handle memory to free 46 | } 47 | 48 | static void unix_socket_handle_close_cb(uv_handle_t *handle) 49 | { 50 | struct tac_ctx *ctx = handle->data; 51 | 52 | if (ctx->closing) { 53 | return; 54 | } 55 | 56 | //todo: error checks 57 | //space requests a little bit out in time to prevent basically busy loop if 58 | //something should happen to socket 59 | uv_udp_init(&(ctx->event_loop), &(ctx->unix_socket_handle)); 60 | uv_timer_start(&(ctx->unix_socket_timeout_handle), unix_socket_timeout_cb, 61 | 100, 0); 62 | } 63 | 64 | static void unix_socket_stop_recv(struct tac_ctx *ctx) 65 | { 66 | uv_udp_recv_stop(&(ctx->unix_socket_handle)); 67 | uv_timer_stop(&(ctx->request_timeout_handle)); 68 | uv_timer_stop(&(ctx->unix_socket_timeout_handle)); 69 | 70 | if (!uv_is_closing((uv_handle_t*) & (ctx->unix_socket_handle))) 71 | uv_close((uv_handle_t*) &(ctx->unix_socket_handle), 72 | unix_socket_handle_close_cb); 73 | } 74 | 75 | static void unix_socket_alloc_cb(uv_handle_t* handle, size_t suggested_size, 76 | uv_buf_t* buf) 77 | { 78 | struct tac_ctx *ctx = handle->data; 79 | 80 | buf->base = (char*) ctx->rcv_buf; 81 | buf->len = TA_SHARED_MAX_JSON_LEN; 82 | } 83 | 84 | //check fail count, return 1 if we are below limit and 0 if we are over 85 | static uint8_t table_allocator_client_check_fail_count(struct tac_ctx *ctx) 86 | { 87 | struct tac_address *address = ctx->address; 88 | 89 | ctx->num_failed++; 90 | 91 | if (ctx->num_failed < NUM_FAILED_LIMIT) { 92 | return 1; 93 | } 94 | 95 | TA_PRINT_SYSLOG(ctx, LOG_DEBUG, "Failed to receive table lease\n"); 96 | printf("%u\n", address->rt_table); 97 | fflush(stdout); 98 | uv_stop(&(ctx->event_loop)); 99 | ctx->closing = 1; 100 | return 0; 101 | } 102 | 103 | static void unix_socket_recv_cb(uv_udp_t* handle, ssize_t nread, 104 | const uv_buf_t* buf, const struct sockaddr* addr, unsigned flags) 105 | { 106 | struct tac_ctx *ctx = handle->data; 107 | struct tac_address *address = ctx->address; 108 | uint8_t ver, cmd; 109 | const struct sockaddr_un *un_addr = (const struct sockaddr_un*) addr; 110 | struct timespec tv_now; 111 | uint32_t tdiff, rt_table = 0; 112 | 113 | //rearm send timer 114 | //todo: look at error handling here, if I have missed something 115 | 116 | if (ctx->closing || nread == 0) { 117 | return; 118 | } else if (flags & UV_UDP_PARTIAL) { 119 | uv_timer_start(&(ctx->request_timeout_handle), 120 | client_request_timeout_handle_cb, REQUEST_RETRANSMISSION_MS, 0); 121 | return; 122 | } else if (nread < 0) { 123 | TA_PRINT_SYSLOG(ctx, LOG_DEBUG, "Client socket failed, error: %s\n", 124 | uv_strerror(nread)); 125 | 126 | if (address->rt_table || table_allocator_client_check_fail_count(ctx)) 127 | { 128 | unix_socket_stop_recv(ctx); 129 | } 130 | return; 131 | } 132 | 133 | //parse json 134 | if (!tables_allocator_shared_json_parse_client_reply(buf->base, 135 | &ver, &cmd, &rt_table, &(address->lease_expires))) { 136 | TA_PRINT_SYSLOG(ctx, LOG_ERR, "Failed to parse request from: %s\n", 137 | un_addr->sun_path + 1); 138 | return; 139 | } 140 | 141 | if (ver != TA_VERSION) { 142 | TA_PRINT_SYSLOG(ctx, LOG_ERR, "Received unsupported version: %u\n", 143 | ver); 144 | uv_stop(&(ctx->event_loop)); 145 | return; 146 | } 147 | 148 | //check cmd and version 149 | if (cmd != TA_SHARED_CMD_RESP || !rt_table) { 150 | uv_timer_start(&(ctx->request_timeout_handle), 151 | client_request_timeout_handle_cb, REQUEST_RETRANSMISSION_MS, 0); 152 | return; 153 | } 154 | 155 | address->rt_table = rt_table; 156 | ctx->num_failed = 0; 157 | TA_PRINT_SYSLOG(ctx, LOG_INFO, "Server: %s Table: %u Lease: %u Ifname: %s " 158 | "Address: %s Family: %u Table: %u\n", 159 | un_addr->sun_path + 1, address->rt_table, 160 | address->lease_expires, address->ifname, 161 | address->address_str, address->addr_family, 162 | address->rt_table); 163 | 164 | clock_gettime(CLOCK_MONOTONIC_RAW, &tv_now); 165 | //todo: guard this better 166 | tdiff = address->lease_expires - tv_now.tv_sec; 167 | 168 | //write table 169 | printf("%u\n", address->rt_table); 170 | fflush(stdout); 171 | 172 | //start running as daemon 173 | //todo: add some error handling or just fail? 174 | if (!ctx->daemonized && ctx->daemonize && daemon(0, 0)) { 175 | TA_PRINT_SYSLOG(ctx, LOG_CRIT, "Failed to daemonize client"); 176 | exit(EXIT_FAILURE); 177 | } 178 | 179 | //add rules 180 | if (!ctx->address->rules_added) { 181 | table_allocator_client_netlink_update_rules(ctx, RTM_NEWRULE); 182 | } 183 | 184 | ctx->daemonized = 1; 185 | //start request timeout again 186 | uv_timer_start(&(ctx->request_timeout_handle), 187 | client_request_timeout_handle_cb, (tdiff/2)*1000, 0); 188 | } 189 | 190 | static void client_request_timeout_handle_cb(uv_timer_t *handle) 191 | { 192 | struct tac_ctx *ctx = handle->data; 193 | 194 | if (ctx->address->rt_table || 195 | table_allocator_client_check_fail_count(ctx)) { 196 | table_allocator_client_send_request(ctx, TA_SHARED_CMD_REQ); 197 | } 198 | } 199 | 200 | void table_allocator_client_send_request(struct tac_ctx *ctx, uint8_t cmd) 201 | { 202 | struct tac_address *address = ctx->address; 203 | struct sockaddr_un remote_addr; 204 | //this is just a test until we have config files in place 205 | int32_t retval = -1, sock_fd; 206 | struct json_object *req_obj = NULL; 207 | const char *json_str; 208 | 209 | req_obj = table_allocator_shared_json_create_req(address->address_str, 210 | address->ifname, address->tag, address->addr_family, cmd); 211 | 212 | if (!req_obj) { 213 | if (uv_timer_start(&(ctx->request_timeout_handle), 214 | client_request_timeout_handle_cb, REQUEST_RETRANSMISSION_MS, 215 | 0)) { 216 | TA_PRINT_SYSLOG(ctx, LOG_CRIT, "Can't start request timer\n"); 217 | exit(EXIT_FAILURE); 218 | } 219 | 220 | TA_PRINT_SYSLOG(ctx, LOG_ERR, "Failed to create request json for " 221 | "%s-%s-%u\n", address->ifname, address->address_str, 222 | address->addr_family); 223 | return; 224 | } 225 | 226 | json_str = json_object_to_json_string_ext(req_obj, JSON_C_TO_STRING_PLAIN); 227 | 228 | TA_PRINT_SYSLOG(ctx, LOG_DEBUG, "JSON request: %s\n", json_str); 229 | 230 | //populate address 231 | memset(&remote_addr, 0, sizeof(remote_addr)); 232 | remote_addr.sun_family = AF_UNIX; 233 | 234 | //we use abstract naming, so first byte of path is always \0 235 | strncpy(remote_addr.sun_path + 1, ctx->destination, 236 | strlen(ctx->destination)); 237 | uv_fileno((const uv_handle_t*) &(ctx->unix_socket_handle), &sock_fd); 238 | 239 | //todo: I need to use sendto, it seems the diffrent send-methods in libuv 240 | //expects something that is either sockaddr_in or sockaddr_in6 (investigate) 241 | retval = sendto(sock_fd, json_str, strlen(json_str), 0, 242 | (const struct sockaddr*) &remote_addr, sizeof(struct sockaddr_un)); 243 | json_object_put(req_obj); 244 | 245 | if (retval < 0) { 246 | TA_PRINT_SYSLOG(ctx, LOG_ERR, "Sending error: %s\n", 247 | uv_strerror(retval)); 248 | } else { 249 | TA_PRINT(ctx->logfile, "Sent %d bytes\n", retval); 250 | 251 | //todo: check for failure here? 252 | uv_udp_recv_start(&(ctx->unix_socket_handle), unix_socket_alloc_cb, 253 | unix_socket_recv_cb); 254 | } 255 | 256 | //always start retransmission timer, independent of success of failure 257 | //timer will be updated in the different handler functions 258 | if (uv_timer_start(&(ctx->request_timeout_handle), 259 | client_request_timeout_handle_cb, REQUEST_RETRANSMISSION_MS, 260 | 0)) { 261 | TA_PRINT_SYSLOG(ctx, LOG_CRIT, "Can't start request timer\n"); 262 | exit(EXIT_FAILURE); 263 | } 264 | } 265 | 266 | //todo: consider making shared 267 | static void unix_socket_timeout_cb(uv_timer_t *handle) 268 | { 269 | int32_t sock_fd = -1; 270 | uint8_t success = 1; 271 | struct tac_ctx *ctx = handle->data; 272 | struct tac_address *address = ctx->address; 273 | //format is ifname-addr-family. The space for the two "-" comes for free via 274 | //the additional byte in IFNAMSIZE and INET6_ADDRSTRLEN, family is maximum 275 | //two digits (IPv6 is 10) 276 | char unix_socket_addr[IFNAMSIZ + INET6_ADDRSTRLEN + 3]; 277 | 278 | if (ctx->closing) { 279 | return; 280 | } 281 | 282 | if (uv_fileno((const uv_handle_t*) &(ctx->unix_socket_handle), &sock_fd) 283 | == UV_EBADF) { 284 | snprintf(unix_socket_addr, sizeof(unix_socket_addr), 285 | "%s-%s-%u", address->ifname, address->address_str, 286 | address->addr_family); 287 | 288 | //path will be read from config, stored in ctx 289 | sock_fd = ta_socket_helpers_create_unix_socket(unix_socket_addr); 290 | 291 | if (sock_fd < 0 || uv_udp_open(&(ctx->unix_socket_handle), sock_fd)) { 292 | //print error 293 | if (sock_fd < 0) { 294 | TA_PRINT_SYSLOG(ctx, LOG_ERR, "Failed to create domain socket: " 295 | "%s\n", strerror(errno)); 296 | //log error from errno 297 | } else { 298 | close(sock_fd); 299 | } 300 | 301 | success = 0; 302 | } 303 | } 304 | 305 | if (success) { 306 | uv_timer_stop(handle); 307 | table_allocator_client_send_request(ctx, TA_SHARED_CMD_REQ); 308 | } else { 309 | if (!uv_timer_get_repeat(handle)) { 310 | uv_timer_set_repeat(handle, DOMAIN_SOCKET_TIMEOUT_MS); 311 | uv_timer_again(handle); 312 | } 313 | } 314 | } 315 | 316 | static void free_ctx(struct tac_ctx *ctx) 317 | { 318 | ctx->closing = 1; 319 | 320 | if (ctx->rt_mnl_socket) { 321 | table_allocator_client_netlink_stop(ctx); 322 | } 323 | 324 | uv_timer_stop(&(ctx->unix_socket_timeout_handle)); 325 | uv_timer_stop(&(ctx->request_timeout_handle)); 326 | uv_timer_stop(&(ctx->netlink_timeout_handle)); 327 | 328 | uv_close((uv_handle_t*) &(ctx->unix_socket_timeout_handle), 329 | table_allocator_client_close_cb); 330 | uv_close((uv_handle_t*) &(ctx->request_timeout_handle), 331 | table_allocator_client_close_cb); 332 | uv_close((uv_handle_t*) &(ctx->netlink_timeout_handle), 333 | table_allocator_client_close_cb); 334 | 335 | uv_close((uv_handle_t*) &(ctx->unix_socket_handle), 336 | table_allocator_client_close_cb); 337 | uv_close((uv_handle_t*) &(ctx->netlink_handle), 338 | table_allocator_client_close_cb); 339 | 340 | uv_run(&(ctx->event_loop), UV_RUN_ONCE); 341 | uv_loop_close(&(ctx->event_loop)); 342 | 343 | mnl_socket_close(ctx->rt_mnl_socket); 344 | free(ctx->mnl_recv_buf); 345 | free(ctx->address); 346 | free(ctx); 347 | } 348 | 349 | static void usage() 350 | { 351 | fprintf(stdout, "Usage: table_allocator_client [options ...]\n"); 352 | fprintf(stdout, "Required arguments are marked with \n"); 353 | fprintf(stdout, "\t-4: set address family to IPv4 (default is UNSPEC)\n"); 354 | fprintf(stdout, "\t-6: set address family to IPv6\n"); 355 | fprintf(stdout, "\t-s/--syslog: enable logging to syslog (default off)\n"); 356 | fprintf(stdout, "\t-l/--log_path: path to logfile (default stderr)\n"); 357 | fprintf(stdout, "\t-a/--address: address to allocate table for \n"); 358 | fprintf(stdout, "\t-n/--netmask: netmask for use with address \n"); 359 | fprintf(stdout, "\t-i/--ifname: interface to allocate table for \n"); 360 | fprintf(stdout, "\t-t/--tag: optional tag to send to server\n"); 361 | fprintf(stdout, "\t-r/--release: set command to release instead of request\n"); 362 | fprintf(stdout, "\t-d/--destination: Path to server socket \n"); 363 | fprintf(stdout, "\t-f/--foreground: Run application in foreground\n"); 364 | fprintf(stdout, "\t-h/--help: this information\n"); 365 | } 366 | 367 | static uint8_t compute_prefix_len_4(struct in_addr *netmask) 368 | { 369 | return 32 - (ffs(ntohl(netmask->s_addr)) - 1); 370 | } 371 | 372 | static uint8_t parse_cmd_args(struct tac_ctx *ctx, int argc, char *argv[]) 373 | { 374 | int32_t option_index, opt; 375 | const char *address = NULL, *ifname = NULL, *log_path = NULL, *tag = NULL; 376 | const char *destination = NULL, *netmask = NULL; 377 | 378 | union { 379 | struct sockaddr_in *addr4; 380 | struct sockaddr_in6 *addr6; 381 | } u_addr; 382 | 383 | union { 384 | struct in_addr netmask4; 385 | struct in6_addr netmask6; 386 | } u_netmask; 387 | 388 | struct option options[] = { 389 | {"syslog", no_argument, NULL, 's'}, 390 | {"log_path", required_argument, NULL, 'l'}, 391 | {"address", required_argument, NULL, 'a'}, 392 | {"netmask", required_argument, NULL, 'n'}, 393 | {"ifname", required_argument, NULL, 'i'}, 394 | {"tag", required_argument, NULL, 't'}, 395 | {"release", no_argument, NULL, 'r'}, 396 | {"destination", required_argument, NULL, 'd'}, 397 | {"foreground", required_argument, NULL, 'f'}, 398 | {"help", no_argument, NULL, 'h'}, 399 | }; 400 | 401 | while (1) { 402 | opt = getopt_long(argc, argv, "46sl:a:n:i:t:d:r:fh", options, &option_index); 403 | 404 | if (opt == -1) 405 | break; 406 | 407 | switch (opt) { 408 | case '4': 409 | ctx->address->addr_family = AF_INET; 410 | break; 411 | case '6': 412 | ctx->address->addr_family = AF_INET6; 413 | break; 414 | case 's': 415 | ctx->use_syslog = 1; 416 | break; 417 | case 'l': 418 | log_path = optarg; 419 | break; 420 | case 'a': 421 | address = optarg; 422 | break; 423 | case 'n': 424 | netmask = optarg; 425 | break; 426 | case 'i': 427 | ifname = optarg; 428 | break; 429 | case 't': 430 | tag = optarg; 431 | break; 432 | case 'r': 433 | ctx->cmd = TA_SHARED_CMD_REL; 434 | break; 435 | case 'd': 436 | destination = optarg; 437 | break; 438 | case 'f': 439 | ctx->daemonize = 0; 440 | break; 441 | case 'h': 442 | default: 443 | usage(); 444 | return 0; 445 | } 446 | } 447 | 448 | if (!address || (ctx->address->addr_family == AF_INET && !netmask) || 449 | !ifname || !destination) { 450 | fprintf(stderr, "Missing required argument\n"); 451 | usage(); 452 | return 0; 453 | } 454 | 455 | if (tag) { 456 | if (strlen(tag) >= TA_SHARED_MAX_TAG_SIZE) { 457 | fprintf(stderr, "Tag name too long (%zd > %u)\n", strlen(tag), 458 | TA_SHARED_MAX_TAG_SIZE - 1); 459 | return 0; 460 | } else { 461 | memcpy(ctx->address->tag, tag, strlen(tag)); 462 | } 463 | } 464 | 465 | if (strlen(ifname) >= IFNAMSIZ) { 466 | fprintf(stderr, "Interface name too long (%zd > %u)\n", strlen(ifname), 467 | IFNAMSIZ - 1); 468 | return 0; 469 | } else { 470 | memcpy(ctx->address->ifname, ifname, strlen(ifname)); 471 | } 472 | 473 | if (strlen(destination) >= TA_SHARED_MAX_ADDR_SIZE) { 474 | fprintf(stderr, "Destination too long (%zd > %u)\n", strlen(destination), 475 | TA_SHARED_MAX_ADDR_SIZE - 1); 476 | return 0; 477 | } else { 478 | memcpy(ctx->destination, destination, strlen(destination)); 479 | } 480 | 481 | if (ctx->address->addr_family == AF_INET) { 482 | u_addr.addr4 = (struct sockaddr_in*) &(ctx->address->addr); 483 | if (!inet_pton(AF_INET, address, &(u_addr.addr4->sin_addr))) { 484 | fprintf(stderr, "Address is not valid: %s\n", address); 485 | return 0; 486 | } 487 | 488 | if (!inet_pton(AF_INET, netmask, &(u_netmask.netmask4))) { 489 | fprintf(stderr, "Netmask is not valid: %s\n", netmask); 490 | return 0; 491 | } 492 | 493 | ctx->address->subnet_prefix_len = 494 | compute_prefix_len_4(&(u_netmask.netmask4)); 495 | 496 | //keep both string and sockaddr around, since we use the string when 497 | //sending requests 498 | memcpy(ctx->address->address_str, address, strlen(address)); 499 | } else if (ctx->address->addr_family == AF_INET6) { 500 | u_addr.addr6 = (struct sockaddr_in6*) &(ctx->address->addr); 501 | if (!inet_pton(AF_INET6, address, &(u_addr.addr6->sin6_addr))) { 502 | fprintf(stderr, "Address is not valid: %s\n", address); 503 | return 0; 504 | } 505 | 506 | //todo: find out how prefix len is passed to dhcp 507 | memcpy(ctx->address->address_str, address, strlen(address)); 508 | } else { 509 | if (strlen(address) >= INET6_ADDRSTRLEN) { 510 | fprintf(stderr, "Address too long (%zd > %u)\n", strlen(address), 511 | INET6_ADDRSTRLEN - 1); 512 | return 0; 513 | } else { 514 | memcpy(ctx->address->address_str, address, strlen(address)); 515 | } 516 | } 517 | 518 | if (log_path) { 519 | if (!(ctx->logfile = fopen(log_path, "w"))) { 520 | fprintf(stderr, "Could not open file: %s (%s)\n", log_path, 521 | strerror(errno)); 522 | return 0; 523 | } 524 | } 525 | 526 | if (!(ctx->address->ifidx = if_nametoindex(ctx->address->ifname))) { 527 | fprintf(stderr, "Could not get interface index: %s (%s)\n", 528 | strerror(errno), ctx->address->ifname); 529 | return 0; 530 | } 531 | 532 | return 1; 533 | } 534 | 535 | int main(int argc, char *argv[]) 536 | { 537 | struct tac_ctx *ctx; 538 | 539 | //parse command line arguments 540 | 541 | //create the application context 542 | ctx = calloc(sizeof(struct tac_ctx), 1); 543 | 544 | if (!ctx) { 545 | TA_PRINT(stderr, "Allocating memory for context failed\n"); 546 | exit(EXIT_FAILURE); 547 | } 548 | 549 | ctx->mnl_recv_buf = calloc(MNL_SOCKET_BUFFER_SIZE, 1); 550 | 551 | if (!ctx->mnl_recv_buf) { 552 | TA_PRINT(stderr, "Allocating memory for mnl recv buf failed\n"); 553 | exit(EXIT_FAILURE); 554 | } 555 | 556 | ctx->address = calloc(sizeof(struct tac_address), 1); 557 | 558 | if (!ctx->address) { 559 | TA_PRINT(stderr, "Allocating memory for address failed\n"); 560 | exit(EXIT_FAILURE); 561 | } 562 | 563 | //set default values for context 564 | ctx->use_syslog = 0; 565 | ctx->logfile = stderr; 566 | ctx->address->addr_family = AF_UNSPEC; 567 | ctx->cmd = TA_SHARED_CMD_REQ; 568 | ctx->daemonize = 1; 569 | 570 | if (!parse_cmd_args(ctx, argc, argv)) { 571 | free(ctx); 572 | exit(EXIT_FAILURE); 573 | } 574 | 575 | //create event loop 576 | if (uv_loop_init(&(ctx->event_loop))) { 577 | TA_PRINT_SYSLOG(ctx, LOG_CRIT, "Event loop creation failed\n"); 578 | free_ctx(ctx); 579 | exit(EXIT_FAILURE); 580 | } 581 | 582 | if (!ta_allocator_libuv_helpers_configure_unix_handle(&(ctx->event_loop), 583 | &(ctx->unix_socket_handle), &(ctx->unix_socket_timeout_handle), 584 | unix_socket_timeout_cb, ctx)) { 585 | TA_PRINT_SYSLOG(ctx, LOG_CRIT, "Failed to configure domain handle\n"); 586 | free_ctx(ctx); 587 | exit(EXIT_FAILURE); 588 | } 589 | 590 | if (uv_timer_init(&(ctx->event_loop), &(ctx->request_timeout_handle))) { 591 | TA_PRINT_SYSLOG(ctx, LOG_CRIT, "Failed to init request timeout\n"); 592 | free_ctx(ctx); 593 | exit(EXIT_FAILURE); 594 | } 595 | 596 | ctx->request_timeout_handle.data = ctx; 597 | 598 | if (ctx->address->addr_family == AF_INET && 599 | !table_allocator_client_netlink_configure(ctx)) { 600 | TA_PRINT_SYSLOG(ctx, LOG_CRIT, "Netlink init failed\n"); 601 | free_ctx(ctx); 602 | exit(EXIT_FAILURE); 603 | } 604 | 605 | TA_PRINT_SYSLOG(ctx, LOG_INFO, "Started allocator client\n"); 606 | 607 | uv_run(&(ctx->event_loop), UV_RUN_DEFAULT); 608 | free_ctx(ctx); 609 | 610 | exit(EXIT_SUCCESS); 611 | } 612 | -------------------------------------------------------------------------------- /table_allocator/client/src/table_allocator_client_netlink.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Kristian Evensen 3 | * 4 | * This file is part of Usb Monitor. Usb Monitor is free software: you can 5 | * redistribute it and/or modify it under the terms of the Lesser GNU General 6 | * Public License as published by the Free Software Foundation, either version 3 7 | * of the License, or (at your option) any later version. 8 | * 9 | * Usb Montior is distributed in the hope that it will be useful, but WITHOUT ANY 10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 11 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU General Public License along with 14 | * Usb Monitor. If not, see http://www.gnu.org/licenses/. 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #include 26 | #include 27 | 28 | #include "table_allocator_client_netlink.h" 29 | #include "table_allocator_client.h" 30 | 31 | static int32_t table_allocator_client_netlink_modify_rule(struct tac_ctx *ctx, 32 | uint32_t msg_type, uint32_t flags, uint8_t prefix_len, uint8_t dir, 33 | uint32_t prio, const char *ifname) 34 | { 35 | uint8_t buf[MNL_SOCKET_BUFFER_SIZE]; 36 | struct nlmsghdr *nlh; 37 | struct rtmsg *rt; 38 | union { 39 | struct sockaddr_in *addr4; 40 | struct sockaddr_in6 *addr6; 41 | } u_addr; 42 | 43 | memset(buf, 0, MNL_SOCKET_BUFFER_SIZE); 44 | 45 | nlh = mnl_nlmsg_put_header(buf); 46 | nlh->nlmsg_type = msg_type; 47 | nlh->nlmsg_flags = NLM_F_REQUEST | flags; 48 | nlh->nlmsg_seq = 0; 49 | 50 | rt = mnl_nlmsg_put_extra_header(nlh, sizeof(struct rtmsg)); 51 | rt->rtm_family = ctx->address->addr_family; 52 | rt->rtm_dst_len = 0; 53 | rt->rtm_table = ctx->address->rt_table; 54 | rt->rtm_protocol = RTPROT_BOOT; 55 | rt->rtm_scope = RT_SCOPE_UNIVERSE; 56 | rt->rtm_type = RTN_UNICAST; 57 | 58 | mnl_attr_put_u32(nlh, FRA_PRIORITY, prio); 59 | mnl_attr_put_u32(nlh, FRA_TABLE, ctx->address->rt_table); 60 | 61 | if (dir == FRA_SRC) { 62 | rt->rtm_src_len = prefix_len; 63 | } else if (dir == FRA_DST) { 64 | rt->rtm_dst_len = prefix_len; 65 | } 66 | 67 | if (rt->rtm_src_len || rt->rtm_dst_len) { 68 | if (ctx->address->addr_family == AF_INET) { 69 | u_addr.addr4 = (struct sockaddr_in*) &(ctx->address->addr); 70 | mnl_attr_put_u32(nlh, dir, u_addr.addr4->sin_addr.s_addr); 71 | } else { 72 | u_addr.addr6 = (struct sockaddr_in6*) &(ctx->address->addr); 73 | mnl_attr_put(nlh, dir, sizeof(u_addr.addr6->sin6_addr.s6_addr), 74 | u_addr.addr6->sin6_addr.s6_addr); 75 | } 76 | } 77 | 78 | if (ifname) { 79 | mnl_attr_put_strz(nlh, FRA_IFNAME, ifname); 80 | } 81 | 82 | return mnl_socket_sendto(ctx->rt_mnl_socket, nlh, nlh->nlmsg_len); 83 | } 84 | 85 | static void table_allocator_client_netlink_timeout_cb(uv_timer_t *handle) 86 | { 87 | struct tac_ctx *ctx = handle->data; 88 | 89 | //we might have partial failures, causing update_rules to be called multiple 90 | //times when adding rules. This is not critical, if a rule is equal to an 91 | //existent rule then it will not be added 92 | table_allocator_client_netlink_update_rules(ctx, RTM_NEWRULE); 93 | } 94 | 95 | void table_allocator_client_netlink_update_rules(struct tac_ctx *ctx, 96 | uint32_t msg_type) 97 | { 98 | int32_t retval = 0; 99 | 100 | //potential race between timer and lease expired 101 | if (msg_type == RTM_NEWRULE && ctx->address->rules_added) { 102 | return; 103 | } 104 | 105 | retval = table_allocator_client_netlink_modify_rule(ctx, msg_type, 106 | NLM_F_CREATE | NLM_F_EXCL, 32, FRA_SRC, ADDR_RULE_PRIO, NULL); 107 | 108 | if (retval < 0) { 109 | TA_PRINT_SYSLOG(ctx, LOG_INFO, "Failed to update rules. Ifname: %s " 110 | "Address: %s Family: %u Table: %u Type: %u\n", 111 | ctx->address->ifname, ctx->address->address_str, 112 | ctx->address->addr_family, ctx->address->rt_table, msg_type); 113 | 114 | //only start timeout on newrule, deleting rule when interface goes down 115 | //is a best-effort thing 116 | if (msg_type == RTM_NEWRULE) { 117 | uv_timer_start(&(ctx->netlink_timeout_handle), 118 | table_allocator_client_netlink_timeout_cb, 119 | TAC_NETLINK_TIMEOUT_MS, 0); 120 | } 121 | return; 122 | } 123 | 124 | retval = table_allocator_client_netlink_modify_rule(ctx, msg_type, 125 | NLM_F_CREATE | NLM_F_EXCL, ctx->address->subnet_prefix_len, 126 | FRA_DST, NW_RULE_PRIO, NULL); 127 | 128 | if (retval < 0) { 129 | TA_PRINT_SYSLOG(ctx, LOG_INFO, "Failed to update rules. Ifname: %s " 130 | "Address: %s Family: %u Table: %u Type: %u\n", 131 | ctx->address->ifname, ctx->address->address_str, 132 | ctx->address->addr_family, ctx->address->rt_table, msg_type); 133 | 134 | if (msg_type == RTM_NEWRULE) { 135 | uv_timer_start(&(ctx->netlink_timeout_handle), 136 | table_allocator_client_netlink_timeout_cb, 137 | TAC_NETLINK_TIMEOUT_MS, 0); 138 | } 139 | return; 140 | } 141 | 142 | retval = table_allocator_client_netlink_modify_rule(ctx, msg_type, 143 | NLM_F_CREATE | NLM_F_EXCL, 0, FRA_DST, DEF_RULE_PRIO, "lo"); 144 | 145 | if (retval < 0) { 146 | TA_PRINT_SYSLOG(ctx, LOG_INFO, "Failed to update rules. Ifname: %s " 147 | "Address: %s Family: %u Table: %u Type: %u\n", 148 | ctx->address->ifname, ctx->address->address_str, 149 | ctx->address->addr_family, ctx->address->rt_table, msg_type); 150 | 151 | if (msg_type == RTM_NEWRULE) { 152 | uv_timer_start(&(ctx->netlink_timeout_handle), 153 | table_allocator_client_netlink_timeout_cb, 154 | TAC_NETLINK_TIMEOUT_MS, 0); 155 | } 156 | } else { 157 | TA_PRINT_SYSLOG(ctx, LOG_INFO, "Updated rules. Ifname: %s " 158 | "Address: %s Family: %u Table: %u Type: %u\n", 159 | ctx->address->ifname, ctx->address->address_str, 160 | ctx->address->addr_family, ctx->address->rt_table, msg_type); 161 | 162 | ctx->address->rules_added = 1; 163 | } 164 | }; 165 | 166 | static int table_allocator_client_netlink_parse_nlattr( 167 | const struct nlattr *attr, void *data) 168 | { 169 | struct nlattr_storage *storage = (struct nlattr_storage*) data; 170 | int32_t type = mnl_attr_get_type(attr); 171 | 172 | if (mnl_attr_type_valid(attr, storage->limit) < 0) 173 | return MNL_CB_OK; 174 | 175 | storage->tb[type] = attr; 176 | return MNL_CB_OK; 177 | } 178 | 179 | static uint8_t table_allocator_client_netlink_handle_dellink(struct tac_ctx *ctx, 180 | struct nlmsghdr *nl_hdr) 181 | { 182 | struct tac_address *address = ctx->address; 183 | struct ifinfomsg *ifi_msg = (struct ifinfomsg*) 184 | mnl_nlmsg_get_payload(nl_hdr); 185 | 186 | return ifi_msg->ifi_index == address->ifidx; 187 | } 188 | 189 | static uint8_t table_allocator_client_netlink_cmp_ip6addr(struct in6_addr *a, 190 | struct in6_addr *b) 191 | { 192 | return a->s6_addr32[0] == b->s6_addr32[0] && 193 | a->s6_addr32[1] == b->s6_addr32[1] && 194 | a->s6_addr32[2] == b->s6_addr32[2] && 195 | a->s6_addr32[3] == b->s6_addr32[3]; 196 | } 197 | 198 | static uint8_t table_allocator_client_netlink_handle_deladdr( 199 | struct tac_ctx *ctx, struct nlmsghdr *nl_hdr) 200 | { 201 | struct tac_address *address = ctx->address; 202 | struct ifaddrmsg *ifa_msg = (struct ifaddrmsg*) 203 | mnl_nlmsg_get_payload(nl_hdr); 204 | const struct nlattr *tb[IFA_MAX + 1] = {}; 205 | struct nlattr_storage tb_storage = {tb, IFA_MAX}; 206 | uint32_t *addr6_raw; 207 | uint8_t i; 208 | union { 209 | struct in_addr addr4; 210 | struct in6_addr addr6; 211 | } u_addr; 212 | 213 | union { 214 | struct sockaddr_in *sockaddr4; 215 | struct sockaddr_in6 *sockaddr6; 216 | } u_sockaddr; 217 | 218 | 219 | if (mnl_attr_parse(nl_hdr, sizeof(struct ifaddrmsg), 220 | table_allocator_client_netlink_parse_nlattr, &tb_storage) != 221 | MNL_CB_OK) { 222 | TA_PRINT_SYSLOG(ctx, LOG_ERR, "Failed to parse netlink attrs\n"); 223 | return 0; 224 | } 225 | 226 | if (ifa_msg->ifa_index != address->ifidx || 227 | ifa_msg->ifa_family != address->addr_family || 228 | ifa_msg->ifa_prefixlen != address->subnet_prefix_len) { 229 | return 0; 230 | } 231 | 232 | if (ifa_msg->ifa_family == AF_INET && tb[IFA_LOCAL]) { 233 | u_sockaddr.sockaddr4 = (struct sockaddr_in*) &(address->addr); 234 | u_addr.addr4.s_addr = mnl_attr_get_u32(tb[IFA_LOCAL]); 235 | 236 | if (u_addr.addr4.s_addr != u_sockaddr.sockaddr4->sin_addr.s_addr) { 237 | return 0; 238 | } 239 | } else if (ifa_msg->ifa_family == AF_INET6 && tb[IFA_ADDRESS]) { 240 | u_sockaddr.sockaddr6 = (struct sockaddr_in6*) &(address->addr); 241 | addr6_raw = (uint32_t*) mnl_attr_get_payload(tb[IFA_ADDRESS]); 242 | 243 | //todo: remove this copy when testing ipv6 properly 244 | for (i = 0; i < 4; i++) { 245 | u_addr.addr6.s6_addr32[i] = *(addr6_raw + i); 246 | } 247 | 248 | if (!table_allocator_client_netlink_cmp_ip6addr( 249 | &(u_sockaddr.sockaddr6->sin6_addr), &(u_addr.addr6))) { 250 | return 0; 251 | } 252 | } 253 | 254 | return 1; 255 | } 256 | 257 | static void table_allocator_client_netlink_recv_cb(uv_udp_t* handle, 258 | ssize_t nread, const uv_buf_t* buf, const struct sockaddr* addr, 259 | unsigned flags) 260 | { 261 | struct tac_ctx *ctx = handle->data; 262 | struct tac_address *address = ctx->address; 263 | struct nlmsghdr *nl_hdr = (struct nlmsghdr*) buf->base; 264 | //this cast is to prevent getting compilation warnings on mnl_nlmsg_next 265 | int32_t numbytes = (int32_t) nread; 266 | uint8_t should_delete = 0; 267 | 268 | if (ctx->closing) { 269 | return; 270 | } else if (nread == 0) { 271 | return; 272 | } else if (nread < 0) { 273 | TA_PRINT_SYSLOG(ctx, LOG_ERR, "Netlink socket read error\n"); 274 | return; 275 | } 276 | 277 | while (mnl_nlmsg_ok(nl_hdr, numbytes)) { 278 | if (nl_hdr->nlmsg_type == RTM_DELLINK) { 279 | //todo: if this returns true, just stop 280 | if (table_allocator_client_netlink_handle_dellink(ctx, nl_hdr)) { 281 | TA_PRINT_SYSLOG(ctx, LOG_DEBUG, 282 | "Will flush rules and stop after DELLINK " 283 | "(fam. %u if. %s addr, %s/%u)\n", address->addr_family, 284 | address->ifname, address->address_str, 285 | address->subnet_prefix_len); 286 | should_delete = 1; 287 | } 288 | } else if (nl_hdr->nlmsg_type == RTM_DELADDR) { 289 | if (table_allocator_client_netlink_handle_deladdr(ctx, nl_hdr)) { 290 | TA_PRINT_SYSLOG(ctx, LOG_DEBUG, 291 | "Will flush rules and stop after DELADDR " 292 | "(fam. %u if. %s addr, %s/%u)\n", address->addr_family, 293 | address->ifname, address->address_str, 294 | address->subnet_prefix_len); 295 | should_delete = 1; 296 | } 297 | } 298 | 299 | if (should_delete) { 300 | //flush rules + release lease (attempt to) 301 | table_allocator_client_netlink_update_rules(ctx, RTM_DELRULE); 302 | table_allocator_client_send_request(ctx, TA_SHARED_CMD_REL); 303 | uv_stop(&(ctx->event_loop)); 304 | ctx->closing = 1; 305 | break; 306 | } 307 | 308 | nl_hdr = mnl_nlmsg_next(nl_hdr, &numbytes); 309 | } 310 | } 311 | 312 | static void table_allocator_client_netlink_alloc_cb(uv_handle_t *handle_size, 313 | size_t suggested_size, uv_buf_t *buf) 314 | { 315 | struct tac_ctx *ctx = handle_size->data; 316 | 317 | memset(ctx->mnl_recv_buf, 0, MNL_SOCKET_BUFFER_SIZE); 318 | buf->base = (char*) ctx->mnl_recv_buf; 319 | buf->len = MNL_SOCKET_BUFFER_SIZE; 320 | } 321 | 322 | uint8_t table_allocator_client_netlink_configure(struct tac_ctx *ctx) 323 | { 324 | struct mnl_socket *mnl_sock = NULL; 325 | 326 | if ((mnl_sock = mnl_socket_open(NETLINK_ROUTE)) == NULL) { 327 | TA_PRINT(stderr, "Failed to open netlink socket\n"); 328 | return 0; 329 | } 330 | 331 | //todo: make this depend on which address family is set 332 | if (mnl_socket_bind(mnl_sock, (1 << (RTNLGRP_IPV4_IFADDR - 1)) | 333 | (1 << (RTNLGRP_LINK - 1)), MNL_SOCKET_AUTOPID) < 0) 334 | { 335 | TA_PRINT(stderr, "Failed to bind netlink socket\n"); 336 | mnl_socket_close(mnl_sock); 337 | return 0; 338 | } 339 | 340 | ctx->rt_mnl_socket = mnl_sock; 341 | 342 | if (uv_timer_init(&(ctx->event_loop), &(ctx->netlink_timeout_handle))) { 343 | TA_PRINT(stderr, "Initializing netlink timer failed\n"); 344 | mnl_socket_close(mnl_sock); 345 | return 0; 346 | } 347 | 348 | ctx->netlink_timeout_handle.data = ctx; 349 | 350 | if (uv_udp_init(&(ctx->event_loop), &(ctx->netlink_handle))) { 351 | TA_PRINT(stderr, "Intializing netlink udp handle failed\n"); 352 | mnl_socket_close(mnl_sock); 353 | return 0; 354 | } 355 | 356 | if (uv_udp_open(&(ctx->netlink_handle), mnl_socket_get_fd(mnl_sock))) { 357 | TA_PRINT(stderr, "Opening netlink udp handle failed\n"); 358 | mnl_socket_close(mnl_sock); 359 | return 0; 360 | } 361 | 362 | if (uv_udp_recv_start(&(ctx->netlink_handle), 363 | table_allocator_client_netlink_alloc_cb, 364 | table_allocator_client_netlink_recv_cb)) { 365 | TA_PRINT(stderr, "Starting netlink receive failed\n"); 366 | mnl_socket_close(mnl_sock); 367 | return 0; 368 | } 369 | 370 | ctx->netlink_handle.data = ctx; 371 | 372 | return 1; 373 | } 374 | 375 | void table_allocator_client_netlink_stop(struct tac_ctx *ctx) 376 | { 377 | //todo: we should really do uv_clos here to make sure we shut down cleanly, 378 | //but as long as this function is only used right before application exits, 379 | //we don't really have to 380 | uv_udp_recv_stop(&(ctx->netlink_handle)); 381 | } 382 | -------------------------------------------------------------------------------- /table_allocator/server/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | #Standard stuff 2 | cmake_minimum_required(VERSION 2.6) 3 | project(table_allocator_server) 4 | 5 | set(CMAKE_C_FLAGS "-O1 -Wall -std=gnu99 -g") 6 | 7 | set (PROJECT_INCLUDE_DIR ${PROJECT_SOURCE_DIR}/include) 8 | set (PROJECT_SRC_DIR ${PROJECT_SOURCE_DIR}/src) 9 | 10 | find_library(LIBUV_LIBRARY uv) 11 | find_library(LIBSQLITE3_LIBRARY sqlite3) 12 | find_library(LIBJSON_LIBRARY json-c) 13 | 14 | set(SOURCE 15 | ${PROJECT_SRC_DIR}/table_allocator_server.c 16 | ${PROJECT_SRC_DIR}/table_allocator_server_sockets.c 17 | ${PROJECT_SRC_DIR}/table_allocator_server_clients.c 18 | ${PROJECT_SRC_DIR}/table_allocator_server_sqlite.c 19 | ) 20 | 21 | set(CPACK_GENERATOR "DEB") 22 | set(CPACK_DEBIAN_PACKAGE_MAINTAINER "Kristian Evensen ") 23 | set(CPACK_DEBIAN_PACKAGE_NAME "table-allocator-server") 24 | set(CPACK_DEBIAN_PACKAGE_VERSION "0.1-1") 25 | set(CPACK_DEBIAN_PACKAGE_ARCHITECTURE "amd64") 26 | set(CPACK_DEBIAN_PACKAGE_DEPENDS "libuv1, libjson-c3, libsqlite3-0") 27 | set(CPACK_DEBIAN_PACKAGE_DESCRIPTION "This package contains the Table Allocator Server, for use together with the table allocator client. The purpose of this tool is to distribute and keep track of routing tables on multihomed hosts. Clients will request tables using a basic, well-defined protocols and are then required to refresh them at some configurable interval. The server will free any dead table leases.") 28 | set(CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA 29 | "${CMAKE_CURRENT_SOURCE_DIR}/deb-files/conffiles;" 30 | "${CMAKE_CURRENT_SOURCE_DIR}/deb-files/changelog;" 31 | "${CMAKE_CURRENT_SOURCE_DIR}/files/systemd/postinst;" 32 | "${CMAKE_CURRENT_SOURCE_DIR}/files/systemd/prerm;") 33 | INCLUDE(CPack) 34 | 35 | #required for individual builds 36 | if(NOT TARGET table_allocator_shared) 37 | add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../shared 38 | ${CMAKE_CURRENT_BINARY_DIR}/shared) 39 | include_directories("${CMAKE_CURRENT_SOURCE_DIR}/../shared/include") 40 | endif() 41 | 42 | include_directories("${PROJECT_INCLUDE_DIR}") 43 | add_executable(${PROJECT_NAME} ${SOURCE}) 44 | target_link_libraries(${PROJECT_NAME} table_allocator_shared) 45 | target_link_libraries(${PROJECT_NAME} ${LIBUV_LIBRARY}) 46 | target_link_libraries(${PROJECT_NAME} ${LIBSQLITE3_LIBRARY}) 47 | target_link_libraries(${PROJECT_NAME} ${LIBJSON_LIBRARY}) 48 | install(TARGETS ${PROJECT_NAME} RUNTIME DESTINATION sbin) 49 | install(FILES ${PROJECT_SOURCE_DIR}/files/server_config.json DESTINATION 50 | /etc/ PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ 51 | RENAME table_allocator_server_config.json) 52 | install(FILES ${PROJECT_SOURCE_DIR}/files/systemd/table-allocator-server.service DESTINATION 53 | /lib/systemd/system/ PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ 54 | RENAME table-allocator-server.service) 55 | 56 | -------------------------------------------------------------------------------- /table_allocator/server/deb-files/changelog: -------------------------------------------------------------------------------- 1 | table-allocator-server (0.1-1) unstable; urgency=low 2 | 3 | * Initial release. Commit 3b25b71dbe69c8ff110a5cb69231fe3991faa1fc. 4 | 5 | -- Kristian Evensen Mon, 01 May 2017 15:58:36 +0200 6 | -------------------------------------------------------------------------------- /table_allocator/server/deb-files/conffiles: -------------------------------------------------------------------------------- 1 | /etc/table_allocator_server_config.json 2 | -------------------------------------------------------------------------------- /table_allocator/server/files/server_config.json: -------------------------------------------------------------------------------- 1 | { 2 | "socket_path": "tas_socket", 3 | "table_offset": 10000, 4 | "num_tables": 40, 5 | "table_timeout": 3600, 6 | "db_path": "/tmp/table_allocator_server.db", 7 | "do_syslog": true, 8 | //"log_path": "/tmp/tas.log", 9 | "addr_families": { 10 | "inet": true, 11 | "inet6": false, 12 | "unspec": false 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /table_allocator/server/files/systemd/postinst: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | systemctl daemon-reload && \ 4 | systemctl enable table-allocator-server && \ 5 | systemctl restart table-allocator-server || true 6 | -------------------------------------------------------------------------------- /table_allocator/server/files/systemd/prerm: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | systemctl stop table-allocator-server 4 | systemctl disable table-allocator-server 5 | -------------------------------------------------------------------------------- /table_allocator/server/files/systemd/table-allocator-server.service: -------------------------------------------------------------------------------- 1 | [Install] 2 | WantedBy=network.target 3 | 4 | [Unit] 5 | Description=Table Allocator Server 6 | Before=network-pre.target 7 | Wants=network-pre.target 8 | 9 | [Service] 10 | ExecStart=/usr/sbin/table_allocator_server -c /etc/table_allocator_server_config.json 11 | Type=simple 12 | Restart=on-failure 13 | -------------------------------------------------------------------------------- /table_allocator/server/include/table_allocator_server.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Kristian Evensen 3 | * 4 | * This file is part of Usb Monitor. Usb Monitor is free software: you can 5 | * redistribute it and/or modify it under the terms of the Lesser GNU General 6 | * Public License as published by the Free Software Foundation, either version 3 7 | * of the License, or (at your option) any later version. 8 | * 9 | * Usb Montior is distributed in the hope that it will be useful, but WITHOUT ANY 10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 11 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU General Public License along with 14 | * Usb Monitor. If not, see http://www.gnu.org/licenses/. 15 | */ 16 | 17 | #ifndef TABLE_ALLOCATOR_SERVER_H 18 | #define TABLE_ALLOCATOR_SERVER_H 19 | 20 | #include 21 | #include 22 | #include 23 | 24 | #include 25 | 26 | #define TAS_SOCKET_PATH_KEY "socket_path" 27 | #define TAS_TABLE_OFFSET_KEY "table_offset" 28 | #define TAS_NUM_TABLES_KEY "num_tables" 29 | #define TAS_TABLE_TIMEOUT_KEY "table_timeout" 30 | #define TAS_DB_PATH_KEY "db_path" 31 | #define TAS_DO_SYSLOG_KEY "do_syslog" 32 | #define TAS_LOG_PATH_KEY "log_path" 33 | #define TAS_ADDR_FAMILIES_KEY "addr_families" 34 | 35 | #define TAS_ADDR_FAMILIES_INET_KEY "inet" 36 | #define TAS_ADDR_FAMILIES_INET6_KEY "inet6" 37 | #define TAS_ADDR_FAMILIES_UNSPEC_KEY "unspec" 38 | 39 | #define CLIENT_REQ_BUFFER_SIZE 512 40 | #define MAX_DB_PATH_LEN 256 41 | 42 | #define ADDR_FAMILY_INET 0x1 43 | #define ADDR_FAMILY_INET6 0x2 44 | #define ADDR_FAMILY_UNSPEC 0x4 45 | 46 | //check for dead leases every fifth minute 47 | #define DEAD_LEASE_TIMEOUT 300000 48 | 49 | struct tas_client_req { 50 | char ifname[IFNAMSIZ]; 51 | char tag[TA_SHARED_MAX_TAG_SIZE]; 52 | char address[INET6_ADDRSTRLEN]; 53 | uint8_t ver; 54 | uint8_t cmd; 55 | uint8_t addr_family; 56 | }; 57 | 58 | struct tas_ctx { 59 | uv_loop_t event_loop; 60 | uv_udp_t unix_socket_handle; 61 | uv_timer_t unix_socket_timeout_handle; 62 | uv_timer_t dead_leases_timeout_handle; 63 | FILE *logfile; 64 | 65 | //different database pointers/handlers 66 | sqlite3 *db_handle; 67 | sqlite3_stmt *insert_rt_table; 68 | sqlite3_stmt *select_rt_table; 69 | sqlite3_stmt *delete_rt_table; 70 | sqlite3_stmt *update_rt_table; 71 | sqlite3_stmt *select_dead_leases; 72 | sqlite3_stmt *delete_dead_leases; 73 | 74 | //current we only support one request 75 | struct tas_client_req *req; 76 | 77 | //in memory table map 78 | uint32_t *tables_inet; 79 | uint32_t *tables_inet6; 80 | uint32_t *tables_unspec; 81 | uint32_t num_table_elements; 82 | uint32_t num_tables; 83 | uint32_t table_offset; 84 | 85 | //paths (we don't carry the logfile around) 86 | uint8_t socket_path[TA_SHARED_MAX_ADDR_SIZE]; 87 | uint8_t db_path[MAX_DB_PATH_LEN]; 88 | 89 | //todo: allocate this separatly? 90 | uint8_t client_req_buffer[CLIENT_REQ_BUFFER_SIZE]; 91 | uint16_t table_timeout; 92 | uint8_t use_syslog; 93 | }; 94 | 95 | #endif 96 | -------------------------------------------------------------------------------- /table_allocator/server/include/table_allocator_server_clients.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Kristian Evensen 3 | * 4 | * This file is part of Usb Monitor. Usb Monitor is free software: you can 5 | * redistribute it and/or modify it under the terms of the Lesser GNU General 6 | * Public License as published by the Free Software Foundation, either version 3 7 | * of the License, or (at your option) any later version. 8 | * 9 | * Usb Montior is distributed in the hope that it will be useful, but WITHOUT ANY 10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 11 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU General Public License along with 14 | * Usb Monitor. If not, see http://www.gnu.org/licenses/. 15 | */ 16 | 17 | #ifndef TABLE_ALLOCATOR_SERVER_CLIENTS_H 18 | #define TABLE_ALLOCATOR_SERVER_CLIENTS_H 19 | 20 | struct tas_ctx; 21 | struct tas_client_req; 22 | 23 | uint8_t table_allocator_server_clients_handle_req(struct tas_ctx *ctx, 24 | struct tas_client_req *req, uint32_t *rt_table, 25 | uint32_t *lease_sec_ptr); 26 | 27 | uint8_t table_allocator_server_clients_handle_release(struct tas_ctx *ctx, 28 | struct tas_client_req *req); 29 | 30 | void table_allocator_server_clients_delete_dead_leases(struct tas_ctx *ctx); 31 | 32 | void table_allocator_server_clients_set_table(struct tas_ctx *ctx, 33 | uint8_t addr_family, uint32_t rt_table); 34 | #endif 35 | -------------------------------------------------------------------------------- /table_allocator/server/include/table_allocator_server_sockets.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Kristian Evensen 3 | * 4 | * This file is part of Usb Monitor. Usb Monitor is free software: you can 5 | * redistribute it and/or modify it under the terms of the Lesser GNU General 6 | * Public License as published by the Free Software Foundation, either version 3 7 | * of the License, or (at your option) any later version. 8 | * 9 | * Usb Montior is distributed in the hope that it will be useful, but WITHOUT ANY 10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 11 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU General Public License along with 14 | * Usb Monitor. If not, see http://www.gnu.org/licenses/. 15 | */ 16 | 17 | #ifndef TABLE_ALLOCATOR_SERVER_SOCKETS_H 18 | #define TABLE_ALLOCATOR_SERVER_SOCKETS_H 19 | 20 | #include 21 | 22 | void unix_socket_timeout_cb(uv_timer_t *handle); 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /table_allocator/server/include/table_allocator_server_sqlite.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Kristian Evensen 3 | * 4 | * This file is part of Usb Monitor. Usb Monitor is free software: you can 5 | * redistribute it and/or modify it under the terms of the Lesser GNU General 6 | * Public License as published by the Free Software Foundation, either version 3 7 | * of the License, or (at your option) any later version. 8 | * 9 | * Usb Montior is distributed in the hope that it will be useful, but WITHOUT ANY 10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 11 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU General Public License along with 14 | * Usb Monitor. If not, see http://www.gnu.org/licenses/. 15 | */ 16 | 17 | #ifndef TABLE_ALLOCATOR_SERVER_SQLITE_H 18 | #define TABLE_ALLOCATOR_SERVER_SQLITE_H 19 | 20 | #include 21 | #include 22 | #include 23 | 24 | #define CREATE_SQL "CREATE TABLE IF NOT EXISTS RtTables(" \ 25 | "RtTable INTEGER NOT NULL," \ 26 | "AddrFamily INTEGER NOT NULL," \ 27 | "Ifname TEXT NOT NULL," \ 28 | "Addr TEXT NOT NULL," \ 29 | "LeaseExpires INTEGER NOT NULL,"\ 30 | "Tag TEXT," \ 31 | "PRIMARY KEY(RtTable,AddrFamily)"\ 32 | "ON CONFLICT REPLACE)" 33 | 34 | #define SELECT_RT_TABLE "SELECT "\ 35 | "RtTable "\ 36 | "FROM "\ 37 | "RtTables "\ 38 | "WHERE "\ 39 | "AddrFamily=? AND Ifname=? AND Addr=?" 40 | 41 | #define INSERT_RT_TABLE "INSERT INTO "\ 42 | "RtTables (RtTable,AddrFamily,Ifname,Addr,"\ 43 | "LeaseExpires, Tag) "\ 44 | "VALUES "\ 45 | "(?,?,?,?,?,?)" 46 | 47 | #define DELETE_RT_TABLE "DELETE FROM "\ 48 | "RtTables "\ 49 | "WHERE "\ 50 | "AddrFamily=? AND Ifname=? AND Addr=?" 51 | 52 | #define UPDATE_RT_TABLE "UPDATE "\ 53 | "RtTables "\ 54 | "SET "\ 55 | "LeaseExpires=? "\ 56 | "WHERE "\ 57 | "AddrFamily=? AND RtTable=?" 58 | 59 | #define SELECT_DEAD_LEASES "SELECT "\ 60 | "RtTable,AddrFamily "\ 61 | "FROM "\ 62 | "RtTables "\ 63 | "WHERE "\ 64 | "LeaseExpires <= ?" 65 | 66 | #define DELETE_DEAD_LEASES "DELETE FROM "\ 67 | "RtTables "\ 68 | "WHERE "\ 69 | "LeaseExpires <= ?" 70 | 71 | //used when constructing the map on application start 72 | #define SELECT_ALIVE_LEASES "SELECT "\ 73 | "RtTable,AddrFamily "\ 74 | "FROM "\ 75 | "RtTables "\ 76 | "WHERE "\ 77 | "LeaseExpires > ?" 78 | 79 | struct tas_ctx; 80 | struct tas_client_req; 81 | 82 | typedef void (*check_table_cb)(void *ptr, uint8_t addr_family, 83 | uint32_t rt_table); 84 | 85 | //create db and set up queries. Returns 0/1 on failure/success 86 | uint8_t table_allocator_server_sqlite_create_db(struct tas_ctx *ctx); 87 | 88 | //insert a table into the database 89 | uint8_t table_allocator_sqlite_insert_table(struct tas_ctx *ctx, 90 | struct tas_client_req *req, uint32_t rt_table, time_t lease_sec); 91 | 92 | //checks if the current (family, ifname, addr) has a lease and returns the table 93 | //if it is the case 94 | uint32_t table_allocator_sqlite_get_table(struct tas_ctx *ctx, 95 | struct tas_client_req *req); 96 | 97 | //remove one table allocation from the database 98 | uint8_t table_allocator_sqlite_remove_table(struct tas_ctx *ctx, 99 | struct tas_client_req *req); 100 | 101 | //update lease seq 102 | uint8_t table_allocator_sqlite_update_lease(struct tas_ctx *ctx, 103 | uint32_t rt_table, uint8_t addr_family, uint32_t lease_sec); 104 | 105 | //delete dead leases, don't care about family 106 | uint8_t table_allocator_sqlite_delete_dead_leases(struct tas_ctx *ctx, 107 | uint32_t lease_limit, check_table_cb cb); 108 | 109 | //update table map with info from db on start of application 110 | uint8_t table_allocator_sqlite_build_table_map(struct tas_ctx *ctx, 111 | uint32_t lease_limit, check_table_cb cb); 112 | 113 | #endif 114 | -------------------------------------------------------------------------------- /table_allocator/server/src/table_allocator_server.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Kristian Evensen 3 | * 4 | * This file is part of Usb Monitor. Usb Monitor is free software: you can 5 | * redistribute it and/or modify it under the terms of the Lesser GNU General 6 | * Public License as published by the Free Software Foundation, either version 3 7 | * of the License, or (at your option) any later version. 8 | * 9 | * Usb Montior is distributed in the hope that it will be useful, but WITHOUT ANY 10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 11 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU General Public License along with 14 | * Usb Monitor. If not, see http://www.gnu.org/licenses/. 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #include 26 | #include 27 | 28 | #include "table_allocator_server.h" 29 | #include "table_allocator_server_sockets.h" 30 | #include "table_allocator_server_sqlite.h" 31 | #include "table_allocator_server_clients.h" 32 | 33 | static void populate_table_map_cb(void *ptr, uint8_t addr_family, 34 | uint32_t rt_table) 35 | { 36 | table_allocator_server_clients_set_table(ptr, addr_family, rt_table); 37 | } 38 | 39 | static void dead_leases_timeout_cb(uv_timer_t *handle) 40 | { 41 | table_allocator_server_clients_delete_dead_leases(handle->data); 42 | } 43 | 44 | static void populate_table_map(uint32_t *table_map, uint32_t num_elems) 45 | { 46 | uint32_t i; 47 | 48 | for (i = 0; i < num_elems; i++) { 49 | table_map[i] = UINT32_MAX; 50 | } 51 | } 52 | 53 | static uint8_t configure_rt_tables(struct tas_ctx *ctx, uint8_t addr_families) 54 | { 55 | uint32_t num_table_elements = 0; 56 | 57 | //comput the number of entries we need in the table, done by diving the 58 | //maximum number of elements on 32 59 | if (ctx->num_tables < 32) { 60 | num_table_elements = 0; 61 | } else if (ctx->num_tables & 0x1F) { 62 | //If number of values is not divisible by 32, I need an additional 63 | //element to store the remainders. Divisibility is checked by masking 64 | //with 31. If any bit is set, then value is not divisible by 32 65 | num_table_elements = (ctx->num_tables >> 5) + 1; 66 | } else { 67 | num_table_elements = ctx->num_tables >> 5; 68 | } 69 | 70 | if (addr_families & ADDR_FAMILY_INET) { 71 | if (!(ctx->tables_inet = calloc(sizeof(uint32_t) * num_table_elements, 72 | 1))) { 73 | TA_PRINT(stderr, "Failed to allocate v4-tables\n"); 74 | return 0; 75 | } else { 76 | populate_table_map(ctx->tables_inet, num_table_elements); 77 | } 78 | } 79 | 80 | if (addr_families & ADDR_FAMILY_INET6) { 81 | if (!(ctx->tables_inet6 = calloc(sizeof(uint32_t) * num_table_elements, 82 | 1))) { 83 | TA_PRINT(stderr, "Failed to allocate v6-tables\n"); 84 | return 0; 85 | } else { 86 | populate_table_map(ctx->tables_inet6, num_table_elements); 87 | } 88 | } 89 | 90 | if (addr_families & ADDR_FAMILY_UNSPEC) { 91 | if (!(ctx->tables_unspec = calloc(sizeof(uint32_t) * num_table_elements, 92 | 1))) { 93 | TA_PRINT(stderr, "Failed to allocate unspec-tables\n"); 94 | return 0; 95 | } else { 96 | populate_table_map(ctx->tables_unspec, num_table_elements); 97 | } 98 | } 99 | 100 | ctx->num_table_elements = num_table_elements; 101 | 102 | return 1; 103 | } 104 | 105 | static uint8_t configure_table_db(struct tas_ctx *ctx) 106 | { 107 | if (!table_allocator_server_sqlite_create_db(ctx)) 108 | return 0; 109 | 110 | return 1; 111 | } 112 | 113 | static void parse_addr_families(struct json_object *fam_obj, uint8_t *mask) 114 | { 115 | json_bool add_addr_family; 116 | 117 | json_object_object_foreach(fam_obj, key, val) { 118 | if (!strcmp(key, TAS_ADDR_FAMILIES_INET_KEY) && 119 | json_object_is_type(val, json_type_boolean)) { 120 | add_addr_family = json_object_get_boolean(val); 121 | if (add_addr_family) { 122 | *mask |= ADDR_FAMILY_INET; 123 | } 124 | } else if (!strcmp(key, TAS_ADDR_FAMILIES_INET6_KEY) && 125 | json_object_is_type(val, json_type_boolean)) { 126 | add_addr_family = json_object_get_boolean(val); 127 | if (add_addr_family) { 128 | *mask |= ADDR_FAMILY_INET6; 129 | } 130 | } else if (!strcmp(key, TAS_ADDR_FAMILIES_UNSPEC_KEY) && 131 | json_object_is_type(val, json_type_boolean)) { 132 | add_addr_family = json_object_get_boolean(val); 133 | if (add_addr_family) { 134 | *mask |= ADDR_FAMILY_UNSPEC; 135 | } 136 | } 137 | } 138 | } 139 | 140 | static uint8_t parse_config(struct tas_ctx *ctx, const char *conf_file_path) 141 | { 142 | uint8_t conf_file_buf[1024] = {0}; 143 | uint8_t addr_fam_mask = 0; 144 | FILE *conf_file = NULL; 145 | size_t num_bytes; 146 | struct json_object *conf_obj, *addr_families_obj = NULL; 147 | const char *socket_path = NULL, *db_path = NULL, *log_path = NULL; 148 | 149 | if (!(conf_file = fopen(conf_file_path, "r"))) { 150 | TA_PRINT(stderr, "Failed to open config file: %s\n", conf_file_path); 151 | return 0; 152 | } 153 | 154 | num_bytes = fread(conf_file_buf, sizeof(conf_file_buf), 1, conf_file); 155 | 156 | if (ferror(conf_file)) { 157 | TA_PRINT(stderr, "Reading config file failed\n"); 158 | fclose(conf_file); 159 | return 0; 160 | } 161 | 162 | fclose(conf_file); 163 | 164 | //todo: add better handling here! 165 | if (num_bytes == sizeof(conf_file_buf)) { 166 | TA_PRINT(stderr, "Buffer too small to store config file\n"); 167 | return 0; 168 | } 169 | 170 | if (!(conf_obj = json_tokener_parse((const char*) conf_file_buf))) { 171 | TA_PRINT(stderr, "Could not parse config json\n"); 172 | return 0; 173 | } 174 | 175 | json_object_object_foreach(conf_obj, key, val) { 176 | if (!strcmp(key, TAS_SOCKET_PATH_KEY) && 177 | json_object_is_type(val, json_type_string)) { 178 | //path to domain socket 179 | socket_path = json_object_get_string(val); 180 | } else if (!strcmp(key, TAS_TABLE_OFFSET_KEY) && 181 | json_object_is_type(val, json_type_int)) { 182 | //table offset (i.e., first table returned will be this value) 183 | ctx->table_offset = json_object_get_int(val); 184 | } else if (!strcmp(key, TAS_NUM_TABLES_KEY) && 185 | json_object_is_type(val, json_type_int)) { 186 | //how many tables we can allocate 187 | ctx->num_tables = json_object_get_int(val); 188 | } else if (!strcmp(key, TAS_TABLE_TIMEOUT_KEY) && 189 | json_object_is_type(val, json_type_int)) { 190 | //how long a table lease is valid for (a lease has to updated within 191 | //this interval) 192 | ctx->table_timeout = json_object_get_int(val); 193 | } else if (!strcmp(key, TAS_DB_PATH_KEY) && 194 | json_object_is_type(val, json_type_string)) { 195 | //path to the db used to store leases (we use sqlite3 for now) 196 | db_path = json_object_get_string(val); 197 | } else if (!strcmp(key, TAS_DO_SYSLOG_KEY) && 198 | json_object_is_type(val, json_type_boolean)) { 199 | //if we should write to syslog (default to false) 200 | ctx->use_syslog = json_object_get_boolean(val); 201 | } else if (!strcmp(key, TAS_LOG_PATH_KEY) && 202 | json_object_is_type(val, json_type_string)) { 203 | //log path (default stderr) 204 | log_path = json_object_get_string(val); 205 | } else if (!strcmp(key, TAS_ADDR_FAMILIES_KEY) && 206 | json_object_is_type(val, json_type_object)) { 207 | //address families object, will be parsed later 208 | addr_families_obj = val; 209 | } 210 | } 211 | 212 | if (!socket_path || !ctx->table_offset || !ctx->num_tables || 213 | !ctx->table_timeout || !db_path || !addr_families_obj) { 214 | TA_PRINT(stderr, "Required argument is missing\n"); 215 | json_object_put(conf_obj); 216 | return 0; 217 | } 218 | 219 | if (strlen(socket_path) > TA_SHARED_MAX_ADDR_SIZE) { 220 | TA_PRINT(stderr, "Socket path is too long\n"); 221 | json_object_put(conf_obj); 222 | return 0; 223 | } else { 224 | memcpy(ctx->socket_path, socket_path, strlen(socket_path)); 225 | } 226 | 227 | if (strlen(db_path) >= MAX_DB_PATH_LEN) { 228 | TA_PRINT(stderr, "Database path is too long\n"); 229 | json_object_put(conf_obj); 230 | return 0; 231 | } else { 232 | memcpy(ctx->db_path, db_path, strlen(db_path)); 233 | } 234 | 235 | if (((uint32_t) (ctx->table_offset + ctx->num_tables)) <= 236 | ctx->table_offset) { 237 | TA_PRINT(stderr, "Table counter is wrapping\n"); 238 | json_object_put(conf_obj); 239 | return 0; 240 | } 241 | 242 | if (log_path && !(ctx->logfile = fopen(log_path, "a"))) { 243 | //remember that logfile might be NULL here 244 | TA_PRINT(stderr, "Could not open logilfe: %s\n", log_path); 245 | json_object_put(conf_obj); 246 | return 0; 247 | } 248 | 249 | //check families 250 | parse_addr_families(addr_families_obj, &addr_fam_mask); 251 | 252 | if (!addr_fam_mask) { 253 | TA_PRINT(stderr, "Could not open logilfe: %s\n", log_path); 254 | json_object_put(conf_obj); 255 | } 256 | 257 | if (!configure_rt_tables(ctx, addr_fam_mask)) { 258 | TA_PRINT(stderr, "Failed to configure routing tables\n"); 259 | json_object_put(conf_obj); 260 | return 0; 261 | } 262 | 263 | if (!configure_table_db(ctx)) { 264 | TA_PRINT(stderr, "Failed to configure database\n"); 265 | json_object_put(conf_obj); 266 | return 0; 267 | } 268 | 269 | json_object_put(conf_obj); 270 | 271 | return 1; 272 | } 273 | 274 | int main(int argc, char *argv[]) 275 | { 276 | struct tas_ctx *ctx; 277 | const char *conf_file_path = NULL; 278 | int32_t opt; 279 | struct timespec t_now; 280 | 281 | //parse the one command line argument we support 282 | while ((opt = getopt(argc, argv, "c:")) != -1) { 283 | switch (opt) { 284 | case 'c': 285 | conf_file_path = optarg; 286 | break; 287 | default: 288 | TA_PRINT(stderr, "Got unknown argument (%c)\n", opt); 289 | exit(EXIT_FAILURE); 290 | } 291 | } 292 | 293 | if (!conf_file_path) { 294 | TA_PRINT(stderr, "Missing configuration file (specified with -c)\n"); 295 | exit(EXIT_FAILURE); 296 | } 297 | 298 | //create the application context 299 | ctx = calloc(sizeof(struct tas_ctx), 1); 300 | 301 | if (!ctx) { 302 | TA_PRINT(stderr, "Failed to allocate context\n"); 303 | exit(EXIT_FAILURE); 304 | } 305 | 306 | //this application will (so far) only handle one request at a time, so 307 | //allocate memory already here 308 | //todo: if we ever want to scale ... 309 | ctx->req = calloc(sizeof(struct tas_client_req), 1); 310 | 311 | if (!ctx->req) { 312 | TA_PRINT(stderr, "Failed to allocate client request\n"); 313 | exit(EXIT_FAILURE); 314 | } 315 | 316 | ctx->logfile = stderr; 317 | ctx->use_syslog = 0; 318 | 319 | //create event loop 320 | if (uv_loop_init(&(ctx->event_loop))) { 321 | TA_PRINT(stderr, "Event loop creation failed\n"); 322 | exit(EXIT_FAILURE); 323 | } 324 | 325 | //parse config 326 | if (!parse_config(ctx, conf_file_path)) { 327 | TA_PRINT(stderr, "Option parsing failed\n"); 328 | exit(EXIT_FAILURE); 329 | } 330 | 331 | if (!ta_allocator_libuv_helpers_configure_unix_handle(&(ctx->event_loop), 332 | &(ctx->unix_socket_handle), &(ctx->unix_socket_timeout_handle), 333 | unix_socket_timeout_cb, ctx)) { 334 | TA_PRINT(stderr, "Failed to configure domain handle\n"); 335 | exit(EXIT_FAILURE); 336 | } 337 | 338 | //configure the dead lease detection timer - when this is triggered, we will 339 | //free any expired lease 340 | if (uv_timer_init(&(ctx->event_loop), &(ctx->dead_leases_timeout_handle))) { 341 | TA_PRINT(stderr, "Initializing dead leases timeout failed\n"); 342 | exit(EXIT_FAILURE); 343 | } 344 | 345 | ctx->dead_leases_timeout_handle.data = ctx; 346 | 347 | if (uv_timer_start(&(ctx->dead_leases_timeout_handle), 348 | dead_leases_timeout_cb, DEAD_LEASE_TIMEOUT, 349 | DEAD_LEASE_TIMEOUT)) { 350 | TA_PRINT(stderr, "Starting dead leases timer failed\n"); 351 | exit(EXIT_FAILURE); 352 | } 353 | 354 | //do the monotonic_raw test and building map in the same go 355 | if (clock_gettime(CLOCK_MONOTONIC_RAW, &t_now)) { 356 | TA_PRINT(stderr, "No support for MONOTONIC_RAW, can't continue\n"); 357 | exit(EXIT_FAILURE); 358 | } else { 359 | table_allocator_sqlite_build_table_map(ctx, t_now.tv_sec, 360 | populate_table_map_cb); 361 | } 362 | 363 | TA_PRINT_SYSLOG(ctx, LOG_INFO, "Started Table Allocator Server\n" 364 | "\tSocket path: %s\n" 365 | "\tDatabase path: %s\n" 366 | "\tNum. tables: %u\n" 367 | "\tTable offset: %u\n" 368 | "\tMax. table number: %u\n" 369 | "\tTable timeout: %u sec\n", ctx->socket_path, ctx->db_path, 370 | ctx->num_tables, ctx->table_offset, 371 | ctx->num_tables + ctx->table_offset, ctx->table_timeout); 372 | 373 | uv_run(&(ctx->event_loop), UV_RUN_DEFAULT); 374 | 375 | //clean up allocated memory 376 | 377 | exit(EXIT_SUCCESS); 378 | } 379 | -------------------------------------------------------------------------------- /table_allocator/server/src/table_allocator_server_clients.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Kristian Evensen 3 | * 4 | * This file is part of Usb Monitor. Usb Monitor is free software: you can 5 | * redistribute it and/or modify it under the terms of the Lesser GNU General 6 | * Public License as published by the Free Software Foundation, either version 3 7 | * of the License, or (at your option) any later version. 8 | * 9 | * Usb Montior is distributed in the hope that it will be useful, but WITHOUT ANY 10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 11 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU General Public License along with 14 | * Usb Monitor. If not, see http://www.gnu.org/licenses/. 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | 21 | #include 22 | 23 | #include "table_allocator_server_clients.h" 24 | #include "table_allocator_server.h" 25 | #include "table_allocator_server_sqlite.h" 26 | 27 | static uint32_t allocate_table(struct tas_ctx *ctx, uint8_t addr_family) 28 | { 29 | uint32_t rt_table = 0; 30 | uint32_t *rt_tables = NULL; 31 | 32 | if (addr_family == AF_INET) { 33 | rt_tables = ctx->tables_inet; 34 | } else if (addr_family == AF_INET6) { 35 | rt_tables = ctx->tables_inet6; 36 | } else { 37 | rt_tables = ctx->tables_unspec; 38 | } 39 | 40 | for (int i = 0; i < ctx->num_table_elements; i++) { 41 | //Zero means all indexes represented by this element is taken 42 | if(!rt_tables[i]) 43 | continue; 44 | 45 | //Lowest value returned by ffs is 1, so must fix when setting 46 | rt_table = ffs(rt_tables[i]); 47 | rt_tables[i] ^= (1 << (rt_table - 1)); 48 | rt_table += (i*(sizeof(rt_tables[i]))*8); 49 | break; 50 | } 51 | 52 | //Prevent reurning file descriptors larger than the limit. Larger than 53 | //because lowest bit has index 1, not 0 (so we have 1-MAX and not 0-MAX -1) 54 | if (rt_table > ctx->num_tables) 55 | return 0; 56 | else 57 | return rt_table; 58 | } 59 | 60 | static void release_table(struct tas_ctx *ctx, uint8_t addr_family, 61 | uint32_t rt_table) 62 | { 63 | uint32_t element_index, element_bit; 64 | 65 | //we do not add +1, since when using the normal bitwise operators we index 66 | //at 0 67 | rt_table = rt_table - ctx->table_offset; 68 | 69 | element_index = rt_table >> 5; 70 | //What we do here is to mask out the lowest five bits. They contain the 71 | //index of the bit to be set (remember that 32 is 0x20); 72 | element_bit = rt_table & 0x1F; 73 | 74 | if (addr_family == AF_INET) { 75 | ctx->tables_inet[element_index] ^= (1 << element_bit); 76 | } else if (addr_family == AF_INET6) { 77 | ctx->tables_inet6[element_index] ^= (1 << element_bit); 78 | } else { 79 | ctx->tables_unspec[element_index] ^= (1 << element_bit); 80 | } 81 | } 82 | 83 | static void set_table(struct tas_ctx *ctx, uint8_t addr_family, 84 | uint32_t rt_table) 85 | { 86 | uint32_t element_index, element_bit; 87 | 88 | //we do not add +1, since when using the normal bitwise operators we index 89 | //at 0 90 | rt_table = rt_table - ctx->table_offset; 91 | 92 | element_index = rt_table >> 5; 93 | //What we do here is to mask out the lowest five bits. They contain the 94 | //index of the bit to be set (remember that 32 is 0x20); 95 | element_bit = rt_table & 0x1F; 96 | 97 | if (element_index >= ctx->num_table_elements) 98 | return; 99 | 100 | if (addr_family == AF_INET) { 101 | ctx->tables_inet[element_index] ^= (1 << element_bit); 102 | } else if (addr_family == AF_INET6) { 103 | ctx->tables_inet6[element_index] ^= (1 << element_bit); 104 | } else { 105 | ctx->tables_unspec[element_index] ^= (1 << element_bit); 106 | } 107 | } 108 | 109 | static uint8_t is_table_free(struct tas_ctx *ctx, uint8_t addr_family, 110 | uint32_t rt_table) 111 | { 112 | uint32_t element_index, element_bit, element_mask, element_masked; 113 | 114 | //we do not add +1, since when using the normal bitwise operators we index 115 | //at 0 116 | rt_table = rt_table - ctx->table_offset; 117 | 118 | element_index = rt_table >> 5; 119 | //What we do here is to mask out the lowest five bits. They contain the 120 | //index of the bit to be set (remember that 32 is 0x20); 121 | element_bit = rt_table & 0x1F; 122 | 123 | if (element_index >= ctx->num_table_elements) 124 | return 0; 125 | 126 | element_mask = 1 << element_bit; 127 | 128 | if (addr_family == AF_INET) { 129 | element_masked = ctx->tables_inet[element_index] & element_mask; 130 | } else if (addr_family == AF_INET6) { 131 | element_masked = ctx->tables_inet6[element_index] & element_mask; 132 | } else { 133 | element_masked = ctx->tables_unspec[element_index] & element_mask; 134 | } 135 | 136 | return !!element_masked; 137 | } 138 | 139 | static void release_dead_lease(void *ptr, uint8_t addr_family, 140 | uint32_t rt_table) 141 | { 142 | struct tas_ctx *ctx = ptr; 143 | 144 | if (!is_table_free(ctx, addr_family, rt_table)) { 145 | TA_PRINT_SYSLOG(ctx, LOG_INFO, "Will release dead lease on table " 146 | "%u-%u\n", addr_family, rt_table); 147 | release_table(ctx, addr_family, rt_table); 148 | } 149 | } 150 | 151 | void table_allocator_server_clients_delete_dead_leases(struct tas_ctx *ctx) 152 | { 153 | struct timespec t_now; 154 | 155 | if (clock_gettime(CLOCK_MONOTONIC_RAW, &t_now)) { 156 | return; 157 | } 158 | 159 | table_allocator_sqlite_delete_dead_leases(ctx, t_now.tv_sec, 160 | release_dead_lease); 161 | } 162 | 163 | //return 0/1 on success, on successs, table is stored in table. Reason for not 164 | //just returning table, is that we might want to expand with more error codes 165 | //later 166 | uint8_t table_allocator_server_clients_handle_req(struct tas_ctx *ctx, 167 | struct tas_client_req *req, uint32_t *rt_table, uint32_t *lease_sec_ptr) 168 | { 169 | uint32_t rt_table_returned = 0; 170 | struct timespec t_now; 171 | time_t lease_sec = 0; 172 | 173 | switch(req->addr_family) { 174 | case AF_INET: 175 | if (!ctx->tables_inet) { 176 | return 0; 177 | } 178 | break; 179 | case AF_INET6: 180 | if (!ctx->tables_inet6) { 181 | return 0; 182 | } 183 | break; 184 | case AF_UNSPEC: 185 | if (!ctx->tables_unspec) { 186 | return 0; 187 | } 188 | break; 189 | default: 190 | return 0; 191 | } 192 | 193 | if (clock_gettime(CLOCK_MONOTONIC_RAW, &t_now)) { 194 | return 0; 195 | } 196 | 197 | lease_sec = t_now.tv_sec + ctx->table_timeout; 198 | 199 | //check database for existing table allocation 200 | rt_table_returned = table_allocator_sqlite_get_table(ctx, req); 201 | 202 | if (rt_table_returned) { 203 | //if for some reason the initial database read should fail, we need to 204 | //update map when we find leases in the databases. There is no race with 205 | //new leases. If we get the lease request for a new tuple, then the 206 | //value stored in the db will (potentially) be overwritten 207 | if (is_table_free(ctx, req->addr_family, rt_table_returned)) { 208 | set_table(ctx, req->addr_family, rt_table_returned); 209 | } 210 | 211 | //update lease, silently fail and trust client logic (for now) 212 | if (!table_allocator_sqlite_update_lease(ctx, rt_table_returned, 213 | req->addr_family, lease_sec)) { 214 | return 0; 215 | } 216 | 217 | TA_PRINT_SYSLOG(ctx, LOG_INFO, "Reallocated table %u to %s (%s)\n", 218 | rt_table_returned, req->address, req->ifname); 219 | 220 | *rt_table = rt_table_returned; 221 | *lease_sec_ptr = lease_sec; 222 | return 1; 223 | } 224 | 225 | //allocate table if not found 226 | if (!(rt_table_returned = allocate_table(ctx, req->addr_family))) { 227 | return 0; 228 | } 229 | 230 | //subtract 1 from table returned so that offset works correctly (ffs returns 231 | //1 as index for the first bit) 232 | rt_table_returned = ctx->table_offset + (rt_table_returned - 1); 233 | TA_PRINT(ctx->logfile, "Allocated table %u for %s (%s)\n", 234 | rt_table_returned, req->address, req->ifname); 235 | 236 | //insert into database 237 | if (!table_allocator_sqlite_insert_table(ctx, req, rt_table_returned, 238 | lease_sec)) { 239 | release_table(ctx, req->addr_family, rt_table_returned); 240 | return 0; 241 | } 242 | 243 | *rt_table = rt_table_returned; 244 | *lease_sec_ptr = lease_sec; 245 | return 1; 246 | } 247 | 248 | uint8_t table_allocator_server_clients_handle_release(struct tas_ctx *ctx, 249 | struct tas_client_req *req) 250 | { 251 | uint32_t rt_table = table_allocator_sqlite_get_table(ctx, req); 252 | 253 | //if no table is find, just return succesful to prevent for example clients 254 | //hanging on releasing non-existent leases 255 | if (!rt_table) { 256 | return 1; 257 | } 258 | 259 | if (table_allocator_sqlite_remove_table(ctx, req)) { 260 | TA_PRINT_SYSLOG(ctx, LOG_INFO, "Release table %u for %s (%s)\n", 261 | rt_table, req->address, req->ifname); 262 | release_table(ctx, req->addr_family, rt_table); 263 | return 1; 264 | } else { 265 | return 0; 266 | } 267 | } 268 | 269 | void table_allocator_server_clients_set_table(struct tas_ctx *ctx, 270 | uint8_t addr_family, uint32_t rt_table) 271 | { 272 | TA_PRINT_SYSLOG(ctx, LOG_INFO, "Will set active table %u-%u\n", addr_family, 273 | rt_table); 274 | set_table(ctx, addr_family, rt_table); 275 | } 276 | -------------------------------------------------------------------------------- /table_allocator/server/src/table_allocator_server_sockets.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Kristian Evensen 3 | * 4 | * This file is part of Usb Monitor. Usb Monitor is free software: you can 5 | * redistribute it and/or modify it under the terms of the Lesser GNU General 6 | * Public License as published by the Free Software Foundation, either version 3 7 | * of the License, or (at your option) any later version. 8 | * 9 | * Usb Montior is distributed in the hope that it will be useful, but WITHOUT ANY 10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 11 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU General Public License along with 14 | * Usb Monitor. If not, see http://www.gnu.org/licenses/. 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | 21 | #include "table_allocator_server_sockets.h" 22 | #include "table_allocator_server.h" 23 | #include "table_allocator_server_clients.h" 24 | 25 | #include 26 | #include 27 | 28 | static void unix_socket_alloc_cb(uv_handle_t* handle, size_t suggested_size, 29 | uv_buf_t* buf) 30 | { 31 | struct tas_ctx *ctx = handle->data; 32 | 33 | buf->base = (char*) ctx->client_req_buffer; 34 | buf->len = sizeof(ctx->client_req_buffer); 35 | } 36 | 37 | static void unix_socket_handle_close_cb(uv_handle_t *handle) 38 | { 39 | struct tas_ctx *ctx = handle->data; 40 | 41 | uv_udp_init(&(ctx->event_loop), &(ctx->unix_socket_handle)); 42 | uv_timer_start(&(ctx->unix_socket_timeout_handle), unix_socket_timeout_cb, 43 | 0, 0); 44 | } 45 | 46 | static void unix_socket_stop_recv(struct tas_ctx *ctx) 47 | { 48 | uv_udp_recv_stop(&(ctx->unix_socket_handle)); 49 | 50 | //in case we get called multiple times, for example from event cache 51 | if (!uv_is_closing((uv_handle_t*) & (ctx->unix_socket_handle))) 52 | uv_close((uv_handle_t*) &(ctx->unix_socket_handle), 53 | unix_socket_handle_close_cb); 54 | } 55 | 56 | static void unix_socket_recv_cb(uv_udp_t* handle, ssize_t nread, 57 | const uv_buf_t* buf, const struct sockaddr* addr, unsigned flags) 58 | { 59 | struct tas_ctx *ctx = handle->data; 60 | struct tas_client_req *req = ctx->req; 61 | int32_t retval, sock_fd; 62 | uint32_t table, reply_buf_len = 0, lease_expires; 63 | uint8_t reply_buf[TA_SHARED_MAX_JSON_LEN] = {0}; 64 | const struct sockaddr_un *un_addr = (const struct sockaddr_un*) addr; 65 | 66 | //ignore data done or partial reads, or buffer is big enough to store all 67 | //messages we send 68 | if (nread == 0 || flags & UV_UDP_PARTIAL) { 69 | return; 70 | } else if (nread < 0) { 71 | //failure handling is not really needed for domain sockets, once created 72 | //they can't really fail. But it add so that it is in place in case we 73 | //ever switch/add support for a different socket type (and it never 74 | //hurts) 75 | TA_PRINT_SYSLOG(ctx, LOG_DEBUG, "Server socket failed, error: %s\n", 76 | uv_strerror(nread)); 77 | unix_socket_stop_recv(ctx); 78 | return; 79 | } 80 | 81 | memset(ctx->req, 0, sizeof(struct tas_client_req)); 82 | 83 | //parse json 84 | if (!table_allocator_shared_json_parse_seq(buf->base, &(req->addr_family), 85 | &(req->cmd), &(req->ver), req->address, req->ifname, 86 | req->tag)) { 87 | TA_PRINT_SYSLOG(ctx, LOG_ERR, "Failed to parse request from: %s\n", 88 | un_addr->sun_path + 1); 89 | return; 90 | } 91 | 92 | TA_PRINT(ctx->logfile, "Parsed request from %s\n", un_addr->sun_path + 1); 93 | 94 | //check command and release/request table 95 | if (req->cmd == TA_SHARED_CMD_REQ) { 96 | //todo: for now, silently fail when we don't get a tunnel. Leave it up 97 | //to the client logic to try again, give up or something else 98 | if (table_allocator_server_clients_handle_req(ctx, req, &table, 99 | &lease_expires)) { 100 | reply_buf_len = table_allocator_shared_json_gen_response(table, 101 | lease_expires, reply_buf); 102 | } 103 | } else if (req->cmd == TA_SHARED_CMD_REL) { 104 | if (table_allocator_server_clients_handle_release(ctx, req)) { 105 | reply_buf_len = table_allocator_shared_json_gen_response(0, 0, 106 | reply_buf); 107 | } 108 | } else { 109 | TA_PRINT_SYSLOG(ctx, LOG_ERR, "Received unknown command %u from %s\n", 110 | req->cmd, un_addr->sun_path); 111 | return; 112 | } 113 | 114 | if (!reply_buf_len) { 115 | return; 116 | } 117 | 118 | TA_PRINT_SYSLOG(ctx, LOG_DEBUG, "Will send %s to client %s\n", reply_buf, 119 | un_addr->sun_path); 120 | 121 | //send reply 122 | uv_fileno((const uv_handle_t*) handle, &sock_fd); 123 | 124 | //addr is always null, client needs to bind and pass the address in the JSON 125 | retval = sendto(sock_fd, reply_buf, reply_buf_len, 0, addr, 126 | sizeof(struct sockaddr_un)); 127 | 128 | if (retval < 0) { 129 | TA_PRINT_SYSLOG(ctx, LOG_ERR, "Sending error: %s\n", strerror(errno)); 130 | } else { 131 | TA_PRINT(ctx->logfile, "Sent %d bytes\n", retval); 132 | } 133 | } 134 | 135 | void unix_socket_timeout_cb(uv_timer_t *handle) 136 | { 137 | int32_t sock_fd = -1, retval; 138 | uint8_t success = 1; 139 | struct tas_ctx *ctx = handle->data; 140 | 141 | if (uv_fileno((const uv_handle_t*) &(ctx->unix_socket_handle), &sock_fd) 142 | == UV_EBADF) { 143 | //path will be read from config, stored in ctx 144 | sock_fd = ta_socket_helpers_create_unix_socket((const char *) 145 | ctx->socket_path); 146 | 147 | if (sock_fd < 0 || uv_udp_open(&(ctx->unix_socket_handle), sock_fd)) { 148 | //print error 149 | if (sock_fd < 0) { 150 | TA_PRINT_SYSLOG(ctx, LOG_ERR, "Failed to create domain socket: " 151 | "%s\n", strerror(errno)); 152 | //log error from errno 153 | } else { 154 | close(sock_fd); 155 | } 156 | 157 | success = 0; 158 | } 159 | } 160 | 161 | if (success) { 162 | retval = uv_udp_recv_start(&(ctx->unix_socket_handle), 163 | unix_socket_alloc_cb, unix_socket_recv_cb); 164 | 165 | if (retval < 0) { 166 | TA_PRINT_SYSLOG(ctx, LOG_ERR, "Failed to start domain socket: " 167 | "%s\n", uv_strerror(retval)); 168 | success = 0; 169 | //reset handle, since we have already attached a socket to it 170 | //todo: if this fails, stop loop 171 | uv_udp_init(&(ctx->event_loop), &(ctx->unix_socket_handle)); 172 | close(sock_fd); 173 | } 174 | } 175 | 176 | if (success) { 177 | uv_timer_stop(handle); 178 | } else { 179 | if (!uv_timer_get_repeat(handle)) { 180 | uv_timer_set_repeat(handle, DOMAIN_SOCKET_TIMEOUT_MS); 181 | uv_timer_again(handle); 182 | } 183 | } 184 | } 185 | -------------------------------------------------------------------------------- /table_allocator/server/src/table_allocator_server_sqlite.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Kristian Evensen 3 | * 4 | * This file is part of Usb Monitor. Usb Monitor is free software: you can 5 | * redistribute it and/or modify it under the terms of the Lesser GNU General 6 | * Public License as published by the Free Software Foundation, either version 3 7 | * of the License, or (at your option) any later version. 8 | * 9 | * Usb Montior is distributed in the hope that it will be useful, but WITHOUT ANY 10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 11 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU General Public License along with 14 | * Usb Monitor. If not, see http://www.gnu.org/licenses/. 15 | */ 16 | 17 | #include 18 | 19 | #include 20 | 21 | #include "table_allocator_server_sqlite.h" 22 | #include "table_allocator_server.h" 23 | 24 | uint8_t table_allocator_server_sqlite_create_db(struct tas_ctx *ctx) 25 | { 26 | sqlite3 *db_handle = NULL; 27 | int retval = 0; 28 | char *db_errmsg = NULL; 29 | 30 | retval = sqlite3_open_v2((const char*) ctx->db_path, &db_handle, 31 | SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX, 32 | NULL); 33 | 34 | if (retval != SQLITE_OK) { 35 | if (db_handle != NULL) 36 | TA_PRINT(stderr, "open failed with message: %s\n", 37 | sqlite3_errmsg(db_handle)); 38 | else 39 | TA_PRINT(stderr, "not enough memory to create db_handle object\n"); 40 | 41 | return 0; 42 | } 43 | 44 | if (sqlite3_exec(db_handle, CREATE_SQL, NULL, NULL, &db_errmsg)) { 45 | TA_PRINT(stderr, "db create failed with message: %s\n", 46 | db_errmsg); 47 | sqlite3_close_v2(db_handle); 48 | return 0; 49 | } 50 | 51 | if (sqlite3_prepare_v2(db_handle, SELECT_RT_TABLE, -1, 52 | &(ctx->select_rt_table), NULL) || 53 | sqlite3_prepare_v2(db_handle, INSERT_RT_TABLE, -1, 54 | &(ctx->insert_rt_table), NULL) || 55 | sqlite3_prepare_v2(db_handle, DELETE_RT_TABLE, -1, 56 | &(ctx->delete_rt_table), NULL) || 57 | sqlite3_prepare_v2(db_handle, UPDATE_RT_TABLE, -1, 58 | &(ctx->update_rt_table), NULL) || 59 | sqlite3_prepare_v2(db_handle, SELECT_DEAD_LEASES, -1, 60 | &(ctx->select_dead_leases), NULL) || 61 | sqlite3_prepare_v2(db_handle, DELETE_DEAD_LEASES, -1, 62 | &(ctx->delete_dead_leases), NULL)) { 63 | TA_PRINT(stderr, "Statement failed: %s\n", sqlite3_errmsg(db_handle)); 64 | sqlite3_close_v2(db_handle); 65 | return 0; 66 | } 67 | 68 | ctx->db_handle = db_handle; 69 | return 1; 70 | } 71 | 72 | uint8_t table_allocator_sqlite_insert_table(struct tas_ctx *ctx, 73 | struct tas_client_req *req, uint32_t rt_table, time_t lease_sec) 74 | { 75 | int32_t retval; 76 | 77 | sqlite3_clear_bindings(ctx->insert_rt_table); 78 | sqlite3_reset(ctx->insert_rt_table); 79 | 80 | if (sqlite3_bind_int(ctx->insert_rt_table, 1, rt_table) || 81 | sqlite3_bind_int(ctx->insert_rt_table, 2, req->addr_family) || 82 | sqlite3_bind_text(ctx->insert_rt_table, 3, req->ifname, 83 | strlen(req->ifname), SQLITE_STATIC) || 84 | sqlite3_bind_text(ctx->insert_rt_table, 4, req->address, 85 | strlen(req->address), SQLITE_STATIC) || 86 | sqlite3_bind_int(ctx->insert_rt_table, 5, lease_sec)) { 87 | TA_PRINT_SYSLOG(ctx, LOG_ERR, "Failed to bind INSERT values\n"); 88 | return 0; 89 | } 90 | 91 | if (req->tag[0] != 0 && sqlite3_bind_text(ctx->insert_rt_table, 6, 92 | req->tag, strlen(req->tag), SQLITE_STATIC)) { 93 | TA_PRINT_SYSLOG(ctx, LOG_ERR, "Failed to bind INSERT values (tag)\n"); 94 | return 0; 95 | } 96 | 97 | retval = sqlite3_step(ctx->insert_rt_table); 98 | 99 | if (retval != SQLITE_DONE) { 100 | TA_PRINT_SYSLOG(ctx, LOG_ERR, "Insert table failed: %s\n", 101 | sqlite3_errstr(retval)); 102 | return 0; 103 | } 104 | 105 | return 1; 106 | } 107 | 108 | uint32_t table_allocator_sqlite_get_table(struct tas_ctx *ctx, 109 | struct tas_client_req *req) 110 | { 111 | uint32_t rt_table = 0; 112 | 113 | sqlite3_clear_bindings(ctx->select_rt_table); 114 | sqlite3_reset(ctx->select_rt_table); 115 | 116 | if (sqlite3_bind_int(ctx->select_rt_table, 1, req->addr_family) || 117 | sqlite3_bind_text(ctx->select_rt_table, 2, req->ifname, 118 | strlen(req->ifname), SQLITE_STATIC) || 119 | sqlite3_bind_text(ctx->select_rt_table, 3, req->address, 120 | strlen(req->address), SQLITE_STATIC)) { 121 | TA_PRINT_SYSLOG(ctx, LOG_ERR, "Failed to bind SELECT values\n"); 122 | return 0; 123 | } 124 | 125 | if (sqlite3_step(ctx->select_rt_table) == SQLITE_ROW) { 126 | rt_table = (uint32_t) sqlite3_column_int(ctx->select_rt_table, 0); 127 | } 128 | 129 | return rt_table; 130 | } 131 | 132 | uint8_t table_allocator_sqlite_remove_table(struct tas_ctx *ctx, 133 | struct tas_client_req *req) 134 | { 135 | int32_t retval; 136 | 137 | sqlite3_clear_bindings(ctx->delete_rt_table); 138 | sqlite3_reset(ctx->delete_rt_table); 139 | 140 | if (sqlite3_bind_int(ctx->delete_rt_table, 1, req->addr_family) || 141 | sqlite3_bind_text(ctx->delete_rt_table, 2, req->ifname, 142 | strlen(req->ifname), SQLITE_STATIC) || 143 | sqlite3_bind_text(ctx->delete_rt_table, 3, req->address, 144 | strlen(req->address), SQLITE_STATIC)) { 145 | TA_PRINT_SYSLOG(ctx, LOG_ERR, "Failed to bind SELECT values\n"); 146 | return 0; 147 | } 148 | 149 | retval = sqlite3_step(ctx->delete_rt_table); 150 | 151 | if (retval != SQLITE_DONE) { 152 | TA_PRINT_SYSLOG(ctx, LOG_ERR, "Delete table failed: %s\n", 153 | sqlite3_errstr(retval)); 154 | return 0; 155 | } 156 | 157 | return 1; 158 | } 159 | 160 | uint8_t table_allocator_sqlite_update_lease(struct tas_ctx *ctx, 161 | uint32_t rt_table, uint8_t addr_family, uint32_t lease_sec) 162 | { 163 | int32_t retval; 164 | 165 | sqlite3_clear_bindings(ctx->update_rt_table); 166 | sqlite3_reset(ctx->update_rt_table); 167 | 168 | if (sqlite3_bind_int(ctx->update_rt_table, 1, lease_sec) || 169 | sqlite3_bind_int(ctx->update_rt_table, 2, addr_family) || 170 | sqlite3_bind_int(ctx->update_rt_table, 3, rt_table)) { 171 | TA_PRINT_SYSLOG(ctx, LOG_ERR, "Failed to bind SELECT values\n"); 172 | return 0; 173 | } 174 | 175 | retval = sqlite3_step(ctx->update_rt_table); 176 | 177 | if (retval != SQLITE_DONE) { 178 | TA_PRINT_SYSLOG(ctx, LOG_ERR, "Update table failed: %s\n", 179 | sqlite3_errstr(retval)); 180 | return 0; 181 | } 182 | 183 | return 1; 184 | } 185 | 186 | uint8_t table_allocator_sqlite_delete_dead_leases(struct tas_ctx *ctx, 187 | uint32_t lease_limit, check_table_cb cb) 188 | { 189 | int32_t retval; 190 | uint32_t rt_table; 191 | uint8_t addr_family; 192 | sqlite3_clear_bindings(ctx->select_dead_leases); 193 | sqlite3_reset(ctx->select_dead_leases); 194 | sqlite3_clear_bindings(ctx->delete_dead_leases); 195 | sqlite3_reset(ctx->delete_dead_leases); 196 | 197 | if (sqlite3_bind_int(ctx->select_dead_leases, 1, lease_limit)) { 198 | TA_PRINT_SYSLOG(ctx, LOG_ERR, "Bind SELECT dead leases failed\n"); 199 | return 0; 200 | } 201 | 202 | while (sqlite3_step(ctx->select_dead_leases) == SQLITE_ROW) { 203 | rt_table = (uint32_t) sqlite3_column_int(ctx->select_dead_leases, 0); 204 | addr_family = (uint8_t) sqlite3_column_int(ctx->select_dead_leases, 1); 205 | cb(ctx, addr_family, rt_table); 206 | } 207 | 208 | if (sqlite3_bind_int(ctx->delete_dead_leases, 1, lease_limit)) { 209 | TA_PRINT_SYSLOG(ctx, LOG_ERR, "Bind DELETE dead leases failed\n"); 210 | return 0; 211 | } 212 | 213 | retval = sqlite3_step(ctx->delete_dead_leases); 214 | 215 | //this is not a critical error, we guard against dead leases when 216 | //reconstructing map from database and we have ON CONFLICT REPLACE on the 217 | //table. So deleting dead leases is done to be nice 218 | if (retval != SQLITE_DONE) { 219 | TA_PRINT_SYSLOG(ctx, LOG_ERR, "Deleting dead leases failed\n"); 220 | } 221 | 222 | return 1; 223 | } 224 | 225 | uint8_t table_allocator_sqlite_build_table_map(struct tas_ctx *ctx, 226 | uint32_t lease_limit, check_table_cb cb) 227 | { 228 | uint32_t rt_table; 229 | uint8_t addr_family; 230 | int32_t retval; 231 | sqlite3_stmt *get_active_leases; 232 | 233 | if ((retval = sqlite3_prepare_v2(ctx->db_handle, SELECT_ALIVE_LEASES, -1, 234 | &get_active_leases, NULL))) { 235 | TA_PRINT_SYSLOG(ctx, LOG_ERR, "Preparing SELECT_ALIVE_LEASES failed." 236 | " Error: %s\n", sqlite3_errstr(retval)); 237 | return 0; 238 | } 239 | 240 | if (sqlite3_bind_int(get_active_leases, 1, lease_limit)) { 241 | TA_PRINT_SYSLOG(ctx, LOG_ERR, "Bind SELECT_ALIVE_LEASES failed\n"); 242 | sqlite3_finalize(get_active_leases); 243 | return 0; 244 | } 245 | 246 | while (sqlite3_step(get_active_leases) == SQLITE_ROW) { 247 | rt_table = (uint32_t) sqlite3_column_int(get_active_leases, 0); 248 | addr_family = (uint8_t) sqlite3_column_int(get_active_leases, 1); 249 | cb(ctx, addr_family, rt_table); 250 | } 251 | 252 | sqlite3_finalize(get_active_leases); 253 | return 1; 254 | } 255 | -------------------------------------------------------------------------------- /table_allocator/shared/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | #Standard stuff 2 | cmake_minimum_required(VERSION 2.6) 3 | project(table_allocator_shared) 4 | 5 | set(CMAKE_C_FLAGS "-O1 -Wall -std=gnu99 -g") 6 | 7 | set (PROJECT_INCLUDE_DIR ${PROJECT_SOURCE_DIR}/include) 8 | set (PROJECT_SRC_DIR ${PROJECT_SOURCE_DIR}/src) 9 | 10 | set(SOURCE 11 | ${PROJECT_SRC_DIR}/table_allocator_shared_socket_helpers.c 12 | ${PROJECT_SRC_DIR}/table_allocator_shared_libuv_helpers.c 13 | ${PROJECT_SRC_DIR}/table_allocator_shared_json.c 14 | ) 15 | 16 | include_directories("${PROJECT_INCLUDE_DIR}") 17 | add_library(${PROJECT_NAME} STATIC ${SOURCE}) 18 | -------------------------------------------------------------------------------- /table_allocator/shared/include/table_allocator_shared_json.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Kristian Evensen 3 | * 4 | * This file is part of Usb Monitor. Usb Monitor is free software: you can 5 | * redistribute it and/or modify it under the terms of the Lesser GNU General 6 | * Public License as published by the Free Software Foundation, either version 3 7 | * of the License, or (at your option) any later version. 8 | * 9 | * Usb Montior is distributed in the hope that it will be useful, but WITHOUT ANY 10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 11 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU General Public License along with 14 | * Usb Monitor. If not, see http://www.gnu.org/licenses/. 15 | */ 16 | 17 | #ifndef TABLE_ALLOCATOR_SHARED_JSON_H 18 | #define TABLE_ALLOCATOR_SHARED_JSON_H 19 | 20 | #include 21 | 22 | #define TA_SHARED_JSON_ADDRESS_KEY "address" 23 | #define TA_SHARED_JSON_FAMILY_KEY "addr_family" 24 | #define TA_SHARED_JSON_IFNAME_KEY "ifname" 25 | #define TA_SHARED_JSON_TAG_KEY "tag" 26 | #define TA_SHARED_JSON_CMD_KEY "cmd" 27 | #define TA_SHARED_JSON_VERSION_KEY "version" 28 | #define TA_SHARED_JSON_TABLE_KEY "table" 29 | #define TA_SHARED_JSON_LEASE_EXPIRES_KEY "lease_expires" 30 | 31 | //todo: move to new header file table_allocator_shared.h 32 | //request or release are the only two commands we support now 33 | #define TA_SHARED_CMD_REQ 0 34 | #define TA_SHARED_CMD_REL 1 35 | #define TA_SHARED_CMD_RESP 2 36 | 37 | //todo: should perhaps move this somewhere else? 38 | #define TA_VERSION 1 39 | 40 | //maximum size of json object we currently can export (+ \0) 41 | #define TA_SHARED_MAX_JSON_LEN 254 42 | 43 | #define TA_SHARED_MAX_TAG_SIZE 128 44 | //max. len. for unix domain sockets is 108, but string needs to be zero 45 | //teminated and we loose one byte (in front) since we use abstract naming 46 | //this is DESTINATION address, limit for address to allocate is INET6_ADDRSTRLEN 47 | #define TA_SHARED_MAX_ADDR_SIZE 106 48 | 49 | //generate table request, returns a new json object 50 | struct json_object *table_allocator_shared_json_create_req(const char *address, 51 | const char *ifname, const char *tag, uint8_t addr_family, uint8_t cmd); 52 | 53 | //parse a request, return 1/0 on success/failure 54 | uint8_t table_allocator_shared_json_parse_seq(const char *json_obj_char, 55 | uint8_t *addr_family, uint8_t *cmd, uint8_t *ver, char *addr_str, 56 | char *ifname_str, char *tag_str); 57 | 58 | //generate table response, return 0 on failure, buf_len on success 59 | uint32_t table_allocator_shared_json_gen_response(uint32_t table, 60 | uint32_t lease_expires, uint8_t *buf); 61 | 62 | uint8_t tables_allocator_shared_json_parse_client_reply( 63 | const char *json_obj_str, uint8_t *ver, uint8_t *cmd, uint32_t *table, 64 | uint32_t *lease_expires); 65 | #endif 66 | -------------------------------------------------------------------------------- /table_allocator/shared/include/table_allocator_shared_libuv_helpers.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Kristian Evensen 3 | * 4 | * This file is part of Usb Monitor. Usb Monitor is free software: you can 5 | * redistribute it and/or modify it under the terms of the Lesser GNU General 6 | * Public License as published by the Free Software Foundation, either version 3 7 | * of the License, or (at your option) any later version. 8 | * 9 | * Usb Montior is distributed in the hope that it will be useful, but WITHOUT ANY 10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 11 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU General Public License along with 14 | * Usb Monitor. If not, see http://www.gnu.org/licenses/. 15 | */ 16 | 17 | #ifndef TABLE_ALLOCATOR_SHARED_LIBUV_HELPERS 18 | #define TABLE_ALLOCATOR_SHARED_LIBUV_HELPERS 19 | 20 | #include 21 | 22 | uint8_t ta_allocator_libuv_helpers_configure_unix_handle(uv_loop_t *event_loop, 23 | uv_udp_t *unix_socket_handle, uv_timer_t *unix_socket_timeout_handle, 24 | uv_timer_cb unix_socket_timeout_cb, void *ptr); 25 | 26 | #endif 27 | -------------------------------------------------------------------------------- /table_allocator/shared/include/table_allocator_shared_log.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Kristian Evensen 3 | * 4 | * This file is part of Usb Monitor. Usb Monitor is free software: you can 5 | * redistribute it and/or modify it under the terms of the Lesser GNU General 6 | * Public License as published by the Free Software Foundation, either version 3 7 | * of the License, or (at your option) any later version. 8 | * 9 | * Usb Montior is distributed in the hope that it will be useful, but WITHOUT ANY 10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 11 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU General Public License along with 14 | * Usb Monitor. If not, see http://www.gnu.org/licenses/. 15 | */ 16 | 17 | #ifndef TABLE_ALLOCATOR_SHARED_LOG_H 18 | #define TABLE_ALLOCATOR_SHARED_LOG_H 19 | #include 20 | #include 21 | #include 22 | 23 | #define TA_LOG_PREFIX "[%.2d:%.2d:%.2d %.2d/%.2d/%d]: " 24 | #define TA_PRINT2(fd, ...){fprintf(fd, __VA_ARGS__);fflush(fd);} 25 | #define TA_SYSLOG(priority, ...){syslog(LOG_MAKEPRI(LOG_DAEMON, priority), __VA_ARGS__);} 26 | //The ## is there so that I dont have to fake an argument when I use the macro 27 | //on string without arguments! 28 | #define TA_PRINT(fd, _fmt, ...) \ 29 | do { \ 30 | time_t rawtime; \ 31 | struct tm *curtime; \ 32 | time(&rawtime); \ 33 | curtime = gmtime(&rawtime); \ 34 | TA_PRINT2(fd, TA_LOG_PREFIX _fmt, curtime->tm_hour, \ 35 | curtime->tm_min, curtime->tm_sec, curtime->tm_mday, \ 36 | curtime->tm_mon + 1, 1900 + curtime->tm_year, \ 37 | ##__VA_ARGS__);} while(0) 38 | 39 | #define TA_PRINT_SYSLOG(ctx, priority, _fmt, ...) \ 40 | do { \ 41 | time_t rawtime; \ 42 | struct tm *curtime; \ 43 | time(&rawtime); \ 44 | curtime = gmtime(&rawtime); \ 45 | if (ctx->use_syslog) \ 46 | TA_SYSLOG(priority, _fmt, ##__VA_ARGS__); \ 47 | TA_PRINT2(ctx->logfile, TA_LOG_PREFIX _fmt, \ 48 | curtime->tm_hour, \ 49 | curtime->tm_min, curtime->tm_sec, curtime->tm_mday, \ 50 | curtime->tm_mon + 1, 1900 + curtime->tm_year, \ 51 | ##__VA_ARGS__);} while(0) 52 | #endif 53 | -------------------------------------------------------------------------------- /table_allocator/shared/include/table_allocator_shared_socket_helpers.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Kristian Evensen 3 | * 4 | * This file is part of Usb Monitor. Usb Monitor is free software: you can 5 | * redistribute it and/or modify it under the terms of the Lesser GNU General 6 | * Public License as published by the Free Software Foundation, either version 3 7 | * of the License, or (at your option) any later version. 8 | * 9 | * Usb Montior is distributed in the hope that it will be useful, but WITHOUT ANY 10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 11 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU General Public License along with 14 | * Usb Monitor. If not, see http://www.gnu.org/licenses/. 15 | */ 16 | 17 | #ifndef TABLE_ALLOCATOR_SHARED_SOCKET_HELPERS_H 18 | #define TABLE_ALLOCATOR_SHARED_SOCKET_HELPERS_H 19 | 20 | #define DOMAIN_SOCKET_TIMEOUT_MS 200 21 | 22 | int ta_socket_helpers_create_unix_socket(const char *path); 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /table_allocator/shared/src/table_allocator_shared_json.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Kristian Evensen 3 | * 4 | * This file is part of Usb Monitor. Usb Monitor is free software: you can 5 | * redistribute it and/or modify it under the terms of the Lesser GNU General 6 | * Public License as published by the Free Software Foundation, either version 3 7 | * of the License, or (at your option) any later version. 8 | * 9 | * Usb Montior is distributed in the hope that it will be useful, but WITHOUT ANY 10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 11 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU General Public License along with 14 | * Usb Monitor. If not, see http://www.gnu.org/licenses/. 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | 21 | #include "table_allocator_shared_json.h" 22 | 23 | struct json_object *table_allocator_shared_json_create_req(const char *address, 24 | const char *ifname, const char *tag, uint8_t addr_family, uint8_t cmd) 25 | { 26 | struct json_object *json_obj = NULL, *obj_add; 27 | 28 | if (!(json_obj = json_object_new_object())) { 29 | return NULL; 30 | } 31 | 32 | if (!(obj_add = json_object_new_string(address))) { 33 | json_object_put(json_obj); 34 | return NULL; 35 | } 36 | json_object_object_add(json_obj, TA_SHARED_JSON_ADDRESS_KEY, obj_add); 37 | 38 | if (!(obj_add = json_object_new_string(ifname))) { 39 | json_object_put(json_obj); 40 | return NULL; 41 | } 42 | json_object_object_add(json_obj, TA_SHARED_JSON_IFNAME_KEY, obj_add); 43 | 44 | if (tag[0]) { 45 | if (!(obj_add = json_object_new_string(tag))) { 46 | json_object_put(json_obj); 47 | return NULL; 48 | } 49 | json_object_object_add(json_obj, TA_SHARED_JSON_TAG_KEY, obj_add); 50 | } 51 | 52 | if (!(obj_add = json_object_new_int(addr_family))) { 53 | json_object_put(json_obj); 54 | return NULL; 55 | } 56 | json_object_object_add(json_obj, TA_SHARED_JSON_FAMILY_KEY, obj_add); 57 | 58 | if (!(obj_add = json_object_new_int(cmd))) { 59 | json_object_put(json_obj); 60 | return NULL; 61 | } 62 | json_object_object_add(json_obj, TA_SHARED_JSON_CMD_KEY, obj_add); 63 | 64 | if (!(obj_add = json_object_new_int(TA_VERSION))) { 65 | json_object_put(json_obj); 66 | return NULL; 67 | } 68 | json_object_object_add(json_obj, TA_SHARED_JSON_VERSION_KEY, obj_add); 69 | 70 | return json_obj; 71 | } 72 | 73 | uint8_t table_allocator_shared_json_parse_seq(const char *json_obj_char, 74 | uint8_t *addr_family, uint8_t *cmd, uint8_t *ver, char *addr_str, 75 | char *ifname_str, char *tag_str) 76 | { 77 | struct json_object *json_obj; 78 | const char *str_val; 79 | 80 | if (!(json_obj = json_tokener_parse(json_obj_char))) { 81 | return 0; 82 | } 83 | 84 | json_object_object_foreach(json_obj, key, val) { 85 | if (!strcmp(key, TA_SHARED_JSON_ADDRESS_KEY) && 86 | json_object_is_type(val, json_type_string)) { 87 | str_val = json_object_get_string(val); 88 | 89 | if (strlen(str_val) < INET6_ADDRSTRLEN) { 90 | memcpy(addr_str, str_val, strlen(str_val)); 91 | } 92 | } else if (!strcmp(key, TA_SHARED_JSON_FAMILY_KEY) && 93 | json_object_is_type(val, json_type_int)) { 94 | *addr_family = json_object_get_int(val); 95 | } else if (!strcmp(key, TA_SHARED_JSON_IFNAME_KEY) && 96 | json_object_is_type(val, json_type_string)) { 97 | str_val = json_object_get_string(val); 98 | 99 | if (strlen(str_val) < IFNAMSIZ) { 100 | memcpy(ifname_str, str_val, strlen(str_val)); 101 | } 102 | } else if (!strcmp(key, TA_SHARED_JSON_TAG_KEY) && 103 | json_object_is_type(val, json_type_string)) { 104 | str_val = json_object_get_string(val); 105 | 106 | if (strlen(str_val) < TA_SHARED_MAX_TAG_SIZE) { 107 | memcpy(tag_str, str_val, strlen(str_val)); 108 | } 109 | } else if (!strcmp(key, TA_SHARED_JSON_CMD_KEY) && 110 | json_object_is_type(val, json_type_int)) { 111 | *cmd = json_object_get_int(val); 112 | } else if (!strcmp(key, TA_SHARED_JSON_VERSION_KEY) && 113 | json_object_is_type(val, json_type_int)) { 114 | *ver = json_object_get_int(val); 115 | } 116 | } 117 | 118 | json_object_put(json_obj); 119 | return 1; 120 | } 121 | 122 | uint32_t table_allocator_shared_json_gen_response(uint32_t table, 123 | uint32_t lease_expires, uint8_t *buf) 124 | { 125 | struct json_object *json_obj = NULL, *obj_add; 126 | const char *json_str; 127 | uint32_t buf_len; 128 | 129 | if (!(json_obj = json_object_new_object())) { 130 | return 0; 131 | } 132 | 133 | if (!(obj_add = json_object_new_int(TA_VERSION))) { 134 | json_object_put(json_obj); 135 | return 0; 136 | } 137 | json_object_object_add(json_obj, TA_SHARED_JSON_VERSION_KEY, obj_add); 138 | 139 | if (!(obj_add = json_object_new_int(TA_SHARED_CMD_RESP))) { 140 | json_object_put(json_obj); 141 | return 0; 142 | } 143 | json_object_object_add(json_obj, TA_SHARED_JSON_CMD_KEY, obj_add); 144 | 145 | if (!(obj_add = json_object_new_int64(table))) { 146 | json_object_put(json_obj); 147 | return 0; 148 | } 149 | json_object_object_add(json_obj, TA_SHARED_JSON_TABLE_KEY, obj_add); 150 | 151 | if (!(obj_add = json_object_new_int64(lease_expires))) { 152 | json_object_put(json_obj); 153 | return 0; 154 | } 155 | json_object_object_add(json_obj, TA_SHARED_JSON_LEASE_EXPIRES_KEY, obj_add); 156 | 157 | json_str = json_object_to_json_string_ext(json_obj, JSON_C_TO_STRING_PLAIN); 158 | 159 | //remember that buffer is expected to be 0 terminated (or at least that 160 | //there is room for the 0 byte on receive) 161 | if (strlen(json_str) >= TA_SHARED_MAX_JSON_LEN) { 162 | json_object_put(json_obj); 163 | return 0; 164 | } 165 | 166 | memcpy(buf, json_str, strlen(json_str)); 167 | buf_len = strlen(json_str); 168 | json_object_put(json_obj); 169 | 170 | return buf_len; 171 | } 172 | 173 | uint8_t tables_allocator_shared_json_parse_client_reply( 174 | const char *json_obj_str, uint8_t *ver, uint8_t *cmd, uint32_t *table, 175 | uint32_t *lease_expires) 176 | { 177 | struct json_object *json_obj; 178 | 179 | if (!(json_obj = json_tokener_parse(json_obj_str))) { 180 | return 0; 181 | } 182 | 183 | json_object_object_foreach(json_obj, key, val) { 184 | if (!strcmp(key, TA_SHARED_JSON_VERSION_KEY) && 185 | json_object_is_type(val, json_type_int)) { 186 | *ver = json_object_get_int(val); 187 | } else if (!strcmp(key, TA_SHARED_JSON_CMD_KEY) && 188 | json_object_is_type(val, json_type_int)) { 189 | *cmd = json_object_get_int(val); 190 | } else if (!strcmp(key, TA_SHARED_JSON_TABLE_KEY) && 191 | json_object_is_type(val, json_type_int)) { 192 | *table = json_object_get_int(val); 193 | } else if (!strcmp(key, TA_SHARED_JSON_LEASE_EXPIRES_KEY) && 194 | json_object_is_type(val, json_type_int)) { 195 | *lease_expires = json_object_get_int(val); 196 | } 197 | } 198 | 199 | json_object_put(json_obj); 200 | return 1; 201 | } 202 | 203 | -------------------------------------------------------------------------------- /table_allocator/shared/src/table_allocator_shared_libuv_helpers.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Kristian Evensen 3 | * 4 | * This file is part of Usb Monitor. Usb Monitor is free software: you can 5 | * redistribute it and/or modify it under the terms of the Lesser GNU General 6 | * Public License as published by the Free Software Foundation, either version 3 7 | * of the License, or (at your option) any later version. 8 | * 9 | * Usb Montior is distributed in the hope that it will be useful, but WITHOUT ANY 10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 11 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU General Public License along with 14 | * Usb Monitor. If not, see http://www.gnu.org/licenses/. 15 | */ 16 | 17 | #include "table_allocator_shared_libuv_helpers.h" 18 | 19 | uint8_t ta_allocator_libuv_helpers_configure_unix_handle(uv_loop_t *event_loop, 20 | uv_udp_t *unix_socket_handle, uv_timer_t *unix_socket_timeout_handle, 21 | uv_timer_cb unix_socket_timeout_cb, void *ptr) 22 | { 23 | if (uv_udp_init(event_loop, unix_socket_handle)) { 24 | return 0; 25 | } 26 | 27 | if (uv_timer_init(event_loop, unix_socket_timeout_handle)) { 28 | return 0; 29 | } 30 | 31 | unix_socket_handle->data = ptr; 32 | unix_socket_timeout_handle->data = ptr; 33 | 34 | //We will by default not do a repeating timer. A repeating timer is only for 35 | //when we fail to create a socket 36 | if (uv_timer_start(unix_socket_timeout_handle, unix_socket_timeout_cb, 0, 37 | 0)) { 38 | return 0; 39 | } 40 | 41 | return 1; 42 | } 43 | 44 | -------------------------------------------------------------------------------- /table_allocator/shared/src/table_allocator_shared_socket_helpers.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Kristian Evensen 3 | * 4 | * This file is part of Usb Monitor. Usb Monitor is free software: you can 5 | * redistribute it and/or modify it under the terms of the Lesser GNU General 6 | * Public License as published by the Free Software Foundation, either version 3 7 | * of the License, or (at your option) any later version. 8 | * 9 | * Usb Montior is distributed in the hope that it will be useful, but WITHOUT ANY 10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 11 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU General Public License along with 14 | * Usb Monitor. If not, see http://www.gnu.org/licenses/. 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #include "table_allocator_shared_socket_helpers.h" 25 | 26 | int ta_socket_helpers_create_unix_socket(const char *path_name) 27 | { 28 | int32_t sock_fd; 29 | struct sockaddr_un local_addr; 30 | 31 | //subtracting two from size is because first byte is set to 0 since we used 32 | //abstract naming space, and to ensure that sun_path will be zero terminated 33 | if (path_name && strlen(path_name) > (sizeof(local_addr.sun_path) - 2)) { 34 | return -1; 35 | } 36 | 37 | if ((sock_fd = socket(AF_UNIX, SOCK_DGRAM, 0)) == -1) { 38 | return -1; 39 | } 40 | 41 | if (path_name == NULL) { 42 | return sock_fd; 43 | } 44 | 45 | memset(&local_addr, 0, sizeof(local_addr)); 46 | 47 | local_addr.sun_family = AF_UNIX; 48 | 49 | //we use abstract naming, so first byte of path is always \0 50 | strncpy(local_addr.sun_path + 1, path_name, strlen(path_name)); 51 | 52 | if (bind(sock_fd, (struct sockaddr*) &local_addr, 53 | sizeof(local_addr)) == -1) { 54 | close(sock_fd); 55 | return -1; 56 | } 57 | 58 | return sock_fd; 59 | } 60 | -------------------------------------------------------------------------------- /udev/99-network-cleanup.rules: -------------------------------------------------------------------------------- 1 | SUBSYSTEM=="net", ACTION=="remove", RUN+="/usr/sbin/network-cleanup.sh $env{INTERFACE}" 2 | -------------------------------------------------------------------------------- /udev/network-cleanup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | DNSMASQ_SERVER_PATH="/tmp/dnsmasq-servers.conf" 4 | 5 | #kill all dhclient processes 6 | DHCLIENT_PIDS=$(pgrep -f "dhclient.*$1" | tr "\n" " ") 7 | 8 | for pid in $DHCLIENT_PIDS; 9 | do 10 | kill -9 $pid 11 | done 12 | 13 | IFACE_SERVER_MATCH="@$1" 14 | grep "$IFACE_SERVER_MATCH" "$DNSMASQ_SERVER_PATH" 15 | 16 | if [ $? -eq 0 ]; 17 | then 18 | sed -i "/$IFACE_SERVER_MATCH/d" "$DNSMASQ_SERVER_PATH" 19 | kill -s HUP $(pgrep dnsmasq) 20 | fi 21 | --------------------------------------------------------------------------------