[ Team LiB ] Previous Section Next Section

18.5 get_ifi_info Function (Revisited)

We now return to the example from Section 17.6: returning all the interfaces that are up as a linked list of ifi_info structures (Figure 17.5). The prifinfo program remains the same (Figure 17.6), but we now show a version of the get_ifi_info function that uses sysctl instead of the SIOCGIFCONF ioctl that was used in Figure 17.7.

We first show the function net_rt_iflist in Figure 18.15. This function calls sysctl with the NET_RT_IFLIST command to return the interface list for a specified address family.

Figure 18.15 Call sysctl to return interface list.

libroute/net_rt_iflist.c

 1 #include    "unproute.h"

 2 char *
 3 net_rt_iflist(int family, int flags, size_t *lenp)
 4 {
 5     int     mib[6];
 6     char   *buf;

 7     mib[0] = CTL_NET;
 8     mib[1] = AF_ROUTE;
 9     mib[2] = 0;
10     mib[3] = family;             /* only addresses of this family */
11     mib[4] = NET_RT_IFLIST;
12     mib[5] = flags;              /* interface index or 0 */
13     if (sysctl (mib, 6, NULL, lenp, NULL, 0) < 0)
14         return (NULL);

15     if ( (buf = malloc(*lenp)) == NULL)
16         return (NULL);
17     if (sysctl (mib, 6, buf, lenp, NULL, 0) < 0) {
18         free(buf);
19         return (NULL);
20     }

21     return (buf);
22 }

7–14 The array mib is initialized as shown in Figure 18.12 to return the interface list and all configured addresses of the specified family. sysctl is then called twice. In the first call, the third argument is null, which returns the buffer size required to hold all the interface information in the variable pointed to by lenp.

15–21 Space is then allocated for the buffer and sysctl is called again, this time with a non-null third argument. This time, the variable pointed to by lenp will return with the amount of information stored in the buffer, and this variable is allocated by the caller. A pointer to the buffer is also returned to the caller.

Since the size of the routing table or the number of interfaces can change between the two calls to sysctl, the value returned by the first call contains a 10% fudge factor (pp. 639–640 of TCPv2).

Figure 18.16 shows the first half of the get_ifi_info function.

Figure 18.16 get_ifi_info function, first half.

