1 | /* |
2 | * Copyright (c) 1997-2021 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. |
47 | */ |
48 | #include <mach/mach_types.h> |
49 | |
50 | #include <sys/param.h> |
51 | #include <sys/systm.h> |
52 | #include <sys/kernel.h> |
53 | #include <sys/malloc.h> |
54 | #include <sys/mbuf.h> |
55 | #include <sys/protosw.h> |
56 | #include <sys/domain.h> |
57 | #include <sys/socket.h> |
58 | #include <sys/socketvar.h> |
59 | #include <sys/ioctl.h> |
60 | #include <sys/sysctl.h> |
61 | #include <sys/errno.h> |
62 | #include <sys/syslog.h> |
63 | #include <sys/proc.h> |
64 | |
65 | #include <kern/queue.h> |
66 | #include <kern/assert.h> |
67 | |
68 | #include <net/ndrv.h> |
69 | #include <net/route.h> |
70 | #include <net/if_llc.h> |
71 | #include <net/if_dl.h> |
72 | #include <net/if_types.h> |
73 | #include <net/ndrv_var.h> |
74 | #include <net/dlil.h> |
75 | |
76 | #if INET |
77 | #include <netinet/in.h> |
78 | #include <netinet/in_var.h> |
79 | #endif |
80 | #include <netinet/if_ether.h> |
81 | |
82 | static unsigned int ndrv_multi_max_count = NDRV_DMUX_MAX_DESCR; |
83 | SYSCTL_UINT(_net, OID_AUTO, ndrv_multi_max_count, CTLFLAG_RW | CTLFLAG_LOCKED, |
84 | &ndrv_multi_max_count, 0, "Number of allowed multicast addresses per NRDV socket" ); |
85 | |
86 | /* |
87 | * The locking strategy relies on the PF_NRDRV domain mutex that protects both the |
88 | * PCB list "ndrvl" and the sockets themselves |
89 | */ |
90 | |
91 | static int ndrv_do_detach(struct ndrv_cb *); |
92 | static int ndrv_do_disconnect(struct ndrv_cb *); |
93 | static struct ndrv_cb *ndrv_find_inbound(struct ifnet *ifp, u_int32_t protocol_family); |
94 | static int ndrv_setspec(struct ndrv_cb *np, struct sockopt *sopt); |
95 | static int ndrv_delspec(struct ndrv_cb *); |
96 | static int ndrv_to_ifnet_demux(struct ndrv_demux_desc* ndrv, struct ifnet_demux_desc* ifdemux); |
97 | static void ndrv_handle_ifp_detach(u_int32_t family, short unit); |
98 | static int ndrv_do_add_multicast(struct ndrv_cb *np, struct sockopt *sopt); |
99 | static int ndrv_do_remove_multicast(struct ndrv_cb *np, struct sockopt *sopt); |
100 | static struct ndrv_multiaddr* ndrv_have_multicast(struct ndrv_cb *np, struct sockaddr* addr); |
101 | static void ndrv_remove_all_multicast(struct ndrv_cb *np); |
102 | static void ndrv_dominit(struct domain *); |
103 | |
104 | u_int32_t ndrv_sendspace = NDRVSNDQ; |
105 | u_int32_t ndrv_recvspace = NDRVRCVQ; |
106 | TAILQ_HEAD(, ndrv_cb) ndrvl = TAILQ_HEAD_INITIALIZER(ndrvl); |
107 | |
108 | static struct domain *ndrvdomain = NULL; |
109 | extern struct domain ndrvdomain_s; |
110 | |
111 | #define NDRV_PROTODEMUX_COUNT 10 |
112 | |
113 | /* |
114 | * Verify these values match. |
115 | * To keep clients from including dlil.h, we define |
116 | * these values independently in ndrv.h. They must |
117 | * match or a conversion function must be written. |
118 | */ |
119 | #if NDRV_DEMUXTYPE_ETHERTYPE != DLIL_DESC_ETYPE2 |
120 | #error NDRV_DEMUXTYPE_ETHERTYPE must match DLIL_DESC_ETYPE2 |
121 | #endif |
122 | #if NDRV_DEMUXTYPE_SAP != DLIL_DESC_SAP |
123 | #error NDRV_DEMUXTYPE_SAP must match DLIL_DESC_SAP |
124 | #endif |
125 | #if NDRV_DEMUXTYPE_SNAP != DLIL_DESC_SNAP |
126 | #error NDRV_DEMUXTYPE_SNAP must match DLIL_DESC_SNAP |
127 | #endif |
128 | |
129 | /* |
130 | * Protocol output - Called to output a raw network packet directly |
131 | * to the driver. |
132 | */ |
133 | static int |
134 | ndrv_output(struct mbuf *m, struct socket *so) |
135 | { |
136 | struct ndrv_cb *np = sotondrvcb(so); |
137 | struct ifnet *ifp = np->nd_if; |
138 | int result = 0; |
139 | |
140 | #if NDRV_DEBUG |
141 | printf("NDRV output: %x, %x, %x\n" , m, so, np); |
142 | #endif |
143 | |
144 | /* |
145 | * No header is a format error |
146 | */ |
147 | if ((m->m_flags & M_PKTHDR) == 0) { |
148 | return EINVAL; |
149 | } |
150 | |
151 | /* Unlock before calling ifnet_output */ |
152 | socket_unlock(so, refcount: 0); |
153 | |
154 | /* |
155 | * Call DLIL if we can. DLIL is much safer than calling the |
156 | * ifp directly. |
157 | */ |
158 | result = ifnet_output_raw(interface: ifp, protocol_family: np->nd_proto_family, packet: m); |
159 | |
160 | socket_lock(so, refcount: 0); |
161 | |
162 | return result; |
163 | } |
164 | |
165 | /* Our input routine called from DLIL */ |
166 | static errno_t |
167 | ndrv_input( |
168 | ifnet_t ifp, |
169 | protocol_family_t proto_family, |
170 | mbuf_t m, |
171 | char *) |
172 | { |
173 | struct socket *so; |
174 | struct sockaddr_dl ndrvsrc = {}; |
175 | struct ndrv_cb *np; |
176 | int error = 0; |
177 | |
178 | ndrvsrc.sdl_len = sizeof(struct sockaddr_dl); |
179 | ndrvsrc.sdl_family = AF_NDRV; |
180 | ndrvsrc.sdl_index = 0; |
181 | |
182 | /* move packet from if queue to socket */ |
183 | /* Should be media-independent */ |
184 | ndrvsrc.sdl_type = IFT_ETHER; |
185 | ndrvsrc.sdl_nlen = 0; |
186 | ndrvsrc.sdl_alen = 6; |
187 | ndrvsrc.sdl_slen = 0; |
188 | bcopy(src: frame_header, dst: &ndrvsrc.sdl_data, n: 6); |
189 | |
190 | /* prepend the frame header */ |
191 | m = m_prepend(m, ifnet_hdrlen(interface: ifp), M_NOWAIT); |
192 | if (m == NULL) { |
193 | return EJUSTRETURN; |
194 | } |
195 | bcopy(src: frame_header, dst: m_mtod_current(m), n: ifnet_hdrlen(interface: ifp)); |
196 | |
197 | /* |
198 | * We need to take the domain mutex before the list RW lock |
199 | */ |
200 | LCK_MTX_ASSERT(ndrvdomain->dom_mtx, LCK_MTX_ASSERT_NOTOWNED); |
201 | lck_mtx_lock(lck: ndrvdomain->dom_mtx); |
202 | |
203 | np = ndrv_find_inbound(ifp, protocol_family: proto_family); |
204 | if (np == NULL) { |
205 | lck_mtx_unlock(lck: ndrvdomain->dom_mtx); |
206 | return ENOENT; |
207 | } |
208 | |
209 | so = np->nd_socket; |
210 | |
211 | if (sbappendaddr(sb: &(so->so_rcv), asa: (struct sockaddr *)&ndrvsrc, |
212 | m0: m, NULL, error_out: &error) != 0) { |
213 | sorwakeup(so); |
214 | } |
215 | |
216 | lck_mtx_unlock(lck: ndrvdomain->dom_mtx); |
217 | |
218 | return 0; /* radar 4030377 - always return 0 */ |
219 | } |
220 | |
221 | /* |
222 | * Allocate an ndrv control block and some buffer space for the socket |
223 | */ |
224 | static int |
225 | ndrv_attach(struct socket *so, int proto, __unused struct proc *p) |
226 | { |
227 | int error; |
228 | struct ndrv_cb *np = sotondrvcb(so); |
229 | |
230 | if ((so->so_state & SS_PRIV) == 0) { |
231 | return EPERM; |
232 | } |
233 | |
234 | #if NDRV_DEBUG |
235 | printf("NDRV attach: %x, %x, %x\n" , so, proto, np); |
236 | #endif |
237 | |
238 | if ((error = soreserve(so, sndcc: ndrv_sendspace, rcvcc: ndrv_recvspace))) { |
239 | return error; |
240 | } |
241 | |
242 | np = kalloc_type(struct ndrv_cb, Z_WAITOK | Z_ZERO | Z_NOFAIL); |
243 | so->so_pcb = (caddr_t)np; |
244 | #if NDRV_DEBUG |
245 | printf("NDRV attach: %x, %x, %x\n" , so, proto, np); |
246 | #endif |
247 | TAILQ_INIT(&np->nd_dlist); |
248 | np->nd_signature = NDRV_SIGNATURE; |
249 | np->nd_socket = so; |
250 | np->nd_proto.sp_family = (uint16_t)SOCK_DOM(so); |
251 | np->nd_proto.sp_protocol = (uint16_t)proto; |
252 | np->nd_if = NULL; |
253 | np->nd_proto_family = 0; |
254 | np->nd_family = 0; |
255 | np->nd_unit = 0; |
256 | |
257 | /* |
258 | * Use the domain mutex to protect the list |
259 | */ |
260 | LCK_MTX_ASSERT(ndrvdomain->dom_mtx, LCK_MTX_ASSERT_NOTOWNED); |
261 | lck_mtx_lock(lck: ndrvdomain->dom_mtx); |
262 | |
263 | TAILQ_INSERT_TAIL(&ndrvl, np, nd_next); |
264 | |
265 | lck_mtx_unlock(lck: ndrvdomain->dom_mtx); |
266 | |
267 | return 0; |
268 | } |
269 | |
270 | /* |
271 | * Destroy state just before socket deallocation. |
272 | * Flush data or not depending on the options. |
273 | */ |
274 | |
275 | static int |
276 | ndrv_detach(struct socket *so) |
277 | { |
278 | struct ndrv_cb *np = sotondrvcb(so); |
279 | |
280 | if (np == 0) { |
281 | return EINVAL; |
282 | } |
283 | return ndrv_do_detach(np); |
284 | } |
285 | |
286 | |
287 | /* |
288 | * If a socket isn't bound to a single address, |
289 | * the ndrv input routine will hand it anything |
290 | * within that protocol family (assuming there's |
291 | * nothing else around it should go to). |
292 | * |
293 | * Don't expect this to be used. |
294 | */ |
295 | |
296 | static int |
297 | ndrv_connect(struct socket *so, struct sockaddr *nam, __unused struct proc *p) |
298 | { |
299 | struct ndrv_cb *np = sotondrvcb(so); |
300 | |
301 | if (np == 0) { |
302 | return EINVAL; |
303 | } |
304 | |
305 | if (np->nd_faddr) { |
306 | return EISCONN; |
307 | } |
308 | |
309 | if (nam->sa_len < sizeof(struct sockaddr_ndrv)) { |
310 | return EINVAL; |
311 | } |
312 | |
313 | /* Allocate memory to store the remote address */ |
314 | np->nd_faddr = kalloc_type(struct sockaddr_ndrv, Z_WAITOK | Z_NOFAIL | Z_ZERO); |
315 | |
316 | bcopy(src: (caddr_t) nam, dst: (caddr_t) np->nd_faddr, MIN(sizeof(struct sockaddr_ndrv), nam->sa_len)); |
317 | np->nd_faddr->snd_len = sizeof(struct sockaddr_ndrv); |
318 | soisconnected(so); |
319 | return 0; |
320 | } |
321 | |
322 | static void |
323 | ndrv_event(struct ifnet *ifp, __unused protocol_family_t protocol, |
324 | const struct kev_msg *event) |
325 | { |
326 | if (event->vendor_code == KEV_VENDOR_APPLE && |
327 | event->kev_class == KEV_NETWORK_CLASS && |
328 | event->kev_subclass == KEV_DL_SUBCLASS && |
329 | event->event_code == KEV_DL_IF_DETACHING) { |
330 | LCK_MTX_ASSERT(ndrvdomain->dom_mtx, LCK_MTX_ASSERT_NOTOWNED); |
331 | lck_mtx_lock(lck: ndrvdomain->dom_mtx); |
332 | ndrv_handle_ifp_detach(family: ifnet_family(interface: ifp), unit: ifp->if_unit); |
333 | lck_mtx_unlock(lck: ndrvdomain->dom_mtx); |
334 | } |
335 | } |
336 | |
337 | /* |
338 | * This is the "driver open" hook - we 'bind' to the |
339 | * named driver. |
340 | * Here's where we latch onto the driver. |
341 | */ |
342 | static int |
343 | ndrv_bind(struct socket *so, struct sockaddr *nam, __unused struct proc *p) |
344 | { |
345 | struct sockaddr_ndrv *sa = (struct sockaddr_ndrv *) nam; |
346 | char *dname; |
347 | struct ndrv_cb *np; |
348 | struct ifnet *ifp; |
349 | int result; |
350 | |
351 | if (TAILQ_EMPTY(&ifnet_head)) { |
352 | return EADDRNOTAVAIL; /* Quick sanity check */ |
353 | } |
354 | np = sotondrvcb(so); |
355 | if (np == 0) { |
356 | return EINVAL; |
357 | } |
358 | |
359 | if (np->nd_laddr) { |
360 | return EINVAL; /* XXX */ |
361 | } |
362 | /* I think we just latch onto a copy here; the caller frees */ |
363 | np->nd_laddr = kalloc_type(struct sockaddr_ndrv, Z_WAITOK | Z_NOFAIL | Z_ZERO); |
364 | bcopy(src: (caddr_t) sa, dst: (caddr_t) np->nd_laddr, MIN(sizeof(struct sockaddr_ndrv), sa->snd_len)); |
365 | np->nd_laddr->snd_len = sizeof(struct sockaddr_ndrv); |
366 | dname = (char *) sa->snd_name; |
367 | if (*dname == '\0') { |
368 | return EINVAL; |
369 | } |
370 | #if NDRV_DEBUG |
371 | printf("NDRV bind: %x, %x, %s\n" , so, np, dname); |
372 | #endif |
373 | /* Track down the driver and its ifnet structure. |
374 | * There's no internal call for this so we have to dup the code |
375 | * in if.c/ifconf() |
376 | */ |
377 | ifnet_head_lock_shared(); |
378 | TAILQ_FOREACH(ifp, &ifnet_head, if_link) { |
379 | if (strncmp(s1: ifp->if_xname, s2: dname, IFNAMSIZ) == 0) { |
380 | break; |
381 | } |
382 | } |
383 | ifnet_head_done(); |
384 | |
385 | if (ifp == NULL) { |
386 | return EADDRNOTAVAIL; |
387 | } |
388 | |
389 | // PPP doesn't support PF_NDRV. |
390 | if (ifnet_family(interface: ifp) != APPLE_IF_FAM_PPP) { |
391 | /* NDRV on this interface */ |
392 | struct ifnet_attach_proto_param ndrv_proto; |
393 | result = 0; |
394 | bzero(s: &ndrv_proto, n: sizeof(ndrv_proto)); |
395 | ndrv_proto.event = ndrv_event; |
396 | |
397 | /* We aren't worried about double attaching, that should just return an error */ |
398 | socket_unlock(so, refcount: 0); |
399 | result = ifnet_attach_protocol(interface: ifp, PF_NDRV, proto_details: &ndrv_proto); |
400 | socket_lock(so, refcount: 0); |
401 | if (result && result != EEXIST) { |
402 | return result; |
403 | } |
404 | np->nd_proto_family = PF_NDRV; |
405 | } else { |
406 | np->nd_proto_family = 0; |
407 | } |
408 | |
409 | np->nd_if = ifp; |
410 | np->nd_family = ifnet_family(interface: ifp); |
411 | np->nd_unit = ifp->if_unit; |
412 | |
413 | return 0; |
414 | } |
415 | |
416 | static int |
417 | ndrv_disconnect(struct socket *so) |
418 | { |
419 | struct ndrv_cb *np = sotondrvcb(so); |
420 | |
421 | if (np == 0) { |
422 | return EINVAL; |
423 | } |
424 | |
425 | if (np->nd_faddr == 0) { |
426 | return ENOTCONN; |
427 | } |
428 | |
429 | ndrv_do_disconnect(np); |
430 | return 0; |
431 | } |
432 | |
433 | /* |
434 | * Mark the connection as being incapable of further input. |
435 | */ |
436 | static int |
437 | ndrv_shutdown(struct socket *so) |
438 | { |
439 | LCK_MTX_ASSERT(ndrvdomain->dom_mtx, LCK_MTX_ASSERT_OWNED); |
440 | socantsendmore(so); |
441 | return 0; |
442 | } |
443 | |
444 | /* |
445 | * Ship a packet out. The ndrv output will pass it |
446 | * to the appropriate driver. The really tricky part |
447 | * is the destination address... |
448 | */ |
449 | static int |
450 | ndrv_send(struct socket *so, __unused int flags, struct mbuf *m, |
451 | __unused struct sockaddr *addr, struct mbuf *control, |
452 | __unused struct proc *p) |
453 | { |
454 | int error; |
455 | |
456 | if (control != NULL) { |
457 | m_freem(control); |
458 | return EOPNOTSUPP; |
459 | } |
460 | |
461 | error = ndrv_output(m, so); |
462 | m = NULL; |
463 | return error; |
464 | } |
465 | |
466 | |
467 | static int |
468 | ndrv_abort(struct socket *so) |
469 | { |
470 | struct ndrv_cb *np = sotondrvcb(so); |
471 | |
472 | if (np == 0) { |
473 | return EINVAL; |
474 | } |
475 | |
476 | ndrv_do_disconnect(np); |
477 | return 0; |
478 | } |
479 | |
480 | static int |
481 | ndrv_sockaddr(struct socket *so, struct sockaddr **nam) |
482 | { |
483 | struct ndrv_cb *np = sotondrvcb(so); |
484 | int len; |
485 | |
486 | if (np == 0) { |
487 | return EINVAL; |
488 | } |
489 | |
490 | if (np->nd_laddr == 0) { |
491 | return EINVAL; |
492 | } |
493 | |
494 | len = np->nd_laddr->snd_len; |
495 | *nam = (struct sockaddr *)alloc_sockaddr(size: len, |
496 | flags: Z_WAITOK | Z_NOFAIL); |
497 | |
498 | bcopy(src: (caddr_t)np->nd_laddr, dst: *nam, |
499 | n: (unsigned)len); |
500 | return 0; |
501 | } |
502 | |
503 | |
504 | static int |
505 | ndrv_peeraddr(struct socket *so, struct sockaddr **nam) |
506 | { |
507 | struct ndrv_cb *np = sotondrvcb(so); |
508 | int len; |
509 | |
510 | if (np == 0) { |
511 | return EINVAL; |
512 | } |
513 | |
514 | if (np->nd_faddr == 0) { |
515 | return ENOTCONN; |
516 | } |
517 | |
518 | len = np->nd_faddr->snd_len; |
519 | *nam = (struct sockaddr *)alloc_sockaddr(size: len, |
520 | flags: Z_WAITOK | Z_NOFAIL); |
521 | |
522 | bcopy(src: (caddr_t)np->nd_faddr, dst: *nam, |
523 | n: (unsigned)len); |
524 | return 0; |
525 | } |
526 | |
527 | |
528 | /* Control output */ |
529 | |
530 | static int |
531 | ndrv_ctloutput(struct socket *so, struct sockopt *sopt) |
532 | { |
533 | struct ndrv_cb *np = sotondrvcb(so); |
534 | int error = 0; |
535 | |
536 | switch (sopt->sopt_name) { |
537 | case NDRV_DELDMXSPEC: /* Delete current spec */ |
538 | /* Verify no parameter was passed */ |
539 | if (sopt->sopt_val != 0 || sopt->sopt_valsize != 0) { |
540 | /* |
541 | * We don't support deleting a specific demux, it's |
542 | * all or nothing. |
543 | */ |
544 | return EINVAL; |
545 | } |
546 | error = ndrv_delspec(np); |
547 | break; |
548 | case NDRV_SETDMXSPEC: /* Set protocol spec */ |
549 | error = ndrv_setspec(np, sopt); |
550 | break; |
551 | case NDRV_ADDMULTICAST: |
552 | error = ndrv_do_add_multicast(np, sopt); |
553 | break; |
554 | case NDRV_DELMULTICAST: |
555 | error = ndrv_do_remove_multicast(np, sopt); |
556 | break; |
557 | default: |
558 | error = ENOTSUP; |
559 | } |
560 | #ifdef NDRV_DEBUG |
561 | log(LOG_WARNING, "NDRV CTLOUT: %x returns %d\n" , sopt->sopt_name, |
562 | error); |
563 | #endif |
564 | return error; |
565 | } |
566 | |
567 | static int |
568 | ndrv_do_detach(struct ndrv_cb *np) |
569 | { |
570 | struct ndrv_cb* cur_np = NULL; |
571 | struct socket *so = np->nd_socket; |
572 | int error = 0; |
573 | struct ifnet * ifp; |
574 | |
575 | #if NDRV_DEBUG |
576 | printf("NDRV detach: %x, %x\n" , so, np); |
577 | #endif |
578 | ndrv_remove_all_multicast(np); |
579 | |
580 | /* Remove from the linked list of control blocks */ |
581 | LCK_MTX_ASSERT(ndrvdomain->dom_mtx, LCK_MTX_ASSERT_OWNED); |
582 | TAILQ_REMOVE(&ndrvl, np, nd_next); |
583 | |
584 | ifp = np->nd_if; |
585 | if (ifp != NULL) { |
586 | u_int32_t proto_family = np->nd_proto_family; |
587 | |
588 | if (proto_family != PF_NDRV && proto_family != 0) { |
589 | socket_unlock(so, refcount: 0); |
590 | ifnet_detach_protocol(interface: ifp, protocol_family: proto_family); |
591 | socket_lock(so, refcount: 0); |
592 | } |
593 | |
594 | /* Check if this is the last socket attached to this interface */ |
595 | LCK_MTX_ASSERT(ndrvdomain->dom_mtx, LCK_MTX_ASSERT_OWNED); |
596 | TAILQ_FOREACH(cur_np, &ndrvl, nd_next) { |
597 | if (cur_np->nd_family == np->nd_family && |
598 | cur_np->nd_unit == np->nd_unit) { |
599 | break; |
600 | } |
601 | } |
602 | |
603 | /* If there are no other interfaces, detach PF_NDRV from the interface */ |
604 | if (cur_np == NULL) { |
605 | socket_unlock(so, refcount: 0); |
606 | ifnet_detach_protocol(interface: ifp, PF_NDRV); |
607 | socket_lock(so, refcount: 0); |
608 | } |
609 | } |
610 | if (np->nd_laddr != NULL) { |
611 | kfree_type(struct sockaddr_ndrv, np->nd_laddr); |
612 | } |
613 | kfree_type(struct ndrv_cb, np); |
614 | so->so_pcb = 0; |
615 | so->so_flags |= SOF_PCBCLEARING; |
616 | sofree(so); |
617 | return error; |
618 | } |
619 | |
620 | static int |
621 | ndrv_do_disconnect(struct ndrv_cb *np) |
622 | { |
623 | struct socket * so = np->nd_socket; |
624 | #if NDRV_DEBUG |
625 | printf("NDRV disconnect: %x\n" , np); |
626 | #endif |
627 | if (np->nd_faddr) { |
628 | kfree_type(struct sockaddr_ndrv, np->nd_faddr); |
629 | } |
630 | /* |
631 | * A multipath subflow socket would have its SS_NOFDREF set by default, |
632 | * so check for SOF_MP_SUBFLOW socket flag before detaching the PCB; |
633 | * when the socket is closed for real, SOF_MP_SUBFLOW would be cleared. |
634 | */ |
635 | if (!(so->so_flags & SOF_MP_SUBFLOW) && (so->so_state & SS_NOFDREF)) { |
636 | ndrv_do_detach(np); |
637 | } |
638 | soisdisconnected(so); |
639 | return 0; |
640 | } |
641 | |
642 | #if 0 |
643 | //### Not used |
644 | /* |
645 | * When closing, dump any enqueued mbufs. |
646 | */ |
647 | void |
648 | ndrv_flushq(struct ifqueue *q) |
649 | { |
650 | struct mbuf *m; |
651 | for (;;) { |
652 | IF_DEQUEUE(q, m); |
653 | if (m == NULL) { |
654 | break; |
655 | } |
656 | IF_DROP(q); |
657 | if (m) { |
658 | m_freem(m); |
659 | } |
660 | } |
661 | } |
662 | #endif |
663 | |
664 | int |
665 | ndrv_setspec(struct ndrv_cb *np, struct sockopt *sopt) |
666 | { |
667 | struct ifnet_attach_proto_param proto_param; |
668 | struct ndrv_protocol_desc ndrvSpec; |
669 | struct ndrv_demux_desc* ndrvDemux = NULL; |
670 | size_t ndrvDemuxSize = 0; |
671 | int error = 0; |
672 | struct socket * so = np->nd_socket; |
673 | user_addr_t user_addr; |
674 | |
675 | /* Sanity checking */ |
676 | if (np->nd_proto_family != PF_NDRV) { |
677 | return EBUSY; |
678 | } |
679 | if (np->nd_if == NULL) { |
680 | return EINVAL; |
681 | } |
682 | |
683 | /* Copy the ndrvSpec */ |
684 | if (proc_is64bit(sopt->sopt_p)) { |
685 | struct ndrv_protocol_desc64 ndrvSpec64; |
686 | |
687 | if (sopt->sopt_valsize != sizeof(ndrvSpec64)) { |
688 | return EINVAL; |
689 | } |
690 | |
691 | error = sooptcopyin(sopt, &ndrvSpec64, len: sizeof(ndrvSpec64), minlen: sizeof(ndrvSpec64)); |
692 | if (error != 0) { |
693 | return error; |
694 | } |
695 | |
696 | ndrvSpec.version = ndrvSpec64.version; |
697 | ndrvSpec.protocol_family = ndrvSpec64.protocol_family; |
698 | ndrvSpec.demux_count = ndrvSpec64.demux_count; |
699 | |
700 | user_addr = CAST_USER_ADDR_T(ndrvSpec64.demux_list); |
701 | } else { |
702 | struct ndrv_protocol_desc32 ndrvSpec32; |
703 | |
704 | if (sopt->sopt_valsize != sizeof(ndrvSpec32)) { |
705 | return EINVAL; |
706 | } |
707 | |
708 | error = sooptcopyin(sopt, &ndrvSpec32, len: sizeof(ndrvSpec32), minlen: sizeof(ndrvSpec32)); |
709 | if (error != 0) { |
710 | return error; |
711 | } |
712 | |
713 | ndrvSpec.version = ndrvSpec32.version; |
714 | ndrvSpec.protocol_family = ndrvSpec32.protocol_family; |
715 | ndrvSpec.demux_count = ndrvSpec32.demux_count; |
716 | |
717 | user_addr = CAST_USER_ADDR_T(ndrvSpec32.demux_list); |
718 | } |
719 | |
720 | /* |
721 | * Do not allow PF_NDRV as it's non-sensical and most importantly because |
722 | * we use PF_NDRV to see if the protocol family has already been set |
723 | */ |
724 | if (ndrvSpec.protocol_family == PF_NDRV) { |
725 | return EINVAL; |
726 | } |
727 | |
728 | /* Verify the parameter */ |
729 | if (ndrvSpec.version > NDRV_PROTOCOL_DESC_VERS) { |
730 | return ENOTSUP; // version is too new! |
731 | } else if (ndrvSpec.version < 1) { |
732 | return EINVAL; // version is not valid |
733 | } else if (ndrvSpec.demux_count > NDRV_PROTODEMUX_COUNT || ndrvSpec.demux_count == 0) { |
734 | return EINVAL; // demux_count is not valid |
735 | } |
736 | bzero(s: &proto_param, n: sizeof(proto_param)); |
737 | proto_param.demux_count = ndrvSpec.demux_count; |
738 | |
739 | /* Allocate storage for demux array */ |
740 | ndrvDemuxSize = proto_param.demux_count * sizeof(struct ndrv_demux_desc); |
741 | ndrvDemux = (struct ndrv_demux_desc*) kalloc_data(ndrvDemuxSize, Z_WAITOK); |
742 | if (ndrvDemux == NULL) { |
743 | return ENOMEM; |
744 | } |
745 | |
746 | /* Allocate enough ifnet_demux_descs */ |
747 | proto_param.demux_array = kalloc_type(struct ifnet_demux_desc, |
748 | ndrvSpec.demux_count, Z_WAITOK | Z_ZERO); |
749 | if (proto_param.demux_array == NULL) { |
750 | error = ENOMEM; |
751 | } |
752 | |
753 | if (error == 0) { |
754 | /* Copy the ndrv demux array from userland */ |
755 | error = copyin(user_addr, ndrvDemux, |
756 | ndrvSpec.demux_count * sizeof(struct ndrv_demux_desc)); |
757 | ndrvSpec.demux_list = ndrvDemux; |
758 | } |
759 | |
760 | if (error == 0) { |
761 | /* At this point, we've at least got enough bytes to start looking around */ |
762 | u_int32_t demuxOn = 0; |
763 | |
764 | proto_param.demux_count = ndrvSpec.demux_count; |
765 | proto_param.input = ndrv_input; |
766 | proto_param.event = ndrv_event; |
767 | |
768 | for (demuxOn = 0; demuxOn < ndrvSpec.demux_count; demuxOn++) { |
769 | /* Convert an ndrv_demux_desc to a ifnet_demux_desc */ |
770 | error = ndrv_to_ifnet_demux(ndrv: &ndrvSpec.demux_list[demuxOn], |
771 | ifdemux: &proto_param.demux_array[demuxOn]); |
772 | if (error) { |
773 | break; |
774 | } |
775 | } |
776 | } |
777 | |
778 | if (error == 0) { |
779 | /* |
780 | * Set the protocol family to prevent other threads from |
781 | * attaching a protocol while the socket is unlocked |
782 | */ |
783 | np->nd_proto_family = ndrvSpec.protocol_family; |
784 | socket_unlock(so, refcount: 0); |
785 | error = ifnet_attach_protocol(interface: np->nd_if, protocol_family: ndrvSpec.protocol_family, |
786 | proto_details: &proto_param); |
787 | socket_lock(so, refcount: 0); |
788 | /* |
789 | * Upon failure, indicate that no protocol is attached |
790 | */ |
791 | if (error != 0) { |
792 | np->nd_proto_family = PF_NDRV; |
793 | } |
794 | } |
795 | |
796 | /* Free any memory we've allocated */ |
797 | if (proto_param.demux_array) { |
798 | kfree_type(struct ifnet_demux_desc, ndrvSpec.demux_count, |
799 | proto_param.demux_array); |
800 | } |
801 | if (ndrvDemux) { |
802 | kfree_data(ndrvDemux, ndrvDemuxSize); |
803 | } |
804 | |
805 | return error; |
806 | } |
807 | |
808 | |
809 | int |
810 | ndrv_to_ifnet_demux(struct ndrv_demux_desc* ndrv, struct ifnet_demux_desc* ifdemux) |
811 | { |
812 | bzero(s: ifdemux, n: sizeof(*ifdemux)); |
813 | |
814 | if (ndrv->type < DLIL_DESC_ETYPE2) { |
815 | /* using old "type", not supported */ |
816 | return ENOTSUP; |
817 | } |
818 | |
819 | if (ndrv->length > 28) { |
820 | return EINVAL; |
821 | } |
822 | |
823 | ifdemux->type = ndrv->type; |
824 | ifdemux->data = ndrv->data.other; |
825 | ifdemux->datalen = ndrv->length; |
826 | |
827 | return 0; |
828 | } |
829 | |
830 | int |
831 | ndrv_delspec(struct ndrv_cb *np) |
832 | { |
833 | int result = 0; |
834 | |
835 | if (np->nd_proto_family == PF_NDRV || |
836 | np->nd_proto_family == 0) { |
837 | return EINVAL; |
838 | } |
839 | |
840 | /* Detach the protocol */ |
841 | result = ifnet_detach_protocol(interface: np->nd_if, protocol_family: np->nd_proto_family); |
842 | np->nd_proto_family = PF_NDRV; |
843 | |
844 | return result; |
845 | } |
846 | |
847 | struct ndrv_cb * |
848 | ndrv_find_inbound(struct ifnet *ifp, u_int32_t protocol) |
849 | { |
850 | struct ndrv_cb* np; |
851 | |
852 | LCK_MTX_ASSERT(ndrvdomain->dom_mtx, LCK_MTX_ASSERT_OWNED); |
853 | |
854 | if (protocol == PF_NDRV) { |
855 | return NULL; |
856 | } |
857 | |
858 | TAILQ_FOREACH(np, &ndrvl, nd_next) { |
859 | if (np->nd_proto_family == protocol && |
860 | np->nd_if == ifp) { |
861 | return np; |
862 | } |
863 | } |
864 | |
865 | return NULL; |
866 | } |
867 | |
868 | static void |
869 | ndrv_handle_ifp_detach(u_int32_t family, short unit) |
870 | { |
871 | struct ndrv_cb* np; |
872 | struct ifnet *ifp = NULL; |
873 | struct socket *so; |
874 | |
875 | /* Find all sockets using this interface. */ |
876 | TAILQ_FOREACH(np, &ndrvl, nd_next) { |
877 | if (np->nd_family == family && |
878 | np->nd_unit == unit) { |
879 | /* This cb is using the detaching interface, but not for long. */ |
880 | /* Let the protocol go */ |
881 | ifp = np->nd_if; |
882 | if (np->nd_proto_family != 0) { |
883 | ndrv_delspec(np); |
884 | } |
885 | |
886 | /* Delete the multicasts first */ |
887 | ndrv_remove_all_multicast(np); |
888 | |
889 | /* Disavow all knowledge of the ifp */ |
890 | np->nd_if = NULL; |
891 | np->nd_unit = 0; |
892 | np->nd_family = 0; |
893 | |
894 | so = np->nd_socket; |
895 | /* Make sure sending returns an error */ |
896 | LCK_MTX_ASSERT(ndrvdomain->dom_mtx, LCK_MTX_ASSERT_OWNED); |
897 | socantsendmore(so); |
898 | socantrcvmore(so); |
899 | } |
900 | } |
901 | |
902 | /* Unregister our protocol */ |
903 | if (ifp) { |
904 | ifnet_detach_protocol(interface: ifp, PF_NDRV); |
905 | } |
906 | } |
907 | |
908 | static struct ndrv_multiaddr * |
909 | ndrv_multiaddr_alloc(size_t size) |
910 | { |
911 | struct ndrv_multiaddr *ndrv_multi; |
912 | |
913 | ndrv_multi = kalloc_type(struct ndrv_multiaddr, Z_WAITOK_ZERO_NOFAIL); |
914 | ndrv_multi->addr = kalloc_data(size, Z_WAITOK_ZERO_NOFAIL); |
915 | return ndrv_multi; |
916 | } |
917 | |
918 | static void |
919 | ndrv_multiaddr_free(struct ndrv_multiaddr *ndrv_multi, size_t size) |
920 | { |
921 | kfree_data(ndrv_multi->addr, size); |
922 | kfree_type(struct ndrv_multiaddr, ndrv_multi); |
923 | } |
924 | |
925 | static int |
926 | ndrv_do_add_multicast(struct ndrv_cb *np, struct sockopt *sopt) |
927 | { |
928 | struct ndrv_multiaddr *ndrv_multi; |
929 | int result; |
930 | |
931 | if (sopt->sopt_val == 0 || sopt->sopt_valsize < 2 || |
932 | sopt->sopt_level != SOL_NDRVPROTO || sopt->sopt_valsize > SOCK_MAXADDRLEN) { |
933 | return EINVAL; |
934 | } |
935 | if (np->nd_if == NULL) { |
936 | return ENXIO; |
937 | } |
938 | if (!(np->nd_dlist_cnt < ndrv_multi_max_count)) { |
939 | return EPERM; |
940 | } |
941 | |
942 | ndrv_multi = ndrv_multiaddr_alloc(size: sopt->sopt_valsize); |
943 | |
944 | // Copy in the address |
945 | result = copyin(sopt->sopt_val, ndrv_multi->addr, sopt->sopt_valsize); |
946 | |
947 | // Validate the sockaddr |
948 | if (result == 0 && sopt->sopt_valsize != ndrv_multi->addr->sa_len) { |
949 | result = EINVAL; |
950 | } |
951 | |
952 | if (result == 0 && ndrv_have_multicast(np, addr: ndrv_multi->addr)) { |
953 | result = EEXIST; |
954 | } |
955 | |
956 | if (result == 0) { |
957 | // Try adding the multicast |
958 | result = ifnet_add_multicast(interface: np->nd_if, maddr: ndrv_multi->addr, |
959 | multicast: &ndrv_multi->ifma); |
960 | } |
961 | |
962 | if (result == 0) { |
963 | // Add to our linked list |
964 | ndrv_multi->next = np->nd_multiaddrs; |
965 | np->nd_multiaddrs = ndrv_multi; |
966 | np->nd_dlist_cnt++; |
967 | } else { |
968 | // Free up the memory, something went wrong |
969 | ndrv_multiaddr_free(ndrv_multi, size: sopt->sopt_valsize); |
970 | } |
971 | |
972 | return result; |
973 | } |
974 | |
975 | static void |
976 | ndrv_cb_remove_multiaddr(struct ndrv_cb *np, struct ndrv_multiaddr *ndrv_entry) |
977 | { |
978 | struct ndrv_multiaddr *cur = np->nd_multiaddrs; |
979 | bool removed = false; |
980 | |
981 | if (cur == ndrv_entry) { |
982 | /* we were the head */ |
983 | np->nd_multiaddrs = cur->next; |
984 | removed = true; |
985 | } else { |
986 | /* find our entry */ |
987 | struct ndrv_multiaddr *cur_next = NULL; |
988 | |
989 | for (; cur != NULL; cur = cur_next) { |
990 | cur_next = cur->next; |
991 | if (cur_next == ndrv_entry) { |
992 | cur->next = cur_next->next; |
993 | removed = true; |
994 | break; |
995 | } |
996 | } |
997 | } |
998 | ASSERT(removed); |
999 | } |
1000 | |
1001 | static int |
1002 | ndrv_do_remove_multicast(struct ndrv_cb *np, struct sockopt *sopt) |
1003 | { |
1004 | struct sockaddr* multi_addr; |
1005 | struct ndrv_multiaddr* ndrv_entry = NULL; |
1006 | int result; |
1007 | |
1008 | if (sopt->sopt_val == 0 || sopt->sopt_valsize < 2 || |
1009 | sopt->sopt_valsize > SOCK_MAXADDRLEN || |
1010 | sopt->sopt_level != SOL_NDRVPROTO) { |
1011 | return EINVAL; |
1012 | } |
1013 | if (np->nd_if == NULL || np->nd_dlist_cnt == 0) { |
1014 | return ENXIO; |
1015 | } |
1016 | |
1017 | // Allocate storage |
1018 | multi_addr = (struct sockaddr*) kalloc_data(sopt->sopt_valsize, Z_WAITOK); |
1019 | if (multi_addr == NULL) { |
1020 | return ENOMEM; |
1021 | } |
1022 | |
1023 | // Copy in the address |
1024 | result = copyin(sopt->sopt_val, multi_addr, sopt->sopt_valsize); |
1025 | |
1026 | // Validate the sockaddr |
1027 | if (result == 0 && sopt->sopt_valsize != multi_addr->sa_len) { |
1028 | result = EINVAL; |
1029 | } |
1030 | |
1031 | if (result == 0) { |
1032 | /* Find the old entry */ |
1033 | ndrv_entry = ndrv_have_multicast(np, addr: multi_addr); |
1034 | |
1035 | if (ndrv_entry == NULL) { |
1036 | result = ENOENT; |
1037 | } |
1038 | } |
1039 | |
1040 | if (result == 0) { |
1041 | // Try deleting the multicast |
1042 | result = ifnet_remove_multicast(multicast: ndrv_entry->ifma); |
1043 | } |
1044 | |
1045 | if (result == 0) { |
1046 | // Remove from our linked list |
1047 | ifmaddr_release(ifmaddr: ndrv_entry->ifma); |
1048 | |
1049 | ndrv_cb_remove_multiaddr(np, ndrv_entry); |
1050 | np->nd_dlist_cnt--; |
1051 | |
1052 | ndrv_multiaddr_free(ndrv_multi: ndrv_entry, size: ndrv_entry->addr->sa_len); |
1053 | } |
1054 | kfree_data(multi_addr, sopt->sopt_valsize); |
1055 | |
1056 | return result; |
1057 | } |
1058 | |
1059 | static struct ndrv_multiaddr* |
1060 | ndrv_have_multicast(struct ndrv_cb *np, struct sockaddr* inAddr) |
1061 | { |
1062 | struct ndrv_multiaddr* cur; |
1063 | for (cur = np->nd_multiaddrs; cur != NULL; cur = cur->next) { |
1064 | if ((inAddr->sa_len == cur->addr->sa_len) && |
1065 | (bcmp(s1: cur->addr, s2: inAddr, n: inAddr->sa_len) == 0)) { |
1066 | // Found a match |
1067 | return cur; |
1068 | } |
1069 | } |
1070 | |
1071 | return NULL; |
1072 | } |
1073 | |
1074 | static void |
1075 | ndrv_remove_all_multicast(struct ndrv_cb* np) |
1076 | { |
1077 | struct ndrv_multiaddr* cur; |
1078 | |
1079 | if (np->nd_if != NULL) { |
1080 | while (np->nd_multiaddrs != NULL) { |
1081 | cur = np->nd_multiaddrs; |
1082 | np->nd_multiaddrs = cur->next; |
1083 | |
1084 | ifnet_remove_multicast(multicast: cur->ifma); |
1085 | ifmaddr_release(ifmaddr: cur->ifma); |
1086 | ndrv_multiaddr_free(ndrv_multi: cur, size: cur->addr->sa_len); |
1087 | } |
1088 | } |
1089 | } |
1090 | |
1091 | static struct pr_usrreqs ndrv_usrreqs = { |
1092 | .pru_abort = ndrv_abort, |
1093 | .pru_attach = ndrv_attach, |
1094 | .pru_bind = ndrv_bind, |
1095 | .pru_connect = ndrv_connect, |
1096 | .pru_detach = ndrv_detach, |
1097 | .pru_disconnect = ndrv_disconnect, |
1098 | .pru_peeraddr = ndrv_peeraddr, |
1099 | .pru_send = ndrv_send, |
1100 | .pru_shutdown = ndrv_shutdown, |
1101 | .pru_sockaddr = ndrv_sockaddr, |
1102 | .pru_sosend = sosend, |
1103 | .pru_soreceive = soreceive, |
1104 | }; |
1105 | |
1106 | static struct protosw ndrvsw[] = { |
1107 | { |
1108 | .pr_type = SOCK_RAW, |
1109 | .pr_protocol = NDRVPROTO_NDRV, |
1110 | .pr_flags = PR_ATOMIC | PR_ADDR, |
1111 | .pr_output = ndrv_output, |
1112 | .pr_ctloutput = ndrv_ctloutput, |
1113 | .pr_usrreqs = &ndrv_usrreqs, |
1114 | } |
1115 | }; |
1116 | |
1117 | static int ndrv_proto_count = (sizeof(ndrvsw) / sizeof(struct protosw)); |
1118 | |
1119 | struct domain ndrvdomain_s = { |
1120 | .dom_family = PF_NDRV, |
1121 | .dom_name = "NetDriver" , |
1122 | .dom_init = ndrv_dominit, |
1123 | }; |
1124 | |
1125 | static void |
1126 | ndrv_dominit(struct domain *dp) |
1127 | { |
1128 | struct protosw *pr; |
1129 | int i; |
1130 | |
1131 | VERIFY(!(dp->dom_flags & DOM_INITIALIZED)); |
1132 | VERIFY(ndrvdomain == NULL); |
1133 | |
1134 | ndrvdomain = dp; |
1135 | |
1136 | for (i = 0, pr = &ndrvsw[0]; i < ndrv_proto_count; i++, pr++) { |
1137 | net_add_proto(pr, dp, 1); |
1138 | } |
1139 | } |
1140 | |