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