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