route/get_ifi_info.c

 3 struct ifi_info *
 4 get_ifi_info(int family, int doaliases)
 5 {
 6     int     flags;
 7     char   *buf, *next, *lim;
 8     size_t  len;
 9     struct if_msghdr *ifm;
10     struct ifa_msghdr *ifam;
11     struct sockaddr *sa, *rti_info [RTAX_MAX];
12     struct sockaddr_dl *sdl;
13     struct ifi_info *ifi, *ifisave, *ifihead, **ifipnext;

14     buf = Net_rt_iflist(family, 0, &len);

15     ifihead = NULL;
16     ifipnext = &ifihead;

17     lim = buf + len;
18     for (next = buf; next < lim; next += ifm->ifm_msglen) {
19         ifm = (struct if_msghdr *) next;
20         if (ifm->ifm_type == RTM_IFINFO) {
21             if (((flags = ifm->ifm_flags) & IFF_UP) == 0)
22                 continue;  /* ignore if interface not up */
 
23             sa = (struct sockaddr *) (ifm + 1);
24             get_rtaddrs (ifm->ifm_addrs, sa, rti_info);
25             if ( (sa = rti_info [RTAX_IFP]) != NULL) {
26                 ifi = Calloc (1, sizeof (struct ifi_info));
27                 *ifipnext = ifi;     /* prev points to this new one */
28                 ifipnext = &ifi->ifi_next;     /* ptr to next one goes here */

29                 ifi->ifi_flags = flags;
30                 if (sa->sa_family == AF_LINK) {
31                     sdl = (struct sockaddr_dl *) sa;
32                     ifi->ifi_index = sdl->sdl_index;
33                     if (sdl->sdl_nlen > 0)
34                         snprintf (ifi->ifi_name, IFI_NAME, "%*s",
35                                   sdl->sdl_nlen, &sdl->sdl_data[0]);
36                     else
37                         snprintf(ifi->ifi_name, IFI_NAME, "index %d",
38                                  sdl->sdl_index);
 
3 9                    if ( (ifi->ifi_hlen = sdl->sdl_alen) > 0)
40                         memcpy (ifi->ifi_haddr, LLADDR (sdl),
41                                 min (IFI_HADDR, sdl->sdl_alen));
42                 }
43         }

6–14 We declare the local variables and then call our net_rt_iflist function.

17–19 The for loop steps through each routing message in the buffer filled in by sysctl. We assume that the message is an if_msghdr structure and look at the ifm_type field. (Recall that the first three members of all three structures are identical, so it doesn't matter which of the three structures we use to look at the type member.)

Check if interface is up

20–22 An RTM_IFINFO structure is returned for each interface. If the interface is not up, it is ignored.

Determine which socket address structures are present

23–24 sa points to the first socket address structure following the if_msghdr structure. Our get_rtaddrs function initializes the rti_info array, depending on which socket address structures are present.

Handle interface name

25–43 If the socket address structure with the interface name is present, an ifi_info structure is allocated and the interface flags are stored. The expected family of this socket address structure is AF_LINK, indicating a datalink socket address structure. We store the interface index into the ifi_index member. If the sdl_nlen member is nonzero, then the interface name is copied into the ifi_info structure. Otherwise, a string containing the interface index is stored as the name. If the sdl_alen member is nonzero, then the hardware address (e.g., the Ethernet address) is copied into the ifi_info structure and its length is also returned as ifi_hlen.

Figure 18.17 shows the second half of our get_ifi_info function, which returns the IP addresses for the interface.

Return IP addresses

44–65 An RTM_NEWADDR message is returned by sysctl for each address associated with the interface: the primary address and all aliases. If we have already filled in the IP address for this interface, then we are dealing with an alias. In that case, if the caller wants the alias address, we must allocate memory for another ifi_info structure, copy the fields that have been filled in, and then fill in the addresses that have been returned.

Return broadcast and destination addresses

66–75 If the interface supports broadcasting, the broadcast address is returned, and if the interface is a point-to-point interface, the destination address is returned.

Figure 18.17 get_ifi_info function, second half.

route/get_ifi_info.c

44     } else if (ifm->ifm_type == RTM_NEWADDR) {
45         if (ifi->ifi_addr) {     /* already have an IP addr for i/f */
46             if (doaliases == 0)
47                 continue;

48                 /* we have a new IP addr for existing interface */
49             ifisave = ifi;
50             ifi = Calloc(1, sizeof (struct ifi_info));
51             *ifipnext = ifi;     /* prev points to this new one */
52             ifipnext = &ifi->ifi_next;     /* ptr to next one goes here */
53             ifi->ifi_flags = ifisave->ifi_flags;
54             ifi->ifi_index = ifisave->ifi_index;
55             ifi->ifi_hlen = ifisave->ifi_hlen;
56             memcpy(ifi->ifi_name, ifisave->ifi_name, IFI_NAME);
57             memcpy(ifi->ifi_haddr, ifisave->ifi_haddr, IFI_HADDR);
58        }

59        ifam = (struct ifa_msghdr *) next;
60        sa = (struct sockaddr *) (ifam + 1);
61        get_rtaddrs(ifam->ifam_addrs, sa, rti_info);

62        if ( (sa = rti_info[RTAX_IFA]) != NULL) {
63            ifi->ifi_addr = Calloc(1, sa->sa_len);
64            memcpy(ifi->ifi_addr, sa, sa->sa_len);
65        }

66        if ((flags & IFF_BROADCAST) && (sa = rti_info[RTAX_BRD]) != NULL) {
67            ifi->ifi_brdaddr = Calloc (1, sa->sa_len);
68            memcpy(ifi->ifi_brdaddr, sa, sa->sa_len);
69        }

70       if ((flags & IFF_POINTOPOINT) &&
71           (sa = rti_info[RTAX_BRD]) != NULL) {
72           ifi->ifi_dstaddr = Calloc (1, sa->sa_len);
73           memcpy(ifi->ifi_dstaddr, sa, sa->sa_len);
74       }

75   } else
76       err_quit("unexpected message type %d", ifm->ifm_type);
77  }
78  /* "ifihead" points to the first structure in the linked list */
79  return (ifihead);     /* ptr to first structure in linked list */
80 }
    [ Team LiB ] Previous Section Next Section