Utility for parsing /proc/net/route - linux

Is there an available utility to parse the content of /proc/net/route into more human readable format (i.e. dotted decimal for addresses)?

ROUTE(8) does exactly that if you invoke it with -n flag. Moreover, it could be used on systems without procfs support. For example:
$ route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 192.168.1.2 0.0.0.0 UG 100 0 0 eth0
192.168.1.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0

Nowadays you should be using the ip route list command instead of route.

Check out the /sbin/route command.

libnetlink may be the right way to go for this. I know it is the "proper" interface for a lot of low-level and physical network stuff, I'm not 100% sure about route tables, though.
See: libnetlink netlink rtnetlink

Related

Kernel API to know up address of interface

Is there any kernel side/space API to know the ip address of an interface , given it's name?
I think you're looking for rtnetlink (man page)
Rtnetlink allows the kernel's routing tables to be read and altered.
It is used within the kernel to communicate between various
subsystems, though this usage is not documented here, and for
communication with user-space programs. Network routes, IP addresses,
link parameters, neighbor setups, queueing disciplines, traffic
classes and packet classifiers may all be controlled through
NETLINK_ROUTE sockets.
According to strace, tt's the api ip addr show dev XXX uses:
strace ip addr sh dev lo 2>&1 | grep sendmsg
sendmsg(4, {msg_name={sa_family=AF_NETLINK, nl_pid=0, nl_groups=00000000}, msg_namelen=12, msg_iov=[{iov_base={{len=48, type=RTM_GETLINK, flags=NLM_F_REQUEST, seq=1596838225, pid=0}, {ifi_family=AF_UNSPEC, ifi_type=ARPHRD_NETROM, ifi_index=0, ifi_flags=0, ifi_change=0}, [{{nla_len=8, nla_type=IFLA_EXT_MASK}, 9}, {{nla_len=7, nla_type=IFLA_IFNAME}, "lo"}]}, iov_len=48}], msg_iovlen=1, msg_controllen=0, msg_flags=0}, 0) = 48
sendmsg(3, {msg_name={sa_family=AF_NETLINK, nl_pid=0, nl_groups=00000000}, msg_namelen=12, msg_iov=[{iov_base={{len=40, type=RTM_GETLINK, flags=NLM_F_REQUEST, seq=1596838225, pid=0}, {ifi_family=AF_UNSPEC, ifi_type=ARPHRD_NETROM, ifi_index=if_nametoindex("lo"), ifi_flags=0, ifi_change=0}, {{nla_len=8, nla_type=IFLA_EXT_MASK}, 9}}, iov_len=40}], msg_iovlen=1, msg_controllen=0, msg_flags=0}, 0) = 40
However, it looks like a non-trivial api so if you don't need it often, it might be easier to just run ip addr sh dev XXX and parse the response.
Edit:
Looks like it's also possible using netdevice (man page), specifically, the SIOCGIFADDR ioctl:
SIOCGIFADDR, SIOCSIFADDR
Get or set the address of the device using ifr_addr. Setting the interface address is a privileged operation. For compatibility, only
AF_INET addresses are accepted or returned.
There's example code here

merging tcpdump from two different interfaces

Can you please help me to get an idea, how to do the following requirement?
I have two ethernet ports on a server. Eth0 and Eth1. Both interfaces are carrying traffic. I want to capture packet-dump both of the interfaces and merge into one file.
Thank you
Luke
As the answers to Tcpdump on multiple interfaces provide, which #Marged linked to above, you can run tcpdump (or tshark or dumpcap) specifying -i any as the interface if you don't mind capturing traffic on all interfaces. And if you only want traffic on those 2 specific interfaces, then you can simultaneously run 2 separate instances of the capture tool, one capturing on the eth0 interface, the other capturing on the eth1 interface and then merge the two capture files together using a tool such as mergecap.
Alternatively - and much simpler in my opinion - is to just use a single instance of either dumpcap or tshark to capture traffic on both interfaces to a single capture file with no merging of separate capture files needed at all. As the man pages for those tools indicate, "This option can occur multiple times. When capturing from multiple interfaces, the capture file will be saved in pcapng format."
For example:
tshark -i eth0 -i eth1 -w eth0_eth1.pcapng

measuring TX retransmission amount per interface

