1 #if defined(HAVE_CONFIG_H)
2 #include <config.h>
3 #endif
4 
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <string.h>
8 #include <bits/sockaddr.h>
9 #include <asm/types.h>
10 #include <linux/rtnetlink.h>
11 #include <netinet/in.h>
12 #include <sys/socket.h>
13 #include <net/if.h>
14 #include <netdb.h>
15 
16 #include <sys/types.h>
17 #include <unistd.h>
18 
19 char *
find_egress_interface(struct sockaddr * source,struct sockaddr * dest)20 find_egress_interface(struct sockaddr *source, struct sockaddr *dest) {
21 
22   struct sockaddr_nl me,them;
23   struct sockaddr_in  *in4;
24   struct sockaddr_in6 *in6;
25 
26   int interface_index = -1;
27   char interface_name[IF_NAMESIZE];
28 
29   int s;
30 
31   /* so, in our msghdr we will put the address and an iovec pointing
32      to our request.  that request will consist of a netlink message
33      header, a routing message header and some routing attributes.
34      the chalice with the palace holds the pellet with the poison, the
35      vessel with the pestle holds the brew which is true.  i guess it
36      would have been "too easy" to just have a plain old ioctl that
37      one calls to get the route for a given destination and
38      source... raj 2008-02-11 */
39 
40   struct msghdr msg;
41 
42   struct iovec iov;
43 
44   struct {
45     struct nlmsghdr nl;
46     struct rtmsg    rt;
47     char   buf[1024];
48   } request;
49 
50   char reply[1024];
51 
52   struct nlmsghdr *nlp;
53   struct rtmsg *rtp;
54   struct rtattr *rtap;
55   int nll,rtl;
56 
57   int ret;
58 
59   /* create and bind the netlink socket */
60   s = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
61 
62   memset(&me, 0, sizeof(me));
63   me.nl_family = AF_NETLINK;
64   me.nl_pid = getpid();
65 
66   /* probably should be checking a return value... */
67   bind(s, (struct sockaddr *)&me, sizeof(me));
68 
69   /* create the message we are going to send */
70 
71   memset(&request, 0, sizeof(request));
72   request.nl.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
73   request.nl.nlmsg_flags = NLM_F_REQUEST;
74   request.nl.nlmsg_type = RTM_GETROUTE;
75 
76   /* time to add the destination attribute to the request */
77   if (dest) {
78     in4 = (struct sockaddr_in *)dest;
79     in6 = (struct sockaddr_in6 *)dest;
80     request.rt.rtm_family = in4->sin_family;
81     rtap = (struct rtattr *)request.buf;
82     rtap->rta_type = RTA_DST;
83     if (AF_INET == in4->sin_family) {
84       /* while a sin_addr is a multiple of 4 bytes in length, we
85 	 should still use RTA_SPACE rather than RTA_LENGTH. kudos to
86 	 Thomas Graf for pointing that out */
87       rtap->rta_len = RTA_SPACE(sizeof(in4->sin_addr));
88       memcpy(RTA_DATA(rtap), &(in4->sin_addr), sizeof(in4->sin_addr));
89     }
90     else if (AF_INET6 == in6->sin6_family) {
91       rtap->rta_len = RTA_SPACE(sizeof(in6->sin6_addr));
92       memcpy(RTA_DATA(rtap), &(in6->sin6_addr), sizeof(in6->sin6_addr));
93     }
94     else {
95       close(s);
96       return strdup("UnknownAddressFamily");
97     }
98   }
99   else {
100     /* there must always be a destination */
101     printf("No destination specified.\n");
102     close(s);
103     return strdup("NoDestination");
104   }
105 
106   /* add the length of our request to our overall message length. it
107      should already be suitably padded by the previous RTA_SPACE */
108   request.nl.nlmsg_len += rtap->rta_len;
109 
110   /* now the src */
111   if (source) {
112     /* the source goes after the dest, so we can just increment by the
113        current value of rta_len */
114     in4 = (struct sockaddr_in *)source;
115     in6 = (struct sockaddr_in6 *)source;
116 
117     /* pointer math is fun.  silly me initially tried to to rtap +=
118        rtap->rta_len but since rtap isn't pointing at bytes... again
119        kudos to Thomas Graf for finding that mistake */
120     rtap = (struct rtattr *)((char *)rtap + (rtap->rta_len));
121     rtap->rta_type = RTA_SRC;
122     if (AF_INET == in4->sin_family) {
123       rtap->rta_len = RTA_SPACE(sizeof(in4->sin_addr));
124       memcpy(RTA_DATA(rtap), &(in4->sin_addr), sizeof(in4->sin_addr));
125     }
126     else if (AF_INET6 == in6->sin6_family) {
127       rtap->rta_len = RTA_SPACE(sizeof(in6->sin6_addr));
128       memcpy(RTA_DATA(rtap), &(in6->sin6_addr), sizeof(in6->sin6_addr));
129     }
130     else {
131       close(s);
132       return strdup("UnknownAddressFamily");
133     }
134 
135 
136     /* add the length of the just added attribute to the overall
137      message length. it should already be suitably padded by the
138      previous RTA_SPACE */
139     request.nl.nlmsg_len += rtap->rta_len;
140   }
141 
142   /* address it */
143   memset(&them, 0, sizeof(them));
144   them.nl_family = AF_NETLINK;
145 
146   memset(&msg, 0, sizeof(msg));
147   msg.msg_name = (void *)&them;
148   msg.msg_namelen = sizeof(them);
149 
150   iov.iov_base = (void *) &request.nl;
151   iov.iov_len  = request.nl.nlmsg_len;
152 
153   msg.msg_iov = &iov;
154   msg.msg_iovlen = 1;
155 
156   /* send it */
157   ret = sendmsg(s, &msg, 0);
158 
159   if (ret < 0) {
160     close(s);
161     return strdup("SendmsgFailure");
162   }
163 
164   memset(reply,0,sizeof(reply));
165   ret = recv(s, reply, sizeof(reply), 0);
166 
167   if (ret < 0) {
168     close(s);
169     return strdup("RecvmsgFailure");
170   }
171 
172   nll = ret;
173 
174   /* Since we are looking for a single route, one has to wonder if
175      this is really necessary, but since all the examples I could find
176      seemed to be doing it, I'll simply follow along. raj
177      2008-02-11 */
178 
179   for (nlp = (struct nlmsghdr *)reply;
180        NLMSG_OK(nlp,nll);
181        nlp = NLMSG_NEXT(nlp, nll)) {
182     /* where oh where might the route header be? */
183     rtp = (struct rtmsg *) NLMSG_DATA(nlp);
184 
185 #if 0
186     /* we will ass-u-me we are only interested in results for the main
187        routing table */
188     if (RT_TABLE_MAIN != rtp->rtm_table) {
189       printf("skipping table %d\n",rtp->rtm_table);
190       continue;
191     }
192 #endif
193 
194     for (rtap = (struct rtattr *) RTM_RTA(rtp), rtl = RTM_PAYLOAD(nlp);
195 	 RTA_OK(rtap, rtl);
196 	 rtap = RTA_NEXT(rtap,rtl)) {
197       if (RTA_OIF == rtap->rta_type) {
198 	if (-1 == interface_index){
199 	  interface_index = *((int *) RTA_DATA(rtap));
200 	}
201 	else {
202 	  close(s);
203 	  return strdup("MultipleInterfacesFound");
204 	}
205       }
206     }
207   }
208 
209   if (interface_index == -1) {
210     /* we didn't find it */
211     return strdup("InterfaceNotFound");
212   }
213   else {
214     if (NULL == if_indextoname(interface_index,interface_name)) {
215       close(s);
216       return strdup("IfIndexToNameFailure");
217     }
218     else {
219       close(s);
220       return strdup(interface_name);
221     }
222   }
223 }
224 #if defined(NETPERF_STANDALONE_DEBUG)
225 int
main(int argc,char * argv[])226 main(int argc, char *argv[]) {
227 
228   struct sockaddr_storage source,destination;
229   struct sockaddr_in *sin;
230   int ret;
231   char *egress_if;
232 
233   if ((argc < 2) || (argc > 3)) {
234     fprintf(stderr,"%s <destIP> [srcip]\n",argv[0]);
235     return -1;
236   }
237 
238   sin = (struct sockaddr_in *)&destination;
239   sin->sin_family = AF_INET;
240   sin->sin_addr.s_addr = inet_addr(argv[1]);
241 
242   printf("destination address is %s\n",inet_ntoa(sin->sin_addr));
243 
244   sin = NULL;
245 
246   if (argc == 3) {
247     sin = (struct sockaddr_in *)&source;
248     sin->sin_family = AF_INET;
249     sin->sin_addr.s_addr = inet_addr(argv[2]);
250   }
251 
252   egress_if = find_egress_interface((struct sockaddr *)sin,(struct sockaddr *)&destination);
253 
254   printf("egress interface %p %s\n",egress_if,egress_if);
255 
256 }
257 #endif
258