Manipulating the Networking Environment Using RTNETLINK
March 30th, 2006 by Asanga Udugama in
NETLINK is a facility in the Linux operating system for user-space applications to communicate with the kernel. NETLINK is an extension of the standard socket implementation. Using NETLINK, an application can send/receive information to/from different kernel features, such as networking, to check current status and control them.
In this article, I describe how a programmer can use the networking environment manipulation capability of NETLINK known as RTNETLINK. I discuss some areas of use of RTNETLINK, the relevant socket operations, the functionality, how RTNETLINK messages are formed and finally, provide a set of sample code that uses RTNETLINK. RTNETLINK for the IP version 4 environment is referred to as NETLINK_ROUTE, and for the IP version 6 environment, it is referred to as NETLINK_ROUTE6. The explanations given here are applicable for both IP versions 4 and 6.
Developers of network layer protocol handlers can use RTNETLINK to modify and monitor different components of networking, such as the routing table and network interfaces. There are many existing and upcoming protocol standards at the Internet Engineering Task Force (IETF) that can be implemented in user space. These implementations will require manipulating the routing and knowing what is being modified by other processes. Some of these protocol categories are as follows:
Dynamic routing protocols: protocols of this category, including the Routing Information Protocol (RIP), Open Shortest Path First (OSPF) and Exterior Gateway Protocol (EGP) actively manage the routing environment of a host while communicating with other equally capable hosts or routers in the network or Internet.
Mobility protocols: hosts that are mobile and connect to different networks at different times use protocols such as Mobile IP (MIP), Session Initiation Protocol (SIP) and Network Mobility (NEMO) to manage routing to maintain connectivity and continuity of communications.
Ad hoc networking protocols: hosts that are mobile and located in places where there is no networking infrastructure, such as routers and WLAN access points, require peer-to-peer communications with differently configured hosts. Mobile computers of rescue workers in an earthquake-struck area or other such emergencies can use ad hoc networking protocols. These protocols, such as the Ad hoc On-demand Distance Vector (AODV) and Optimized Link State Routing (OLSR), require managing the routing to find and communicate with other hosts using neighboring hosts as routers and gateways.
It helps reduce the complexity of the kernel code if you implement these protocols in user space. Further, it simplifies the development and testing of these protocols because of the availability of many user-space development tools. Problems, such as kernel crashes, that are likely with kernel-based code when testing or when used by end users will not occur in a user-space protocol handler.
The socket implementation of Linux allows two end points to communicate. The socket API provides a standard set of functions and data structures. With RTNETLINK, the two end points in communication are user space and kernel space. The following sequence of socket calls have to be made when manipulating the networking environment through RTNETLINK:
Open socket.
Bind socket to local address (using process ID).
Send message to the other end point.
Receive message from the other end point.
Close socket.
The socket() function opens an unattached end point to communicate with the kernel. The function prototype of this call is as follows:
int socket(int domain, int type, int protocol);
The domain refers to what type of socket is being used. For RTNETLINK, we use AF_NETLINK (PF_NETLINK). type refers to the type of protocol used when communicating. This can be raw (SOCK_RAW) or datagram (SOCK_DGRAM). This is not relevant for RTNETLINK sockets and either can be used. protocol refers to the exact NETLINK capability that we use; in our case, it is NETLINK_ROUTE. This function returns an integer with a positive number called the socket descriptor, if the socket opening was successful. This descriptor will be used in all the future RTNETLINK calls until the socket is closed. If there was a failure, a negative value is returned, and the system error variable errno included in errno.h is set to the appropriate error code.
The following is an example of a call to open an RTNETLINK socket:
int fd; ... fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
Once the socket is opened, it has to be bound to a local address. The user application can use a unique 32-bit ID to identify the local address. The function prototype of bind is as follows:
int bind(int fd, struct sockaddr *my_addr,
socklen_t addrlen);
To bind, the caller must provide a local address using the sockaddr_nl structure. This structure in the linux/netlink.h #include file has the following format:
struct sockaddr_nl
{
sa_family_t nl_family; // AF_NETLINK
unsigned short nl_pad; // zero
__u32 nl_pid; // process pid
__u32 nl_groups; // multicast grps mask
};
The nl_pid must contain a unique ID, which can be created using the return of the getpid() function. This function returns the process ID of the current user process that opened the RTNETLINK socket. But, if our process consists of multiple threads with each thread opening different RTNETLINK sockets, a modified process ID can be used.
Once this structure is filled, the binding can be done. The bind function returns zero if the operation succeeded. A negative number is returned in the case of failure, and the system error variable is set. The following is an example of calling bind:
struct sockaddr_nl la; ... bzero(&la, sizeof(la)); la.nl_family = AF_NETLINK; la.nl_pad = 0; la.nl_pid = getpid(); la.nl_groups = 0; rtn = bind(fd, (struct sockaddr*) &la, sizeof(la));
If the operation you require is multicast-based, you must set nl_groups to join the multicast group associated with the required RTNETLINK operation. For example, if you want to be notified of the changes to the routing table by other processes, you must OR (|) the RTMGRP_IPV4_ROUTE and RTMGRP_NOTIFY.
Sending routing RTNETLINK messages to the kernel is done through the use of the standard sendmsg() function of the socket interface. The following is the prototype of this function:
ssize_t sendmsg(int fd, const struct msghdr *msg,
int flags);
msg is a pointer to a msghdr structure. The following is the format of this structure:
struct msghdr
{
void *msg_name; //Address to send to
socklen_t msg_namelen; //Length of address data
struct iovec *msg_iov; //Vector of data to send
size_t msg_iovlen; //Number of iovec entries
void *msg_control; //Ancillary data
size_t msg_controllen; //Ancillary data buf len
int msg_flags; //Flags on received msg
};
The msg_name is a pointer to a variable of the type struct sockaddr_nl. This is the destination address of the sendmsg() function. Because this message is directed to the kernel, all variables of sockaddr_nl will be initialized to zero, except the nl_family member variable. The field msg_namelen should contain the size of a struct sockaddr_nl.
msg_iov should contain a pointer to a struct iovec, which is filled with the RTNETLINK message relevant to the request being made. The caller is allowed to place multiple RTNETLINK requests, if required. msg_iovlen points to the number of struct iovec structures that were placed in msg_iov. The rest of the variables are initialized to zero.
To receive RTNETLINK messages, the recv() function is used. Here is the prototype of this function:
ssize_t recv(int fd, void *buf, size_t len,
int flags);
The second and third variables are a pointer to a buffer to place the bytes read and the length of this buffer, respectively. For RTNETLINK, the buffer will contain a set of RTNETLINK messages that have to be read one after the other using a set of macros provided in the netlink.h and rtnetlink.h #include files. flags is a set of flags to indicate how the receive should be performed. For RTNETLINK, this simply can be initialized to zero.
Once the socket communications are complete, the socket has to be closed using the close() function. Here's the prototype of this function:
int close(int fd);
A programmer who develops applications that use RTNETLINK must include the following #include files at a minimum:
#include <bits/sockaddr.h> #include <asm/types.h> #include <linux/rtnetlink.h> #include <sys/socket.h>
These files contain the different definitions, such as data types and structures, required to make RTNETLINK calls. Here is a short explanation of what is defined in these files relevant to RTNETLINK:
bits/sockaddr.h: provides the definitions for the addresses used by socket functions.
asm/types.h: provides the definitions of the data types used in the header files related to NETLINK and RTNETLINK.
linux/rtnetlink.h: provides the macros and data structures that are used in RTNETLINK. Because RTNETLINK is based on NETLINK, this also includes the linux/netlink.h. netlink.h defines the general macros and structures that are used in all the NETLINK-based capabilities.
sys/socket.h: provides the function prototypes and the different data structures related to the socket implementation.
The operations that can be invoked using RTNETLINK are defined in the rtnetlink.h file. Each of the operations provides three possibilities of manipulation: add/update, delete or query. These three possibilities are identified by NEW, DEL and GET. Following are the manipulation operations allowed by RTNETLINK.
General networking environment manipulation services:
Link layer interface settings: identified by RTM_NEWLINK, RTM_DELLINK and RTM_GETLINK.
Network layer (IP) interface settings: RTM_NEWADDR, RTM_DELADDR and RTM_GETADDR.
Network layer routing tables: RTM_NEWROUTE, RTM_DELROUTE and RTM_GETROUTE.
Neighbor cache that associates network layer and link layer addressing: RTM_NEWNEIGH, RTM_DELNEIGH and RTM_GETNEIGH.
Traffic shaping (management) services:
Routing rules to direct network layer packets: RTM_NEWRULE, RTM_DELRULE and RTM_GETRULE.
Queuing discipline settings associated with network interfaces: RTM_NEWQDISC, RTM_DELQDISC and RTM_GETQDISC.
Traffic classes used together with queues: RTM_NEWTCLASS, RTM_DELTCLASS and RTM_GETTCLASS.
Traffic filters associated with a queuing: RTM_NEWTFILTER, RTM_DELTFILTER and RTM_GETTFILTER.
RTNETLINK employs a request-response mechanism to send and receive information to manipulate the networking environment. A request or a response of RTNETLINK consists of a stream of message structures. These structures are filled by the caller, in the case of a request, or filled by the kernel, in the case of a response. To place information into these structures or to retrieve information, RTNETLINK provides a set of macros (using #define statements). Every request must contain the following structure at the beginning:
struct nlmsghdr
{
__u32 nlmsg_len; //Length of msg incl. hdr
__u16 nlmsg_type; //Message content
__u16 nlmsg_flags; //Additional flags
__u32 nlmsg_seq; //Sequence number
__u32 nlmsg_pid; //Sending process PID
}
This structure provides information about what type of RTNETLINK message is specified in the rest of the request. It is also called the NETLINK header. Here is a brief explanation of these fields:
nlmsg_len: should contain the length of the whole RTNETLINK message, including the length of the nlmsghdr structure. This field can be filled using the macro NLMSG_ALIGN(len), where len is the length of the message that follows this structure.
nlmsg_type: a 16-bit flag to indicate what is contained in the message, such as RTM_NEWROUTE.
nlmsg_flags: another 16-bit flag that further clarifies the operation specified in nlmsg_type, such as NLM_F_REQUEST.
nlmsg_seq and nlmsg_pid: these two fields are used to identify an RTNETLINK request uniquely. The caller can place the process ID and a sequence number in these fields.
Following the nlmsghdr structure are the structures relevant to the operation being requested. Depending on the type of RTNETLINK operation, the caller must include one or more of the following structures. These are called the RTNETLINK operation headers:
struct rtmsg: retrieving or modifying entries of the routing table requires the use of this structure.
struct rtnexthop: a next hop in a routing entry is the next host to consider on the way to the destination. A single routing entry can have multiple next hops. Each next hop entry has many types of attributes, such as the network interface in addition to the next hop IP address.
struct rta_cacheinfo: each route entry consists of status information that the kernel updates regularly, mainly usage information. Using this structure, a user can retrieve this information.
struct ifaddrmsg: retrieving or modifying network layer attributes associated with a network interface requires the use of this structure.
struct ifa_cacheinfo: similar to a route entry, a network interface also consists of information about its status, which is updated by the kernel. This structure is used to retrieve this information.
struct ndmsg: retrieving or modifying the association information between link layer addressing and network layer addressing of neighbors, referred to as neighbor discovery, is specified through this structure.
struct nda_cacheinfo: holds the kernel updated information related to each neighbor discovery entry.
struct ifinfomsg: retrieving or modifying the link layer attributes related to a network interface requires the use of this structure.
struct tcmsg: retrieving or modifying traffic shaping attributes is supplied using this structure.
Following the RTNETLINK operation header are the attributes related to the operation, such as an interface number and IP address. These attributes are specified using the struct rtattr. There is one structure for each attribute. This structure has the following format:
struct rtattr
{
unsigned short rta_len;
unsigned short rta_type;
};
Immediately following this structure is the value of the attribute. An attribute such as an IP version 4 address will occupy a 4-byte area. The variable rta_len should contain the size of this structure plus the size of the attribute. rta_type should contain the value identifying the attribute, which are given in the enumerations defined in rtnetlink.h. enum rtattr_type_t and other enumerations provide the attribute identifiers, such as IFA_ADDRESS and NDA_DST, to be used in this field. The maximum number of attributes that you can attach is up to only the macro RTATTR_MAX. An example of attaching an attribute is as follows:
rtap->rta_type = RTA_DST;
rtap->rta_len = sizeof(struct rtattr) + 4;
inet_pton(AF_INET, dsts,
((char *)rtap) + sizeof(struct rtattr));
Information that is received from an RTNETLINK socket is again a stream of structures. A programmer has to identify and extract information by moving a pointer along this byte stream. To simplify this process, RTNETLINK provides a set of macros to make the buffer positioning easier:
NLMSG_NEXT(nlh, len): returns the pointer to the next NETLINK header. nlh is the header returned previously, and len is the total length of the message. This will be called in a loop to read every message.
NLMSG_DATA(nlh): returns the pointer to the RTNETLINK header related to the requested operation given the NETLINK header. If a route entry is being manipulated, this will return a pointer to a struct rtmsg.
RTM_RTA(r), IFA_RTA(r), NDA_RTA(r), IFLA_RTA(r) and TCA_RTA(r): return a pointer to the start of the attributes of the respective RTNETLINK operation given the header of the RTNETLINK message (r).
RTM_PAYLOAD(n), IFA_PAYLOAD(n), NDA_PAYLOAD(n), IFLA_PAYLOAD(n) and TCA_PAYLOAD(n): return the total length of the attributes that follow the RTNETLINK operation header given the pointer to the NETLINK header (n).
RTA_NEXT(rta, attrlen): returns a pointer to the start of the next attribute given the last returned attribute (rta) and the remaining size (attrlen) of the attributes.
Considering a simple example where an RTNETLINK request to retrieve the routing table was sent, the reply is processed in the following manner:
char *buf; // ptr to RTNETLINK data
int nll; // byte length of all data
struct nlmsghdr *nlp;
struct rtmsg *rtp;
int rtl;
struct rtattr *rtap;
nlp = (struct nlmsghdr *) buf;
for(;NLMSG_OK(nlp, nll); nlp=NLMSG_NEXT(nlp, nll))
{
// get RTNETLINK message header
rtp = (struct rtmsg *) NLMSG_DATA(nlp);
// get start of attributes
rtap = (struct rtattr *) RTM_RTA(rtp);
// get length of attributes
rtl = RTM_PAYLOAD(nlp);
// loop & get every attribute
for(;RTA_OK(rtap, rtl); rtap=RTA_NEXT(rtap, rtl))
{
// check and process every attribute
}
}
The sample code presented here focuses on three of the operations that can be performed on the routing table:
get_routing_table: reads the main routing table in the system.
set_routing_table: inserts a new routing entry to the table.
mon_routing_table: monitors the routing table changes.
All three samples use a similar main() function that calls a set of subfunctions to form RTNETLINK messages and send, receive and process the received messages. To simplify the explanation, no error handling is considered. These samples perform on the IP version 4 environment of the system (AF_INET). Here is the main() function:
int main(int argc, char *argv[])
{
// open socket
fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
// setup local address & bind using
// this address
bzero(&la, sizeof(la));
la.nl_family = AF_NETLINK;
la.nl_pid = getpid();
bind(fd, (struct sockaddr*) &la, sizeof(la));
// sub functions to create RTNETLINK message,
// send over socket, receive reply & process
// message
form_request();
send_request();
recv_reply();
read_reply();
// close socket
close(fd);
}
Similar to the above function, the two functions that perform the socket communications are almost common to all the samples. These two functions simply send a formed message to the kernel and receive messages sent by the kernel. Exceptions here are the set_routing_table and mon_routing_table samples. In set_routing_table, a receive phase is not considered. In the mon_routing_table, a send phase is not present as it attempts to monitor only the state of the routing environment to see what is being changed. This information is mulitcast by the kernel to all the RTNETLINK sockets that are in the appropriate receiving state.
First, here's the code for send_request():
void send_request()
{
// create the remote address
// to communicate
bzero(&pa, sizeof(pa));
pa.nl_family = AF_NETLINK;
// initialize & create the struct msghdr supplied
// to the sendmsg() function
bzero(&msg, sizeof(msg));
msg.msg_name = (void *) &pa;
msg.msg_namelen = sizeof(pa);
// place the pointer & size of the RTNETLINK
// message in the struct msghdr
iov.iov_base = (void *) &req.nl;
iov.iov_len = req.nl.nlmsg_len;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
// send the RTNETLINK message to kernel
rtn = sendmsg(fd, &msg, 0);
}
And, here's the recv_reply():
void recv_reply()
{
char *p;
// initialize the socket read buffer
bzero(buf, sizeof(buf));
p = buf;
nll = 0;
// read from the socket until the NLMSG_DONE is
// returned in the type of the RTNETLINK message
// or if it was a monitoring socket
while(1) {
rtn = recv(fd, p, sizeof(buf) - nll, 0);
nlp = (struct nlmsghdr *) p;
if(nlp->nlmsg_type == NLMSG_DONE)
break;
// increment the buffer pointer to place
// next message
p += rtn;
// increment the total size by the size of
// the last received message
nll += rtn;
if((la.nl_groups & RTMGRP_IPV4_ROUTE)
== RTMGRP_IPV4_ROUTE)
break;
}
}
The above functions and the following ones use a set of globally defined variables. These are used for all the socket operations as well as for forming and processing RTNETLINK messages:
// buffer to hold the RTNETLINK request
struct {
struct nlmsghdr nl;
struct rtmsg rt;
char buf[8192];
} req;
// variables used for
// socket communications
int fd;
struct sockaddr_nl la;
struct sockaddr_nl pa;
struct msghdr msg;
struct iovec iov;
int rtn;
// buffer to hold the RTNETLINK reply(ies)
char buf[8192];
// RTNETLINK message pointers & lengths
// used when processing messages
struct nlmsghdr *nlp;
int nll;
struct rtmsg *rtp;
int rtl;
struct rtattr *rtap;
The get_routing_table sample retrieves the main routing table of the IPv4 environment. The form_request() function is as follows:
void form_request()
{
// initialize the request buffer
bzero(&req, sizeof(req));
// set the NETLINK header
req.nl.nlmsg_len
= NLMSG_LENGTH(sizeof(struct rtmsg));
req.nl.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
req.nl.nlmsg_type = RTM_GETROUTE;
// set the routing message header
req.rt.rtm_family = AF_INET;
req.rt.rtm_table = RT_TABLE_MAIN;
}
The received message for the RTNETLINK request in the buf variable to retrieve the routing table is processed by the read_reply() function. Here is the code of this function:
void read_reply()
{
// string to hold content of the route
// table (i.e. one entry)
char dsts[24], gws[24], ifs[16], ms[24];
// outer loop: loops thru all the NETLINK
// headers that also include the route entry
// header
nlp = (struct nlmsghdr *) buf;
for(;NLMSG_OK(nlp, nll);nlp=NLMSG_NEXT(nlp, nll))
{
// get route entry header
rtp = (struct rtmsg *) NLMSG_DATA(nlp);
// we are only concerned about the
// main route table
if(rtp->rtm_table != RT_TABLE_MAIN)
continue;
// init all the strings
bzero(dsts, sizeof(dsts));
bzero(gws, sizeof(gws));
bzero(ifs, sizeof(ifs));
bzero(ms, sizeof(ms));
// inner loop: loop thru all the attributes of
// one route entry
rtap = (struct rtattr *) RTM_RTA(rtp);
rtl = RTM_PAYLOAD(nlp);
for(;RTA_OK(rtap, rtl);rtap=RTA_NEXT(rtap,rtl))
{
switch(rtap->rta_type)
{
// destination IPv4 address
case RTA_DST:
inet_ntop(AF_INET, RTA_DATA(rtap),
dsts, 24);
break;
// next hop IPv4 address
case RTA_GATEWAY:
inet_ntop(AF_INET, RTA_DATA(rtap),
gws, 24);
break;
// unique ID associated with the network
// interface
case RTA_OIF:
sprintf(ifs, "%d",
*((int *) RTA_DATA(rtap)));
default:
break;
}
}
sprintf(ms, "%d", rtp->rtm_dst_len);
printf("dst %s/%s gw %s if %s\n",
dsts, ms, gws, ifs);
}
}
The set_routing_table sample sends an RTNETLINK request to insert an entry to the routing table. The route entry that is inserted is a host route (32-bit network prefix) to a private IP address (192.168.0.100) through interface number 2. These values are defined in the variables dsts (destination IP address), ifcn (interface number) and pn (prefix length). You can run the get_routing_table sample to get an idea about the interface numbers and the IP network in your system. Here's the form_request():
void form_request()
{
// attributes of the route entry
char dsts[24] = "192.168.0.100";
int ifcn = 2, pn = 32;
// initialize RTNETLINK request buffer
bzero(&req, sizeof(req));
// compute the initial length of the
// service request
rtl = sizeof(struct rtmsg);
// add first attrib:
// set destination IP addr and increment the
// RTNETLINK buffer size
rtap = (struct rtattr *) req.buf;
rtap->rta_type = RTA_DST;
rtap->rta_len = sizeof(struct rtattr) + 4;
inet_pton(AF_INET, dsts,
((char *)rtap) + sizeof(struct rtattr));
rtl += rtap->rta_len;
// add second attrib:
// set ifc index and increment the size
rtap = (struct rtattr *) (((char *)rtap)
+ rtap->rta_len);
rtap->rta_type = RTA_OIF;
rtap->rta_len = sizeof(struct rtattr) + 4;
memcpy(((char *)rtap) + sizeof(struct rtattr),
&ifcn, 4);
rtl += rtap->rta_len;
// setup the NETLINK header
req.nl.nlmsg_len = NLMSG_LENGTH(rtl);
req.nl.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE;
req.nl.nlmsg_type = RTM_NEWROUTE;
// setup the service header (struct rtmsg)
req.rt.rtm_family = AF_INET;
req.rt.rtm_table = RT_TABLE_MAIN;
req.rt.rtm_protocol = RTPROT_STATIC;
req.rt.rtm_scope = RT_SCOPE_UNIVERSE;
req.rt.rtm_type = RTN_UNICAST;
// set the network prefix size
req.rt.rtm_dst_len = pn;
}
The mon_routing_table sample reads the RTNETLINK messages received when other processes change the system's main routing table. This function will use the same read_reply() function to process the messages. The main() function requires a slight change. Because this operation involves listening to multicast messages of the kernel, the local address to which we bind, it also must include the two flags RTMGRP_IPV4_ROUTE and RTMGRP_NOTIFY. Here is the required change:
la.nl_groups = RTMGRP_IPV4_ROUTE | RTMGRP_NOTIFY;
Once mon_routing_table is executed, run a route add or a route del command from another shell prompt to see the results.
RTNETLINK is a simple, yet versatile way of manipulating the networking environment of a Linux host. User-space network protocol handlers are ideal candidates for using RTNETLINK. The advanced IP routing command suite, referred to as IPROUTE2, is based on RTNETLINK. More information about the different operations and flags of RTNETLINK can be found at NETLINK(7) and RTNETLINK(7).
The sample code for this article is available at ftp.ssc.com/pub/lj/listings/issue145/8498.tgz.
Special Magazine Offer -- 2 Free Trial Issues!
Receive 2 free trial issues of Linux Journal as well as instant online access to current and past issues. There's NO RISK and NO OBLIGATION to buy. CLICK HERE for offer
Linux Journal: delivering readers the advice and inspiration they need to get the most out of their Linux systems since 1994.
Sorry, offer available in the US only. International orders, click here.
Subscribe now!
Recently Popular
| Linux HOWTO: Video Editing Magic with ffmpeg | Jul-23-08 |
| The new business of free radio | Jul-24-08 |
| Why We Must React to ACTA | Jul-24-08 |
| Chapter 16: Ubuntu and Your iPod | Aug-30-06 |
| Boot with GRUB | May-01-01 |
| Building a Call Center with LTSP and Soft Phones | Aug-25-05 |
Featured Videos
Non-linear video editing tools are great, but they're not always the best tool for the job. This is where a powerful tool like ffmpeg becomes useful. This tutorial by Elliot Isaacson covers the basics of transcoding video, as well as more advanced tricks like creating animations, screen captures, and slow motion effects.
Shawn Powers reviews the HP Mini-Note portable computer.
Thanks to our sponsor: Silicon Mechanics
Silicon Mechanics is a leading manufacturer of rackmount servers, storage, and high performance computing hardware. The best warranty offerings available are backed by experts dedicated to customer satisfaction.
From the Magazine
August 2008, #172
There's nuttin like a Cool Project to give you some relief from the summer heat, so get out your parka cuz we got a bunch of em. First up is the BUG, not a bug, The BUG. It's got a GPS, camera and more, in a hand-sized package that's user programmable. The BUG does everything. It's both a floor wax and a dessert topping. Get one now. Need a software version of a Swiss Army knife? Take a look at Billix, and don't leave home without it. Then, chew on this one, an X server on a Gumstix device driving an E-Ink display. Need more storage? How about 16 Terabytes? Can do.
And, of course, we have the usual cast of characters: Marcel, Reuven, Dave, Kyle, Doc, plus the new kid on the block Shawn Powers. But it doesn't stop there: build a MythTV box on a budget, build your own GIS system, set up the tools to monitor your enterprise and more. Finally, remember The War of the Worlds? Now you can play too.
Delicious
Digg
Reddit
Newsvine
Technorati







Hello Asanga... I am
On December 18th, 2007 Anonymous (not verified) says:
Hello Asanga...
I am newbie to Linux. As far as u have googled i found only your material for a sample.With my understanding on ur illustration I have made the below module to get the destination address and the gateway address when i give "route add -host 192.168.2.45 gw 202.34.2.1"
I get the gateway address as 192.168.2.45 from the module. but i expect the gateway to be 202.34.2.1...
Please help me on this regard.
Thanks in advance...
/* Read message from kernel */
recv(sock_fd,nlh,size, 0);
printf(" Received message payload: %s\n",
NLMSG_DATA(nlh));
rtp = (struct rtmsg*)NLMSG_DATA(nlh);
rtap =(struct rtattr*) RTM_RTA(rtp);
rtl = RTM_PAYLOAD(nlh);
for(;RTA_OK(rtap,rtl);rtap = RTA_NEXT(rtap,rtl))
{
switch(rtap_rta_type)
{
case RTA_GATEWAY:
inet_ntop(AF_INET,RTA_DATA(rtap),gws,24);
printf("\n gateway address is %s",gws);
break;
case RTA_DST:
inet_ntop(AF_INET,RTA_DATA(rtap),dsts,24);
printf("\n destination address is %s",dsts);
break;
case RTA_SRC:
printf("\n received source address");
break;
default:
break;
}
}
Getting the IPV6 Address of a Device via rtnetlink
On February 6th, 2007 saltorfer (not verified) says:
Hi, I currently have the problem that i want to get the IPV6 Address of a device via rtnetlink. Your article was already very helpful, but I still cannot find out, which fields of struct ifaddrmsg I have to fill out if I pass it with a request so that i get the IP that I am looking for. I set ifa_family to AF_INET6 and ifa_index to the device that I am looking at. Nevertheless when parsing the "answer" buffer so to speak I get NLMSG_ERROR and nothing else. Well and there is the problem that the programm never does more than one iteration in while(1) but it also never leaves it .. well that is a different problem I guess. Still, I would like to know why you put the second break condition in there, is it not always true? You didn't even set nl_groups in your programm ?
Sorry for my bad English but it has been a frustrating day full of debugging.
Greetings from Switzerland,
S.
Re. Getting the IPV6 Address of a Device via rtnetlink
On February 7th, 2007 Asanga (not verified) says:
Hello,
Here is the request init part of a sample RTNETLINK program that shows the IPv6 address info of interfaces.
bzero(&local, sizeof(local));
local.nl_family = AF_NETLINK;
local.nl_pid = getpid();
if(bind(fd, (struct sockaddr*) &local, sizeof(local)) < 0) {
printf("Error in sock bind\n");
exit(1);
}
bzero(&peer, sizeof(peer));
peer.nl_family = AF_NETLINK;
bzero(&msg_info, sizeof(msg_info));
msg_info.msg_name = (void *) &peer;
msg_info.msg_namelen = sizeof(peer);
bzero(&netlink_req, sizeof(netlink_req));
netlink_req.nlmsg_info.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
netlink_req.nlmsg_info.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
netlink_req.nlmsg_info.nlmsg_type = RTM_GETADDR;
netlink_req.nlmsg_info.nlmsg_pid = getpid();
netlink_req.ifaddrmsg_info.ifa_family = AF_INET6;
iov_info.iov_base = (void *) &netlink_req.nlmsg_info;
iov_info.iov_len = netlink_req.nlmsg_info.nlmsg_len;
msg_info.msg_iov = &iov_info;
msg_info.msg_iovlen = 1;
rtn = sendmsg(fd, &msg_info, 0);
If you require the whole code, look in my home page.
Re. second break; Usually the end of a returned message is indicated by a NLMSG_DONE. But for monitoring of routing table changes, this will not work. Since the example code in this article was common, that second break is also part of the loop.
Kind regards,
Asanga
Getting route updates
On January 9th, 2007 Nagendra (not verified) says:
Hi..
This document was really helpfull. Thanks a lot.
I have a question regarding receiving route updates from the kernel.
I have a process that waits for any routing table changes. It is able to get updates when ever a new route is added or deleted. It gets arround 52 bytes of data.
When I add a new route entry I get 52 bytes but, it fails to enter
"for(;NLMSG_OK(nlp, nll);nlp=NLMSG_NEXT(nlp, nll))" loop of read_reply() as given in this document and more over when I try to print "nlp->nlmsg_type" its always RTM_NEWROUTE even though I deleted a route entry in my previous operation.
What I want is...
1) When ever a new entry gets added read_reply() function should print the new entry that got added.
2) When ever a entry is deleted from the route table, it should print the entry that got deleted as well as nlp->nlmsg_type shud be RTM_DELROUTE so that I know that the netlink message I got is because of delete operation.
Your help in this regard will be appreciated.
Thanks and regards,
Nagendra KS.
re: Getting route updates
On January 11th, 2007 Asanga (not verified) says:
Hello,
I added the statement
printf("Type %d\n", nlp->nlmsg_type);
just after the statement
nlp = (struct nlmsghdr *) buf;
in the mon_routing_table.c file and I see 25 (RTM_DELROUTE) for a route delete and 24 (RTM_ADDROUTE) for a route add.
Kind regards,
Asanga
Flush Cache
On January 8th, 2007 Mike CC (not verified) says:
It is possible to flush the route cache via rtnetlink sockets?
re: Flush Cache
On January 11th, 2007 Asanga (not verified) says:
Hello,
> It is possible to flush the route cache via rtnetlink sockets?
As far as I know, there isn't any RTNETLINK command to flush the routing cache. But after looking at the source code of the "ip" command suit I found that they write a -1 to
/proc/sys/net/ipv4/route/flush to flush the routing cache.
Kind regards,
Asanga
How to specify a NIC?
On November 7th, 2006 CC (not verified) says:
Hallo,
this article helps me to understand the way to implement a protocol.
But some questions are still confusing me.
If an application just wants to send message through a specified NIC ( e.g. the node has more than one NIC, like LAN, WLAN etc), how can the application just set this selectivly ?
Is it able to set up more than one NIC for sending/receiving at the same time, or it should be done in different threads ?
Is there a Windows-Version of NETLINK & RTNETLINK ?
thanks in advance
Re: How to specify a NIC?
On November 25th, 2006 Asanga (not verified) says:
Hello,
>If an application just wants to send message through a specified NIC ( e.g. the node has more than one NIC, like LAN, WLAN etc), how can the application just set this selectivly ?
I assume that you are asking about sending IP packets over an interface. If that is the case, you must use INET type sockets to do this.
> Is it able to set up more than one NIC for sending/receiving at the same time, or it should be done in different threads ?
What interface a packet takes, is usually decided by the routing table, depending on the destination address of the packet. But I think INET sockets also has a facility to send packets from a given interface (thru sendmsg())
> Is there a Windows-Version of NETLINK & RTNETLINK ?
As far as I know, no (atleast up to XP)
Regards,
Asanga
Via gateway
On July 28th, 2006 Miguel (not verified) says:
I'm having problems adding a route via a gateway, i tried to add it but it simply wont work.
This is the code i'm adding after the iface
rtap= (struct rtattr *) (((char *)rtap) + sizeof(struct rtattr));
rtap->rta_type=RTA_GATEWAY;
rtap->rta_len = sizeof (struct rtattr) + 4;
inet_pton(AF_INET, gw, ((char *)rtap) + sizeof (struct rtattr));
rtl +=rtap->rta_len;
Thanks
Re: Via gateway
On August 15th, 2006 Asanga (not verified) says:
Hello Miguel,
Here is the code to add the gateway to a route,
rtap = (struct rtattr *) (((char *)rtap)
+ rtap->rta_len);
rtap->rta_type = RTA_GATEWAY;
rtap->rta_len = sizeof(struct rtattr) + 4;
inet_pton(AF_INET, gw,
((char *)rtap) + sizeof(struct rtattr));
rtl += rtap->rta_len;
Your code piece is almost the same except for the rta_len addition. If there is no problem here, also check whether you can add the same route entry that you are trying to add programatically using ip route add command. A frequent problem of adding gateways to routes is that the gateway should be reachable.
Kind regards,
Asanga