Support for dynamically loadable and unloadable protocols within existing protocol
families. The protosw[] array of any particular protocol family ("domain") is of fixed size defined at compile time. This made it impossible to dynamically add or remove any protocols to or from it. We work around this by introducing so called SPACER's which are embedded into the protosw[] array at compile time. The SPACER's have a special protocol number (32767) to indicate the fact that they are SPACER's but are otherwise NULL. Only as many protocols can be dynamically loaded as SPACER's are provided in the protosw[] structure. The pr_usrreqs structure is treated more special and contains pointers to dummy functions only returning EOPNOTSUPP. This is needed because the use of those functions pointers is usually not checked within the kernel because until now it was assumed to be a valid function pointer. Instead of fixing all potential callers we just return a proper error code. Two new functions provide a clean API to register and unregister a protocol. The register function expects a pointer to a valid and complete struct protosw including a pointer to struct pru_usrreqs provided by the caller. Upon successful registration the pr_init() function will be called to finish initialization of the protocol. The unregister function restores the SPACER in place of the protocol again. It is the responseability of the caller to ensure proper closing of all sockets and freeing of memory allocation by the unloading protocol. sys/protosw.h o Define generic PROTO_SPACER to be 32767 o Prototypes for all pru_*_notsupp() functions o Prototypes for pf_proto_[un]register() functions kern/uipc_domain.c o Global struct pr_usrreqs nousrreqs containing valid pointers to the pru_*_notsupp() functions o New functions pf_proto_[un]register() kern/uipc_socket2.c o New functions bodies for all pru_*_notsupp() functions
This commit is contained in:
parent
1cf15713ed
commit
312c75c362
@ -69,9 +69,23 @@ struct domain *domains; /* registered protocol domains */
|
||||
struct mtx dom_mtx; /* domain list lock */
|
||||
MTX_SYSINIT(domain, &dom_mtx, "domain list", MTX_DEF);
|
||||
|
||||
/*
|
||||
* Dummy protocol specific user requests function pointer array.
|
||||
* All functions return EOPNOTSUPP.
|
||||
*/
|
||||
struct pr_usrreqs nousrreqs = {
|
||||
pru_abort_notsupp, pru_accept_notsupp, pru_attach_notsupp,
|
||||
pru_bind_notsupp, pru_connect_notsupp, pru_connect2_notsupp,
|
||||
pru_control_notsupp, pru_detach_notsupp, pru_disconnect_notsupp,
|
||||
pru_listen_notsupp, pru_peeraddr_notsupp, pru_rcvd_notsupp,
|
||||
pru_rcvoob_notsupp, pru_send_notsupp, pru_sense_null,
|
||||
pru_shutdown_notsupp, pru_sockaddr_notsupp, pru_sosend_notsupp,
|
||||
pru_soreceive_notsupp, pru_sopoll_notsupp, pru_sosetlabel_null
|
||||
};
|
||||
|
||||
/*
|
||||
* Add a new protocol domain to the list of supported domains
|
||||
* Note: you cant unload it again because a socket may be using it.
|
||||
* Note: you cant unload it again because a socket may be using it.
|
||||
* XXX can't fail at this time.
|
||||
*/
|
||||
static void
|
||||
@ -98,7 +112,7 @@ net_init_domain(struct domain *dp)
|
||||
|
||||
/*
|
||||
* Add a new protocol domain to the list of supported domains
|
||||
* Note: you cant unload it again because a socket may be using it.
|
||||
* Note: you cant unload it again because a socket may be using it.
|
||||
* XXX can't fail at this time.
|
||||
*/
|
||||
void
|
||||
@ -190,6 +204,126 @@ pffindproto(family, protocol, type)
|
||||
return (maybe);
|
||||
}
|
||||
|
||||
/*
|
||||
* The caller must make sure that the new protocol is fully set up and ready to
|
||||
* accept requests before it is registered.
|
||||
*/
|
||||
int
|
||||
pf_proto_register(family, npr)
|
||||
int family;
|
||||
struct protosw *npr;
|
||||
{
|
||||
struct domain *dp;
|
||||
struct protosw *pr, *fpr;
|
||||
|
||||
/* Sanity checks. */
|
||||
if (family == 0)
|
||||
return (EPFNOSUPPORT);
|
||||
if (npr->pr_type == 0)
|
||||
return (EPROTOTYPE);
|
||||
if (npr->pr_protocol == 0)
|
||||
return (EPROTONOSUPPORT);
|
||||
if (npr->pr_usrreqs == NULL)
|
||||
return (ENXIO);
|
||||
|
||||
/* Try to find the specified domain based on the family. */
|
||||
for (dp = domains; dp; dp = dp->dom_next)
|
||||
if (dp->dom_family == family)
|
||||
goto found;
|
||||
return (EPFNOSUPPORT);
|
||||
|
||||
found:
|
||||
/* Initialize backpointer to struct domain. */
|
||||
npr->pr_domain = dp;
|
||||
fpr = NULL;
|
||||
|
||||
/* The new protocol must not yet exist. */
|
||||
for (pr = dp->dom_protosw; pr < dp->dom_protoswNPROTOSW; pr++) {
|
||||
if ((pr->pr_type == npr->pr_type) &&
|
||||
(pr->pr_protocol == npr->pr_protocol))
|
||||
return (EEXIST); /* XXX: Check only protocol? */
|
||||
/* While here, remember the first free spacer. */
|
||||
if ((fpr == NULL) && (pr->pr_protocol == PROTO_SPACER))
|
||||
fpr = pr;
|
||||
}
|
||||
|
||||
/* If no free spacer is found we can't add the new protocol. */
|
||||
if (fpr == NULL)
|
||||
return (ENOMEM);
|
||||
|
||||
/* Copy the new struct protosw over the spacer. */
|
||||
bcopy(npr, fpr, sizeof(*fpr));
|
||||
|
||||
/* Initialize and activate the protocol. */
|
||||
if (fpr->pr_init)
|
||||
(fpr->pr_init)();
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* The caller must make sure the protocol and its functions correctly shut down
|
||||
* all sockets and release all locks and memory references.
|
||||
*/
|
||||
int
|
||||
pf_proto_unregister(family, protocol, type)
|
||||
int family;
|
||||
int protocol;
|
||||
int type;
|
||||
{
|
||||
struct domain *dp;
|
||||
struct protosw *pr, *dpr;
|
||||
|
||||
/* Sanity checks. */
|
||||
if (family == 0)
|
||||
return (EPFNOSUPPORT);
|
||||
if (protocol == 0)
|
||||
return (EPROTONOSUPPORT);
|
||||
if (type == 0)
|
||||
return (EPROTOTYPE);
|
||||
|
||||
/* Try to find the specified domain based on the family type. */
|
||||
for (dp = domains; dp; dp = dp->dom_next)
|
||||
if (dp->dom_family == family)
|
||||
goto found;
|
||||
return (EPFNOSUPPORT);
|
||||
|
||||
found:
|
||||
dpr = NULL;
|
||||
|
||||
/* The protocol must exist and only once. */
|
||||
for (pr = dp->dom_protosw; pr < dp->dom_protoswNPROTOSW; pr++) {
|
||||
if ((pr->pr_type == type) && (pr->pr_protocol == protocol)) {
|
||||
if (dpr != NULL)
|
||||
return (EMLINK); /* Should not happen! */
|
||||
else
|
||||
dpr = pr;
|
||||
}
|
||||
}
|
||||
|
||||
/* Protocol does not exist. */
|
||||
if (dpr == NULL)
|
||||
return (EPROTONOSUPPORT);
|
||||
|
||||
/* De-orbit the protocol and make the slot available again. */
|
||||
dpr->pr_type = 0;
|
||||
dpr->pr_domain = dp;
|
||||
dpr->pr_protocol = PROTO_SPACER;
|
||||
dpr->pr_flags = 0;
|
||||
dpr->pr_input = NULL;
|
||||
dpr->pr_output = NULL;
|
||||
dpr->pr_ctlinput = NULL;
|
||||
dpr->pr_ctloutput = NULL;
|
||||
dpr->pr_ousrreq = NULL;
|
||||
dpr->pr_init = NULL;
|
||||
dpr->pr_fasttimo = NULL;
|
||||
dpr->pr_slowtimo = NULL;
|
||||
dpr->pr_drain = NULL;
|
||||
dpr->pr_usrreqs = &nousrreqs;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
void
|
||||
pfctlinput(cmd, sa)
|
||||
int cmd;
|
||||
|
@ -1276,12 +1276,30 @@ sbcreatecontrol(p, size, type, level)
|
||||
* Some routines that return EOPNOTSUPP for entry points that are not
|
||||
* supported by a protocol. Fill in as needed.
|
||||
*/
|
||||
int
|
||||
pru_abort_notsupp(struct socket *so)
|
||||
{
|
||||
return EOPNOTSUPP;
|
||||
}
|
||||
|
||||
int
|
||||
pru_accept_notsupp(struct socket *so, struct sockaddr **nam)
|
||||
{
|
||||
return EOPNOTSUPP;
|
||||
}
|
||||
|
||||
int
|
||||
pru_attach_notsupp(struct socket *so, int proto, struct thread *td)
|
||||
{
|
||||
return EOPNOTSUPP;
|
||||
}
|
||||
|
||||
int
|
||||
pru_bind_notsupp(struct socket *so, struct sockaddr *nam, struct thread *td)
|
||||
{
|
||||
return EOPNOTSUPP;
|
||||
}
|
||||
|
||||
int
|
||||
pru_connect_notsupp(struct socket *so, struct sockaddr *nam, struct thread *td)
|
||||
{
|
||||
@ -1296,7 +1314,19 @@ pru_connect2_notsupp(struct socket *so1, struct socket *so2)
|
||||
|
||||
int
|
||||
pru_control_notsupp(struct socket *so, u_long cmd, caddr_t data,
|
||||
struct ifnet *ifp, struct thread *td)
|
||||
struct ifnet *ifp, struct thread *td)
|
||||
{
|
||||
return EOPNOTSUPP;
|
||||
}
|
||||
|
||||
int
|
||||
pru_detach_notsupp(struct socket *so)
|
||||
{
|
||||
return EOPNOTSUPP;
|
||||
}
|
||||
|
||||
int
|
||||
pru_disconnect_notsupp(struct socket *so)
|
||||
{
|
||||
return EOPNOTSUPP;
|
||||
}
|
||||
@ -1307,6 +1337,12 @@ pru_listen_notsupp(struct socket *so, struct thread *td)
|
||||
return EOPNOTSUPP;
|
||||
}
|
||||
|
||||
int
|
||||
pru_peeraddr_notsupp(struct socket *so, struct sockaddr **nam)
|
||||
{
|
||||
return EOPNOTSUPP;
|
||||
}
|
||||
|
||||
int
|
||||
pru_rcvd_notsupp(struct socket *so, int flags)
|
||||
{
|
||||
@ -1319,6 +1355,13 @@ pru_rcvoob_notsupp(struct socket *so, struct mbuf *m, int flags)
|
||||
return EOPNOTSUPP;
|
||||
}
|
||||
|
||||
int
|
||||
pru_send_notsupp(struct socket *so, int flags, struct mbuf *m,
|
||||
struct sockaddr *addr, struct mbuf *control, struct thread *td)
|
||||
{
|
||||
return EOPNOTSUPP;
|
||||
}
|
||||
|
||||
/*
|
||||
* This isn't really a ``null'' operation, but it's the default one
|
||||
* and doesn't do anything destructive.
|
||||
@ -1330,6 +1373,40 @@ pru_sense_null(struct socket *so, struct stat *sb)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
pru_shutdown_notsupp(struct socket *so)
|
||||
{
|
||||
return EOPNOTSUPP;
|
||||
}
|
||||
|
||||
int
|
||||
pru_sockaddr_notsupp(struct socket *so, struct sockaddr **nam)
|
||||
{
|
||||
return EOPNOTSUPP;
|
||||
}
|
||||
|
||||
int
|
||||
pru_sosend_notsupp(struct socket *so, struct sockaddr *addr, struct uio *uio,
|
||||
struct mbuf *top, struct mbuf *control, int flags, struct thread *td)
|
||||
{
|
||||
return EOPNOTSUPP;
|
||||
}
|
||||
|
||||
int
|
||||
pru_soreceive_notsupp(struct socket *so, struct sockaddr **paddr,
|
||||
struct uio *uio, struct mbuf **mp0, struct mbuf **controlp,
|
||||
int *flagsp)
|
||||
{
|
||||
return EOPNOTSUPP;
|
||||
}
|
||||
|
||||
int
|
||||
pru_sopoll_notsupp(struct socket *so, int events, struct ucred *cred,
|
||||
struct thread *td)
|
||||
{
|
||||
return EOPNOTSUPP;
|
||||
}
|
||||
|
||||
/*
|
||||
* For protocol types that don't keep cached copies of labels in their
|
||||
* pcbs, provide a null sosetlabel that does a NOOP.
|
||||
|
@ -1276,12 +1276,30 @@ sbcreatecontrol(p, size, type, level)
|
||||
* Some routines that return EOPNOTSUPP for entry points that are not
|
||||
* supported by a protocol. Fill in as needed.
|
||||
*/
|
||||
int
|
||||
pru_abort_notsupp(struct socket *so)
|
||||
{
|
||||
return EOPNOTSUPP;
|
||||
}
|
||||
|
||||
int
|
||||
pru_accept_notsupp(struct socket *so, struct sockaddr **nam)
|
||||
{
|
||||
return EOPNOTSUPP;
|
||||
}
|
||||
|
||||
int
|
||||
pru_attach_notsupp(struct socket *so, int proto, struct thread *td)
|
||||
{
|
||||
return EOPNOTSUPP;
|
||||
}
|
||||
|
||||
int
|
||||
pru_bind_notsupp(struct socket *so, struct sockaddr *nam, struct thread *td)
|
||||
{
|
||||
return EOPNOTSUPP;
|
||||
}
|
||||
|
||||
int
|
||||
pru_connect_notsupp(struct socket *so, struct sockaddr *nam, struct thread *td)
|
||||
{
|
||||
@ -1296,7 +1314,19 @@ pru_connect2_notsupp(struct socket *so1, struct socket *so2)
|
||||
|
||||
int
|
||||
pru_control_notsupp(struct socket *so, u_long cmd, caddr_t data,
|
||||
struct ifnet *ifp, struct thread *td)
|
||||
struct ifnet *ifp, struct thread *td)
|
||||
{
|
||||
return EOPNOTSUPP;
|
||||
}
|
||||
|
||||
int
|
||||
pru_detach_notsupp(struct socket *so)
|
||||
{
|
||||
return EOPNOTSUPP;
|
||||
}
|
||||
|
||||
int
|
||||
pru_disconnect_notsupp(struct socket *so)
|
||||
{
|
||||
return EOPNOTSUPP;
|
||||
}
|
||||
@ -1307,6 +1337,12 @@ pru_listen_notsupp(struct socket *so, struct thread *td)
|
||||
return EOPNOTSUPP;
|
||||
}
|
||||
|
||||
int
|
||||
pru_peeraddr_notsupp(struct socket *so, struct sockaddr **nam)
|
||||
{
|
||||
return EOPNOTSUPP;
|
||||
}
|
||||
|
||||
int
|
||||
pru_rcvd_notsupp(struct socket *so, int flags)
|
||||
{
|
||||
@ -1319,6 +1355,13 @@ pru_rcvoob_notsupp(struct socket *so, struct mbuf *m, int flags)
|
||||
return EOPNOTSUPP;
|
||||
}
|
||||
|
||||
int
|
||||
pru_send_notsupp(struct socket *so, int flags, struct mbuf *m,
|
||||
struct sockaddr *addr, struct mbuf *control, struct thread *td)
|
||||
{
|
||||
return EOPNOTSUPP;
|
||||
}
|
||||
|
||||
/*
|
||||
* This isn't really a ``null'' operation, but it's the default one
|
||||
* and doesn't do anything destructive.
|
||||
@ -1330,6 +1373,40 @@ pru_sense_null(struct socket *so, struct stat *sb)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
pru_shutdown_notsupp(struct socket *so)
|
||||
{
|
||||
return EOPNOTSUPP;
|
||||
}
|
||||
|
||||
int
|
||||
pru_sockaddr_notsupp(struct socket *so, struct sockaddr **nam)
|
||||
{
|
||||
return EOPNOTSUPP;
|
||||
}
|
||||
|
||||
int
|
||||
pru_sosend_notsupp(struct socket *so, struct sockaddr *addr, struct uio *uio,
|
||||
struct mbuf *top, struct mbuf *control, int flags, struct thread *td)
|
||||
{
|
||||
return EOPNOTSUPP;
|
||||
}
|
||||
|
||||
int
|
||||
pru_soreceive_notsupp(struct socket *so, struct sockaddr **paddr,
|
||||
struct uio *uio, struct mbuf **mp0, struct mbuf **controlp,
|
||||
int *flagsp)
|
||||
{
|
||||
return EOPNOTSUPP;
|
||||
}
|
||||
|
||||
int
|
||||
pru_sopoll_notsupp(struct socket *so, int events, struct ucred *cred,
|
||||
struct thread *td)
|
||||
{
|
||||
return EOPNOTSUPP;
|
||||
}
|
||||
|
||||
/*
|
||||
* For protocol types that don't keep cached copies of labels in their
|
||||
* pcbs, provide a null sosetlabel that does a NOOP.
|
||||
|
@ -104,6 +104,12 @@ struct protosw {
|
||||
#define PR_SLOWHZ 2 /* 2 slow timeouts per second */
|
||||
#define PR_FASTHZ 5 /* 5 fast timeouts per second */
|
||||
|
||||
/*
|
||||
* This number should be defined again within each protocol family to avoid
|
||||
* confusion.
|
||||
*/
|
||||
#define PROTO_SPACER 32767 /* spacer for loadable protocols */
|
||||
|
||||
/*
|
||||
* Values for pr_flags.
|
||||
* PR_ADDR requires PR_ATOMIC;
|
||||
@ -231,16 +237,41 @@ struct pr_usrreqs {
|
||||
void (*pru_sosetlabel)(struct socket *so);
|
||||
};
|
||||
|
||||
/*
|
||||
* The dummy protocol specific user requests function pointer array is
|
||||
* initialized to the functions below. All functions return EOPNOTSUPP.
|
||||
*/
|
||||
extern struct pr_usrreqs nousrreqs;
|
||||
|
||||
int pru_abort_notsupp(struct socket *so);
|
||||
int pru_accept_notsupp(struct socket *so, struct sockaddr **nam);
|
||||
int pru_attach_notsupp(struct socket *so, int proto, struct thread *td);
|
||||
int pru_bind_notsupp(struct socket *so, struct sockaddr *nam,
|
||||
struct thread *td);
|
||||
int pru_connect_notsupp(struct socket *so, struct sockaddr *nam,
|
||||
struct thread *td);
|
||||
int pru_connect2_notsupp(struct socket *so1, struct socket *so2);
|
||||
int pru_control_notsupp(struct socket *so, u_long cmd, caddr_t data,
|
||||
struct ifnet *ifp, struct thread *td);
|
||||
int pru_detach_notsupp(struct socket *so);
|
||||
int pru_disconnect_notsupp(struct socket *so);
|
||||
int pru_listen_notsupp(struct socket *so, struct thread *td);
|
||||
int pru_peeraddr_notsupp(struct socket *so, struct sockaddr **nam);
|
||||
int pru_rcvd_notsupp(struct socket *so, int flags);
|
||||
int pru_rcvoob_notsupp(struct socket *so, struct mbuf *m, int flags);
|
||||
int pru_send_notsupp(struct socket *so, int flags, struct mbuf *m,
|
||||
struct sockaddr *addr, struct mbuf *control, struct thread *td);
|
||||
int pru_sense_null(struct socket *so, struct stat *sb);
|
||||
int pru_shutdown_notsupp(struct socket *so);
|
||||
int pru_sockaddr_notsupp(struct socket *so, struct sockaddr **nam);
|
||||
int pru_sosend_notsupp(struct socket *so, struct sockaddr *addr,
|
||||
struct uio *uio, struct mbuf *top, struct mbuf *control, int flags,
|
||||
struct thread *td);
|
||||
int pru_soreceive_notsupp(struct socket *so, struct sockaddr **paddr,
|
||||
struct uio *uio, struct mbuf **mp0, struct mbuf **controlp,
|
||||
int *flagsp);
|
||||
int pru_sopoll_notsupp(struct socket *so, int events, struct ucred *cred,
|
||||
struct thread *td);
|
||||
void pru_sosetlabel_null(struct socket *so);
|
||||
|
||||
#endif /* _KERNEL */
|
||||
@ -319,6 +350,8 @@ void pfctlinput(int, struct sockaddr *);
|
||||
void pfctlinput2(int, struct sockaddr *, void *);
|
||||
struct protosw *pffindproto(int family, int protocol, int type);
|
||||
struct protosw *pffindtype(int family, int type);
|
||||
int pf_proto_register(int family, struct protosw *npr);
|
||||
int pf_proto_unregister(int family, int protocol, int type);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user