i'm trying to get the TX re-transmission amount for a specific network interface on a Linux 2.6.33.3 kernel.
is there any Linux raw data i can use in order to get/calculate this kind of information?
i've tried netstat, but i couldn't find a way to get the retransmissions value for a specific interface.
with no other "quick" choice, what would it require from me in order to build a small app especially for that? listen to all outgoing packets using any capturing library and count.. what? duplicate packets? by some kind of a packet id?
i'm not a network pro.. am i suppose to find troubles in this kind of mission? (btw, i'll probably try to do it with python. any reason why not to?)
You can check how your interface is configured with the following commands:
mii-tool eth0
ethtool eth0
mii-diag eth0
Not all of them are installed by default in any Linux system, but they are definitely in the distributive and installable.
When you want to measure, how fast is your interface really, you can use dd + netcat:
host1$ cat /dev/zero | nc -l -p 3000
host2$ nc host1 3000 | dd of=/dev/null
^C
dd will write you with which speed was the retransmission between host1 and host2.
If you want per interface, try ifconfig eth0, you probably have what you want there. Just parse it through python.

Identifying the preferred IPv6 source address for an adapter

If you have a IPv6 enabled host that has more than one global-scope address, how can you programmatically identify the preferred address for bind()?
Example address list:
eth0 Link encap:Ethernet HWaddr 00:14:5e:bd:6d:da
inet addr:10.6.28.31 Bcast:10.6.28.255 Mask:255.255.255.0
inet6 addr: 2002:dce8:d28e:0:214:5eff:febd:6dda/64 Scope:Global
inet6 addr: fe80::214:5eff:febd:6dda/64 Scope:Link
inet6 addr: 2002:dce8:d28e::31/64 Scope:Global
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
On Solaris you can indicate a preferred address with an interface flag and it is available programmatically via SIOCGLIFCONF:
/usr/include/net/if.h:
#define IFF_PREFERRED 0x0400000000 /* Prefer as source address */
As listed in the interface list:
eri0: flags=2104841<UP,RUNNING,MULTICAST,DHCP,ROUTER,IPv6> mtu 1500 index 2
inet6 fe80::203:baff:fe4e:6cc8/10
eri0:1: flags=402100841<UP,RUNNING,MULTICAST,ROUTER,IPv6,PREFERRED> mtu 1500 index 2
inet6 2002:dce8:d28e::36/64
This is not portable to OSX, Linux, FreeBSD, or Windows though. Windows is let off easy though as it has completely useless, from an administrators perspective, UUID based adapter names (depending upon the Windows version).
For Linux this article details how the parameter preferred_lft, where lft is short for "lifetime", can be altered to weight the selection process by the kernel. This setting doesn't appear conveniently available in the results of SIOCGIFCONF or getifaddrs() though.
So I want to bind to eth0, eri0, or whatever available interface name. The choices are a bit stark:
Fail on adapter names resolving to multiple interfaces. I take this approach for handling multicast transports (OpenPGM) as the protocol MUST have one-only sending address.
Bind to everything. This is a cop out and would be unexpected to users.
Bind to the adapter with SO_BINDTODEVICE. This requires CAP_NET_RAW system capability on Linux which can be quite a cumbersome overhead for administrators.
Bind to the first IPv6 interface on the adapter. The ordering tends to be completely bogus.
Bind to the last interface. David Croft's article implies Linux does this, but is also a bit bogus.
Enumerate over every interface and create a new socket explicitly for each.
With option #6 I would expect you could usually be smarter and take the approach that if only a link-local scope address is available bind to that, otherwise bind to just the available global-link scope addresses.
When connecting to another host then RFC 3484 can be used, but as you can see all the choices are dependent upon matching the destination address:
Prefer same address. (i.e. destination is local machine)
Prefer appropriate scope. (i.e. smallest scope shared with the destination)
Avoid deprecated addresses.
Prefer home addresses. Prefer outgoing
interface. (i.e. prefer an address on the interface we're sending
out of)
Prefer matching label.
Prefer public addresses.
Use longest matching prefix.
In some circumstances we can use #7 here, but in the interface example above both global-scope interfaces have a 64-bit prefix length.
RFC 3484 has the following pertinent lines:
The IPv6 addressing architecture 5 allows multiple unicast
addresses to be assigned to interfaces. These addresses may have
different reachability scopes (link-local, site-local, or global).
These addresses may also be "preferred" or "deprecated" 6.
The link being to RFC 2462, similarly expanded:
preferred address - an address assigned to an interface whose use
by
upper layer protocols is unrestricted. Preferred addresses may
be used as the source (or destination) address of packets sent
from (or to) the interface.
But no methods to programmatically acquire this detail.
Props to Win32 API that exposes an ioctl SIO_ADDRESS_LIST_SORT that allows a developer to use not only RFC 3484 sorting but to take into consideration any system administrator overrides. Linux has /etc/gai.conf as used for RFC 3484 sorting in getaddrinfo() but no API for directly accessing the sorting. Solaris has the ipaddrsel command. OSX is following FreeBSD by adding ip6addrctl in 10.7.
edit: Some concerns with RFC 3484 sorting are listed and referred to in this additional IETF draft document:
https://datatracker.ietf.org/doc/html/draft-axu-addr-sel-01
Solaris, for example, creates new alias-interfaces for each new
address assigned to a physical interface. So if_index could also be
used to uniquely identify a source address specific routing table on
that platform. Other operating systems do not work the same way.
The author likes Solaris's approach of giving each additional IPv6 interface a new alias, so that eri0 would become the link-local scope address, and eri0:1 or eri0:2, etc, must be specified to use a global-scope address.
Clearly whilst a nice idea one couldn't expect to see other OS change for quite some time.
I'm not sure this is in the direction you're seeking, but...
Poking around in the iproute bundle's ip code (ip/ipaddress.c) under linux shows that the ip command digs up interface flags like primary and secondary from a struct ifaddrmsg, member ifa_flags. The ifaddmsg seems to be acquired through a struct nlmsghdr which is documented in man 7 netlink, and used via sendmsg and recvmsg interaction with the kernel, which overall sounds like a royal pain but it's at least programmatic. Whether primary and secondary would be enough to be useful is a separate question.

