xref: /haiku/src/libs/compat/freebsd_network/if.c (revision b90c801037e2a351806e0dc7ad45e801fd7bfdec)
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 int ifqmaxlen = IFQ_MAXLEN;
31 
32 
33 #define IFNET_HOLD (void *)(uintptr_t)(-1)
34 
35 
36 static void
37 insert_into_device_name_list(struct ifnet * ifp)
38 {
39 	int i;
40 	for (i = 0; i < MAX_DEVICES; i++) {
41 		if (gDeviceNameList[i] == NULL) {
42 			gDeviceNameList[i] = ifp->device_name;
43 			return;
44 		}
45 	}
46 
47 	panic("too many devices");
48 }
49 
50 
51 static void
52 remove_from_device_name_list(struct ifnet * ifp)
53 {
54 	int i;
55 	for (i = 0; i < MAX_DEVICES; i++) {
56 		if (ifp->device_name == gDeviceNameList[i]) {
57 			int last;
58 			for (last = i + 1; last < MAX_DEVICES; last++) {
59 				if (gDeviceNameList[last] == NULL)
60 					break;
61 			}
62 			last--;
63 
64 			if (i == last)
65 				gDeviceNameList[i] = NULL;
66 			else {
67 				// switch positions with the last entry
68 				gDeviceNameList[i] = gDeviceNameList[last];
69 				gDeviceNameList[last] = NULL;
70 			}
71 			break;
72 		}
73 	}
74 }
75 
76 
77 struct ifnet *
78 ifnet_byindex(u_short idx)
79 {
80 	struct ifnet *ifp;
81 
82 	IFNET_RLOCK_NOSLEEP();
83 	ifp = ifnet_byindex_locked(idx);
84 	IFNET_RUNLOCK_NOSLEEP();
85 
86 	return (ifp);
87 }
88 
89 
90 struct ifnet *
91 ifnet_byindex_locked(u_short idx)
92 {
93 	struct ifnet *ifp;
94 
95 	ifp = gDevices[idx];
96 
97 	return (ifp);
98 }
99 
100 
101 static void
102 ifnet_setbyindex_locked(u_short idx, struct ifnet *ifp)
103 {
104 	gDevices[idx] = ifp;
105 }
106 
107 
108 static void
109 ifnet_setbyindex(u_short idx, struct ifnet *ifp)
110 {
111 	IFNET_WLOCK();
112 	ifnet_setbyindex_locked(idx, ifp);
113 	IFNET_WUNLOCK();
114 }
115 
116 
117 static int
118 ifindex_alloc_locked(u_short *idxp)
119 {
120 	u_short index;
121 
122 	for (index = 0; index < MAX_DEVICES; index++) {
123 		if (gDevices[index] == NULL) {
124 			break;
125 		}
126 	}
127 
128 	if (index == MAX_DEVICES)
129 		return ENOSPC;
130 
131 	gDeviceCount++;
132 	*idxp = index;
133 
134 	return ENOERR;
135 }
136 
137 
138 static void
139 ifindex_free_locked(u_short idx)
140 {
141 	gDevices[idx] = NULL;
142 	gDeviceCount--;
143 }
144 
145 
146 struct ifnet *
147 if_alloc(u_char type)
148 {
149 	char semName[64];
150 	u_short index;
151 
152 	struct ifnet *ifp = _kernel_malloc(sizeof(struct ifnet), M_ZERO);
153 	if (ifp == NULL)
154 		return NULL;
155 
156 	snprintf(semName, sizeof(semName), "%s receive", gDriverName);
157 
158 	ifp->receive_sem = create_sem(0, semName);
159 	if (ifp->receive_sem < B_OK)
160 		goto err1;
161 
162 	switch (type) {
163 		case IFT_ETHER:
164 		{
165 			ifp->if_l2com = _kernel_malloc(sizeof(struct arpcom), M_ZERO);
166 			if (ifp->if_l2com == NULL)
167 				goto err2;
168 			IFP2AC(ifp)->ac_ifp = ifp;
169 			break;
170 		}
171 		case IFT_IEEE80211:
172 		{
173 			if (wlan_if_l2com_alloc(ifp) != B_OK)
174 				goto err2;
175 			break;
176 		}
177 	}
178 
179 	ifp->link_state_sem = -1;
180 	ifp->open_count = 0;
181 	ifp->flags = 0;
182 	ifp->if_type = type;
183 	ifq_init(&ifp->receive_queue, semName);
184 
185 	ifp->scan_done_sem = -1;
186 		// WLAN specific, doesn't hurt when initilized for other devices
187 
188 	// Search for the first free device slot, and use that one
189 	IFNET_WLOCK();
190 	if (ifindex_alloc_locked(&index) != ENOERR) {
191 		IFNET_WUNLOCK();
192 		panic("too many devices");
193 		goto err3;
194 	}
195 	ifnet_setbyindex_locked(index, IFNET_HOLD);
196 	IFNET_WUNLOCK();
197 
198 	ifp->if_index = index;
199 	ifnet_setbyindex(ifp->if_index, ifp);
200 
201 	IF_ADDR_LOCK_INIT(ifp);
202 	return ifp;
203 
204 err3:
205 	switch (type) {
206 		case IFT_ETHER:
207 		case IFT_IEEE80211:
208 			_kernel_free(ifp->if_l2com);
209 			break;
210 	}
211 
212 err2:
213 	delete_sem(ifp->receive_sem);
214 
215 err1:
216 	_kernel_free(ifp);
217 	return NULL;
218 }
219 
220 
221 void
222 if_free(struct ifnet *ifp)
223 {
224 	// IEEE80211 devices won't be in this list,
225 	// so don't try to remove them.
226 	if (ifp->if_type == IFT_ETHER)
227 		remove_from_device_name_list(ifp);
228 
229 	IFNET_WLOCK();
230 	ifindex_free_locked(ifp->if_index);
231 	IFNET_WUNLOCK();
232 
233 	IF_ADDR_LOCK_DESTROY(ifp);
234 	switch (ifp->if_type) {
235 		case IFT_ETHER:
236 		case IFT_IEEE80211:
237 			_kernel_free(ifp->if_l2com);
238 			break;
239 	}
240 
241 	delete_sem(ifp->receive_sem);
242 	ifq_uninit(&ifp->receive_queue);
243 
244 	_kernel_free(ifp);
245 }
246 
247 
248 void
249 if_initname(struct ifnet *ifp, const char *name, int unit)
250 {
251 	dprintf("if_initname(%p, %s, %d)\n", ifp, name, unit);
252 
253 	if (name == NULL || name[0] == '\0')
254 		panic("interface goes unnamed");
255 
256 	ifp->if_dname = name;
257 	ifp->if_dunit = unit;
258 
259 	strlcpy(ifp->if_xname, name, sizeof(ifp->if_xname));
260 
261 	snprintf(ifp->device_name, sizeof(ifp->device_name), "net/%s/%i",
262 		gDriverName, ifp->if_index);
263 
264 	driver_printf("%s: /dev/%s\n", gDriverName, ifp->device_name);
265 
266 	// For wlan devices we only want to see the cloned wlan device
267 	// in the list.
268 	// Remember: For each wlan device, there is a base device of type
269 	//           IFT_IEEE80211. On top of that a clone device is created of
270 	//           type IFT_ETHER.
271 	//           Haiku shall only see the cloned device as it is the one
272 	//           FreeBSD 8 uses for wireless i/o, too.
273 	if (ifp->if_type == IFT_ETHER)
274 		insert_into_device_name_list(ifp);
275 
276 	ifp->root_device = find_root_device(unit);
277 }
278 
279 
280 void
281 ifq_init(struct ifqueue *ifq, const char *name)
282 {
283 	ifq->ifq_head = NULL;
284 	ifq->ifq_tail = NULL;
285 	ifq->ifq_len = 0;
286 	ifq->ifq_maxlen = IFQ_MAXLEN;
287 	ifq->ifq_drops = 0;
288 
289 	mtx_init(&ifq->ifq_mtx, name, NULL, MTX_DEF);
290 }
291 
292 
293 void
294 ifq_uninit(struct ifqueue *ifq)
295 {
296 	mtx_destroy(&ifq->ifq_mtx);
297 }
298 
299 
300 static int
301 if_transmit(struct ifnet *ifp, struct mbuf *m)
302 {
303 	int error;
304 
305 	IFQ_HANDOFF(ifp, m, error);
306 	return (error);
307 }
308 
309 
310 void
311 if_attach(struct ifnet *ifp)
312 {
313 	TAILQ_INIT(&ifp->if_addrhead);
314 	TAILQ_INIT(&ifp->if_prefixhead);
315 	TAILQ_INIT(&ifp->if_multiaddrs);
316 
317 	IF_ADDR_LOCK_INIT(ifp);
318 
319 	ifp->if_lladdr.sdl_family = AF_LINK;
320 
321 	ifq_init((struct ifqueue *) &ifp->if_snd, ifp->if_xname);
322 
323 	if (ifp->if_transmit == NULL) {
324 		ifp->if_transmit = if_transmit;
325 	}
326 }
327 
328 
329 void
330 if_detach(struct ifnet *ifp)
331 {
332 	if (HAIKU_DRIVER_REQUIRES(FBSD_SWI_TASKQUEUE))
333 		taskqueue_drain(taskqueue_swi, &ifp->if_linktask);
334 
335 	IF_ADDR_LOCK_DESTROY(ifp);
336 	ifq_uninit((struct ifqueue *) &ifp->if_snd);
337 }
338 
339 
340 void
341 if_start(struct ifnet *ifp)
342 {
343 #ifdef IFF_NEEDSGIANT
344 	if (ifp->if_flags & IFF_NEEDSGIANT)
345 	panic("freebsd compat.: unsupported giant requirement");
346 #endif
347 	ifp->if_start(ifp);
348 }
349 
350 
351 int
352 if_printf(struct ifnet *ifp, const char *format, ...)
353 {
354 	char buf[256];
355 	va_list vl;
356 	va_start(vl, format);
357 	vsnprintf(buf, sizeof(buf), format, vl);
358 	va_end(vl);
359 
360 	dprintf("[%s] %s", ifp->device_name, buf);
361 	return 0;
362 }
363 
364 
365 void
366 if_link_state_change(struct ifnet *ifp, int linkState)
367 {
368 	if (ifp->if_link_state == linkState)
369 		return;
370 
371 	ifp->if_link_state = linkState;
372 	release_sem_etc(ifp->link_state_sem, 1, B_DO_NOT_RESCHEDULE);
373 }
374 
375 
376 static struct ifmultiaddr *
377 if_findmulti(struct ifnet *ifp, struct sockaddr *_address)
378 {
379 	struct sockaddr_dl *address = (struct sockaddr_dl *) _address;
380 	struct ifmultiaddr *ifma;
381 
382 	TAILQ_FOREACH (ifma, &ifp->if_multiaddrs, ifma_link) {
383 		if (memcmp(LLADDR(address),
384 			LLADDR((struct sockaddr_dl *)ifma->ifma_addr), ETHER_ADDR_LEN) == 0)
385 			return ifma;
386 	}
387 
388 	return NULL;
389 }
390 
391 
392 /*
393  * if_freemulti: free ifmultiaddr structure and possibly attached related
394  * addresses.  The caller is responsible for implementing reference
395  * counting, notifying the driver, handling routing messages, and releasing
396  * any dependent link layer state.
397  */
398 static void
399 if_freemulti(struct ifmultiaddr *ifma)
400 {
401 
402 	KASSERT(ifma->ifma_refcount == 0, ("if_freemulti: refcount %d",
403 	    ifma->ifma_refcount));
404 	KASSERT(ifma->ifma_protospec == NULL,
405 	    ("if_freemulti: protospec not NULL"));
406 
407 	if (ifma->ifma_lladdr != NULL)
408 		free(ifma->ifma_lladdr);
409 	free(ifma->ifma_addr);
410 	free(ifma);
411 }
412 
413 
414 static struct ifmultiaddr *
415 _if_addmulti(struct ifnet *ifp, struct sockaddr *address)
416 {
417 	struct ifmultiaddr *addr = if_findmulti(ifp, address);
418 
419 	if (addr != NULL) {
420 		addr->ifma_refcount++;
421 		return addr;
422 	}
423 
424 	addr = (struct ifmultiaddr *) malloc(sizeof(struct ifmultiaddr));
425 	if (addr == NULL)
426 		return NULL;
427 
428 	addr->ifma_lladdr = NULL;
429 	addr->ifma_ifp = ifp;
430 	addr->ifma_protospec = NULL;
431 
432 	memcpy(&addr->ifma_addr_storage, address, sizeof(struct sockaddr_dl));
433 	addr->ifma_addr = (struct sockaddr *) &addr->ifma_addr_storage;
434 
435 	addr->ifma_refcount = 1;
436 
437 	TAILQ_INSERT_HEAD(&ifp->if_multiaddrs, addr, ifma_link);
438 
439 	return addr;
440 }
441 
442 
443 int
444 if_addmulti(struct ifnet *ifp, struct sockaddr *address,
445 	struct ifmultiaddr **out)
446 {
447 	struct ifmultiaddr *result;
448 	int refcount = 0;
449 
450 	IF_ADDR_LOCK(ifp);
451 	result = _if_addmulti(ifp, address);
452 	if (result)
453 		refcount = result->ifma_refcount;
454 	IF_ADDR_UNLOCK(ifp);
455 
456 	if (result == NULL)
457 		return ENOBUFS;
458 
459 	if (refcount == 1 && ifp->if_ioctl != NULL)
460 		ifp->if_ioctl(ifp, SIOCADDMULTI, NULL);
461 
462 	if (out)
463 		(*out) = result;
464 
465 	return 0;
466 }
467 
468 
469 static int
470 if_delmulti_locked(struct ifnet *ifp, struct ifmultiaddr *ifma, int detaching)
471 {
472 	struct ifmultiaddr *ll_ifma;
473 
474 	if (ifp != NULL && ifma->ifma_ifp != NULL) {
475 		KASSERT(ifma->ifma_ifp == ifp,
476 		    ("%s: inconsistent ifp %p", __func__, ifp));
477 		IF_ADDR_LOCK_ASSERT(ifp);
478 	}
479 
480 	ifp = ifma->ifma_ifp;
481 
482 	/*
483 	 * If the ifnet is detaching, null out references to ifnet,
484 	 * so that upper protocol layers will notice, and not attempt
485 	 * to obtain locks for an ifnet which no longer exists. The
486 	 * routing socket announcement must happen before the ifnet
487 	 * instance is detached from the system.
488 	 */
489 	if (detaching) {
490 #ifdef DIAGNOSTIC
491 		printf("%s: detaching ifnet instance %p\n", __func__, ifp);
492 #endif
493 		/*
494 		 * ifp may already be nulled out if we are being reentered
495 		 * to delete the ll_ifma.
496 		 */
497 		if (ifp != NULL) {
498 #ifndef __HAIKU__
499 			rt_newmaddrmsg(RTM_DELMADDR, ifma);
500 #endif
501 			ifma->ifma_ifp = NULL;
502 		}
503 	}
504 
505 	if (--ifma->ifma_refcount > 0)
506 		return 0;
507 
508 #ifndef __HAIKU__
509 	/*
510 	 * If this ifma is a network-layer ifma, a link-layer ifma may
511 	 * have been associated with it. Release it first if so.
512 	 */
513 	ll_ifma = ifma->ifma_llifma;
514 	if (ll_ifma != NULL) {
515 		KASSERT(ifma->ifma_lladdr != NULL,
516 		    ("%s: llifma w/o lladdr", __func__));
517 		if (detaching)
518 			ll_ifma->ifma_ifp = NULL;	/* XXX */
519 		if (--ll_ifma->ifma_refcount == 0) {
520 			if (ifp != NULL) {
521 				TAILQ_REMOVE(&ifp->if_multiaddrs, ll_ifma,
522 				    ifma_link);
523 			}
524 			if_freemulti(ll_ifma);
525 		}
526 	}
527 #endif
528 
529 	if (ifp != NULL)
530 		TAILQ_REMOVE(&ifp->if_multiaddrs, ifma, ifma_link);
531 
532 	if_freemulti(ifma);
533 
534 	/*
535 	 * The last reference to this instance of struct ifmultiaddr
536 	 * was released; the hardware should be notified of this change.
537 	 */
538 	return 1;
539 }
540 
541 
542 /*
543  * Delete all multicast group membership for an interface.
544  * Should be used to quickly flush all multicast filters.
545  */
546 void
547 if_delallmulti(struct ifnet *ifp)
548 {
549 	struct ifmultiaddr *ifma;
550 	struct ifmultiaddr *next;
551 
552 	IF_ADDR_LOCK(ifp);
553 	TAILQ_FOREACH_SAFE(ifma, &ifp->if_multiaddrs, ifma_link, next)
554 		if_delmulti_locked(ifp, ifma, 0);
555 	IF_ADDR_UNLOCK(ifp);
556 }
557 
558 
559 static void
560 if_delete_multiaddr(struct ifnet *ifp, struct ifmultiaddr *ifma)
561 {
562 	TAILQ_REMOVE(&ifp->if_multiaddrs, ifma, ifma_link);
563 	free(ifma);
564 }
565 
566 
567 int
568 if_delmulti(struct ifnet *ifp, struct sockaddr *sa)
569 {
570 	struct ifmultiaddr *ifma;
571 	int lastref;
572 #ifdef INVARIANTS
573 	struct ifnet *oifp;
574 
575 	IFNET_RLOCK_NOSLEEP();
576 	TAILQ_FOREACH(oifp, &V_ifnet, if_link)
577 		if (ifp == oifp)
578 			break;
579 	if (ifp != oifp)
580 		ifp = NULL;
581 	IFNET_RUNLOCK_NOSLEEP();
582 
583 	KASSERT(ifp != NULL, ("%s: ifnet went away", __func__));
584 #endif
585 	if (ifp == NULL)
586 		return (ENOENT);
587 
588 	IF_ADDR_LOCK(ifp);
589 	lastref = 0;
590 	ifma = if_findmulti(ifp, sa);
591 	if (ifma != NULL)
592 		lastref = if_delmulti_locked(ifp, ifma, 0);
593 	IF_ADDR_UNLOCK(ifp);
594 
595 	if (ifma == NULL)
596 		return (ENOENT);
597 
598 	if (lastref && ifp->if_ioctl != NULL) {
599 		(void)(*ifp->if_ioctl)(ifp, SIOCDELMULTI, 0);
600 	}
601 
602 	return (0);
603 }
604 
605 
606 void
607 if_purgemaddrs(struct ifnet *ifp)
608 {
609 	struct ifmultiaddr *ifma;
610 	struct ifmultiaddr *next;
611 
612 	IF_ADDR_LOCK(ifp);
613 	TAILQ_FOREACH_SAFE(ifma, &ifp->if_multiaddrs, ifma_link, next)
614 		if_delmulti_locked(ifp, ifma, 1);
615 	IF_ADDR_UNLOCK(ifp);
616 }
617 
618 
619 void
620 if_addr_rlock(struct ifnet *ifp)
621 {
622 	IF_ADDR_LOCK(ifp);
623 }
624 
625 
626 void
627 if_addr_runlock(struct ifnet *ifp)
628 {
629 	IF_ADDR_UNLOCK(ifp);
630 }
631 
632 
633 void
634 if_maddr_rlock(struct ifnet *ifp)
635 {
636 	IF_ADDR_LOCK(ifp);
637 }
638 
639 
640 void
641 if_maddr_runlock(struct ifnet *ifp)
642 {
643 	IF_ADDR_UNLOCK(ifp);
644 }
645 
646 
647 int
648 ether_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst,
649 	struct route *ro)
650 {
651 	int error = 0;
652 	IFQ_HANDOFF(ifp, m, error);
653 	return error;
654 }
655 
656 
657 static void ether_input(struct ifnet *ifp, struct mbuf *m)
658 {
659 	IF_ENQUEUE(&ifp->receive_queue, m);
660 	release_sem_etc(ifp->receive_sem, 1, B_DO_NOT_RESCHEDULE);
661 }
662 
663 
664 void
665 ether_ifattach(struct ifnet *ifp, const uint8_t *macAddress)
666 {
667 	ifp->if_addrlen = ETHER_ADDR_LEN;
668 	ifp->if_hdrlen = ETHER_HDR_LEN;
669 	if_attach(ifp);
670 	ifp->if_mtu = ETHERMTU;
671 	ifp->if_output = ether_output;
672 	ifp->if_input = ether_input;
673 	ifp->if_resolvemulti = NULL; // done in the stack
674 	ifp->if_broadcastaddr = etherbroadcastaddr;
675 
676 	memcpy(IF_LLADDR(ifp), macAddress, ETHER_ADDR_LEN);
677 
678 	// TODO: according to FreeBSD's if_ethersubr.c, this should be removed
679 	//       once all drivers are cleaned up.
680 	if (macAddress != IFP2ENADDR(ifp))
681 		memcpy(IFP2ENADDR(ifp), macAddress, ETHER_ADDR_LEN);
682 }
683 
684 
685 void
686 ether_ifdetach(struct ifnet *ifp)
687 {
688 	if_detach(ifp);
689 }
690 
691 
692 int
693 ether_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
694 {
695 	struct ifreq *ifr = (struct ifreq *) data;
696 
697 	switch (command) {
698 		case SIOCSIFMTU:
699 			if (ifr->ifr_mtu > ETHERMTU)
700 				return EINVAL;
701 			else
702 				;
703 			// need to fix our ifreq to work with C...
704 			// ifp->ifr_mtu = ifr->ifr_mtu;
705 			break;
706 
707 		default:
708 			return EINVAL;
709 	}
710 
711 	return 0;
712 }
713