xref: /haiku/src/add-ons/kernel/network/stack/domains.cpp (revision 746cac055adc6ac3308c7bc2d29040fb95689cc9)
1 /*
2  * Copyright 2006-2007, Haiku, Inc. All Rights Reserved.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Axel Dörfler, axeld@pinc-software.de
7  */
8 
9 
10 #include "domains.h"
11 #include "interfaces.h"
12 #include "utility.h"
13 #include "stack_private.h"
14 
15 #include <net_device.h>
16 
17 #include <lock.h>
18 #include <util/AutoLock.h>
19 
20 #include <KernelExport.h>
21 
22 #include <net/if_media.h>
23 #include <new>
24 #include <string.h>
25 #include <sys/sockio.h>
26 
27 
28 #define TRACE_DOMAINS
29 #ifdef TRACE_DOMAINS
30 #	define TRACE(x) dprintf x
31 #else
32 #	define TRACE(x) ;
33 #endif
34 
35 static mutex sDomainLock;
36 static list sDomains;
37 
38 
39 /*!
40 	Scans the domain list for the specified family.
41 	You need to hold the sDomainLock when calling this function.
42 */
43 static net_domain_private *
44 lookup_domain(int family)
45 {
46 	net_domain_private *domain = NULL;
47 	while (true) {
48 		domain = (net_domain_private *)list_get_next_item(&sDomains, domain);
49 		if (domain == NULL)
50 			break;
51 
52 		if (domain->family == family)
53 			return domain;
54 	}
55 
56 	return NULL;
57 }
58 
59 
60 //	#pragma mark -
61 
62 
63 /*!
64 	Gets the domain of the specified family.
65 */
66 net_domain *
67 get_domain(int family)
68 {
69 	MutexLocker locker(sDomainLock);
70 	return lookup_domain(family);
71 }
72 
73 
74 uint32
75 count_domain_interfaces()
76 {
77 	MutexLocker locker(sDomainLock);
78 
79 	net_domain_private *domain = NULL;
80 	uint32 count = 0;
81 
82 	while (true) {
83 		domain = (net_domain_private *)list_get_next_item(&sDomains, domain);
84 		if (domain == NULL)
85 			break;
86 
87 		net_interface *interface = NULL;
88 		while (true) {
89 			interface = (net_interface *)list_get_next_item(&domain->interfaces,
90 				interface);
91 			if (interface == NULL)
92 				break;
93 
94 			count++;
95 		}
96 	}
97 
98 	return count;
99 }
100 
101 
102 /*!
103 	Dumps a list of all interfaces into the supplied userland buffer.
104 	If the interfaces don't fit into the buffer, an error (\c ENOBUFS) is
105 	returned.
106 */
107 status_t
108 list_domain_interfaces(void *_buffer, size_t *bufferSize)
109 {
110 	MutexLocker locker(sDomainLock);
111 
112 	UserBuffer buffer(_buffer, *bufferSize);
113 	net_domain_private *domain = NULL;
114 
115 	while (true) {
116 		domain = (net_domain_private *)list_get_next_item(&sDomains, domain);
117 		if (domain == NULL)
118 			break;
119 
120 		MutexLocker locker(domain->lock);
121 
122 		net_interface *interface = NULL;
123 		while (true) {
124 			interface = (net_interface *)list_get_next_item(&domain->interfaces,
125 				interface);
126 			if (interface == NULL)
127 				break;
128 
129 			ifreq request;
130 			strlcpy(request.ifr_name, interface->name, IF_NAMESIZE);
131 			if (interface->address != NULL) {
132 				memcpy(&request.ifr_addr, interface->address,
133 					interface->address->sa_len);
134 			} else {
135 				// empty address
136 				request.ifr_addr.sa_len = 2;
137 				request.ifr_addr.sa_family = AF_UNSPEC;
138 			}
139 
140 			if (buffer.Copy(&request, IF_NAMESIZE
141 					+ request.ifr_addr.sa_len) == NULL)
142 				return buffer.Status();
143 		}
144 	}
145 
146 	*bufferSize = buffer.ConsumedAmount();
147 	return B_OK;
148 }
149 
150 
151 status_t
152 add_interface_to_domain(net_domain *_domain,
153 	struct ifreq& request)
154 {
155 	net_domain_private *domain = (net_domain_private *)_domain;
156 
157 	const char *deviceName = request.ifr_parameter.device[0]
158 		? request.ifr_parameter.device : request.ifr_name;
159 	const char *baseName = request.ifr_parameter.base_name[0]
160 		? request.ifr_parameter.base_name : request.ifr_name;
161 
162 	net_device_interface *deviceInterface = get_device_interface(deviceName);
163 	if (deviceInterface == NULL)
164 		return ENODEV;
165 
166 	MutexLocker locker(domain->lock);
167 
168 	net_interface_private *interface = NULL;
169 	status_t status;
170 
171 	if (find_interface(domain, request.ifr_name) != NULL)
172 		status = B_NAME_IN_USE;
173 	else
174 		status = create_interface(domain, request.ifr_name,
175 			baseName, deviceInterface, &interface);
176 
177 	put_device_interface(deviceInterface);
178 
179 	if (status == B_OK)
180 		list_add_item(&domain->interfaces, interface);
181 
182 	return status;
183 }
184 
185 
186 /*!
187 	Removes the interface from its domain, and deletes it.
188 	You need to hold the domain's lock when calling this function.
189 */
190 status_t
191 remove_interface_from_domain(net_interface *interface)
192 {
193 	net_domain_private *domain = (net_domain_private *)interface->domain;
194 
195 	list_remove_item(&domain->interfaces, interface);
196 	delete_interface((net_interface_private *)interface);
197 	return B_OK;
198 }
199 
200 
201 status_t
202 domain_interface_control(net_domain_private *domain, int32 option,
203 	ifreq *request)
204 {
205 	const char *name = request->ifr_name;
206 	status_t status = B_OK;
207 
208 	net_device_interface *device = get_device_interface(name, false);
209 	if (device == NULL)
210 		return ENODEV;
211 	else {
212 		// The locking protocol dictates that if both the RX lock
213 		// and domain locks are required, we MUST obtain the RX
214 		// lock before the domain lock. This order MUST NOT ever
215 		// be reversed under the penalty of deadlock.
216 		RecursiveLocker _1(device->rx_lock);
217 		MutexLocker _2(domain->lock);
218 
219 		net_interface *interface = find_interface(domain, name);
220 		if (interface != NULL) {
221 			switch (option) {
222 				case SIOCDIFADDR:
223 					remove_interface_from_domain(interface);
224 					break;
225 
226 				case SIOCSIFFLAGS:
227 				{
228 					uint32 requestFlags = request->ifr_flags;
229 					request->ifr_flags &= ~(IFF_UP | IFF_LINK | IFF_BROADCAST);
230 
231 					if ((requestFlags & IFF_UP) != (interface->flags & IFF_UP)) {
232 						if (requestFlags & IFF_UP) {
233 							status = interface->first_info->interface_up(
234 								interface->first_protocol);
235 							if (status == B_OK)
236 								interface->flags |= IFF_UP;
237 						} else {
238 							interface_set_down(interface);
239 						}
240 					}
241 
242 					if (status == B_OK)
243 						interface->flags |= request->ifr_flags;
244 					break;
245 				}
246 			}
247 		}
248 	}
249 
250 	// If the SIOCDIFADDR call above removed the last interface
251 	// associated with the device interface, this put_() will
252 	// effectively remove the interface
253 	put_device_interface(device);
254 
255 	return status;
256 }
257 
258 
259 void
260 domain_interface_went_down(net_interface *interface)
261 {
262 	// the domain should be locked here. always check
263 	// all callers to be sure. We get here via
264 	// interface_set_down().
265 
266 	dprintf("domain_interface_went_down(%i, %s)\n",
267 		interface->domain->family, interface->name);
268 
269 	// domain might have been locked by:
270 	//  - domain_removed_device_interface() <--- here
271 	//     remove_interface_from_domain()
272 	//      delete_interface()
273 	//       interface_set_down()
274 	//  - datalink_control() <--- here
275 	//     interface_set_down()
276 	invalidate_routes(interface->domain, interface);
277 }
278 
279 
280 void
281 domain_removed_device_interface(net_device_interface *interface)
282 {
283 	MutexLocker locker(sDomainLock);
284 
285 	net_domain_private *domain = NULL;
286 	while (true) {
287 		domain = (net_domain_private *)list_get_next_item(&sDomains, domain);
288 		if (domain == NULL)
289 			break;
290 
291 		MutexLocker locker(domain->lock);
292 
293 		net_interface_private *priv = find_interface(domain,
294 			interface->device->name);
295 		if (priv == NULL)
296 			continue;
297 
298 		remove_interface_from_domain(priv);
299 	}
300 }
301 
302 
303 status_t
304 register_domain(int family, const char *name,
305 	struct net_protocol_module_info *module,
306 	struct net_address_module_info *addressModule,
307 	net_domain **_domain)
308 {
309 	TRACE(("register_domain(%d, %s)\n", family, name));
310 	MutexLocker locker(sDomainLock);
311 
312 	struct net_domain_private *domain = lookup_domain(family);
313 	if (domain != NULL)
314 		return B_NAME_IN_USE;
315 
316 	domain = new (std::nothrow) net_domain_private;
317 	if (domain == NULL)
318 		return B_NO_MEMORY;
319 
320 	mutex_init_etc(&domain->lock, name, MUTEX_FLAG_CLONE_NAME);
321 
322 	domain->family = family;
323 	domain->name = name;
324 	domain->module = module;
325 	domain->address_module = addressModule;
326 
327 	list_init(&domain->interfaces);
328 
329 	list_add_item(&sDomains, domain);
330 
331 	*_domain = domain;
332 	return B_OK;
333 }
334 
335 
336 status_t
337 unregister_domain(net_domain *_domain)
338 {
339 	TRACE(("unregister_domain(%p, %d, %s)\n", _domain, _domain->family, _domain->name));
340 
341 	net_domain_private *domain = (net_domain_private *)_domain;
342 	MutexLocker locker(sDomainLock);
343 
344 	list_remove_item(&sDomains, domain);
345 
346 	net_interface_private *interface = NULL;
347 	while (true) {
348 		interface = (net_interface_private *)list_remove_head_item(&domain->interfaces);
349 		if (interface == NULL)
350 			break;
351 
352 		delete_interface(interface);
353 	}
354 
355 	mutex_destroy(&domain->lock);
356 	delete domain;
357 	return B_OK;
358 }
359 
360 
361 status_t
362 init_domains()
363 {
364 	mutex_init(&sDomainLock, "net domains");
365 
366 	list_init_etc(&sDomains, offsetof(struct net_domain_private, link));
367 	return B_OK;
368 }
369 
370 
371 status_t
372 uninit_domains()
373 {
374 	mutex_destroy(&sDomainLock);
375 	return B_OK;
376 }
377 
378