1 /*
2     GSK - a library to write servers
3     Copyright (C) 1999-2000 Dave Benson
4 
5     This library is free software; you can redistribute it and/or
6     modify it under the terms of the GNU Lesser General Public
7     License as published by the Free Software Foundation; either
8     version 2 of the License, or (at your option) any later version.
9 
10     This library is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13     Lesser General Public License for more details.
14 
15     You should have received a copy of the GNU Lesser General Public
16     License along with this library; if not, write to the Free Software
17     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
18 
19     Contact:
20         daveb@ffem.org <Dave Benson>
21 */
22 
23 #include "gsknetworkinterface.h"
24 #include "config.h"
25 #include <sys/types.h>
26 #include <sys/socket.h>
27 #include <string.h>
28 
29 /* needed under solaris */
30 #define BSD_COMP
31 
32 #if HAVE_NET_IF_H
33 #include <net/if.h>
34 #endif
35 #if HAVE_SYS_IOCTL_H
36 #include <sys/ioctl.h>
37 #endif
38 #include <errno.h>
39 #include <netdb.h>
40 #include <unistd.h>
41 
42 
43 
44 static int
get_IPPROTO_IP()45 get_IPPROTO_IP ()
46 {
47   static int proto = -1;
48   if (proto < 0)
49     {
50       struct protoent *entry;
51       entry = getprotobyname ("ip");
52       if (entry == NULL)
53 	{
54 	  g_warning ("The ip protocol was not found in /etc/services...");
55 	  proto = 0;
56 	}
57       else
58 	{
59 	  proto = entry->p_proto;
60 	}
61     }
62   return proto;
63 }
64 
65 /**
66  * gsk_network_interface_set_new:
67  * @flags: constraints on the interfaces to return.  All the constraints
68  * must be satisfied.
69  *
70  * Create a new list of interfaces, subject to the constraints given.
71  *
72  * Note that the constraints must all be satified, so
73  * using GSK_NETWORK_INTERFACE_NO_LOOKBACK and GSK_NETWORK_INTERFACE_LOOKBACK
74  * will always return an empty set.
75  *
76  * returns: a newly allocated list of interfaces that
77  * must be freed with gsk_network_interface_set_destroy().
78  */
79 GskNetworkInterfaceSet *
gsk_network_interface_set_new(GskNetworkInterfaceFlags flags)80 gsk_network_interface_set_new(GskNetworkInterfaceFlags  flags)
81 {
82   GArray *ifreq_array;
83   GArray *rv;
84   int tmp_socket;
85   guint i;
86   tmp_socket = socket (AF_INET, SOCK_DGRAM, get_IPPROTO_IP ());
87   if (tmp_socket < 0)
88     {
89       g_warning ("gsk_network_interface: error creating internal ns socket: %s",
90 		 g_strerror (errno));
91       return NULL;
92     }
93   ifreq_array = g_array_new (FALSE, FALSE, sizeof (struct ifreq));
94   g_array_set_size (ifreq_array, 16);
95   for (;;)
96     {
97       struct ifconf all_interface_config;
98       guint num_got;
99       all_interface_config.ifc_len = ifreq_array->len * sizeof (struct ifreq);
100       all_interface_config.ifc_buf = ifreq_array->data;
101       if (ioctl (tmp_socket, SIOCGIFCONF, (char *) &all_interface_config) < 0)
102 	{
103 	  g_warning ("gsk_network_interface:"
104 		     "error getting interface configuration: %s",
105 		     g_strerror (errno));
106 	  close (tmp_socket);
107 	  g_array_free (ifreq_array, TRUE);
108 	  return NULL;
109 	}
110       num_got = all_interface_config.ifc_len / sizeof (struct ifreq);
111       if (num_got == ifreq_array->len)
112 	g_array_set_size (ifreq_array, ifreq_array->len * 2);
113       else
114 	{
115 	  g_array_set_size (ifreq_array, num_got);
116 	  break;
117 	}
118     }
119 
120   /* now query each of those interfaces. */
121   rv = g_array_new (FALSE, FALSE, sizeof (GskNetworkInterface));
122   for (i = 0; i < ifreq_array->len; i++)
123     {
124       struct ifreq *req_array = (struct ifreq *)(ifreq_array->data) + i;
125       struct ifreq tmp_req;
126       gboolean is_up;
127       gboolean is_loopback;
128       gboolean has_broadcast;
129       gboolean has_multicast;
130       gboolean is_p2p;
131       guint if_flags;
132       GskNetworkInterface interface;
133 
134       /* XXX: we don't at all no how to handle a generic interface. */
135       /* XXX: is this an IPv6 problem? */
136       if (req_array->ifr_addr.sa_family != AF_INET)
137 	continue;
138 
139       memcpy (tmp_req.ifr_name, req_array->ifr_name, sizeof (tmp_req.ifr_name));
140       if (ioctl (tmp_socket, SIOCGIFFLAGS, (char *) &tmp_req) < 0)
141 	{
142 	  g_warning ("error getting information about interface %s",
143 		     tmp_req.ifr_name);
144 	  continue;
145 	}
146 
147       if_flags = tmp_req.ifr_flags;
148       is_up = (if_flags & IFF_UP) == IFF_UP;
149       is_loopback = (if_flags & IFF_LOOPBACK) == IFF_LOOPBACK;
150       has_broadcast = (if_flags & IFF_BROADCAST) == IFF_BROADCAST;
151       has_multicast = (if_flags & IFF_MULTICAST) == IFF_MULTICAST;
152       is_p2p = (if_flags & IFF_POINTOPOINT) == IFF_POINTOPOINT;
153 
154       if ((flags & GSK_NETWORK_INTERFACE_UP) != 0 && !is_up)
155 	continue;
156       if ((flags & GSK_NETWORK_INTERFACE_LOOPBACK) != 0 && !is_loopback)
157 	continue;
158       if ((flags & GSK_NETWORK_INTERFACE_NON_LOOPBACK) != 0 && is_loopback)
159 	continue;
160       if ((flags & GSK_NETWORK_INTERFACE_HAS_BROADCAST) != 0 && !has_broadcast)
161 	continue;
162       if ((flags & GSK_NETWORK_INTERFACE_HAS_MULTICAST) != 0 && !has_multicast)
163 	continue;
164 
165       interface.supports_multicast = has_multicast ? 1 : 0;
166       interface.is_promiscuous = (if_flags & IFF_PROMISC) ? 1 : 0;
167 
168       if (is_up)
169 	{
170 	  struct sockaddr *saddr;
171 	  if (ioctl (tmp_socket, SIOCGIFADDR, (char *) &tmp_req) < 0)
172 	    {
173 	      g_warning ("error getting the ip address for interface %s",
174 			 tmp_req.ifr_name);
175 	      continue;
176 	    }
177 	  saddr = &tmp_req.ifr_addr;
178 	  interface.address = gsk_socket_address_from_native (saddr, sizeof (*saddr));
179 	}
180       else
181 	interface.address = NULL;
182 
183       interface.is_loopback = is_loopback ? 1 : 0;
184 #ifdef SIOCGIFHWADDR
185       if (!interface.is_loopback)
186 	interface.hw_address = NULL;
187       else
188 	{
189 	  if (ioctl (tmp_socket, SIOCGIFHWADDR, (char *) &tmp_req) < 0)
190 	    {
191 	      g_warning ("error getting the hardware address for interface %s",
192 			 tmp_req.ifr_name);
193 	      continue;
194 	    }
195 	  interface.hw_address = gsk_socket_address_ethernet_new ((guint8*)tmp_req.ifr_addr.sa_data);
196 	}
197 #else
198       interface.hw_address = NULL;
199 #endif
200 
201       if (is_p2p)
202 	{
203 	  struct sockaddr *saddr;
204 	  if (ioctl (tmp_socket, SIOCGIFDSTADDR, (char *) &tmp_req) < 0)
205 	    {
206 	      g_warning ("error getting the ip address for interface %s",
207 			 tmp_req.ifr_name);
208 	      continue;
209 	    }
210 	  saddr = &tmp_req.ifr_addr;
211 	  interface.p2p_address = gsk_socket_address_from_native (saddr,
212 								  sizeof (struct sockaddr));
213 	}
214       else
215 	interface.p2p_address = NULL;
216 
217       if (has_broadcast)
218 	{
219 	  struct sockaddr *saddr;
220 	  if (ioctl (tmp_socket, SIOCGIFBRDADDR, (char *) &tmp_req) < 0)
221 	    {
222 	      g_warning ("error getting the broadcast address for interface %s",
223 			 tmp_req.ifr_name);
224 	      continue;
225 	    }
226 	  saddr = &tmp_req.ifr_addr;
227           interface.broadcast = gsk_socket_address_from_native (saddr,
228 								sizeof (struct sockaddr));
229 	}
230       else
231 	interface.broadcast = NULL;
232 
233       interface.ifname = g_strdup (tmp_req.ifr_name);
234       g_array_append_val (rv, interface);
235     }
236 
237   close (tmp_socket);
238 
239   g_array_free (ifreq_array, TRUE);
240   {
241     GskNetworkInterfaceSet *set;
242     set = g_new (GskNetworkInterfaceSet, 1);
243     set->num_interfaces = rv->len;
244     set->interfaces = (GskNetworkInterface *) rv->data;
245     return set;
246   }
247 }
248 
249 /**
250  * gsk_network_interface_set_destroy:
251  * @set: the list of interfaces to destroy.
252  *
253  * Free the memory used by the list of interfaces.
254  */
255 void
gsk_network_interface_set_destroy(GskNetworkInterfaceSet * set)256 gsk_network_interface_set_destroy(GskNetworkInterfaceSet    *set)
257 {
258   guint i;
259   for (i = 0; i < set->num_interfaces; i++)
260     {
261       g_free ((char*) (set->interfaces[i].ifname));
262 
263       if (set->interfaces[i].address != NULL)
264 	g_object_unref (set->interfaces[i].address);
265       if (set->interfaces[i].hw_address != NULL)
266         g_object_unref (set->interfaces[i].hw_address);
267       if (set->interfaces[i].p2p_address != NULL)
268         g_object_unref (set->interfaces[i].p2p_address);
269       if (set->interfaces[i].broadcast != NULL)
270         g_object_unref (set->interfaces[i].broadcast);
271     }
272   g_free (set->interfaces);
273   g_free (set);
274 }
275 
276