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鈥?4
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鈥?1
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鈥?40 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鈥?4
We declare the local variables and then call our
net_rt_iflist function.
17鈥?9
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鈥?2
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鈥?4
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鈥?3
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鈥?5
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鈥?5
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 }
|