In article <[EMAIL PROTECTED]>,
Alan DeKok <[EMAIL PROTECTED]> wrote:
>Chris Parker <[EMAIL PROTECTED]> wrote:
>> Are you running the latest CVS version?  This was fixed since 0.4, so
>> that replies are sent back out with the source ip being the ip on which
>> it was received.
>
>  Not when using 'bind_address = *'.
>
>  There is no way to tell *which* IP the request was received on, and
>there is no way to tell which IP to send the response from.

Yes there is, but it is relatively unknown. So I dove into it
tonight just for fun and I ended up with a recvfromto() function
that does exactly what you expect it to do - albeit only on Linux
and BSD (and it's only tested on Linux).

If the extended functionality to get the destination address is
not available to->sin_addr will simply be 0.0.0.0 or INADDR_ANY

This should be put in src/lib/recvfromto.c I guess.


/*
 * recvfromto   Like recvfrom, but also stores the destination
 *              IP address. Useful on multihomed hosts.
 *
 *              Should work on Linux and BSD.
 *
 *              Copyright (C) 2002 Miquel van Smoorenburg.
 *
 *              This program is free software; you can redistribute it and/or
 *              modify it under the terms of the GNU Lesser General Public
 *              License as published by the Free Software Foundation; either
 *              version 2 of the License, or (at your option) any later version.
 */

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/uio.h>
#include <netinet/in.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>

/* Remove this when autoconf can detect this. */
#if defined(IP_PKTINFO) && !defined(HAVE_IP_PKTINFO)
#  define HAVE_IP_PKTINFO 1
#elif defined(IP_RECVDSTADDR) && !defined(HAVE_IP_RECVDSTADDR)
#  define HAVE_IP_RECVDSTADDR
#endif

int recvfromto(int s, void *buf, size_t len, int flags,
        struct sockaddr *from, socklen_t *fromlen,
        struct sockaddr *to, socklen_t *tolen)
{
        struct msghdr msgh;
        struct cmsghdr *cmsg;
        struct iovec iov;
        char cbuf[1024];
        int opt, err;

        /*
         *      If from or to are set, they must be big enough
         *      to store a struct sockaddr_in.
         */
        if ((from && (!fromlen || *fromlen < sizeof(struct sockaddr_in))) ||
            (to   && (!tolen   || *tolen   < sizeof(struct sockaddr_in)))) {
                errno = EINVAL;
                return -1;
        }
        if (tolen) *tolen = 0;

#ifdef HAVE_IP_PKTINFO
        /*
         *      IP_PKTINFO doesn't provide sin_port so we have to
         *      retrieve it using getsockname().
         */
        if (to) {
                struct sockaddr_in si;
                socklen_t l = sizeof(si);

                ((struct sockaddr_in *)to)->sin_family = AF_INET;
                ((struct sockaddr_in *)to)->sin_port = 0;
                l = sizeof(si);
                if (getsockname(s, (struct sockaddr *)&si, &l) == 0) {
                        ((struct sockaddr_in *)to)->sin_port = si.sin_port;
                        ((struct sockaddr_in *)to)->sin_addr = si.sin_addr;
                }
                *tolen = sizeof(struct sockaddr_in);
        }
#endif

        /* Set MSG_DONTWAIT if O_NONBLOCK was set. */
        if (fcntl(s, F_GETFL) & O_NONBLOCK) flags |= MSG_DONTWAIT;

        /* Set up iov and msgh structures. */
        iov.iov_base = buf;
        iov.iov_len  = len;
        msgh.msg_control = cbuf;
        msgh.msg_controllen = sizeof(cbuf);
        msgh.msg_name = from;
        msgh.msg_namelen = fromlen ? *fromlen : 0;
        msgh.msg_iov  = &iov;
        msgh.msg_iovlen = 1;

#ifdef HAVE_IP_PKTINFO
        /* Set the IP_PKTINFO option (Linux). */
        opt = 1;
        setsockopt(s, SOL_IP, IP_PKTINFO, &opt, sizeof(opt));
#endif

#ifdef HAVE_IP_RECVDSTADDR
        /* Set the IP_RECVDSTADDR option (BSD). */
        opt = 1;
        setsockopt(s, IPPROTO_IP, IP_RECVDSTADDR, &opt, sizeof(opt));
#endif

        /* Receive one packet. */
        err = recvmsg(s, &msgh, flags);
        if (fromlen) *fromlen = msgh.msg_namelen;

        /* Process auxiliary received data in msgh */
        for (cmsg = CMSG_FIRSTHDR(&msgh);
             cmsg != NULL && cmsg->cmsg_len >= sizeof(*cmsg);
             cmsg = CMSG_NXTHDR(&msgh,cmsg)) {
#ifdef HAVE_IP_PKTINFO
                if (cmsg->cmsg_level == SOL_IP
                    && cmsg->cmsg_type == IP_PKTINFO) {
                        struct in_pktinfo *i =
                                (struct in_pktinfo *)CMSG_DATA(cmsg);
                        if (to) {
                                ((struct sockaddr_in *)to)->sin_addr =
                                        i->ipi_addr;
                                *tolen = sizeof(struct sockaddr_in);
                        }
                        break;
                }
#endif

#ifdef HAVE_IP_RECVDSTADDR
                if (cmsg->cmsg_level == IPPROTO_IP
                    && cmsg->cmsg_type == IP_RECVDSTADDR) {
                        struct in_addr *i = (struct in_addr *)CMSG_DATA(cmsg);
                        if (to) {
                                ((struct sockaddr_in *)to)->sin_addr = *i;
                                *tolen = sizeof(struct sockaddr_in);
                        }
                }
#endif
        }

        return err;
}

#ifdef STANDALONE
#include <stdio.h>
#include <stdlib.h>
#include <arpa/inet.h>

/*
 *      Small test program to test recvfromto
 */
int main(int argc, char **argv)
{
        struct sockaddr_in from, to, in;
        char buf[1024];
        int port = 20000;
        int n, s, fl, tl;

        if (argc > 1) port = atoi(argv[1]);

        s = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
        in.sin_family = AF_INET;
        in.sin_port = htons(port);
        in.sin_addr.s_addr = INADDR_ANY;
        bind(s, &in, sizeof(in));

        while (1) {
                fl = tl = sizeof(struct sockaddr_in);
                memset(&from, 0, sizeof(from));
                memset(&to, 0, sizeof(to));
                if ((n = recvfromto(s, buf, sizeof(buf), 0,
                    (struct sockaddr *)&from, &fl,
                    (struct sockaddr *)&to, &tl)) < 0) {
                        perror("recvfromto");
                        break;
                }
                printf("Received a packet of %d bytes\n", n);
                printf("  src ip:port %s:%d\n",
                        inet_ntoa(from.sin_addr), ntohs(from.sin_port));
                printf("  dst ip:port %s:%d\n",
                        inet_ntoa(to.sin_addr), ntohs(to.sin_port));
        }

        return 0;
}

#endif /* STANDALONE */



Mike.


- 
List info/subscribe/unsubscribe? See http://www.freeradius.org/list/users.html

Reply via email to