xref: /haiku/src/add-ons/kernel/network/stack/domains.cpp (revision 62f5ba006a08b0df30631375878effaf67ae5dbc)
1 /*
2  * Copyright 2006-2010, 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 #define ENABLE_DEBUGGER_COMMANDS	1
36 
37 
38 typedef DoublyLinkedList<net_domain_private> DomainList;
39 
40 static mutex sDomainLock;
41 static DomainList sDomains;
42 
43 
44 /*!	Scans the domain list for the specified family.
45 	You need to hold the sDomainLock when calling this function.
46 */
47 static net_domain_private*
48 lookup_domain(int family)
49 {
50 	DomainList::Iterator iterator = sDomains.GetIterator();
51 	while (net_domain_private* domain = iterator.Next()) {
52 		if (domain->family == family)
53 			return domain;
54 	}
55 
56 	return NULL;
57 }
58 
59 
60 #if	ENABLE_DEBUGGER_COMMANDS
61 
62 
63 static int
64 dump_domains(int argc, char** argv)
65 {
66 	DomainList::Iterator iterator = sDomains.GetIterator();
67 	while (net_domain_private* domain = iterator.Next()) {
68 		kprintf("domain: %p, %s, %d\n", domain, domain->name, domain->family);
69 		kprintf("  module:         %p\n", domain->module);
70 		kprintf("  address_module: %p\n", domain->address_module);
71 
72 		if (!list_is_empty(&domain->interfaces))
73 			kprintf("  interfaces:\n");
74 
75 		net_interface* interface = NULL;
76 		while (true) {
77 			interface = (net_interface*)list_get_next_item(&domain->interfaces,
78 				interface);
79 			if (interface == NULL)
80 				break;
81 
82 			kprintf("    %p\n", interface);
83 		}
84 
85 		if (!domain->routes.IsEmpty())
86 			kprintf("  routes:\n");
87 
88 		RouteList::Iterator routeIterator = domain->routes.GetIterator();
89 		while (net_route* route = routeIterator.Next()) {
90 			kprintf("    %p\n", route);
91 		}
92 
93 		if (!domain->route_infos.IsEmpty())
94 			kprintf("  route infos:\n");
95 
96 		RouteInfoList::Iterator infoIterator = domain->route_infos.GetIterator();
97 		while (net_route_info* info = infoIterator.Next()) {
98 			kprintf("    %p\n", info);
99 		}
100 	}
101 
102 	return 0;
103 }
104 
105 
106 #endif	// ENABLE_DEBUGGER_COMMANDS
107 
108 
109 //	#pragma mark -
110 
111 
112 /*!	Gets the domain of the specified family.
113 */
114 net_domain*
115 get_domain(int family)
116 {
117 	MutexLocker locker(sDomainLock);
118 	return lookup_domain(family);
119 }
120 
121 
122 uint32
123 count_domain_interfaces()
124 {
125 	MutexLocker locker(sDomainLock);
126 
127 	uint32 count = 0;
128 
129 	DomainList::Iterator iterator = sDomains.GetIterator();
130 	while (net_domain_private* domain = iterator.Next()) {
131 		net_interface* interface = NULL;
132 		while (true) {
133 			interface = (net_interface*)list_get_next_item(&domain->interfaces,
134 				interface);
135 			if (interface == NULL)
136 				break;
137 
138 			count++;
139 		}
140 	}
141 
142 	return count;
143 }
144 
145 
146 /*!	Dumps a list of all interfaces into the supplied userland buffer.
147 	If the interfaces don't fit into the buffer, an error (\c ENOBUFS) is
148 	returned.
149 */
150 status_t
151 list_domain_interfaces(void* _buffer, size_t* bufferSize)
152 {
153 	MutexLocker locker(sDomainLock);
154 
155 	UserBuffer buffer(_buffer, *bufferSize);
156 
157 	DomainList::Iterator iterator = sDomains.GetIterator();
158 	while (net_domain_private* domain = iterator.Next()) {
159 		RecursiveLocker locker(domain->lock);
160 
161 		net_interface* interface = NULL;
162 		while (true) {
163 			interface = (net_interface*)list_get_next_item(&domain->interfaces,
164 				interface);
165 			if (interface == NULL)
166 				break;
167 
168 			ifreq request;
169 			strlcpy(request.ifr_name, interface->name, IF_NAMESIZE);
170 			if (interface->address != NULL) {
171 				memcpy(&request.ifr_addr, interface->address,
172 					interface->address->sa_len);
173 			} else {
174 				// empty address
175 				request.ifr_addr.sa_len = 2;
176 				request.ifr_addr.sa_family = AF_UNSPEC;
177 			}
178 
179 			if (buffer.Copy(&request, IF_NAMESIZE
180 					+ request.ifr_addr.sa_len) == NULL)
181 				return buffer.Status();
182 		}
183 	}
184 
185 	*bufferSize = buffer.ConsumedAmount();
186 	return B_OK;
187 }
188 
189 
190 status_t
191 add_interface_to_domain(net_domain* _domain,
192 	struct ifreq& request)
193 {
194 	net_domain_private* domain = (net_domain_private*)_domain;
195 
196 	const char* deviceName = request.ifr_parameter.device[0]
197 		? request.ifr_parameter.device : request.ifr_name;
198 	const char* baseName = request.ifr_parameter.base_name[0]
199 		? request.ifr_parameter.base_name : request.ifr_name;
200 
201 	net_device_interface* deviceInterface = get_device_interface(deviceName);
202 	if (deviceInterface == NULL)
203 		return ENODEV;
204 
205 	RecursiveLocker locker(domain->lock);
206 
207 	net_interface_private* interface = NULL;
208 	status_t status;
209 
210 	if (find_interface(domain, request.ifr_name) == NULL) {
211 		// We must not hold the domain's link when creating the interface:
212 		// this will call get_module() which might want to access a network
213 		// device when booting from network.
214 		locker.Unlock();
215 		status = create_interface(domain, request.ifr_name,
216 			baseName, deviceInterface, &interface);
217 		locker.Lock();
218 
219 		if (find_interface(domain, request.ifr_name) != NULL) {
220 			delete_interface(interface);
221 			status = B_NAME_IN_USE;
222 		}
223 	} else
224 		status = B_NAME_IN_USE;
225 
226 	put_device_interface(deviceInterface);
227 
228 	if (status == B_OK) {
229 		list_add_item(&domain->interfaces, interface);
230 		notify_interface_added(interface);
231 	}
232 
233 	return status;
234 }
235 
236 
237 /*!	Removes the interface from its domain, and deletes it.
238 	You need to hold the domain's lock when calling this function.
239 */
240 status_t
241 remove_interface_from_domain(net_interface* interface)
242 {
243 	net_domain_private* domain = (net_domain_private*)interface->domain;
244 
245 	list_remove_item(&domain->interfaces, interface);
246 	notify_interface_removed(interface);
247 	delete_interface((net_interface_private*)interface);
248 	return B_OK;
249 }
250 
251 
252 status_t
253 domain_interface_control(net_domain_private* domain, int32 option,
254 	ifreq* request)
255 {
256 	const char* name = request->ifr_name;
257 	status_t status = B_OK;
258 
259 	net_device_interface* device = get_device_interface(name, false);
260 	if (device == NULL)
261 		return ENODEV;
262 
263 	RecursiveLocker _(domain->lock);
264 
265 	net_interface* interface = find_interface(domain, name);
266 	if (interface != NULL) {
267 		switch (option) {
268 			case SIOCDIFADDR:
269 				remove_interface_from_domain(interface);
270 				break;
271 
272 			case SIOCSIFFLAGS:
273 			{
274 				uint32 requestFlags = request->ifr_flags;
275 				request->ifr_flags &= ~(IFF_UP | IFF_LINK | IFF_BROADCAST);
276 
277 				if ((requestFlags & IFF_UP) != (interface->flags & IFF_UP)) {
278 					if (requestFlags & IFF_UP) {
279 						status = interface->first_info->interface_up(
280 							interface->first_protocol);
281 						if (status == B_OK)
282 							interface->flags |= IFF_UP;
283 					} else {
284 						interface_set_down(interface);
285 					}
286 				}
287 
288 				if (status == B_OK) {
289 					// TODO: why shouldn't we able to delete IFF_BROADCAST?
290 					interface->flags &= IFF_UP | IFF_LINK | IFF_BROADCAST;
291 					interface->flags |= request->ifr_flags;
292 				}
293 				break;
294 			}
295 		}
296 	}
297 
298 	// If the SIOCDIFADDR call above removed the last interface associated with
299 	// the device interface, this will effectively remove the interface
300 	put_device_interface(device);
301 
302 	return status;
303 }
304 
305 
306 /*!	You need to hold the domain lock when calling this function. */
307 void
308 domain_interface_went_down(net_interface* interface)
309 {
310 	ASSERT_LOCKED_RECURSIVE(&((net_domain_private*)interface->domain)->lock);
311 
312 	TRACE(("domain_interface_went_down(%i, %s)\n",
313 		interface->domain->family, interface->name));
314 
315 	invalidate_routes(interface->domain, interface);
316 }
317 
318 
319 void
320 domain_removed_device_interface(net_device_interface* deviceInterface)
321 {
322 	MutexLocker locker(sDomainLock);
323 
324 	DomainList::Iterator iterator = sDomains.GetIterator();
325 	while (net_domain_private* domain = iterator.Next()) {
326 		RecursiveLocker locker(domain->lock);
327 
328 		net_interface_private* interface = find_interface(domain,
329 			deviceInterface->device->name);
330 		if (interface == NULL)
331 			continue;
332 
333 		remove_interface_from_domain(interface);
334 	}
335 }
336 
337 
338 status_t
339 register_domain(int family, const char* name,
340 	struct net_protocol_module_info* module,
341 	struct net_address_module_info* addressModule,
342 	net_domain** _domain)
343 {
344 	TRACE(("register_domain(%d, %s)\n", family, name));
345 	MutexLocker locker(sDomainLock);
346 
347 	struct net_domain_private* domain = lookup_domain(family);
348 	if (domain != NULL)
349 		return B_NAME_IN_USE;
350 
351 	domain = new(std::nothrow) net_domain_private;
352 	if (domain == NULL)
353 		return B_NO_MEMORY;
354 
355 	recursive_lock_init(&domain->lock, name);
356 
357 	domain->family = family;
358 	domain->name = name;
359 	domain->module = module;
360 	domain->address_module = addressModule;
361 
362 	list_init(&domain->interfaces);
363 
364 	sDomains.Add(domain);
365 
366 	*_domain = domain;
367 	return B_OK;
368 }
369 
370 
371 status_t
372 unregister_domain(net_domain* _domain)
373 {
374 	TRACE(("unregister_domain(%p, %d, %s)\n", _domain, _domain->family,
375 		_domain->name));
376 
377 	net_domain_private* domain = (net_domain_private*)_domain;
378 	MutexLocker locker(sDomainLock);
379 
380 	sDomains.Remove(domain);
381 
382 	net_interface_private* interface = NULL;
383 	while (true) {
384 		interface = (net_interface_private*)list_remove_head_item(
385 			&domain->interfaces);
386 		if (interface == NULL)
387 			break;
388 
389 		delete_interface(interface);
390 	}
391 
392 	recursive_lock_destroy(&domain->lock);
393 	delete domain;
394 	return B_OK;
395 }
396 
397 
398 status_t
399 init_domains()
400 {
401 	mutex_init(&sDomainLock, "net domains");
402 
403 	new (&sDomains) DomainList;
404 		// static C++ objects are not initialized in the module startup
405 
406 #if ENABLE_DEBUGGER_COMMANDS
407 	add_debugger_command("net_domains", &dump_domains,
408 		"Dump network domains");
409 #endif
410 	return B_OK;
411 }
412 
413 
414 status_t
415 uninit_domains()
416 {
417 #if ENABLE_DEBUGGER_COMMANDS
418 	remove_debugger_command("net_domains", &dump_domains);
419 #endif
420 
421 	mutex_destroy(&sDomainLock);
422 	return B_OK;
423 }
424 
425