Menu ▾ ▴

#213 cmsg clean up - mutlicast ifrname lookup not working

1.0
accepted
None
2023-01-19
2022-12-28
No

the getsockhame to lookup the rx interface isn't working for multicast. Clean up cmsg code. Ancillary doc here.

Discussion

  • Robert McMahon

    Robert McMahon - 2023-01-19

    Some example code is here:

    /*
    
     *   This library 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.1 of the License, or (at your option) any later version.
     *
     *   This library is distributed in the hope that it will be useful,
     *   but WITHOUT ANY WARRANTY; without even the implied warranty of
     *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
     *   Lesser General Public License for more details.
     *
     *   You should have received a copy of the GNU Lesser General Public
     *   License along with this library; if not, write to the Free Software
     *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
     *
     *  Helper functions to get/set addresses of UDP packets
     *  based on recvfromto by Miquel van Smoorenburg
     *
     * 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.
     *
     * sendfromto   added 18/08/2003, Jan Berkel <jan@sitadelle.com>
     *      Works on Linux and FreeBSD (5.x)
     *
     * Version: $Id: udpfromto.c,v 1.4.4.1 2006/03/15 15:37:58 nbk Exp $
     */
    
    #include "autoconf.h"
    
    #ifdef WITH_UDPFROMTO
    static const char rcsid[] = "$Id: udpfromto.c,v 1.4.4.1 2006/03/15 15:37:58 nbk Exp $";
    
    #include <sys/types.h>
    
    #ifdef HAVE_SYS_UIO_H
    #include <sys/uio.h>
    #endif
    
    #include <netinet/in.h>
    #include <errno.h>
    #include <unistd.h>
    #include <fcntl.h>
    #include <string.h>
    
    #include "udpfromto.h"
    
    int udpfromto_init(int s)
    {
        int err = -1, opt = 1;
        errno = ENOSYS;
    #ifdef HAVE_IP_PKTINFO
        /* Set the IP_PKTINFO option (Linux). */
        err = setsockopt(s, SOL_IP, IP_PKTINFO, &opt, sizeof(opt));
    #endif
    
    #ifdef HAVE_IP_RECVDSTADDR
        /*
    
         * Set the IP_RECVDSTADDR option (BSD).
         * Note: IP_RECVDSTADDR == IP_SENDSRCADDR
         */
        err = setsockopt(s, IPPROTO_IP, IP_RECVDSTADDR, &opt, sizeof(opt));
    #endif
        return err;
    }
    
    int recvfromto(int s, void *buf, size_t len, int flags,
        struct sockaddr *from, socklen_t *fromlen,
        struct sockaddr *to, socklen_t *tolen)
    {
    #if defined(HAVE_IP_PKTINFO) || defined(HAVE_IP_RECVDSTADDR)
        struct msghdr msgh;
        struct cmsghdr *cmsg;
        struct iovec iov;
        char cbuf[256];
        int 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;
        }
    
        /*
    
         *  IP_PKTINFO / IP_RECVDSTADDR don'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;
            }
            if (tolen) *tolen = sizeof(struct sockaddr_in);
        }
    
        /* Set up iov and msgh structures. */
        memset(&msgh, 0, sizeof(struct msghdr));
        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;
        msgh.msg_flags = 0;
    
        /* Receive one packet. */
        if ((err = recvmsg(s, &msgh, flags)) < 0) {
            return err;
        }
        if (fromlen) *fromlen = msgh.msg_namelen;
    
        /* Process auxiliary received data in msgh */
        for (cmsg = CMSG_FIRSTHDR(&msgh);
             cmsg != NULL;
             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;
                    if (tolen) *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;
                    if (tolen) *tolen = sizeof(struct sockaddr_in);
                }
                break;
            }
    # endif
        }
        return err;
    #else
        /* fallback: call recvfrom */
        return recvfrom(s, buf, len, flags, from, fromlen);
    #endif /* defined(HAVE_IP_PKTINFO) || defined(HAVE_IP_RECVDSTADDR) */
    }
    
    int sendfromto(int s, void *buf, size_t len, int flags,
                  struct sockaddr *from, socklen_t fromlen,
                  struct sockaddr *to, socklen_t tolen)
    {
    #if defined(HAVE_IP_PKTINFO) || defined(HAVE_IP_SENDSRCADDR)
        struct msghdr msgh;
        struct cmsghdr *cmsg;
        struct iovec iov;
    # ifdef HAVE_IP_PKTINFO
        char cmsgbuf[CMSG_SPACE(sizeof(struct in_pktinfo))];
        struct in_pktinfo pktinfo, *pktinfo_ptr;
        memset(&pktinfo, 0, sizeof(struct in_pktinfo));
    # endif
    
    # ifdef HAVE_IP_SENDSRCADDR
        char cmsgbuf[CMSG_SPACE(sizeof(struct in_addr))];
    # endif
    
        /* Set up iov and msgh structures. */
        memset(&msgh, 0, sizeof(struct msghdr));
        iov.iov_base = buf;
        iov.iov_len = len;
        msgh.msg_iov = &iov;
        msgh.msg_iovlen = 1;
        msgh.msg_control = cmsgbuf;
        msgh.msg_controllen = sizeof(cmsgbuf);
        msgh.msg_name = to;
        msgh.msg_namelen = tolen;
    
        cmsg = CMSG_FIRSTHDR(&msgh);
    
    # ifdef HAVE_IP_PKTINFO
        cmsg->cmsg_level = SOL_IP;
        cmsg->cmsg_type = IP_PKTINFO;
        cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
        pktinfo.ipi_spec_dst = ((struct sockaddr_in *)from)->sin_addr;
        pktinfo_ptr = (struct in_pktinfo *)CMSG_DATA(cmsg);
        memcpy(pktinfo_ptr, &pktinfo, sizeof(struct in_pktinfo));
    # endif
    # ifdef HAVE_IP_SENDSRCADDR
        cmsg->cmsg_level = IPPROTO_IP;
        cmsg->cmsg_type = IP_SENDSRCADDR;
        cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_addr));
        memcpy((struct in_addr *)CMSG_DATA(cmsg),
               &((struct sockaddr_in *)from)->sin_addr, sizeof(struct in_addr));
    # endif
    
        return sendmsg(s, &msgh, flags);
    #else
        /* fallback: call sendto() */
        return sendto(s, buf, len, flags, to, tolen);
    #endif  /* defined(HAVE_IP_PKTINFO) || defined (HAVE_IP_SENDSRCADDR) */
    }
    
    
    #ifdef TESTING
    /*
    
     *  Small test program to test recvfromto/sendfromto
     *
     *  use a virtual IP address as first argument to test
     *
     *  reply packet should originate from virtual IP and not
     *  from the default interface the alias is bound to
     */
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <arpa/inet.h>
    #include <sys/types.h>
    #include <sys/wait.h>
    
    #define DEF_PORT 20000      /* default port to listen on */
    #define DESTIP "127.0.0.1"  /* send packet to localhost per default */
    #define TESTSTRING "foo"    /* what to send */
    #define TESTLEN 4           /* 4 bytes */
    
    int main(int argc, char **argv)
    {
        struct sockaddr_in from, to, in;
        char buf[TESTLEN];
        char *destip = DESTIP;
        int port = DEF_PORT;
        int n, server_socket, client_socket, fl, tl, pid;
    
        if (argc > 1) destip = argv[1];
        if (argc > 2) port = atoi(argv[2]);
    
        in.sin_family = AF_INET;
        in.sin_addr.s_addr = INADDR_ANY;
        in.sin_port = htons(port);
        fl = tl = sizeof(struct sockaddr_in);
        memset(&from, 0, sizeof(from));
        memset(&to,   0, sizeof(to));
    
        switch(pid = fork()) {
            case -1:
                perror("fork");
                return 0;
            case 0:
                /* child */
                usleep(100000);
                goto client;
        }
    
        /* parent: server */
        server_socket = socket(PF_INET, SOCK_DGRAM, 0);
        if (udpfromto_init(server_socket) != 0) {
            perror("udpfromto_init\n");
            waitpid(pid, NULL, WNOHANG);
            return 0;
        }
    
        if (bind(server_socket, (struct sockaddr *)&in, sizeof(in)) < 0) {
            perror("server: bind");
            waitpid(pid, NULL, WNOHANG);
            return 0;
        }
    
        printf("server: waiting for packets on INADDR_ANY:%d\n", port);
        if ((n = recvfromto(server_socket, buf, sizeof(buf), 0,
            (struct sockaddr *)&from, &fl,
            (struct sockaddr *)&to, &tl)) < 0) {
            perror("server: recvfromto");
            waitpid(pid, NULL, WNOHANG);
            return 0;
        }
    
        printf("server: received a packet of %d bytes [%s] ", n, buf);
        printf("(src ip:port %s:%d ",
            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));
    
        printf("server: replying from address packet was received on to source address\n");
    
        if ((n = sendfromto(server_socket, buf, n, 0,
            (struct sockaddr *)&to, tl,
            (struct sockaddr *)&from, fl)) < 0) {
            perror("server: sendfromto");
        }
    
        waitpid(pid, NULL, 0);
        return 0;
    
    client:
        close(server_socket);
        client_socket = socket(PF_INET, SOCK_DGRAM, 0);
        if (udpfromto_init(client_socket) != 0) {
            perror("udpfromto_init");
            _exit(0);
        }
        /* bind client on different port */
        in.sin_port = htons(port+1);
        if (bind(client_socket, (struct sockaddr *)&in, sizeof(in)) < 0) {
            perror("client: bind");
            _exit(0);
        }
    
        in.sin_port = htons(port);
        in.sin_addr.s_addr = inet_addr(destip);
    
        printf("client: sending packet to %s:%d\n", destip, port);
        if (sendto(client_socket, TESTSTRING, TESTLEN, 0,
                (struct sockaddr *)&in, sizeof(in)) < 0) {
            perror("client: sendto");
            _exit(0);
        }
    
        printf("client: waiting for reply from server on INADDR_ANY:%d\n", port+1);
    
        if ((n = recvfromto(client_socket, buf, sizeof(buf), 0,
            (struct sockaddr *)&from, &fl,
            (struct sockaddr *)&to, &tl)) < 0) {
            perror("client: recvfromto");
            _exit(0);
        }
    
        printf("client: received a packet of %d bytes [%s] ", n, buf);
        printf("(src ip:port %s:%d",
            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));
    
        _exit(0);
    }
    
    #endif /* TESTING */
    #endif /* WITH_UDPFROMTO */
    
     
  • Robert McMahon

    Robert McMahon - 2023-01-19
    • summary: mutlicast ifrname lookup not working --> cmsg clean up - mutlicast ifrname lookup not working
    • Description has changed:

    Diff:

    --- old
    +++ new
    @@ -1 +1 @@
    -the getsockhame to lookup the rx interface isn&#39;t working for multicast
    +the getsockhame to lookup the rx interface isn&#39;t working for multicast. Clean up cmsg code. [Ancillary doc here](https://www.masterraghu.com/subjects/np/introduction/unix_network_programming_v1.3/ch14lev1sec6.html).
    
     

Log in to post a comment.