xref: /haiku/src/libs/compat/freebsd_network/if.c (revision 508f54795f39c3e7552d87c95aae9dd8ec6f505b)
1 /*
2  * Copyright 2009, Colin Günther, coling@gmx.de.
3  * Copyright 2007-2009, Axel Dörfler, axeld@pinc-software.de.
4  * Copyright 2007, Hugo Santos. All Rights Reserved.
5  * Copyright 2004, Marcus Overhagen. All Rights Reserved.
6  *
7  * Distributed under the terms of the MIT License.
8  */
9 
10 
11 #include "device.h"
12 
13 #include <stdio.h>
14 #include <net/if_types.h>
15 #include <sys/sockio.h>
16 
17 #include <compat/sys/bus.h>
18 #include <compat/sys/kernel.h>
19 #include <compat/sys/taskqueue.h>
20 
21 #include <compat/net/if.h>
22 #include <compat/net/if_arp.h>
23 #include <compat/net/if_media.h>
24 #include <compat/net/if_var.h>
25 #include <compat/sys/malloc.h>
26 
27 #include <compat/net/ethernet.h>
28 
29 
30 #define IFNET_HOLD (void *)(uintptr_t)(-1)
31 
32 
33 static void
34 insert_into_device_name_list(struct ifnet * ifp)
35 {
36 	int i;
37 	for (i = 0; i < MAX_DEVICES; i++) {
38 		if (gDeviceNameList[i] == NULL) {
39 			gDeviceNameList[i] = ifp->device_name;
40 			return;
41 		}
42 	}
43 
44 	panic("too many devices");
45 }
46 
47 
48 static void
49 remove_from_device_name_list(struct ifnet * ifp)
50 {
51 	int i;
52 	for (i = 0; i < MAX_DEVICES; i++) {
53 		if (ifp->device_name == gDeviceNameList[i]) {
54 			int last;
55 			for (last = i + 1; last < MAX_DEVICES; last++) {
56 				if (gDeviceNameList[last] == NULL)
57 					break;
58 			}
59 			last--;
60 
61 			if (i == last)
62 				gDeviceNameList[i] = NULL;
63 			else {
64 				// switch positions with the last entry
65 				gDeviceNameList[i] = gDeviceNameList[last];
66 				gDeviceNameList[last] = NULL;
67 			}
68 			break;
69 		}
70 	}
71 }
72 
73 
74 struct ifnet *
75 ifnet_byindex(u_short idx)
76 {
77 	struct ifnet *ifp;
78 
79 	IFNET_RLOCK_NOSLEEP();
80 	ifp = ifnet_byindex_locked(idx);
81 	IFNET_RUNLOCK_NOSLEEP();
82 
83 	return (ifp);
84 }
85 
86 
87 struct ifnet *
88 ifnet_byindex_locked(u_short idx)
89 {
90 	struct ifnet *ifp;
91 
92 	ifp = gDevices[idx];
93 
94 	return (ifp);
95 }
96 
97 
98 static void
99 ifnet_setbyindex_locked(u_short idx, struct ifnet *ifp)
100 {
101 	gDevices[idx] = ifp;
102 }
103 
104 
105 static void
106 ifnet_setbyindex(u_short idx, struct ifnet *ifp)
107 {
108 	IFNET_WLOCK();
109 	ifnet_setbyindex_locked(idx, ifp);
110 	IFNET_WUNLOCK();
111 }
112 
113 
114 static int
115 ifindex_alloc_locked(u_short *idxp)
116 {
117 	u_short index;
118 
119 	for (index = 0; index < MAX_DEVICES; index++) {
120 		if (gDevices[index] == NULL) {
121 			break;
122 		}
123 	}
124 
125 	if (index == MAX_DEVICES)
126 		return ENOSPC;
127 
128 	gDeviceCount++;
129 	*idxp = index;
130 
131 	return ENOERR;
132 }
133 
134 
135 static void
136 ifindex_free_locked(u_short idx)
137 {
138 	gDevices[idx] = NULL;
139 	gDeviceCount--;
140 }
141 
142 
143 struct ifnet *
144 if_alloc(u_char type)
145 {
146 	char semName[64];
147 	u_short index;
148 
149 	struct ifnet *ifp = _kernel_malloc(sizeof(struct ifnet), M_ZERO);
150 	if (ifp == NULL)
151 		return NULL;
152 
153 	snprintf(semName, sizeof(semName), "%s receive", gDriverName);
154 
155 	ifp->receive_sem = create_sem(0, semName);
156 	if (ifp->receive_sem < B_OK)
157 		goto err1;
158 
159 	switch (type) {
160 		case IFT_ETHER:
161 		{
162 			ifp->if_l2com = _kernel_malloc(sizeof(struct arpcom), M_ZERO);
163 			if (ifp->if_l2com == NULL)
164 				goto err2;
165 			IFP2AC(ifp)->ac_ifp = ifp;
166 			break;
167 		}
168 		case IFT_IEEE80211:
169 		{
170 			if (wlan_if_l2com_alloc(ifp) != B_OK)
171 				goto err2;
172 			break;
173 		}
174 	}
175 
176 	ifp->link_state_sem = -1;
177 	ifp->open_count = 0;
178 	ifp->flags = 0;
179 	ifp->if_type = type;
180 	ifq_init(&ifp->receive_queue, semName);
181 
182 	ifp->scan_done_sem = -1;
183 		// WLAN specific, doesn't hurt when initilized for other devices
184 
185 	// Search for the first free device slot, and use that one
186 	IFNET_WLOCK();
187 	if (ifindex_alloc_locked(&index) != ENOERR) {
188 		IFNET_WUNLOCK();
189 		panic("too many devices");
190 		goto err3;
191 	}
192 	ifnet_setbyindex_locked(index, IFNET_HOLD);
193 	IFNET_WUNLOCK();
194 
195 	ifp->if_index = index;
196 	ifnet_setbyindex(ifp->if_index, ifp);
197 
198 	IF_ADDR_LOCK_INIT(ifp);
199 	return ifp;
200 
201 err3:
202 	switch (type) {
203 		case IFT_ETHER:
204 		case IFT_IEEE80211:
205 			_kernel_free(ifp->if_l2com);
206 			break;
207 	}
208 
209 err2:
210 	delete_sem(ifp->receive_sem);
211 
212 err1:
213 	_kernel_free(ifp);
214 	return NULL;
215 }
216 
217 
218 void
219 if_free(struct ifnet *ifp)
220 {
221 	// IEEE80211 devices won't be in this list,
222 	// so don't try to remove them.
223 	if (ifp->if_type == IFT_ETHER)
224 		remove_from_device_name_list(ifp);
225 
226 	IFNET_WLOCK();
227 	ifindex_free_locked(ifp->if_index);
228 	IFNET_WUNLOCK();
229 
230 	IF_ADDR_LOCK_DESTROY(ifp);
231 	switch (ifp->if_type) {
232 		case IFT_ETHER:
233 		case IFT_IEEE80211:
234 			_kernel_free(ifp->if_l2com);
235 			break;
236 	}
237 
238 	delete_sem(ifp->receive_sem);
239 	ifq_uninit(&ifp->receive_queue);
240 
241 	_kernel_free(ifp);
242 }
243 
244 
245 void
246 if_initname(struct ifnet *ifp, const char *name, int unit)
247 {
248 	dprintf("if_initname(%p, %s, %d)\n", ifp, name, unit);
249 
250 	if (name == NULL || name[0] == '\0')
251 		panic("interface goes unnamed");
252 
253 	ifp->if_dname = name;
254 	ifp->if_dunit = unit;
255 
256 	strlcpy(ifp->if_xname, name, sizeof(ifp->if_xname));
257 
258 	snprintf(ifp->device_name, sizeof(ifp->device_name), "net/%s/%i",
259 		gDriverName, ifp->if_index);
260 
261 	driver_printf("%s: /dev/%s\n", gDriverName, ifp->device_name);
262 
263 	// For wlan devices we only want to see the cloned wlan device
264 	// in the list.
265 	// Remember: For each wlan device, there is a base device of type
266 	//           IFT_IEEE80211. On top of that a clone device is created of
267 	//           type IFT_ETHER.
268 	//           Haiku shall only see the cloned device as it is the one
269 	//           FreeBSD 8 uses for wireless i/o, too.
270 	if (ifp->if_type == IFT_ETHER)
271 		insert_into_device_name_list(ifp);
272 
273 	ifp->root_device = find_root_device(unit);
274 }
275 
276 
277 void
278 ifq_init(struct ifqueue *ifq, const char *name)
279 {
280 	ifq->ifq_head = NULL;
281 	ifq->ifq_tail = NULL;
282 	ifq->ifq_len = 0;
283 	ifq->ifq_maxlen = IFQ_MAXLEN;
284 	ifq->ifq_drops = 0;
285 
286 	mtx_init(&ifq->ifq_mtx, name, NULL, MTX_DEF);
287 }
288 
289 
290 void
291 ifq_uninit(struct ifqueue *ifq)
292 {
293 	mtx_destroy(&ifq->ifq_mtx);
294 }
295 
296 
297 static int
298 if_transmit(struct ifnet *ifp, struct mbuf *m)
299 {
300 	int error;
301 
302 	IFQ_HANDOFF(ifp, m, error);
303 	return (error);
304 }
305 
306 
307 void
308 if_attach(struct ifnet *ifp)
309 {
310 	TAILQ_INIT(&ifp->if_addrhead);
311 	TAILQ_INIT(&ifp->if_prefixhead);
312 	TAILQ_INIT(&ifp->if_multiaddrs);
313 
314 	IF_ADDR_LOCK_INIT(ifp);
315 
316 	ifp->if_lladdr.sdl_family = AF_LINK;
317 
318 	ifq_init((struct ifqueue *) &ifp->if_snd, ifp->if_xname);
319 
320 	if (ifp->if_transmit == NULL) {
321 		ifp->if_transmit = if_transmit;
322 	}
323 }
324 
325 
326 void
327 if_detach(struct ifnet *ifp)
328 {
329 	if (HAIKU_DRIVER_REQUIRES(FBSD_SWI_TASKQUEUE))
330 		taskqueue_drain(taskqueue_swi, &ifp->if_linktask);
331 
332 	IF_ADDR_LOCK_DESTROY(ifp);
333 	ifq_uninit((struct ifqueue *) &ifp->if_snd);
334 }
335 
336 
337 void
338 if_start(struct ifnet *ifp)
339 {
340 #ifdef IFF_NEEDSGIANT
341 	if (ifp->if_flags & IFF_NEEDSGIANT)
342 	panic("freebsd compat.: unsupported giant requirement");
343 #endif
344 	ifp->if_start(ifp);
345 }
346 
347 
348 int
349 if_printf(struct ifnet *ifp, const char *format, ...)
350 {
351 	char buf[256];
352 	va_list vl;
353 	va_start(vl, format);
354 	vsnprintf(buf, sizeof(buf), format, vl);
355 	va_end(vl);
356 
357 	dprintf("[%s] %s", ifp->device_name, buf);
358 	return 0;
359 }
360 
361 
362 void
363 if_link_state_change(struct ifnet *ifp, int linkState)
364 {
365 	if (ifp->if_link_state == linkState)
366 		return;
367 
368 	ifp->if_link_state = linkState;
369 	release_sem_etc(ifp->link_state_sem, 1, B_DO_NOT_RESCHEDULE);
370 }
371 
372 
373 static struct ifmultiaddr *
374 if_findmulti(struct ifnet *ifp, struct sockaddr *_address)
375 {
376 	struct sockaddr_dl *address = (struct sockaddr_dl *) _address;
377 	struct ifmultiaddr *ifma;
378 
379 	TAILQ_FOREACH (ifma, &ifp->if_multiaddrs, ifma_link) {
380 		if (memcmp(LLADDR(address),
381 			LLADDR((struct sockaddr_dl *)ifma->ifma_addr), ETHER_ADDR_LEN) == 0)
382 			return ifma;
383 	}
384 
385 	return NULL;
386 }
387 
388 
389 static struct ifmultiaddr *
390 _if_addmulti(struct ifnet *ifp, struct sockaddr *address)
391 {
392 	struct ifmultiaddr *addr = if_findmulti(ifp, address);
393 
394 	if (addr != NULL) {
395 		addr->ifma_refcount++;
396 		return addr;
397 	}
398 
399 	addr = (struct ifmultiaddr *) malloc(sizeof(struct ifmultiaddr));
400 	if (addr == NULL)
401 		return NULL;
402 
403 	addr->ifma_lladdr = NULL;
404 	addr->ifma_ifp = ifp;
405 	addr->ifma_protospec = NULL;
406 
407 	memcpy(&addr->ifma_addr_storage, address, sizeof(struct sockaddr_dl));
408 	addr->ifma_addr = (struct sockaddr *) &addr->ifma_addr_storage;
409 
410 	addr->ifma_refcount = 1;
411 
412 	TAILQ_INSERT_HEAD(&ifp->if_multiaddrs, addr, ifma_link);
413 
414 	return addr;
415 }
416 
417 
418 int
419 if_addmulti(struct ifnet *ifp, struct sockaddr *address,
420 	struct ifmultiaddr **out)
421 {
422 	struct ifmultiaddr *result;
423 	int refcount = 0;
424 
425 	IF_ADDR_LOCK(ifp);
426 	result = _if_addmulti(ifp, address);
427 	if (result)
428 		refcount = result->ifma_refcount;
429 	IF_ADDR_UNLOCK(ifp);
430 
431 	if (result == NULL)
432 		return ENOBUFS;
433 
434 	if (refcount == 1 && ifp->if_ioctl != NULL)
435 		ifp->if_ioctl(ifp, SIOCADDMULTI, NULL);
436 
437 	if (out)
438 		(*out) = result;
439 
440 	return 0;
441 }
442 
443 
444 static void
445 if_delete_multiaddr(struct ifnet *ifp, struct ifmultiaddr *ifma)
446 {
447 	TAILQ_REMOVE(&ifp->if_multiaddrs, ifma, ifma_link);
448 	free(ifma);
449 }
450 
451 
452 int
453 if_delmulti(struct ifnet *ifp, struct sockaddr *address)
454 {
455 	struct ifmultiaddr *addr;
456 	int deleted = 0;
457 
458 	IF_ADDR_LOCK(ifp);
459 	addr = if_findmulti(ifp, address);
460 	if (addr != NULL) {
461 		addr->ifma_refcount--;
462 		if (addr->ifma_refcount == 0) {
463 			if_delete_multiaddr(ifp, addr);
464 			deleted = 1;
465 		}
466 	}
467 	IF_ADDR_UNLOCK(ifp);
468 
469 	if (deleted && ifp->if_ioctl != NULL)
470 		ifp->if_ioctl(ifp, SIOCDELMULTI, NULL);
471 
472 	return 0;
473 }
474 
475 
476 static void
477 if_freemulti(struct ifmultiaddr *ifma)
478 {
479 	if (ifma->ifma_lladdr != NULL)
480 		free(ifma->ifma_lladdr);
481 	free(ifma->ifma_addr);
482 	free(ifma);
483 }
484 
485 
486 static int
487 if_delmulti_locked(struct ifnet *ifp, struct ifmultiaddr *ifma,
488 	int detaching)
489 {
490 	ifp = ifma->ifma_ifp;
491 
492 	if (detaching) {
493 		if (ifp != NULL) {
494 			ifma->ifma_ifp = NULL;
495 		}
496 	}
497 
498 	if (--ifma->ifma_refcount > 0)
499 		return 0;
500 
501 	if (ifp != NULL)
502 		TAILQ_REMOVE(&ifp->if_multiaddrs, ifma, ifma_link);
503 
504 	if_freemulti(ifma);
505 
506 	return 1;
507 }
508 
509 
510 void
511 if_purgemaddrs(struct ifnet *ifp)
512 {
513 	struct ifmultiaddr *ifma;
514 	struct ifmultiaddr *next;
515 
516 	IF_ADDR_LOCK(ifp);
517 	TAILQ_FOREACH_SAFE(ifma, &ifp->if_multiaddrs, ifma_link, next)
518 		if_delmulti_locked(ifp, ifma, 1);
519 	IF_ADDR_UNLOCK(ifp);
520 }
521 
522 
523 void
524 if_addr_rlock(struct ifnet *ifp)
525 {
526 	IF_ADDR_LOCK(ifp);
527 }
528 
529 
530 void
531 if_addr_runlock(struct ifnet *ifp)
532 {
533 	IF_ADDR_UNLOCK(ifp);
534 }
535 
536 
537 void
538 if_maddr_rlock(struct ifnet *ifp)
539 {
540 	IF_ADDR_LOCK(ifp);
541 }
542 
543 
544 void
545 if_maddr_runlock(struct ifnet *ifp)
546 {
547 	IF_ADDR_UNLOCK(ifp);
548 }
549 
550 
551 int
552 ether_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst,
553 	struct route *ro)
554 {
555 	int error = 0;
556 	IFQ_HANDOFF(ifp, m, error);
557 	return error;
558 }
559 
560 
561 static void ether_input(struct ifnet *ifp, struct mbuf *m)
562 {
563 	IF_ENQUEUE(&ifp->receive_queue, m);
564 	release_sem_etc(ifp->receive_sem, 1, B_DO_NOT_RESCHEDULE);
565 }
566 
567 
568 void
569 ether_ifattach(struct ifnet *ifp, const uint8_t *macAddress)
570 {
571 	ifp->if_addrlen = ETHER_ADDR_LEN;
572 	ifp->if_hdrlen = ETHER_HDR_LEN;
573 	if_attach(ifp);
574 	ifp->if_mtu = ETHERMTU;
575 	ifp->if_output = ether_output;
576 	ifp->if_input = ether_input;
577 	ifp->if_resolvemulti = NULL; // done in the stack
578 	ifp->if_broadcastaddr = etherbroadcastaddr;
579 
580 	memcpy(IF_LLADDR(ifp), macAddress, ETHER_ADDR_LEN);
581 
582 	// TODO: according to FreeBSD's if_ethersubr.c, this should be removed
583 	//       once all drivers are cleaned up.
584 	if (macAddress != IFP2ENADDR(ifp))
585 		memcpy(IFP2ENADDR(ifp), macAddress, ETHER_ADDR_LEN);
586 }
587 
588 
589 void
590 ether_ifdetach(struct ifnet *ifp)
591 {
592 	if_detach(ifp);
593 }
594 
595 
596 int
597 ether_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
598 {
599 	struct ifreq *ifr = (struct ifreq *) data;
600 
601 	switch (command) {
602 		case SIOCSIFMTU:
603 			if (ifr->ifr_mtu > ETHERMTU)
604 				return EINVAL;
605 			else
606 				;
607 			// need to fix our ifreq to work with C...
608 			// ifp->ifr_mtu = ifr->ifr_mtu;
609 			break;
610 
611 		default:
612 			return EINVAL;
613 	}
614 
615 	return 0;
616 }
617