1 /* Copyright (C) 1997-2021 Free Software Foundation, Inc.
2    This file is part of the GNU C Library.
3 
4    The GNU C Library is free software; you can redistribute it and/or
5    modify it under the terms of the GNU Lesser General Public
6    License as published by the Free Software Foundation; either
7    version 2.1 of the License, or (at your option) any later version.
8 
9    The GNU C Library is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12    Lesser General Public License for more details.
13 
14    You should have received a copy of the GNU Lesser General Public
15    License along with the GNU C Library; if not, see
16    <https://www.gnu.org/licenses/>.  */
17 
18 #include <alloca.h>
19 #include <errno.h>
20 #include <string.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <unistd.h>
24 #include <net/if.h>
25 #include <sys/socket.h>
26 #include <sys/ioctl.h>
27 #include <libc-lock.h>
28 #include <not-cancel.h>
29 
30 #include "netlinkaccess.h"
31 
32 
33 unsigned int
__if_nametoindex(const char * ifname)34 __if_nametoindex (const char *ifname)
35 {
36 #ifndef SIOCGIFINDEX
37   __set_errno (ENOSYS);
38   return 0;
39 #else
40   struct ifreq ifr;
41   if (strlen (ifname) >= IFNAMSIZ)
42     {
43       __set_errno (ENODEV);
44       return 0;
45     }
46 
47   strncpy (ifr.ifr_name, ifname, sizeof (ifr.ifr_name));
48 
49   int fd = __opensock ();
50 
51   if (fd < 0)
52     return 0;
53 
54   if (__ioctl (fd, SIOCGIFINDEX, &ifr) < 0)
55     {
56       int saved_errno = errno;
57       __close_nocancel_nostatus (fd);
58       if (saved_errno == EINVAL)
59 	__set_errno (ENOSYS);
60       return 0;
61     }
62   __close_nocancel_nostatus (fd);
63   return ifr.ifr_ifindex;
64 #endif
65 }
66 libc_hidden_def (__if_nametoindex)
weak_alias(__if_nametoindex,if_nametoindex)67 weak_alias (__if_nametoindex, if_nametoindex)
68 libc_hidden_weak (if_nametoindex)
69 
70 
71 void
72 __if_freenameindex (struct if_nameindex *ifn)
73 {
74   struct if_nameindex *ptr = ifn;
75   while (ptr->if_name || ptr->if_index)
76     {
77       free (ptr->if_name);
78       ++ptr;
79     }
80   free (ifn);
81 }
82 libc_hidden_def (__if_freenameindex)
weak_alias(__if_freenameindex,if_freenameindex)83 weak_alias (__if_freenameindex, if_freenameindex)
84 libc_hidden_weak (if_freenameindex)
85 
86 
87 static struct if_nameindex *
88 if_nameindex_netlink (void)
89 {
90   struct netlink_handle nh = { 0, 0, 0, NULL, NULL };
91   struct if_nameindex *idx = NULL;
92 
93   if (__netlink_open (&nh) < 0)
94     return NULL;
95 
96 
97   /* Tell the kernel that we wish to get a list of all
98      active interfaces.  Collect all data for every interface.  */
99   if (__netlink_request (&nh, RTM_GETLINK) < 0)
100     goto exit_free;
101 
102   /* Count the interfaces.  */
103   unsigned int nifs = 0;
104   for (struct netlink_res *nlp = nh.nlm_list; nlp; nlp = nlp->next)
105     {
106       struct nlmsghdr *nlh;
107       size_t size = nlp->size;
108 
109       if (nlp->nlh == NULL)
110 	continue;
111 
112       /* Walk through all entries we got from the kernel and look, which
113          message type they contain.  */
114       for (nlh = nlp->nlh; NLMSG_OK (nlh, size); nlh = NLMSG_NEXT (nlh, size))
115 	{
116 	  /* Check if the message is what we want.  */
117 	  if ((pid_t) nlh->nlmsg_pid != nh.pid || nlh->nlmsg_seq != nlp->seq)
118 	    continue;
119 
120 	  if (nlh->nlmsg_type == NLMSG_DONE)
121 	    break;		/* ok */
122 
123 	  if (nlh->nlmsg_type == RTM_NEWLINK)
124 	    ++nifs;
125 	}
126     }
127 
128   idx = malloc ((nifs + 1) * sizeof (struct if_nameindex));
129   if (idx == NULL)
130     {
131     nomem:
132       __set_errno (ENOBUFS);
133       goto exit_free;
134     }
135 
136   /* Add the interfaces.  */
137   nifs = 0;
138   for (struct netlink_res *nlp = nh.nlm_list; nlp; nlp = nlp->next)
139     {
140       struct nlmsghdr *nlh;
141       size_t size = nlp->size;
142 
143       if (nlp->nlh == NULL)
144 	continue;
145 
146       /* Walk through all entries we got from the kernel and look, which
147          message type they contain.  */
148       for (nlh = nlp->nlh; NLMSG_OK (nlh, size); nlh = NLMSG_NEXT (nlh, size))
149 	{
150 	  /* Check if the message is what we want.  */
151 	  if ((pid_t) nlh->nlmsg_pid != nh.pid || nlh->nlmsg_seq != nlp->seq)
152 	    continue;
153 
154 	  if (nlh->nlmsg_type == NLMSG_DONE)
155 	    break;		/* ok */
156 
157 	  if (nlh->nlmsg_type == RTM_NEWLINK)
158 	    {
159 	      struct ifinfomsg *ifim = (struct ifinfomsg *) NLMSG_DATA (nlh);
160 	      struct rtattr *rta = IFLA_RTA (ifim);
161 	      size_t rtasize = IFLA_PAYLOAD (nlh);
162 
163 	      idx[nifs].if_index = ifim->ifi_index;
164 
165 	      while (RTA_OK (rta, rtasize))
166 		{
167 		  char *rta_data = RTA_DATA (rta);
168 		  size_t rta_payload = RTA_PAYLOAD (rta);
169 
170 		  if (rta->rta_type == IFLA_IFNAME)
171 		    {
172 		      idx[nifs].if_name = __strndup (rta_data, rta_payload);
173 		      if (idx[nifs].if_name == NULL)
174 			{
175 			  idx[nifs].if_index = 0;
176 			  __if_freenameindex (idx);
177 			  idx = NULL;
178 			  goto nomem;
179 			}
180 		      break;
181 		    }
182 
183 		  rta = RTA_NEXT (rta, rtasize);
184 		}
185 
186 	      ++nifs;
187 	    }
188 	}
189     }
190 
191   idx[nifs].if_index = 0;
192   idx[nifs].if_name = NULL;
193 
194  exit_free:
195   __netlink_free_handle (&nh);
196   __netlink_close (&nh);
197 
198   return idx;
199 }
200 
201 
202 struct if_nameindex *
__if_nameindex(void)203 __if_nameindex (void)
204 {
205 #ifndef SIOCGIFINDEX
206   __set_errno (ENOSYS);
207   return NULL;
208 #else
209   struct if_nameindex *result = if_nameindex_netlink ();
210   return result;
211 #endif
212 }
weak_alias(__if_nameindex,if_nameindex)213 weak_alias (__if_nameindex, if_nameindex)
214 libc_hidden_weak (if_nameindex)
215 
216 
217 char *
218 __if_indextoname (unsigned int ifindex, char ifname[IF_NAMESIZE])
219 {
220   /* We may be able to do the conversion directly, rather than searching a
221      list.  This ioctl is not present in kernels before version 2.1.50.  */
222   struct ifreq ifr;
223   int fd;
224   int status;
225 
226   fd = __opensock ();
227 
228   if (fd < 0)
229     return NULL;
230 
231   ifr.ifr_ifindex = ifindex;
232   status = __ioctl (fd, SIOCGIFNAME, &ifr);
233 
234   __close_nocancel_nostatus (fd);
235 
236   if (status  < 0)
237     {
238       if (errno == ENODEV)
239 	/* POSIX requires ENXIO.  */
240 	__set_errno (ENXIO);
241 
242       return NULL;
243     }
244   else
245     return strncpy (ifname, ifr.ifr_name, IFNAMSIZ);
246 }
247 weak_alias (__if_indextoname, if_indextoname)
248 libc_hidden_weak (if_indextoname)
249