]> git.saurik.com Git - apple/xnu.git/blob - bsd/net/ndrv.c
xnu-1228.5.18.tar.gz
[apple/xnu.git] / bsd / net / ndrv.c
1 /*
2 * Copyright (c) 1997-2007 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28 /*
29 * @(#)ndrv.c 1.1 (MacOSX) 6/10/43
30 * Justin Walker, 970604
31 * AF_NDRV support
32 * 980130 - Cleanup, reorg, performance improvemements
33 * 000816 - Removal of Y adapter cruft
34 */
35
36 /*
37 * PF_NDRV allows raw access to a specified network device, directly
38 * with a socket. Expected use involves a socket option to request
39 * protocol packets. This lets ndrv_output() call ifnet_output(), and
40 * lets DLIL find the proper recipient for incoming packets.
41 * The purpose here is for user-mode protocol implementation.
42 * Note that "pure raw access" will still be accomplished with BPF.
43 *
44 * In addition to the former use, when combined with socket NKEs,
45 * PF_NDRV permits a fairly flexible mechanism for implementing
46 * strange protocol support. One of the main ones will be the
47 * BlueBox/Classic Shared IP Address support.
48 */
49 #include <mach/mach_types.h>
50
51 #include <sys/param.h>
52 #include <sys/systm.h>
53 #include <sys/kernel.h>
54 #include <sys/malloc.h>
55 #include <sys/mbuf.h>
56 #include <sys/protosw.h>
57 #include <sys/domain.h>
58 #include <sys/socket.h>
59 #include <sys/socketvar.h>
60 #include <sys/ioctl.h>
61 #include <sys/errno.h>
62 #include <sys/syslog.h>
63 #include <sys/proc.h>
64
65 #include <kern/queue.h>
66
67 #include <net/ndrv.h>
68 #include <net/route.h>
69 #include <net/if_llc.h>
70 #include <net/if_dl.h>
71 #include <net/if_types.h>
72 #include <net/ndrv_var.h>
73 #include <net/dlil.h>
74
75 #if INET
76 #include <netinet/in.h>
77 #include <netinet/in_var.h>
78 #endif
79 #include <netinet/if_ether.h>
80
81 #include <machine/spl.h>
82
83 static int ndrv_do_detach(struct ndrv_cb *);
84 static int ndrv_do_disconnect(struct ndrv_cb *);
85 static struct ndrv_cb *ndrv_find_inbound(struct ifnet *ifp, u_long protocol_family);
86 static int ndrv_setspec(struct ndrv_cb *np, struct sockopt *sopt);
87 static int ndrv_delspec(struct ndrv_cb *);
88 static int ndrv_to_ifnet_demux(struct ndrv_demux_desc* ndrv, struct ifnet_demux_desc* ifdemux);
89 static void ndrv_handle_ifp_detach(u_long family, short unit);
90 static int ndrv_do_add_multicast(struct ndrv_cb *np, struct sockopt *sopt);
91 static int ndrv_do_remove_multicast(struct ndrv_cb *np, struct sockopt *sopt);
92 static struct ndrv_multiaddr* ndrv_have_multicast(struct ndrv_cb *np, struct sockaddr* addr);
93 static void ndrv_remove_all_multicast(struct ndrv_cb *np);
94 static void ndrv_dominit(void) __attribute__((section("__TEXT, initcode")));
95
96 unsigned long ndrv_sendspace = NDRVSNDQ;
97 unsigned long ndrv_recvspace = NDRVRCVQ;
98 TAILQ_HEAD(, ndrv_cb) ndrvl = TAILQ_HEAD_INITIALIZER(ndrvl);
99
100 extern struct domain ndrvdomain;
101 extern struct protosw ndrvsw;
102 extern lck_mtx_t *domain_proto_mtx;
103
104 /*
105 * Verify these values match.
106 * To keep clients from including dlil.h, we define
107 * these values independently in ndrv.h. They must
108 * match or a conversion function must be written.
109 */
110 #if NDRV_DEMUXTYPE_ETHERTYPE != DLIL_DESC_ETYPE2
111 #error NDRV_DEMUXTYPE_ETHERTYPE must match DLIL_DESC_ETYPE2
112 #endif
113 #if NDRV_DEMUXTYPE_SAP != DLIL_DESC_SAP
114 #error NDRV_DEMUXTYPE_SAP must match DLIL_DESC_SAP
115 #endif
116 #if NDRV_DEMUXTYPE_SNAP != DLIL_DESC_SNAP
117 #error NDRV_DEMUXTYPE_SNAP must match DLIL_DESC_SNAP
118 #endif
119
120 /*
121 * Protocol output - Called to output a raw network packet directly
122 * to the driver.
123 */
124 static int
125 ndrv_output(struct mbuf *m, struct socket *so)
126 {
127 struct ndrv_cb *np = sotondrvcb(so);
128 struct ifnet *ifp = np->nd_if;
129 int result = 0;
130
131 #if NDRV_DEBUG
132 kprintf("NDRV output: %x, %x, %x\n", m, so, np);
133 #endif
134
135 /*
136 * No header is a format error
137 */
138 if ((m->m_flags&M_PKTHDR) == 0)
139 return(EINVAL);
140
141 /* Unlock before calling ifnet_output */
142 socket_unlock(so, 0);
143
144 /*
145 * Call DLIL if we can. DLIL is much safer than calling the
146 * ifp directly.
147 */
148 result = ifnet_output_raw(ifp, np->nd_proto_family, m);
149
150 socket_lock(so, 0);
151
152 return (result);
153 }
154
155 /* Our input routine called from DLIL */
156 static errno_t
157 ndrv_input(
158 ifnet_t ifp,
159 protocol_family_t proto_family,
160 mbuf_t m,
161 char *frame_header)
162 {
163 struct socket *so;
164 struct sockaddr_dl ndrvsrc;
165 struct ndrv_cb *np;
166 int error = 0;
167
168 ndrvsrc.sdl_len = sizeof (struct sockaddr_dl);
169 ndrvsrc.sdl_family = AF_NDRV;
170 ndrvsrc.sdl_index = 0;
171
172 /* move packet from if queue to socket */
173 /* Should be media-independent */
174 ndrvsrc.sdl_type = IFT_ETHER;
175 ndrvsrc.sdl_nlen = 0;
176 ndrvsrc.sdl_alen = 6;
177 ndrvsrc.sdl_slen = 0;
178 bcopy(frame_header, &ndrvsrc.sdl_data, 6);
179
180 np = ndrv_find_inbound(ifp, proto_family);
181 if (np == NULL)
182 {
183 return(ENOENT);
184 }
185 so = np->nd_socket;
186 /* prepend the frame header */
187 m = m_prepend(m, ifnet_hdrlen(ifp), M_NOWAIT);
188 if (m == NULL)
189 return EJUSTRETURN;
190 bcopy(frame_header, m->m_data, ifnet_hdrlen(ifp));
191
192 lck_mtx_assert(so->so_proto->pr_domain->dom_mtx, LCK_MTX_ASSERT_NOTOWNED);
193 lck_mtx_lock(so->so_proto->pr_domain->dom_mtx);
194 if (sbappendaddr(&(so->so_rcv), (struct sockaddr *)&ndrvsrc,
195 m, (struct mbuf *)0, &error) != 0) {
196 sorwakeup(so);
197 }
198 lck_mtx_unlock(so->so_proto->pr_domain->dom_mtx);
199 return 0; /* radar 4030377 - always return 0 */
200 }
201
202 /*
203 * Allocate an ndrv control block and some buffer space for the socket
204 */
205 static int
206 ndrv_attach(struct socket *so, int proto, __unused struct proc *p)
207 {
208 int error;
209 struct ndrv_cb *np = sotondrvcb(so);
210
211 if ((so->so_state & SS_PRIV) == 0)
212 return(EPERM);
213
214 #if NDRV_DEBUG
215 kprintf("NDRV attach: %x, %x, %x\n", so, proto, np);
216 #endif
217
218 if ((error = soreserve(so, ndrv_sendspace, ndrv_recvspace)))
219 return(error);
220
221 MALLOC(np, struct ndrv_cb *, sizeof(*np), M_PCB, M_WAITOK);
222 if (np == NULL)
223 return (ENOMEM);
224 so->so_pcb = (caddr_t)np;
225 bzero(np, sizeof(*np));
226 #if NDRV_DEBUG
227 kprintf("NDRV attach: %x, %x, %x\n", so, proto, np);
228 #endif
229 TAILQ_INIT(&np->nd_dlist);
230 np->nd_signature = NDRV_SIGNATURE;
231 np->nd_socket = so;
232 np->nd_proto.sp_family = so->so_proto->pr_domain->dom_family;
233 np->nd_proto.sp_protocol = proto;
234 np->nd_if = NULL;
235 np->nd_proto_family = 0;
236 np->nd_family = 0;
237 np->nd_unit = 0;
238 TAILQ_INSERT_TAIL(&ndrvl, np, nd_next);
239 return(0);
240 }
241
242 /*
243 * Destroy state just before socket deallocation.
244 * Flush data or not depending on the options.
245 */
246
247 static int
248 ndrv_detach(struct socket *so)
249 {
250 struct ndrv_cb *np = sotondrvcb(so);
251
252 if (np == 0)
253 return EINVAL;
254 return ndrv_do_detach(np);
255 }
256
257
258 /*
259 * If a socket isn't bound to a single address,
260 * the ndrv input routine will hand it anything
261 * within that protocol family (assuming there's
262 * nothing else around it should go to).
263 *
264 * Don't expect this to be used.
265 */
266
267 static int
268 ndrv_connect(struct socket *so, struct sockaddr *nam, __unused struct proc *p)
269 {
270 struct ndrv_cb *np = sotondrvcb(so);
271 int result = 0;
272
273 if (np == 0)
274 return EINVAL;
275
276 if (np->nd_faddr)
277 return EISCONN;
278
279 /* Allocate memory to store the remote address */
280 MALLOC(np->nd_faddr, struct sockaddr_ndrv*,
281 nam->sa_len, M_IFADDR, M_WAITOK);
282 if (result != 0)
283 return result;
284 if (np->nd_faddr == NULL)
285 return ENOMEM;
286
287 bcopy((caddr_t) nam, (caddr_t) np->nd_faddr, nam->sa_len);
288 soisconnected(so);
289 return 0;
290 }
291
292 static void
293 ndrv_event(struct ifnet *ifp, __unused protocol_family_t protocol,
294 const struct kev_msg *event)
295 {
296 if (event->vendor_code == KEV_VENDOR_APPLE &&
297 event->kev_class == KEV_NETWORK_CLASS &&
298 event->kev_subclass == KEV_DL_SUBCLASS &&
299 event->event_code == KEV_DL_IF_DETACHING) {
300 lck_mtx_assert(ndrvdomain.dom_mtx, LCK_MTX_ASSERT_NOTOWNED);
301 lck_mtx_lock(ndrvdomain.dom_mtx);
302 ndrv_handle_ifp_detach(ifnet_family(ifp), ifnet_unit(ifp));
303 lck_mtx_unlock(ndrvdomain.dom_mtx);
304 }
305 }
306
307 static int name_cmp(struct ifnet *, char *);
308
309 /*
310 * This is the "driver open" hook - we 'bind' to the
311 * named driver.
312 * Here's where we latch onto the driver.
313 */
314 static int
315 ndrv_bind(struct socket *so, struct sockaddr *nam, __unused struct proc *p)
316 {
317 struct sockaddr_ndrv *sa = (struct sockaddr_ndrv *) nam;
318 char *dname;
319 struct ndrv_cb *np;
320 struct ifnet *ifp;
321 int result;
322
323 if TAILQ_EMPTY(&ifnet_head)
324 return(EADDRNOTAVAIL); /* Quick sanity check */
325 np = sotondrvcb(so);
326 if (np == 0)
327 return EINVAL;
328
329 if (np->nd_laddr)
330 return EINVAL; /* XXX */
331
332 /* I think we just latch onto a copy here; the caller frees */
333 np->nd_laddr = _MALLOC(sizeof(struct sockaddr_ndrv), M_IFADDR, M_WAITOK);
334 if (np->nd_laddr == NULL)
335 return(ENOMEM);
336 bcopy((caddr_t) sa, (caddr_t) np->nd_laddr, sizeof(struct sockaddr_ndrv));
337 dname = (char *) sa->snd_name;
338 if (*dname == '\0')
339 return(EINVAL);
340 #if NDRV_DEBUG
341 kprintf("NDRV bind: %x, %x, %s\n", so, np, dname);
342 #endif
343 /* Track down the driver and its ifnet structure.
344 * There's no internal call for this so we have to dup the code
345 * in if.c/ifconf()
346 */
347 ifnet_head_lock_shared();
348 TAILQ_FOREACH(ifp, &ifnet_head, if_link) {
349 if (name_cmp(ifp, dname) == 0)
350 break;
351 }
352 ifnet_head_done();
353
354 if (ifp == NULL)
355 return(EADDRNOTAVAIL);
356
357 // PPP doesn't support PF_NDRV.
358 if (ifnet_family(ifp) != APPLE_IF_FAM_PPP)
359 {
360 /* NDRV on this interface */
361 struct ifnet_attach_proto_param ndrv_proto;
362 result = 0;
363 bzero(&ndrv_proto, sizeof(ndrv_proto));
364 ndrv_proto.event = ndrv_event;
365
366 /* We aren't worried about double attaching, that should just return an error */
367 socket_unlock(so, 0);
368 result = ifnet_attach_protocol(ifp, PF_NDRV, &ndrv_proto);
369 socket_lock(so, 0);
370 if (result && result != EEXIST) {
371 return result;
372 }
373 np->nd_proto_family = PF_NDRV;
374 }
375 else {
376 np->nd_proto_family = 0;
377 }
378
379 np->nd_if = ifp;
380 np->nd_family = ifnet_family(ifp);
381 np->nd_unit = ifnet_unit(ifp);
382
383 return(0);
384 }
385
386 static int
387 ndrv_disconnect(struct socket *so)
388 {
389 struct ndrv_cb *np = sotondrvcb(so);
390
391 if (np == 0)
392 return EINVAL;
393
394 if (np->nd_faddr == 0)
395 return ENOTCONN;
396
397 ndrv_do_disconnect(np);
398 return 0;
399 }
400
401 /*
402 * Mark the connection as being incapable of further input.
403 */
404 static int
405 ndrv_shutdown(struct socket *so)
406 {
407 lck_mtx_assert(so->so_proto->pr_domain->dom_mtx, LCK_MTX_ASSERT_OWNED);
408 socantsendmore(so);
409 return 0;
410 }
411
412 /*
413 * Ship a packet out. The ndrv output will pass it
414 * to the appropriate driver. The really tricky part
415 * is the destination address...
416 */
417 static int
418 ndrv_send(struct socket *so, __unused int flags, struct mbuf *m,
419 __unused struct sockaddr *addr, struct mbuf *control,
420 __unused struct proc *p)
421 {
422 int error;
423
424 if (control)
425 return EOPNOTSUPP;
426
427 error = ndrv_output(m, so);
428 m = NULL;
429 return error;
430 }
431
432
433 static int
434 ndrv_abort(struct socket *so)
435 {
436 struct ndrv_cb *np = sotondrvcb(so);
437
438 if (np == 0)
439 return EINVAL;
440
441 ndrv_do_disconnect(np);
442 return 0;
443 }
444
445 static int
446 ndrv_sockaddr(struct socket *so, struct sockaddr **nam)
447 {
448 struct ndrv_cb *np = sotondrvcb(so);
449 int len;
450
451 if (np == 0)
452 return EINVAL;
453
454 if (np->nd_laddr == 0)
455 return EINVAL;
456
457 len = np->nd_laddr->snd_len;
458 MALLOC(*nam, struct sockaddr *, len, M_SONAME, M_WAITOK);
459 if (*nam == NULL)
460 return ENOMEM;
461 bcopy((caddr_t)np->nd_laddr, *nam,
462 (unsigned)len);
463 return 0;
464 }
465
466
467 static int
468 ndrv_peeraddr(struct socket *so, struct sockaddr **nam)
469 {
470 struct ndrv_cb *np = sotondrvcb(so);
471 int len;
472
473 if (np == 0)
474 return EINVAL;
475
476 if (np->nd_faddr == 0)
477 return ENOTCONN;
478
479 len = np->nd_faddr->snd_len;
480 MALLOC(*nam, struct sockaddr *, len, M_SONAME, M_WAITOK);
481 if (*nam == NULL)
482 return ENOMEM;
483 bcopy((caddr_t)np->nd_faddr, *nam,
484 (unsigned)len);
485 return 0;
486 }
487
488
489 /* Control output */
490
491 static int
492 ndrv_ctloutput(struct socket *so, struct sockopt *sopt)
493 {
494 struct ndrv_cb *np = sotondrvcb(so);
495 int error = 0;
496
497 switch(sopt->sopt_name)
498 {
499 case NDRV_DELDMXSPEC: /* Delete current spec */
500 /* Verify no parameter was passed */
501 if (sopt->sopt_val != 0 || sopt->sopt_valsize != 0) {
502 /*
503 * We don't support deleting a specific demux, it's
504 * all or nothing.
505 */
506 return EINVAL;
507 }
508 error = ndrv_delspec(np);
509 break;
510 case NDRV_SETDMXSPEC: /* Set protocol spec */
511 error = ndrv_setspec(np, sopt);
512 break;
513 case NDRV_ADDMULTICAST:
514 error = ndrv_do_add_multicast(np, sopt);
515 break;
516 case NDRV_DELMULTICAST:
517 error = ndrv_do_remove_multicast(np, sopt);
518 break;
519 default:
520 error = ENOTSUP;
521 }
522 #ifdef NDRV_DEBUG
523 log(LOG_WARNING, "NDRV CTLOUT: %x returns %d\n", sopt->sopt_name,
524 error);
525 #endif
526 return(error);
527 }
528
529 static int
530 ndrv_do_detach(struct ndrv_cb *np)
531 {
532 struct ndrv_cb* cur_np = NULL;
533 struct socket *so = np->nd_socket;
534 int error = 0;
535 struct ifnet * ifp;
536
537 #if NDRV_DEBUG
538 kprintf("NDRV detach: %x, %x\n", so, np);
539 #endif
540 ndrv_remove_all_multicast(np);
541
542 ifp = np->nd_if;
543 /* Remove from the linked list of control blocks */
544 TAILQ_REMOVE(&ndrvl, np, nd_next);
545 if (ifp != NULL) {
546 u_long proto_family = np->nd_proto_family;
547
548 if (proto_family != PF_NDRV && proto_family != 0) {
549 socket_unlock(so, 0);
550 ifnet_detach_protocol(ifp, proto_family);
551 socket_lock(so, 0);
552 }
553
554 /* Check if this is the last socket attached to this interface */
555 TAILQ_FOREACH(cur_np, &ndrvl, nd_next) {
556 if (cur_np->nd_family == np->nd_family &&
557 cur_np->nd_unit == np->nd_unit) {
558 break;
559 }
560 }
561
562 /* If there are no other interfaces, detach PF_NDRV from the interface */
563 if (cur_np == NULL) {
564 socket_unlock(so, 0);
565 ifnet_detach_protocol(ifp, PF_NDRV);
566 socket_lock(so, 0);
567 }
568 }
569 if (np->nd_laddr != NULL) {
570 FREE((caddr_t)np->nd_laddr, M_IFADDR);
571 np->nd_laddr = NULL;
572 }
573 FREE((caddr_t)np, M_PCB);
574 so->so_pcb = 0;
575 so->so_flags |= SOF_PCBCLEARING;
576 sofree(so);
577 return error;
578 }
579
580 static int
581 ndrv_do_disconnect(struct ndrv_cb *np)
582 {
583 struct socket * so = np->nd_socket;
584 #if NDRV_DEBUG
585 kprintf("NDRV disconnect: %x\n", np);
586 #endif
587 if (np->nd_faddr)
588 {
589 FREE(np->nd_faddr, M_IFADDR);
590 np->nd_faddr = 0;
591 }
592 if (so->so_state & SS_NOFDREF)
593 ndrv_do_detach(np);
594 soisdisconnected(so);
595 return(0);
596 }
597
598 /* Hackery - return a string version of a decimal number */
599 static char *
600 sprint_d(u_int n, char *buf, int buflen)
601 { char dbuf[IFNAMSIZ];
602 char *cp = dbuf+IFNAMSIZ-1;
603
604 *cp = 0;
605 do { buflen--;
606 cp--;
607 *cp = "0123456789"[n % 10];
608 n /= 10;
609 } while (n != 0 && buflen > 0);
610 strncpy(buf, cp, IFNAMSIZ-buflen);
611 return (cp);
612 }
613
614 /*
615 * Try to compare a device name (q) with one of the funky ifnet
616 * device names (ifp).
617 */
618 static int name_cmp(struct ifnet *ifp, char *q)
619 { char *r;
620 int len;
621 char buf[IFNAMSIZ];
622
623 r = buf;
624 len = strlen(ifnet_name(ifp));
625 strncpy(r, ifnet_name(ifp), IFNAMSIZ);
626 r += len;
627 (void)sprint_d(ifnet_unit(ifp), r, IFNAMSIZ-(r-buf));
628 #if NDRV_DEBUG
629 kprintf("Comparing %s, %s\n", buf, q);
630 #endif
631 return(strncmp(buf, q, IFNAMSIZ));
632 }
633
634 #if 0
635 //### Not used
636 /*
637 * When closing, dump any enqueued mbufs.
638 */
639 void
640 ndrv_flushq(struct ifqueue *q)
641 {
642 struct mbuf *m;
643 for (;;)
644 {
645 IF_DEQUEUE(q, m);
646 if (m == NULL)
647 break;
648 IF_DROP(q);
649 if (m)
650 m_freem(m);
651 }
652 }
653 #endif
654
655 int
656 ndrv_setspec(struct ndrv_cb *np, struct sockopt *sopt)
657 {
658 struct ifnet_attach_proto_param proto_param;
659 struct ndrv_protocol_desc ndrvSpec;
660 struct ndrv_demux_desc* ndrvDemux = NULL;
661 int error = 0;
662 struct socket * so = np->nd_socket;
663 user_addr_t user_addr;
664
665 /* Sanity checking */
666 if (np->nd_proto_family != PF_NDRV)
667 return EBUSY;
668 if (np->nd_if == NULL)
669 return EINVAL;
670
671 /* Copy the ndrvSpec */
672 if (proc_is64bit(current_proc())) {
673 struct ndrv_protocol_desc64 ndrvSpec64;
674
675 if (sopt->sopt_valsize != sizeof(ndrvSpec64))
676 return EINVAL;
677
678 error = sooptcopyin(sopt, &ndrvSpec64, sizeof(ndrvSpec64), sizeof(ndrvSpec64));
679 if (error != 0)
680 return error;
681
682 ndrvSpec.version = ndrvSpec64.version;
683 ndrvSpec.protocol_family = ndrvSpec64.protocol_family;
684 ndrvSpec.demux_count = ndrvSpec64.demux_count;
685
686 user_addr = ndrvSpec64.demux_list;
687 }
688 else {
689 if (sopt->sopt_valsize != sizeof(ndrvSpec))
690 return EINVAL;
691
692 error = sooptcopyin(sopt, &ndrvSpec, sizeof(ndrvSpec), sizeof(ndrvSpec));
693 if (error != 0)
694 return error;
695
696 user_addr = CAST_USER_ADDR_T(ndrvSpec.demux_list);
697 }
698
699 /* Verify the parameter */
700 if (ndrvSpec.version > NDRV_PROTOCOL_DESC_VERS)
701 return ENOTSUP; // version is too new!
702 else if (ndrvSpec.version < 1)
703 return EINVAL; // version is not valid
704
705 bzero(&proto_param, sizeof(proto_param));
706 proto_param.demux_count = ndrvSpec.demux_count;
707
708 /* Allocate storage for demux array */
709 MALLOC(ndrvDemux, struct ndrv_demux_desc*, proto_param.demux_count *
710 sizeof(struct ndrv_demux_desc), M_TEMP, M_WAITOK);
711 if (ndrvDemux == NULL)
712 return ENOMEM;
713
714 /* Allocate enough ifnet_demux_descs */
715 MALLOC(proto_param.demux_array, struct ifnet_demux_desc*,
716 sizeof(*proto_param.demux_array) * ndrvSpec.demux_count,
717 M_TEMP, M_WAITOK);
718 if (proto_param.demux_array == NULL)
719 error = ENOMEM;
720
721 if (error == 0)
722 {
723 /* Copy the ndrv demux array from userland */
724 error = copyin(user_addr, ndrvDemux,
725 ndrvSpec.demux_count * sizeof(struct ndrv_demux_desc));
726 ndrvSpec.demux_list = ndrvDemux;
727 }
728
729 if (error == 0)
730 {
731 /* At this point, we've at least got enough bytes to start looking around */
732 u_long demuxOn = 0;
733
734 proto_param.demux_count = ndrvSpec.demux_count;
735 proto_param.input = ndrv_input;
736 proto_param.event = ndrv_event;
737
738 for (demuxOn = 0; demuxOn < ndrvSpec.demux_count; demuxOn++)
739 {
740 /* Convert an ndrv_demux_desc to a ifnet_demux_desc */
741 error = ndrv_to_ifnet_demux(&ndrvSpec.demux_list[demuxOn],
742 &proto_param.demux_array[demuxOn]);
743 if (error)
744 break;
745 }
746 }
747
748 if (error == 0)
749 {
750 /* We've got all our ducks lined up...lets attach! */
751 socket_unlock(so, 0);
752 error = ifnet_attach_protocol(np->nd_if, ndrvSpec.protocol_family,
753 &proto_param);
754 socket_lock(so, 0);
755 if (error == 0)
756 np->nd_proto_family = ndrvSpec.protocol_family;
757 }
758
759 /* Free any memory we've allocated */
760 if (proto_param.demux_array)
761 FREE(proto_param.demux_array, M_TEMP);
762 if (ndrvDemux)
763 FREE(ndrvDemux, M_TEMP);
764
765 return error;
766 }
767
768
769 int
770 ndrv_to_ifnet_demux(struct ndrv_demux_desc* ndrv, struct ifnet_demux_desc* ifdemux)
771 {
772 bzero(ifdemux, sizeof(*ifdemux));
773
774 if (ndrv->type < DLIL_DESC_ETYPE2)
775 {
776 /* using old "type", not supported */
777 return ENOTSUP;
778 }
779
780 if (ndrv->length > 28)
781 {
782 return EINVAL;
783 }
784
785 ifdemux->type = ndrv->type;
786 ifdemux->data = ndrv->data.other;
787 ifdemux->datalen = ndrv->length;
788
789 return 0;
790 }
791
792 int
793 ndrv_delspec(struct ndrv_cb *np)
794 {
795 int result = 0;
796
797 if (np->nd_proto_family == PF_NDRV ||
798 np->nd_proto_family == 0)
799 return EINVAL;
800
801 /* Detach the protocol */
802 result = ifnet_detach_protocol(np->nd_if, np->nd_proto_family);
803 np->nd_proto_family = PF_NDRV;
804
805 return result;
806 }
807
808 struct ndrv_cb *
809 ndrv_find_inbound(struct ifnet *ifp, u_long protocol)
810 {
811 struct ndrv_cb* np;
812
813 if (protocol == PF_NDRV) return NULL;
814
815 TAILQ_FOREACH(np, &ndrvl, nd_next) {
816 if (np->nd_proto_family == protocol &&
817 np->nd_if == ifp) {
818 return np;
819 }
820 }
821
822 return NULL;
823 }
824
825 static void ndrv_dominit(void)
826 {
827 static int ndrv_dominited = 0;
828
829 if (ndrv_dominited == 0 &&
830 net_add_proto(&ndrvsw, &ndrvdomain) == 0)
831 ndrv_dominited = 1;
832 }
833
834 static void
835 ndrv_handle_ifp_detach(u_long family, short unit)
836 {
837 struct ndrv_cb* np;
838 struct ifnet *ifp = NULL;
839 struct socket *so;
840
841 /* Find all sockets using this interface. */
842 TAILQ_FOREACH(np, &ndrvl, nd_next) {
843 if (np->nd_family == family &&
844 np->nd_unit == unit)
845 {
846 /* This cb is using the detaching interface, but not for long. */
847 /* Let the protocol go */
848 ifp = np->nd_if;
849 if (np->nd_proto_family != 0)
850 ndrv_delspec(np);
851
852 /* Delete the multicasts first */
853 ndrv_remove_all_multicast(np);
854
855 /* Disavow all knowledge of the ifp */
856 np->nd_if = NULL;
857 np->nd_unit = 0;
858 np->nd_family = 0;
859
860 so = np->nd_socket;
861 /* Make sure sending returns an error */
862 /* Is this safe? Will we drop the funnel? */
863 lck_mtx_assert(so->so_proto->pr_domain->dom_mtx, LCK_MTX_ASSERT_OWNED);
864 socantsendmore(so);
865 socantrcvmore(so);
866 }
867 }
868
869 /* Unregister our protocol */
870 if (ifp) {
871 ifnet_detach_protocol(ifp, PF_NDRV);
872 }
873 }
874
875 static int
876 ndrv_do_add_multicast(struct ndrv_cb *np, struct sockopt *sopt)
877 {
878 struct ndrv_multiaddr* ndrv_multi;
879 int result;
880
881 if (sopt->sopt_val == 0 || sopt->sopt_valsize < 2 ||
882 sopt->sopt_level != SOL_NDRVPROTO)
883 return EINVAL;
884 if (np->nd_if == NULL)
885 return ENXIO;
886
887 // Allocate storage
888 MALLOC(ndrv_multi, struct ndrv_multiaddr*, sizeof(struct ndrv_multiaddr) -
889 sizeof(struct sockaddr) + sopt->sopt_valsize, M_IFADDR, M_WAITOK);
890 if (ndrv_multi == NULL)
891 return ENOMEM;
892
893 // Copy in the address
894 result = copyin(sopt->sopt_val, &ndrv_multi->addr, sopt->sopt_valsize);
895
896 // Validate the sockaddr
897 if (result == 0 && sopt->sopt_valsize != ndrv_multi->addr.sa_len)
898 result = EINVAL;
899
900 if (result == 0 && ndrv_have_multicast(np, &ndrv_multi->addr))
901 result = EEXIST;
902
903 if (result == 0)
904 {
905 // Try adding the multicast
906 result = ifnet_add_multicast(np->nd_if, &ndrv_multi->addr,
907 &ndrv_multi->ifma);
908 }
909
910 if (result == 0)
911 {
912 // Add to our linked list
913 ndrv_multi->next = np->nd_multiaddrs;
914 np->nd_multiaddrs = ndrv_multi;
915 }
916 else
917 {
918 // Free up the memory, something went wrong
919 FREE(ndrv_multi, M_IFADDR);
920 }
921
922 return result;
923 }
924
925 static int
926 ndrv_do_remove_multicast(struct ndrv_cb *np, struct sockopt *sopt)
927 {
928 struct sockaddr* multi_addr;
929 struct ndrv_multiaddr* ndrv_entry = NULL;
930 int result;
931
932 if (sopt->sopt_val == 0 || sopt->sopt_valsize < 2 ||
933 sopt->sopt_level != SOL_NDRVPROTO)
934 return EINVAL;
935 if (np->nd_if == NULL)
936 return ENXIO;
937
938 // Allocate storage
939 MALLOC(multi_addr, struct sockaddr*, sopt->sopt_valsize,
940 M_TEMP, M_WAITOK);
941 if (multi_addr == NULL)
942 return ENOMEM;
943
944 // Copy in the address
945 result = copyin(sopt->sopt_val, multi_addr, sopt->sopt_valsize);
946
947 // Validate the sockaddr
948 if (result == 0 && sopt->sopt_valsize != multi_addr->sa_len)
949 result = EINVAL;
950
951 if (result == 0)
952 {
953 /* Find the old entry */
954 ndrv_entry = ndrv_have_multicast(np, multi_addr);
955
956 if (ndrv_entry == NULL)
957 result = ENOENT;
958 }
959
960 if (result == 0)
961 {
962 // Try deleting the multicast
963 result = ifnet_remove_multicast(ndrv_entry->ifma);
964 }
965
966 if (result == 0)
967 {
968 // Remove from our linked list
969 struct ndrv_multiaddr* cur = np->nd_multiaddrs;
970
971 ifmaddr_release(ndrv_entry->ifma);
972
973 if (cur == ndrv_entry)
974 {
975 np->nd_multiaddrs = cur->next;
976 }
977 else
978 {
979 for (cur = cur->next; cur != NULL; cur = cur->next)
980 {
981 if (cur->next == ndrv_entry)
982 {
983 cur->next = cur->next->next;
984 break;
985 }
986 }
987 }
988
989 // Free the memory
990 FREE(ndrv_entry, M_IFADDR);
991 }
992 FREE(multi_addr, M_TEMP);
993
994 return result;
995 }
996
997 static struct ndrv_multiaddr*
998 ndrv_have_multicast(struct ndrv_cb *np, struct sockaddr* inAddr)
999 {
1000 struct ndrv_multiaddr* cur;
1001 for (cur = np->nd_multiaddrs; cur != NULL; cur = cur->next)
1002 {
1003
1004 if ((inAddr->sa_len == cur->addr.sa_len) &&
1005 (bcmp(&cur->addr, inAddr, inAddr->sa_len) == 0))
1006 {
1007 // Found a match
1008 return cur;
1009 }
1010 }
1011
1012 return NULL;
1013 }
1014
1015 static void
1016 ndrv_remove_all_multicast(struct ndrv_cb* np)
1017 {
1018 struct ndrv_multiaddr* cur;
1019
1020 if (np->nd_if != NULL)
1021 {
1022 while (np->nd_multiaddrs != NULL)
1023 {
1024 cur = np->nd_multiaddrs;
1025 np->nd_multiaddrs = cur->next;
1026
1027 ifnet_remove_multicast(cur->ifma);
1028 ifmaddr_release(cur->ifma);
1029 FREE(cur, M_IFADDR);
1030 }
1031 }
1032 }
1033
1034 struct pr_usrreqs ndrv_usrreqs = {
1035 ndrv_abort, pru_accept_notsupp, ndrv_attach, ndrv_bind,
1036 ndrv_connect, pru_connect2_notsupp, pru_control_notsupp, ndrv_detach,
1037 ndrv_disconnect, pru_listen_notsupp, ndrv_peeraddr, pru_rcvd_notsupp,
1038 pru_rcvoob_notsupp, ndrv_send, pru_sense_null, ndrv_shutdown,
1039 ndrv_sockaddr, sosend, soreceive, pru_sopoll_notsupp
1040 };
1041
1042 struct protosw ndrvsw =
1043 { SOCK_RAW, &ndrvdomain, NDRVPROTO_NDRV, PR_ATOMIC|PR_ADDR,
1044 NULL, ndrv_output, NULL, ndrv_ctloutput,
1045 NULL,
1046 NULL, NULL, NULL, NULL, NULL,
1047 &ndrv_usrreqs,
1048 NULL, NULL, NULL,
1049 { NULL, NULL}, NULL,
1050 { 0 }
1051 };
1052
1053 struct domain ndrvdomain =
1054 { AF_NDRV,
1055 "NetDriver",
1056 ndrv_dominit,
1057 NULL,
1058 NULL,
1059 NULL,
1060 NULL,
1061 NULL,
1062 0,
1063 0,
1064 0,
1065 0,
1066 NULL,
1067 0,
1068 {0, 0}
1069 };