2 * This library is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU Lesser General Public
4 * License as published by the Free Software Foundation; either
5 * version 2.1 of the License, or (at your option) any later version.
7 * This library is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10 * Lesser General Public License for more details.
12 * You should have received a copy of the GNU Lesser General Public
13 * License along with this library; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 * @brief Like recvfrom, but also stores the destination IP address. Useful on multihomed hosts.
22 * @copyright 2007 Alan DeKok <aland@deployingradius.com>
23 * @copyright 2002 Miquel van Smoorenburg
27 #include <freeradius-devel/udpfromto.h>
38 * More portability idiocy
39 * Mac OSX Lion doesn't define SOL_IP. But IPPROTO_IP works.
42 # define SOL_IP IPPROTO_IP
46 * glibc 2.4 and uClibc 0.9.29 introduce IPV6_RECVPKTINFO etc. and
47 * change IPV6_PKTINFO This is only supported in Linux kernel >=
50 * This is only an approximation because the kernel version that libc
51 * was compiled against could be older or newer than the one being
52 * run. But this should not be a problem -- we just keep using the
53 * old kernel interface.
56 # ifdef IPV6_RECVPKTINFO
57 # include <linux/version.h>
58 # if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,14)
59 # ifdef IPV6_2292PKTINFO
60 # undef IPV6_RECVPKTINFO
62 # define IPV6_RECVPKTINFO IPV6_2292PKTINFO
63 # define IPV6_PKTINFO IPV6_2292PKTINFO
66 /* Fall back to the legacy socket option if IPV6_RECVPKTINFO isn't defined */
67 # elif defined(IPV6_2292PKTINFO)
68 # define IPV6_RECVPKTINFO IPV6_2292PKTINFO
73 * Linux requires IPV6_RECVPKTINFO for the setsockopt() call,
74 * but sendmsg() and recvmsg() require IPV6_PKTINFO. <sigh>
76 * We want all *other* (i.e. sane) systems to use IPV6_PKTINFO
77 * for all three calls.
81 # ifdef IPV6_RECVPKTINFO
82 # define FR_IPV6_RECVPKTINFO IPV6_RECVPKTINFO
83 /* Fallback to to using recvfrom */
85 # undef IPV6_RECVPKTINFO
89 # define FR_IPV6_RECVPKTINFO IPV6_PKTINFO
93 int udpfromto_init(int s)
95 int proto, flag = 0, opt = 1;
96 struct sockaddr_storage si;
97 socklen_t si_len = sizeof(si);
102 * Clang analyzer doesn't see that getsockname initialises
103 * the memory passed to it.
105 #ifdef __clang_analyzer__
106 memset(&si, 0, sizeof(si));
109 if (getsockname(s, (struct sockaddr *) &si, &si_len) < 0) {
113 if (si.ss_family == AF_INET) {
114 #ifdef HAVE_IP_PKTINFO
121 # ifdef IP_RECVDSTADDR
124 * Set the IP_RECVDSTADDR option (BSD). Note:
125 * IP_RECVDSTADDR == IP_SENDSRCADDR
128 flag = IP_RECVDSTADDR;
135 } else if (si.ss_family == AF_INET6) {
138 * This should actually be standard IPv6
140 proto = IPPROTO_IPV6;
143 * Work around Linux-specific hackery.
145 flag = FR_IPV6_RECVPKTINFO;
147 # ifdef EPROTONOSUPPORT
148 errno = EPROTONOSUPPORT;
160 return setsockopt(s, proto, flag, &opt, sizeof(opt));
163 int recvfromto(int s, void *buf, size_t len, int flags,
164 struct sockaddr *from, socklen_t *fromlen,
165 struct sockaddr *to, socklen_t *tolen)
168 struct cmsghdr *cmsg;
172 struct sockaddr_storage si;
173 socklen_t si_len = sizeof(si);
175 #if !defined(IP_PKTINFO) && !defined(IP_RECVDSTADDR) && !defined(IPV6_PKTINFO)
177 * If the recvmsg() flags aren't defined, fall back to
184 * Catch the case where the caller passes invalid arguments.
186 if (!to || !tolen) return recvfrom(s, buf, len, flags, from, fromlen);
189 * Clang analyzer doesn't see that getsockname initialises
190 * the memory passed to it.
192 #ifdef __clang_analyzer__
193 memset(&si, 0, sizeof(si));
197 * recvmsg doesn't provide sin_port so we have to
198 * retrieve it using getsockname().
200 if (getsockname(s, (struct sockaddr *)&si, &si_len) < 0) {
205 * Initialize the 'to' address. It may be INADDR_ANY here,
206 * with a more specific address given by recvmsg(), below.
208 if (si.ss_family == AF_INET) {
209 #if !defined(IP_PKTINFO) && !defined(IP_RECVDSTADDR)
210 return recvfrom(s, buf, len, flags, from, fromlen);
212 struct sockaddr_in *dst = (struct sockaddr_in *) to;
213 struct sockaddr_in *src = (struct sockaddr_in *) &si;
215 if (*tolen < sizeof(*dst)) {
219 *tolen = sizeof(*dst);
225 else if (si.ss_family == AF_INET6) {
226 #if !defined(IPV6_PKTINFO)
227 return recvfrom(s, buf, len, flags, from, fromlen);
229 struct sockaddr_in6 *dst = (struct sockaddr_in6 *) to;
230 struct sockaddr_in6 *src = (struct sockaddr_in6 *) &si;
232 if (*tolen < sizeof(*dst)) {
236 *tolen = sizeof(*dst);
242 * Unknown address family.
249 /* Set up iov and msgh structures. */
250 memset(&cbuf, 0, sizeof(cbuf));
251 memset(&msgh, 0, sizeof(struct msghdr));
254 msgh.msg_control = cbuf;
255 msgh.msg_controllen = sizeof(cbuf);
256 msgh.msg_name = from;
257 msgh.msg_namelen = fromlen ? *fromlen : 0;
262 /* Receive one packet. */
263 if ((err = recvmsg(s, &msgh, flags)) < 0) {
267 if (fromlen) *fromlen = msgh.msg_namelen;
269 /* Process auxiliary received data in msgh */
270 for (cmsg = CMSG_FIRSTHDR(&msgh);
272 cmsg = CMSG_NXTHDR(&msgh,cmsg)) {
275 if ((cmsg->cmsg_level == SOL_IP) &&
276 (cmsg->cmsg_type == IP_PKTINFO)) {
277 struct in_pktinfo *i =
278 (struct in_pktinfo *) CMSG_DATA(cmsg);
279 ((struct sockaddr_in *)to)->sin_addr = i->ipi_addr;
280 *tolen = sizeof(struct sockaddr_in);
285 #ifdef IP_RECVDSTADDR
286 if ((cmsg->cmsg_level == IPPROTO_IP) &&
287 (cmsg->cmsg_type == IP_RECVDSTADDR)) {
288 struct in_addr *i = (struct in_addr *) CMSG_DATA(cmsg);
289 ((struct sockaddr_in *)to)->sin_addr = *i;
290 *tolen = sizeof(struct sockaddr_in);
296 if ((cmsg->cmsg_level == IPPROTO_IPV6) &&
297 (cmsg->cmsg_type == IPV6_PKTINFO)) {
298 struct in6_pktinfo *i =
299 (struct in6_pktinfo *) CMSG_DATA(cmsg);
300 ((struct sockaddr_in6 *)to)->sin6_addr = i->ipi6_addr;
301 *tolen = sizeof(struct sockaddr_in6);
310 int sendfromto(int s, void *buf, size_t len, int flags,
311 struct sockaddr *from, socklen_t fromlen,
312 struct sockaddr *to, socklen_t tolen)
315 struct cmsghdr *cmsg;
321 * FreeBSD is extra pedantic about the use of IP_SENDSRCADDR,
322 * and sendmsg will fail with EINVAL if IP_SENDSRCADDR is used
323 * with a socket which is bound to something other than
326 struct sockaddr bound;
327 socklen_t bound_len = sizeof(bound);
329 if (getsockname(s, &bound, &bound_len) < 0) {
333 switch (bound.sa_family) {
335 if (((struct sockaddr_in *) &bound)->sin_addr.s_addr != INADDR_ANY) {
341 if (!IN6_IS_ADDR_UNSPECIFIED(&((struct sockaddr_in6 *) &bound)->sin6_addr))) {
347 # if !defined(IP_PKTINFO) && !defined(IP_SENDSRCADDR) && !defined(IPV6_PKTINFO)
349 * If the sendmsg() flags aren't defined, fall back to
357 * Catch the case where the caller passes invalid arguments.
359 if (!from || (fromlen == 0) || (from->sa_family == AF_UNSPEC)) {
360 return sendto(s, buf, len, flags, to, tolen);
363 /* Set up control buffer iov and msgh structures. */
364 memset(&cbuf, 0, sizeof(cbuf));
365 memset(&msgh, 0, sizeof(msgh));
366 memset(&iov, 0, sizeof(iov));
372 msgh.msg_namelen = tolen;
374 if (from->sa_family == AF_INET) {
375 #if !defined(IP_PKTINFO) && !defined(IP_SENDSRCADDR)
376 return sendto(s, buf, len, flags, to, tolen);
378 struct sockaddr_in *s4 = (struct sockaddr_in *) from;
381 struct in_pktinfo *pkt;
383 msgh.msg_control = cbuf;
384 msgh.msg_controllen = CMSG_SPACE(sizeof(*pkt));
386 cmsg = CMSG_FIRSTHDR(&msgh);
387 cmsg->cmsg_level = SOL_IP;
388 cmsg->cmsg_type = IP_PKTINFO;
389 cmsg->cmsg_len = CMSG_LEN(sizeof(*pkt));
391 pkt = (struct in_pktinfo *) CMSG_DATA(cmsg);
392 memset(pkt, 0, sizeof(*pkt));
393 pkt->ipi_spec_dst = s4->sin_addr;
396 # ifdef IP_SENDSRCADDR
399 msgh.msg_control = cbuf;
400 msgh.msg_controllen = CMSG_SPACE(sizeof(*in));
402 cmsg = CMSG_FIRSTHDR(&msgh);
403 cmsg->cmsg_level = IPPROTO_IP;
404 cmsg->cmsg_type = IP_SENDSRCADDR;
405 cmsg->cmsg_len = CMSG_LEN(sizeof(*in));
407 in = (struct in_addr *) CMSG_DATA(cmsg);
410 #endif /* IP_PKTINFO or IP_SENDSRCADDR */
414 else if (from->sa_family == AF_INET6) {
415 # if !defined(IPV6_PKTINFO)
416 return sendto(s, buf, len, flags, to, tolen);
418 struct sockaddr_in6 *s6 = (struct sockaddr_in6 *) from;
420 struct in6_pktinfo *pkt;
422 msgh.msg_control = cbuf;
423 msgh.msg_controllen = CMSG_SPACE(sizeof(*pkt));
425 cmsg = CMSG_FIRSTHDR(&msgh);
426 cmsg->cmsg_level = IPPROTO_IPV6;
427 cmsg->cmsg_type = IPV6_PKTINFO;
428 cmsg->cmsg_len = CMSG_LEN(sizeof(*pkt));
430 pkt = (struct in6_pktinfo *) CMSG_DATA(cmsg);
431 memset(pkt, 0, sizeof(*pkt));
432 pkt->ipi6_addr = s6->sin6_addr;
433 # endif /* IPV6_PKTINFO */
438 * Unknown address family.
445 return sendmsg(s, &msgh, flags);
451 * Small test program to test recvfromto/sendfromto
453 * use a virtual IP address as first argument to test
455 * reply packet should originate from virtual IP and not
456 * from the default interface the alias is bound to
458 # include <sys/wait.h>
460 # define DEF_PORT 20000 /* default port to listen on */
461 # define DESTIP "127.0.0.1" /* send packet to localhost per default */
462 # define TESTSTRING "foo" /* what to send */
463 # define TESTLEN 4 /* 4 bytes */
465 int main(int argc, char **argv)
467 struct sockaddr_in from, to, in;
469 char *destip = DESTIP;
470 uint16_t port = DEF_PORT;
471 int n, server_socket, client_socket, fl, tl, pid;
473 if (argc > 1) destip = argv[1];
474 if (argc > 2) port = atoi(argv[2]);
476 in.sin_family = AF_INET;
477 in.sin_addr.s_addr = INADDR_ANY;
478 in.sin_port = htons(port);
479 fl = tl = sizeof(struct sockaddr_in);
480 memset(&from, 0, sizeof(from));
481 memset(&to, 0, sizeof(to));
483 switch(pid = fork()) {
494 server_socket = socket(PF_INET, SOCK_DGRAM, 0);
495 if (udpfromto_init(server_socket) != 0) {
496 perror("udpfromto_init\n");
497 waitpid(pid, NULL, WNOHANG);
501 if (bind(server_socket, (struct sockaddr *)&in, sizeof(in)) < 0) {
502 perror("server: bind");
503 waitpid(pid, NULL, WNOHANG);
507 printf("server: waiting for packets on INADDR_ANY:%d\n", port);
508 if ((n = recvfromto(server_socket, buf, sizeof(buf), 0,
509 (struct sockaddr *)&from, &fl,
510 (struct sockaddr *)&to, &tl)) < 0) {
511 perror("server: recvfromto");
512 waitpid(pid, NULL, WNOHANG);
516 printf("server: received a packet of %d bytes [%s] ", n, buf);
517 printf("(src ip:port %s:%d ",
518 inet_ntoa(from.sin_addr), ntohs(from.sin_port));
519 printf(" dst ip:port %s:%d)\n",
520 inet_ntoa(to.sin_addr), ntohs(to.sin_port));
522 printf("server: replying from address packet was received on to source address\n");
524 if ((n = sendfromto(server_socket, buf, n, 0,
525 (struct sockaddr *)&to, tl,
526 (struct sockaddr *)&from, fl)) < 0) {
527 perror("server: sendfromto");
530 waitpid(pid, NULL, 0);
534 close(server_socket);
535 client_socket = socket(PF_INET, SOCK_DGRAM, 0);
536 if (udpfromto_init(client_socket) != 0) {
537 perror("udpfromto_init");
540 /* bind client on different port */
541 in.sin_port = htons(port+1);
542 if (bind(client_socket, (struct sockaddr *)&in, sizeof(in)) < 0) {
543 perror("client: bind");
547 in.sin_port = htons(port);
548 in.sin_addr.s_addr = inet_addr(destip);
550 printf("client: sending packet to %s:%d\n", destip, port);
551 if (sendto(client_socket, TESTSTRING, TESTLEN, 0,
552 (struct sockaddr *)&in, sizeof(in)) < 0) {
553 perror("client: sendto");
557 printf("client: waiting for reply from server on INADDR_ANY:%d\n", port+1);
559 if ((n = recvfromto(client_socket, buf, sizeof(buf), 0,
560 (struct sockaddr *)&from, &fl,
561 (struct sockaddr *)&to, &tl)) < 0) {
562 perror("client: recvfromto");
566 printf("client: received a packet of %d bytes [%s] ", n, buf);
567 printf("(src ip:port %s:%d",
568 inet_ntoa(from.sin_addr), ntohs(from.sin_port));
569 printf(" dst ip:port %s:%d)\n",
570 inet_ntoa(to.sin_addr), ntohs(to.sin_port));
576 #endif /* WITH_UDPFROMTO */