blob: 025d3d6c8c8d6a5a39bc7b41d8697d8ad63424dd [file] [log] [blame]
#if defined(HAVE_CONFIG_H)
#include <config.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <bits/sockaddr.h>
#include <asm/types.h>
#include <linux/rtnetlink.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <net/if.h>
#include <netdb.h>
#include <sys/types.h>
#include <unistd.h>
char *
find_egress_interface(struct sockaddr *source, struct sockaddr *dest) {
struct sockaddr_nl me,them;
struct sockaddr_in *in4;
struct sockaddr_in6 *in6;
int interface_index = -1;
char interface_name[IF_NAMESIZE];
int s;
/* so, in our msghdr we will put the address and an iovec pointing
to our request. that request will consist of a netlink message
header, a routing message header and some routing attributes.
the chalice with the palace holds the pellet with the poison, the
vessel with the pestle holds the brew which is true. i guess it
would have been "too easy" to just have a plain old ioctl that
one calls to get the route for a given destination and
source... raj 2008-02-11 */
struct msghdr msg;
struct iovec iov;
struct {
struct nlmsghdr nl;
struct rtmsg rt;
char buf[1024];
} request;
char reply[1024];
struct nlmsghdr *nlp;
struct rtmsg *rtp;
struct rtattr *rtap;
int nll,rtl;
int ret;
/* create and bind the netlink socket */
s = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
memset(&me, 0, sizeof(me));
me.nl_family = AF_NETLINK;
me.nl_pid = getpid();
/* probably should be checking a return value... */
bind(s, (struct sockaddr *)&me, sizeof(me));
/* create the message we are going to send */
memset(&request, 0, sizeof(request));
request.nl.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
request.nl.nlmsg_flags = NLM_F_REQUEST;
request.nl.nlmsg_type = RTM_GETROUTE;
/* time to add the destination attribute to the request */
if (dest) {
in4 = (struct sockaddr_in *)dest;
in6 = (struct sockaddr_in6 *)dest;
request.rt.rtm_family = in4->sin_family;
rtap = (struct rtattr *)request.buf;
rtap->rta_type = RTA_DST;
if (AF_INET == in4->sin_family) {
/* while a sin_addr is a multiple of 4 bytes in length, we
should still use RTA_SPACE rather than RTA_LENGTH. kudos to
Thomas Graf for pointing that out */
rtap->rta_len = RTA_SPACE(sizeof(in4->sin_addr));
memcpy(RTA_DATA(rtap), &(in4->sin_addr), sizeof(in4->sin_addr));
}
else if (AF_INET6 == in6->sin6_family) {
rtap->rta_len = RTA_SPACE(sizeof(in6->sin6_addr));
memcpy(RTA_DATA(rtap), &(in6->sin6_addr), sizeof(in6->sin6_addr));
}
else {
return strdup("UnknownAddressFamily");
}
}
else {
/* there must always be a destination */
printf("No destination specified.\n");
return strdup("NoDestination");
}
/* add the length of our request to our overall message length. it
should already be suitably padded by the previous RTA_SPACE */
request.nl.nlmsg_len += rtap->rta_len;
/* now the src */
if (source) {
/* the source goes after the dest, so we can just increment by the
current value of rta_len */
in4 = (struct sockaddr_in *)source;
in6 = (struct sockaddr_in6 *)source;
/* pointer math is fun. silly me initially tried to to rtap +=
rtap->rta_len but since rtap isn't pointing at bytes... again
kudos to Thomas Graf for finding that mistake */
rtap = (struct rtattr *)((char *)rtap + (rtap->rta_len));
rtap->rta_type = RTA_SRC;
if (AF_INET == in4->sin_family) {
rtap->rta_len = RTA_SPACE(sizeof(in4->sin_addr));
memcpy(RTA_DATA(rtap), &(in4->sin_addr), sizeof(in4->sin_addr));
}
else if (AF_INET6 == in6->sin6_family) {
rtap->rta_len = RTA_SPACE(sizeof(in6->sin6_addr));
memcpy(RTA_DATA(rtap), &(in6->sin6_addr), sizeof(in6->sin6_addr));
}
else {
return strdup("UnknownAddressFamily");
}
/* add the length of the just added attribute to the overall
message length. it should already be suitably padded by the
previous RTA_SPACE */
request.nl.nlmsg_len += rtap->rta_len;
}
/* address it */
memset(&them, 0, sizeof(them));
them.nl_family = AF_NETLINK;
memset(&msg, 0, sizeof(msg));
msg.msg_name = (void *)&them;
msg.msg_namelen = sizeof(them);
iov.iov_base = (void *) &request.nl;
iov.iov_len = request.nl.nlmsg_len;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
/* send it */
ret = sendmsg(s, &msg, 0);
if (ret < 0) {
return strdup("SendmsgFailure");
}
memset(reply,0,sizeof(reply));
ret = recv(s, reply, sizeof(reply), 0);
if (ret < 0) {
return strdup("RecvmsgFailure");
}
nll = ret;
/* Since we are looking for a single route, one has to wonder if
this is really necessary, but since all the examples I could find
seemed to be doing it, I'll simply follow along. raj
2008-02-11 */
for (nlp = (struct nlmsghdr *)reply;
NLMSG_OK(nlp,nll);
nlp = NLMSG_NEXT(nlp, nll)) {
/* where oh where might the route header be? */
rtp = (struct rtmsg *) NLMSG_DATA(nlp);
#if 0
/* we will ass-u-me we are only interested in results for the main
routing table */
if (RT_TABLE_MAIN != rtp->rtm_table) {
printf("skipping table %d\n",rtp->rtm_table);
continue;
}
#endif
for (rtap = (struct rtattr *) RTM_RTA(rtp), rtl = RTM_PAYLOAD(nlp);
RTA_OK(rtap, rtl);
rtap = RTA_NEXT(rtap,rtl)) {
if (RTA_OIF == rtap->rta_type) {
if (-1 == interface_index){
interface_index = *((int *) RTA_DATA(rtap));
}
else {
printf("Found a second interface index, which was not expected!\n");
return strdup("MultipleInterfacesFound");
}
}
}
}
if (interface_index == -1) {
/* we didn't find it */
return strdup("InterfaceNotFound");
}
else {
if (NULL == if_indextoname(interface_index,interface_name)) {
return strdup("IfIndexToNameFailure");
}
else {
return strdup(interface_name);
}
}
}
#if defined(NETPERF_STANDALONE_DEBUG)
int
main(int argc, char *argv[]) {
struct sockaddr_storage source,destination;
struct sockaddr_in *sin;
int ret;
char *egress_if;
if ((argc < 2) || (argc > 3)) {
fprintf(stderr,"%s <destIP> [srcip]\n",argv[0]);
return -1;
}
sin = (struct sockaddr_in *)&destination;
sin->sin_family = AF_INET;
sin->sin_addr.s_addr = inet_addr(argv[1]);
printf("destination address is %s\n",inet_ntoa(sin->sin_addr));
sin = NULL;
if (argc == 3) {
sin = (struct sockaddr_in *)&source;
sin->sin_family = AF_INET;
sin->sin_addr.s_addr = inet_addr(argv[2]);
}
egress_if = find_egress_interface((struct sockaddr *)sin,(struct sockaddr *)&destination);
printf("egress interface %p %s\n",egress_if,egress_if);
}
#endif