Specify source IP address for TCP socket when using Linux network device aliases

For some specific networking tests, I've created a VLAN device, eth1.900, and a couple of aliases, eth1.900:1 and eth1.900.2.
eth1.900 Link encap:Ethernet HWaddr 00:18:E7:17:2F:13
inet addr:1.0.1.120 Bcast:1.0.1.255 Mask:255.255.255.0
eth1.900:1 Link encap:Ethernet HWaddr 00:18:E7:17:2F:13
inet addr:1.0.1.200 Bcast:1.0.1.255 Mask:255.255.255.0
eth1.900:2 Link encap:Ethernet HWaddr 00:18:E7:17:2F:13
inet addr:1.0.1.201 Bcast:1.0.1.255 Mask:255.255.255.0
When connecting to a server, is there a way to specify which of these aliases will be used? I can ping using the -I <ip> address option to select which alias to use, but I can't see how to do it with a TCP socket in code without using raw sockets, since I would also like to run without extra socket privileges, i.e. not running as root, if possible.
Unfortunately, even with root, SO_BINDTODEVICE doesn't work because the alias device name is not recognized:
printf("Bind to %s\n", devname);
if (setsockopt(s, SOL_SOCKET, SO_BINDTODEVICE, (char*)devname, sizeof(devname)) != 0)
{
perror("SO_BINDTODEVICE");
return 1;
}
Output:
Bind to eth1.900:1
SO_BINDTODEVICE: No such device
Use getifaddrs() to enumerate all the interfaces and find the IP address for the interface you want to bind to. Then use bind() to bind to that IP address, before you call connect().
Since a packet can't be send out on an aliased interface anyway, it would make no sense to use SO_BINDTODEVICE on one. SO_BINDTODEVICE controls which device a packet is sent out from if routing cannot be used for this purpose (for example, if it's a raw Ethernet frame).
You don't show the definition of devname, but if it's a string pointer, e.g.:
char *devname = "eth1.900:1";
Then perhaps it's failing since you specify the argument size using sizeof devname, which would in this case be the same as sizeof (char *), i.e. typically 4 on a 32-bit system.
If setsockopt() expects to see the actual size of the argument, i.e. the length of the string, this could explain the issue since it's then perhaps just inspecting the first four characters and failing since the result is an invalid interface name.

Resources