In Shill, a network interface is represented by a Device
instance. Among other things, the Device
class provides basic functionality to configure its interface through /proc/sys/net
parameters, to acquire and use IP configuration parameters, and to drive Service
state. Device
is the base class for a hierarchy of Device
children that perform technology-specific behavior and that actually trigger base Device
functionality:
Most Device
instances are created by the DeviceInfo
class. DeviceInfo
listens to device and address-configuration events using RTNL
. On startup, it also requests a dump of existing device and address-configuration information in order to be in sync with the current system state. When DeviceInfo
has enough information about an interface, it will create a Device
child instance of the proper type. DeviceInfo
will also update existing Device
instances with new information that it receives about the corresponding interface. A common exception to the “Devices
are created by DeviceInfo
” rule are VirtualDevices
corresponding to virtual interfaces created by Shill (for use-cases like PPPoE, VPN, and cellular dongles). Cellular
instances are another exception, which are created by Modem
instances (which are themselves a representation of the ModemManager Modem D-Bus object). For the cellular case, DeviceInfo
sends relevant link information to the ModemInfo
class.
A network interface on its own doesn't magically provide network connectivity, but instead must connect to “something” (e.g., a WiFi Access Point) in order to achieve that end result. A Service
represents that “something”; it is a representation of an entity that can provide network connectivity if interacted with properly. The Service
class acts as a connection state machine that Device
piggybacks on, provides the core connect/disconnect behavior, and provides the basic functionality for persisting network-specific configuration options, among other things. Not unlike Device
, Service
also forms a hierarchy in which children perform technology-specific behavior:
A Service
and Device
of matching technology type must interact (conceptually they are bound to each other) in order for the Service
to reach a connected state.
Service
instances in most cases are created by Provider
instances. At most one instance exists for each child Provider
type. Provider
instances generally create new Services
either from persisted state (see the Profile
section) or from the D-Bus interface (see the Manager
section). For WiFiProvider
, Services
are also created based on network scans performed by wpa_supplicant
, which leads to the reception of BSSAdded D-Bus signals that trigger WiFiProvider
to create a corresponding WiFiService
. The CellularServiceProvider
instance ensures that a CellularService
for a Cellular
Device is created when it is ready, corresponding to the IMSI associated with the active SIM. Additional CellularService
instances may also be created if they match the SIM Card Id (eID or ICCID) of the active SIM. The EthernetProvider
by default has a single EthernetService
, which the first Ethernet
instance will use. Additional Ethernet
instances will cause the EthernetProvider
to create additional EthernetService
instances.
A Profile
represents a set of persisted data and is used for both Device
and Service
. Device
and Service
(base classes and children) take care of loading from and saving to the underlying storage used by the relevant Profile
.
Shill allows for a stack of Profiles
rather than just having a single Profile
at a time. On startup, the Profile
stack contains a single DefaultProfile
, which serves to provide pre-login configuration data. In addition to the regular Profile
behavior of persisting Service
properties, a DefaultProfile
will also persist Device
properties and a subset of Manager
properties.
On user login, the shill_login_user script is run, which creates a Profile
for that user if one doesn‘t already exist, and then pushes that Profile
onto the Profile
stack. Correspondingly, shill_logout_user will pop the user’s Profile
when logging out. When a guest user is used, a Profile
will still be created and pushed onto the stack as usual, but the persisted data will simply be deleted so that the data is essentially not persisted. An EphemeralProfile
, which is essentially a “null profile” that doesn't persist any data, is not used for guest users. One benefit of this is resilience to Shill crashes within a single guest user session.
Every Service
has exactly one Profile
associated with it, which is theProfile
most recently pushed onto the Profile
stack that contains persisted data for that Service
. An EphemeralProfile
is used exclusively for Service
instances that are created but that have no Profile
in the Profile
stack that contains data for it (e.g., a WiFiService
that was created from a WiFi scan but that the user has never attempted to configure or connect to). A Service
can be “linked” to a different Profile
through the use of the Service
kProfileProperty D-Bus property, which is how Service
instances currently using the EphemeralProfile
can be moved over to a Profile
that allows its configuration parameters to be persisted.
Given that Shill's D-Bus clients aside from Autotest/Tast do not create additional Profiles
, the Profile
stack in non-test cases contains only the DefaultProfile
and potentially a user Profile
. The EphemeralProfile
is not part of the Profile
stack.
Manager
is the “top-level” class in Shill. Its responsibilities include:
Devices
representing interfaces managed by Shill.Manager
is informed of Device
additions/removals by DeviceInfo
.Service
instances.Profile
stack, which includes updating Device
and Service
instances when Profiles
are pushed onto or popped from the stack.Service
instances sorted according to the Service ordering described in the Manager
D-Bus Specification.Service
auto-connections (a Service
determines whether or not it can auto-connect, but Manager
triggers the auto-connection attempt).Devices
and Services
.Manager
also provides the top-level Shill D-Bus interface. Note that Device
, Service
, and Profile
instances tracked by Manager
will all have corresponding D-Bus objects that clients can interact with. Clients first learn about these objects via the Manager
D-Bus object.
RTNL
- Acronym for rtnetlink.
Netlink is a protocol that can be used for kernel <-> user-space and user-space <-> user-space communication. Unlike a syscall, which requires a user to initiate communication with the kernel, any entity may send netlink messages at any time. This allows the kernel to notify listeners of events without those listeners needing to do something like poll for a state change. It is based on the socket API, and therefore provides built-in asynchronous semantics through the use of socket queues. Netlink also provides support for multicast, which allows--for example--multiple user-space daemons to listen to the same events from the kernel simply by registering with the appropriate multicast group(s).
Different usages of the netlink protocol will have different information being sent and will generally want multicast groups isolated from other netlink usages. Netlink employs the concept of a family to satisfy this need, where a family can define custom multicast groups and message types. rtnetlink refers specifically to the NETLINK_ROUTE netlink family, which is defined by the kernel itself (see rtnetlink.h).