4. Netlink Library
4.1. Introduction
Netlink library provides an abstraction on top of open source libnl library using which application gets notified for a received netlink message from LINUX. Applications are notified via function callback for the netlink protocols they have registered.
4.2. Native netlink registration
Registering for a netlink protocol via dao_netlink_register()
is named
as Native netlink registration. dao_netlink_register()
is a lowest level
abstraction API to get notifications for any netlink message that corresponds
to netlink protocol provided as an argument to it. dao_netlink_register()
has following declaration:
int dao_netlink_register(int protocol, dao_netlink_parse_cb_t parse_cb,
void *app_ops, void *app_cookie, ...);
where,
protocol eg: NETLINK_ROUTE, NETLINK_XFRM etc.
parse_cb : See documentation for more details
app_ops : Application specific function pointers
app_cookie : Application cookie to identify received notification
... : comma-separated netlink multicast groups. See documentation for more details
This registration is native in a sense that application is expected to parse
received netlink object (struct nl_object *
) by itself, perhaps via native
libnl APIs, which is
passed as an argument to parse_cb
Applications which does not wish to parse netlink object (struct nl_object
*
) or rather work with any libnl APIs, please refer to high level
netlink registration section
4.2.1. Netlink protocols
dao_netlink_register()
takes netlink protocol or netlink family
as a first argument. Examples of netlink protocols are: NETLINK_ROUTE,
NETLINK_XFRM, NETLINK_GENERIC
4.2.1.1. Netlink Object
On successful return, dao_netlink_register()
internally opens a netlink
socket for provided netlink protocol
. It also internally creates a
netlink
object for registered protocol. For a given netlink protocol,
exactly one netlink
object is created which holds netlink
socket and
file descriptor associated with it.
Application can perform following actions on a netlink
object
4.2.1.1.1. Registered netlink object lookup
Get netlink object corresponding registered netlink protocol
void *notifier = dao_netlink_lookup(NETLINK_ROUTE);
4.2.1.1.2. Get netlink file descriptor
int fd = dao_netlink_fd_get(netlink);
4.2.1.1.3. Netlink object cleanup
Close netlink socket and free any associated memory including all notifier
objects
dao_netlink_close(netlink);
4.2.2. Native parse callbacks
Function callbacks of type dao_netlink_parse_cb_t
are called as native
parse callbacks which has following function declaration
typedef void (*dao_netlink_parse_cb_t) (struct nl_object *nl_obj, void *notifier);
Applications are expected to parse struct nl_object *
by itself using
libnl core APIs.``notifier`` is
a notifier object
4.2.3. Netlink multicast groups
Each netlink family or protocol has set of defined multicast groups. Application should be able to provide specific multicast group it would like to get notification for within a given protocol. Examples for multicast groups are
Protocol Multicast Groups
-------- ----------------
NETLINK_ROUTE RTNLGRP_LINK, RTNLGRP_IPV4_IFADDR, RTNLGRP_IPV4_ROUTE,
RTNLGRP_IPV4_RULE, RTNLGRP_IPV6_IFADDR, RTNLGRP_IPV6_ROUTE,
RTNLGRP_IPV6_RULE, RTNLGRP_NOTIFY, RTNLGRP_NEIGH etc..
NETLINK_XFRM XFRMGRP_SA, XFRMGRP_POLICY, XFRMGRP_EXPIRE
Multicast groups for a netlink protocol are provided as
comma-separated arguments to dao_netlink_register()
. For eg:
dao_netlink_register(NETLINK_ROUTE, route_parse_cb, NULL, NULL,
RTNLGRP_IPV4_ROUTE, RTNLGRP_LINK, RTNL_GRP_IPV4_IFADDR);
dao_netlink_register(NETLINK_XFRM, xfrm_parse_cb, NULL, NULL,
XFRMGRP_SA, XFRMGRP_POLICY, XFRMGRP_EXPIRE);
It is possible to provide separate parse_cb()
for each multicast group like
following
dao_netlink_register(NETLINK_ROUTE, parse_cb1, ops1, aux1, RTNLGRP_IPV4_ROUTE);
dao_netlink_register(NETLINK_ROUTE, parse_cb2, ops2, aux2,, RTNLGRP_LINK);
In above case, parse_cb1() will be called once RTNLGRP_IPV4_ROUTE netlink messages are received while parse_cb2() will be called if netlink messages corresponding to RTNLGRP_LINK are received.
Warning
Providing different parse callbacks for same multicast group is not supported
4.2.4. Notifier Object
As described above, dao_netlink_register()
can be
called multiple times for each combination of [protocol, multicast group].
For each multicast group, different specific cookies can be provided. For eg:
dao_netlink_register(NETLINK_ROUTE, parse_cb1, app_ops1, app_cookie1, RTNLGRP_IPV4_ROUTE);
dao_netlink_register(NETLINK_ROUTE, parse_cb2, app_ops2, app_cookie2, RTNLGRP_LINK);
For each dao_netlink_register()
successful registration, library internally
creates a notifier
object which keep hold of all provided multicast groups,
application specific app_ops
and app_cookie
.
Notifier
object is passed as an argument to parse_cb(struct nl_object
*obj, void *netlink). Application specific cookies can
be retrieved in parse_cb()
4.2.4.1. Application specific ops
void *app_ops = dao_netlink_notifier_callback_ops_get(notifier);
4.2.4.3. Get notifier object for a given parse_cb
void *notifier = dao_netlink_notifier_lookup_by_parse_cb(netlink, parse_cb);
4.3. High level netlink registration
High level netlink registrations are wrappers on top of native netlink registration where application does not deal with libnl APIs or structures, instead this library defines function callbacks for each of the netlink protocol supported for high level registration.
Following protocols are supported for high level netlink registration.
4.3.1. NETLINK_ROUTE
dao_netlink_route_notifier_register(dao_netlink_route_callback_ops_t *ops,
const char *filter_prefix);
Refer to dao_netlink_route_callback_ops_t
for getting route netlink message
notifications
4.3.2. NETLINK_XFRM
dao_netlink_xfrm_notifier_register(dao_netlink_xfrm_callback_ops_t *ops, void *app_cookie);
Refer to dao_netlink_xfrm_callback_ops_t
for getting XFRM netlink message
notifications
4.4. Programming model
4.4.1. Initialization
Either use native or high level registration mechanism for getting netlink message notifications
4.4.2. Periodic Netlink polling
A control core is supposed to poll all netlink
objects for any new netlink
message arrival and hence recvmsg()
like function must be invoked on all
opened netlink sockets.
For netlink polling, applications are required to call following APIs periodically for getting any new netlink notifications
4.4.2.1. dao_netlink_poll()
Periodically calls recvmsg()
on all created netlink sockets and call
application specific function callback for any new netlink message
4.4.2.2. dao_netlink_poll_complete()
Once a notification is sent to application for a netlink message via
dao_netlink_poll()
, library disables further polling on that specific
netlink socket until application does not call dao_netlink_poll_complete()
.
This API enables polling on all netlink sockets which are disabled temporarily
after new message notification
dao_netlink_poll_complete()
is useful in use-case where dao_netlink_poll()
is
running in another thread context, perhaps in continuous loop, and current
thread wants to control its polling using dao_netlink_poll_complete()
4.4.2.3. Pseudo-code
Following example shows how to receive route updates for LINUX tap interfaces:
dtap0
and dtap1
/* Get application specific identifier or cookie for each interface name
* which is passed in remaining function callbacks
*/
int rops_app_interface_cookie (char *interface_name, int interface_name,
uint32_t *cookie)
{
if(strstr(interface_name, "dtap0") {
/* Return 0th index for tap0 interface */
*cookie = 0;
return 0;
} else if (strstr (interface_name, "dptap1") {
/* Return 1st index for tap1 interface */
*cookie = 0;
return 0;
} else {
/* interested only on dtap0 and dtap1 interface only */
return -1;
}
}
/* Set Local IP to interface */
int rops_app_ip_local_addr_add_del(dao_netlink_route_ip_addr_t *addr, int is_add)
{
int interface_cookie = addr->app_if_cookie; /*< Set in get_app_interface_cookie() */
if(interface_cookie == 0){
/* Apply IP address to interface dtap0 */
} else
/* Apply IP address to interface dtap1 */
}
/* Update mac address */
int rops_app_link_addr_add_del(dao_netlink_route_link_t *link, int is_add)
{
int interface_cookie = link->app_if_cookie; /*< Set in get_app_interface_cookie() */
if(interface_cookie == 0){
/* Update link "dtap0" */
} else
/* Update link "dtap1" *//
}
dao_netlink_route_callback_ops_t rops {
.get_app_interface_cookie = rops_app_interface_cookie,
.ip_local_addr_add_del = rops_app_ip_local_addr_add_del,
.ip_route_add_del = rops_app_ip_route_add_del,
.link_add_del = rops_app_link_add_del,
.ip_neigh_add_del = rops_app_ip_neigh_add_del,
};
int __poll_function(void *obj, const int is_main)
{
rte_graph_t *graph = (rte_graph_t *)obj
/* Get this worker core handle for graph */
graph = graph + rte_lcore_id();
while (1) {
if (is_main) {
dao_netlink_poll();
...
... Do other stuff
...
dao_netlink_poll_complete();
} else {
rte_graph_walk(graph);
}
}
}
void poll_function(void *obj)
{
if (rte_lcore_id() == rte_get_main_lcore()) {
__poll_function(obj, 1);
} else {
__poll_function(obj, 0);
}
}
int main ()
{
rte_graph_t graph[RTE_LCORE_MAX];
/* Use high level netlink registration method */
if (dao_netlink_route_notifier_register(&rops, "dtap" /* prefix string for dtap0 and dtap1 */) < 0)
return -1;
/* Create rte_graph object for every lcore_id */
...
...
...
rte_eal_remote_launch(poll_function, graph, CALL_MAIN);
}
4.4.3. Netlink Cleanup
Call dao_netlink_cleanup()
to close all netlink sockets and notifier object including any associated memory