From 239aebee61b85d9a050d09a37fce2c2df8f36fc1 Mon Sep 17 00:00:00 2001 From: Kyle Evans Date: Thu, 25 Jun 2020 21:17:47 -0500 Subject: [PATCH] domain: give domains a chance to probe for availability This gives any given domain a chance to indicate that it's not actually supported on the current system. If dom_probe isn't supplied, we assume the domain is universally applicable as most of them are. Keeping fully-initialized and registered domains around that physically can't work on a large majority of FreeBSD deployments is sub-optimal and leads to errors that aren't consistent with the reality of why the socket can't be created (e.g. ESOCKTNOSUPPORT) because such scenario has to be caught upon pru_attach, at which point kicking back the more-appropriate EAFNOSUPPORT would seem weird. The initial consumer of this will be hvsock, which is only available on HyperV guests. Reviewed by: cem (earlier version), bcr (manpages) Differential Revision: https://reviews.freebsd.org/D25062 --- share/man/man9/Makefile | 1 + share/man/man9/domain.9 | 32 +++++++++++++++++++++++++++++--- sys/kern/uipc_domain.c | 9 +++++++++ sys/sys/domain.h | 5 +++++ 4 files changed, 44 insertions(+), 3 deletions(-) diff --git a/share/man/man9/Makefile b/share/man/man9/Makefile index 47c642319049..d7d26419b042 100644 --- a/share/man/man9/Makefile +++ b/share/man/man9/Makefile @@ -1011,6 +1011,7 @@ MLINKS+=dnv.9 dnvlist.9 \ dnv.9 dnvlist_take_string.9 MLINKS+=domain.9 DOMAIN_SET.9 \ domain.9 domain_add.9 \ + domain.9 domain_init.9 \ domain.9 pfctlinput.9 \ domain.9 pffinddomain.9 \ domain.9 pffindproto.9 \ diff --git a/share/man/man9/domain.9 b/share/man/man9/domain.9 index e8f97253a1d4..03e83edfc14b 100644 --- a/share/man/man9/domain.9 +++ b/share/man/man9/domain.9 @@ -31,6 +31,7 @@ .Os .Sh NAME .Nm domain_add , +.Nm domain_init , .Nm pfctlinput , .Nm pffinddomain , .Nm pffindproto , @@ -45,6 +46,8 @@ .Ft void .Fn domain_add "void *data" .Ft void +.Fn domain_init "void *data" +.Ft void .Fn pfctlinput "int cmd" "struct sockaddr *sa" .Ft struct domain * .Fn pffinddomain "int family" @@ -65,8 +68,10 @@ and struct domain { int dom_family; /* AF_xxx */ char *dom_name; + int dom_flags; void (*dom_init) /* initialize domain data structures */ (void); + int (*dom_probe)(void); /* check for support (optional) */ void (*dom_destroy) /* cleanup structures / state */ (void); int (*dom_externalize) /* externalize access rights */ @@ -143,15 +148,36 @@ is not called directly, instead .Fn DOMAIN_SET is used. .Pp -If the new domain has defined an initialization routine, it is called by -.Fn domain_add ; +If the new domain has defined a probe routine, it is called first in +.Fn domain_add +to determine if the domain should be supported on the current system. +If the probe routine returns a non-0 value, then the domain will not be +marked as supported. +Unsupported domains do not proceed with the initialization process and are not +discoverable by +.Fn pffinddomain , +.Fn pffindtype , +or +.Fn pffindproto . +.Pp +.Fn domain_init +is called after +.Fn domain_add +during boot and for each +.Xr vnet 9 . +If the new domain has defined an initialization routine, it is called during +.Fn domain_init ; as well, each of the protocols within the domain that have defined an initialization routine will have theirs called. +Note that domain initialization cannot fail at this time. .Pp -Once a domain is added it cannot be unloaded. +Once a domain is added it cannot be completely unloaded. This is because there is no reference counting system in place to determine if there are any active references from sockets within that domain. +If the domain defines a +.Fn dom_destroy +routine, then it will be invoked during vnet teardown. .Pp .Fn pffinddomain finds a domain by family. diff --git a/sys/kern/uipc_domain.c b/sys/kern/uipc_domain.c index 60e30eb1ae00..50d6c0a9d9f8 100644 --- a/sys/kern/uipc_domain.c +++ b/sys/kern/uipc_domain.c @@ -172,7 +172,11 @@ domain_init(void *arg) { struct domain *dp = arg; struct protosw *pr; + int flags; + flags = atomic_load_acq_int(&dp->dom_flags); + if ((flags & DOMF_SUPPORTED) == 0) + return; if (dp->dom_init) (*dp->dom_init)(); for (pr = dp->dom_protosw; pr < dp->dom_protoswNPROTOSW; pr++) @@ -200,6 +204,8 @@ vnet_domain_uninit(void *arg) { struct domain *dp = arg; + if ((atomic_load_acq_int(&dp->dom_flags) & DOMF_SUPPORTED) == 0) + return; if (dp->dom_destroy) (*dp->dom_destroy)(); } @@ -216,6 +222,9 @@ domain_add(void *data) struct domain *dp; dp = (struct domain *)data; + if (dp->dom_probe != NULL && (*dp->dom_probe)() != 0) + return; + atomic_set_rel_int(&dp->dom_flags, DOMF_SUPPORTED); mtx_lock(&dom_mtx); dp->dom_next = domains; domains = dp; diff --git a/sys/sys/domain.h b/sys/sys/domain.h index 3d17879f1ccd..a59f4e230a1d 100644 --- a/sys/sys/domain.h +++ b/sys/sys/domain.h @@ -50,8 +50,10 @@ struct rib_head; struct domain { int dom_family; /* AF_xxx */ char *dom_name; + int dom_flags; void (*dom_init) /* initialize domain data structures */ (void); + int (*dom_probe)(void); /* check for support (optional) */ void (*dom_destroy) /* cleanup structures / state */ (void); int (*dom_externalize) /* externalize access rights */ @@ -70,6 +72,9 @@ struct domain { /* af-dependent data on ifnet */ }; +/* dom_flags */ +#define DOMF_SUPPORTED 0x0001 /* System supports this domain. */ + #ifdef _KERNEL extern int domain_init_status; extern struct domain *domains;