Re: notion of a local address [was: Re: ioctl SIOCGIFNETMASK: ip alias bug 2.4.9 and 2.2.19]

From: Mike Jagdis (jaggy@purplet.demon.co.uk)
Date: Fri Sep 07 2001 - 05:30:24 EST


Wietse Venema wrote:
> The SMTP RFC requires that user@[ip.address] is correctly recognized
> as a final destination. This requires that Linux provides the MTA
> with information about IP addresses that correspond with INADDR_ANY.
>
> I am susprised that it is not possible to ask such information up
> front (same with netmasks), and that an application has to actually
> query a complex oracle, again and again, for every IP address.

If I understand correctly you want to get a full list of addresses
and netmasks that have been assigned to interfaces?

   Use netlink. I'm sure someone else has said that but I haven't
read the entire thread, just scanned for the almost certainly
required example attachment (netlink is only straight forward once
you have an example :-( ). Attached is something that simply
asks for the current addresses (and "aliases"). It should be fairly
easy to use.

   The advantage with netlink is that you can just add the socket to
your select/poll list and listen for address changes as they happen
(useful for DHCP clients, maybe fail over scenarios etc.?). This code
doesn't do that. But if you add "while (1) netlink_read(s);" at the
bottom it will merrily report address changes - probably while sucking
CPU time, I suddenly have the feeling I left the set non-blocking
line in there!

                                Mike
P.S. It's IPv4. Adding IPv6 is left as an exercise for the reader :-)


#include <sys/types.h>
#include <sys/socket.h>
#include <sys/syslog.h>
#include <sys/time.h>
#include <sys/uio.h>
#include <netinet/in.h>
#include <net/if.h>
#include <arpa/inet.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#include <asm/types.h>
#include <linux/rtnetlink.h>

static int netlink_seq = 0;

static void
netlink_parse_rtattr(struct rtattr **tb, int max, struct rtattr *rta, int len)
{
        while (RTA_OK(rta, len)) {
                if (rta->rta_type <= max)
                        tb[rta->rta_type] = rta;
                rta = RTA_NEXT(rta,len);
        }
}

static int
netlink_ifaddr(struct sockaddr_nl *snl, struct nlmsghdr *h)
{
        int len;
        struct ifaddrmsg *ifa;
        struct rtattr *tb[IFA_MAX + 1];
        struct in_addr *addr = NULL;

        ifa = NLMSG_DATA(h);

        len = h->nlmsg_len - NLMSG_LENGTH(sizeof(struct ifaddrmsg));
        if (len < 0)
                return -1;

        memset(tb, '\0', sizeof(tb));
        netlink_parse_rtattr(tb, IFA_MAX, IFA_RTA(ifa), len);

        if (tb[IFA_LOCAL])
                addr = RTA_DATA(tb[IFA_LOCAL]);
          else if (tb[IFA_ADDRESS])
                    addr = RTA_DATA(tb[IFA_ADDRESS]);
        else
                return -1;

        fprintf(stderr,
                "Netlink %s: iface index %d, IPv4 address: %s/%d\n",
                h->nlmsg_type == RTM_NEWADDR ? "add" : "del",
                ifa->ifa_index, inet_ntoa(*addr), ifa->ifa_prefixlen);
        return 0;
}

int
netlink_open()
{
        int s;
        struct sockaddr_nl snl;

        s = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
        if (s < 0) {
                fprintf(stderr, "Can't open netlink socket: %s\n", strerror(errno));
                return -1;
        }

        fcntl(s, F_SETFL, O_NONBLOCK);
  
        memset(&snl, '\0', sizeof(snl));
        snl.nl_family = AF_NETLINK;
        snl.nl_groups = RTMGRP_IPV4_IFADDR;
  
        if (bind(s, (struct sockaddr *)&snl, sizeof(snl)) < 0) {
                fprintf(stderr, "Can't bind netlink socket: %s\n", strerror(errno));
                close(s);
                return -1;
        }

        return s;
}

int
netlink_request(int s, int family, int type)
{
        struct sockaddr_nl snl;

        struct {
                struct nlmsghdr nlh;
                struct rtgenmsg g;
        } req;

        memset(&snl, '\0', sizeof(snl));
        snl.nl_family = AF_NETLINK;

        req.nlh.nlmsg_len = sizeof(req);
        req.nlh.nlmsg_type = type;
        req.nlh.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST;
        req.nlh.nlmsg_pid = 0;
        req.nlh.nlmsg_seq = ++netlink_seq;
        req.g.rtgen_family = family;
  
        if (sendto(s, (void *)&req, sizeof(req), 0,
                (struct sockaddr *)&snl, sizeof(snl)) < 0)
        {
                fprintf(stderr, "netlink sendto failed: %s\n", strerror (errno));
                return -1;
        }

        return 0;
}

int
netlink_read(int s)
{
        int status;
        int seq = 0;

        while (1) {
                char buf[4096];
                struct iovec iov = { buf, sizeof(buf) };
                struct sockaddr_nl snl;
                struct msghdr msg = {
                        (void *)&snl, sizeof(snl), &iov, 1, NULL, 0, 0
                };
                struct nlmsghdr *h;

                status = recvmsg(s, &msg, 0);
                if (status < 0) {
                        if (errno == EINTR)
                                continue;
                        if (errno == EWOULDBLOCK)
                                return 0;
                        fprintf(stderr, "netlink recvmsg: %s\n", strerror(errno));
                          continue;
                }

                if (status == 0) {
                        fprintf(stderr, "netlink EOF\n");
                        return -1;
                }

                if (msg.msg_namelen != sizeof(snl)) {
                        fprintf(stderr, "netlink length error: %d != %d\n",
                                msg.msg_namelen, sizeof(snl));
                        return -1;
                }

                for (h = (struct nlmsghdr *) buf;
                        NLMSG_OK(h, status);
                        h = NLMSG_NEXT(h, status))
                {
                        seq = h->nlmsg_seq;
                        if (h->nlmsg_type == NLMSG_DONE)
                                return 0;

                        if (h->nlmsg_type == NLMSG_ERROR) {
                                struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(h);
                                if (h->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr)))
                                        fprintf(stderr, "netlink: message truncated\n");
                                else
                                        fprintf(stderr, "netlink: %s\n", strerror(-err->error));
                                return -1;
                        }

                        switch (h->nlmsg_type) {
                                case RTM_NEWADDR:
                                case RTM_DELADDR:
                                        netlink_ifaddr(&snl, h);
                                        break;
                        }
                }

                if (msg.msg_flags & MSG_TRUNC) {
                        fprintf(stderr, "netlink: message truncated\n");
                        continue;
                }

                if (status) {
                        fprintf(stderr, "netlink: excess data, size %d\n", status);
                        return -1;
                }

                /* This message is in reply to a user request. */
                if (seq == 0)
                        return 0;
        }

        return 0;
}

int
main(int argc, char *argv[])
{
        int s;

        s = netlink_open();
        if (s >= 0) {
                netlink_request(s, AF_INET, RTM_GETADDR);
                netlink_read(s);
        }
}

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/



This archive was generated by hypermail 2b29 : Fri Sep 07 2001 - 21:00:40 EST