blob: a97b5dda49983bad3611e9a78b25e670af4665d7 [file] [log] [blame] [view] [edit]
# Routing
## Overview
The purpose of the `RoutingTable` singleton is to allow clients to examine and
modify routing tables and policy routing rules. Its primary client is the
`Connection` class, whose instances modify routes/rules in order for network
traffic to comply with configuration rules such as priority of connected
`Service`s and tunnelling of traffic through a VPN.
## Background
Routing is the means by which network packets are sent from source nodes to
destination nodes. With the IP protocol, a routing decision is made locally at
every node that a packet reaches, until the packet either reaches its
destination or expires (as determined by a packet's TTL field). Since routing
decisions are made locally, the questions to answer at each node are "should
this packet be dropped instead of sent forward?" and "if this packet should be
sent forward, which interface should it be sent out of and to what node on that
LAN?". Note that sometimes the source and destination nodes are the same, in
which case a loopback interface is used to send packets directly to the
receiving local process rather than sent out of the machine and back. While
different devices may use different methods for making these decisions, the most
common way this is done, particularly for the second question, is through the
use of **routing tables**.
A routing table is simply a set of rules matching destination address to egress
interface. Since IP addresses are organized hierarchically, broad sets of
destination addresses can be represented with a small set of addresses within a
routing table through the use of the [longest prefix match
algorithm](https://en.wikipedia.org/wiki/Longest_prefix_match) at routing
time. Entries in a routing table can also have priorities--sometimes called a
metric in utilities like `ip route`--that allow for disambiguation between
routes that match equally well. The routes in a routing table can come from a
number of sources. There exist a number of routing protocols that allow for
routers to populate their own routing tables dynamically. In the case of Chrome
OS hosts, however, routing tables are populated by the kernel itself and by
Shill.
A single routing table, however, cannot be used to represent all of the routing
decisions one could reasonably want their machine to employ. Properties of a
packet such as source destination, quality of service, or packet markings left
by a firewall are not considered by a standard routing table. While routing
tables could be modified to consider all of these cases, an alternative that
helps to contain complexity is to use **policies** (also known as **rules**) as
a means of selecting a routing table based on properties of the packet to send,
in a process known as **policy-based routing**. Policies have their own
priorities, and a packet will be compared with policies in order of priority
(lowest to highest) until a match is found. If the matched routing table does
not have a suitable routing entry or the packet matches with a *throw* entry in
the routing table, the routing process returns to the list of policies in order
to find the next matching routing table. On Linux, the list of policies
generally ends with a rule sending all packets to the main routing table.
> As an aside, note that policy-based routing allows for sophisticated answers
> to the first of the two questions asked in the beginning of this section:
> "should the packet be dropped instead of sent forward?". For example, one
> could create a routing table that only contains a *blackhole* route, which
> simply drops the packet. Policies can then be set to send particular traffic
> to that routing table to prevent it from being sent. For such
> firewall-related behavior on Linux, however, tools such as iptables that
> utilize the kernel's netfilter architecture are generally more
> popular. Aside from network administrator familiarity with iptables, using
> the netfilter architecture also allows for packet filtering of ingress and
> forwarded traffic, applying filtering logic at many stages in the lifetime
> of a packet, and taking advantages of performance improvements applied to
> the netfilter architecture. With that said, there is no consistent netfilter
> API between kernel versions, making it inconvenient to programmatically deal
> with netfilter. Dynamically modifying routing tables and rules is sufficient
> for our needs in Shill.
On the kernel side, a routing table is represented as a [`fib_table`], while a
policy routing rule is represented as a [`fib_rule`].
> Note the use of "FIB" rather than "routing table" in kernel code. A FIB, or
> Forwarding Information Base, refers specifically to the set of information
> used to forward a packet (i.e. to send it to another node), which does
> indeed correspond to our original definition of a routing table. This is in
> contrast to a RIB, or Routing Information Base, which refers to the set of
> information a node has about the routes around itself. The distinction,
> while seemingly unimportant for normal end nodes, is significant for routers
> or other nodes that use routing protocols to dynamically determine route
> information. Each routing protocol used by the node (e.g. OSPF, BGP, RIP,
> etc) maintains its own view of the available routes with a protocol-specific
> data structure. The information in these data structures are then selected
> and used to update the RIB, which then serves as the central,
> protocol-independent representation of routes. Finally, the FIB can be
> updated to reflect the information in the RIB. Since both RIB and FIB can be
> called "routing table" in various contexts, the kernel's use of "FIB" helps
> eliminate any potential ambiguity.
## Design
The `RoutingTable` class is a singleton whose two primary responsibilities are:
* to send client requests to add or remove routes/rules to the kernel through
the [rtnetlink interface]
* to maintain an internal representation of routes and policy rules organized
on a per-interface basis for use both in internal routing logic and in
servicing client requests for information about available routes/rules
When the `RoutingTable` is `Start()`ed, it will request all of the routes and
rules on the system. After that point, it will keep track of newly-added
routes/rules by listening to the RTNL interface (for routes/rules added directly
by the kernel) and by updating the internal representation whenever a client
request successfully adds or removes a route or rule.
Each `Device` instance has at most one `Connection`. When a `Device` is
connecting, an `IPConfig` instance representing configuration information such
as local address, gateway address, DNS servers, etc will be populated and passed
to the `Connection` instance. The relevant information from that `IPConfig`,
along with `Service` priority information provided by the `Manager` class, is
then used to set up routes and rules appropriately.
[`fib_rule`]: https://elixir.bootlin.com/linux/v4.20/source/include/net/fib_rules.h#L19
[`fib_table`]: https://elixir.bootlin.com/linux/v4.20/source/include/net/ip_fib.h#L216
[rtnetlink interface]: https://elixir.bootlin.com/linux/v4.20/source/include/uapi/linux/rtnetlink.h