Remove old hack abusing domattach from NFS code.

According to IANA RPC uaddr registry, there are no AFs
except IPv4 and IPv6, so it's not worth being too abstract here.

Remove ne_rtable[AF_MAX+1] and use explicit per-AF radix tries.
Use own initialization without relying on domattach code.

While I admit that this was one of the rare places in kernel
networking code which really was capable of doing multi-AF
without any AF-depended code, it is not possible anymore to
rely on dom* code.

While here, change terrifying "Invalid radix node head, rn:" message,
to different non-understandable "netcred already exists for given addr/mask",
but less terrifying. Since we know that rn_addaddr() returns NULL if
the same record already exists, we should provide more friendly error.

MFC after:	1 month
This commit is contained in:
Alexander V. Chernikov 2014-11-05 00:58:01 +00:00
parent 0d265707aa
commit 9f25cbe45e
4 changed files with 75 additions and 74 deletions

View File

@ -37,9 +37,11 @@
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include "opt_inet.h"
#include "opt_inet6.h"
#include <sys/param.h>
#include <sys/dirent.h>
#include <sys/domain.h>
#include <sys/jail.h>
#include <sys/kernel.h>
#include <sys/lock.h>
@ -54,12 +56,16 @@ __FBSDID("$FreeBSD$");
#include <sys/systm.h>
#include <sys/vnode.h>
#include <netinet/in.h>
#include <net/radix.h>
static MALLOC_DEFINE(M_NETADDR, "export_host", "Export host address structure");
static struct radix_node_head *vfs_create_addrlist_af(
struct radix_node_head **prnh, int off);
static void vfs_free_addrlist(struct netexport *nep);
static int vfs_free_netcred(struct radix_node *rn, void *w);
static void vfs_free_addrlist_af(struct radix_node_head **prnh);
static int vfs_hang_addrlist(struct mount *mp, struct netexport *nep,
struct export_args *argp);
static struct netcred *vfs_export_lookup(struct mount *, struct sockaddr *);
@ -80,7 +86,8 @@ struct netcred {
*/
struct netexport {
struct netcred ne_defexported; /* Default export */
struct radix_node_head *ne_rtable[AF_MAX+1]; /* Individual exports */
struct radix_node_head *ne4;
struct radix_node_head *ne6;
};
/*
@ -96,7 +103,7 @@ vfs_hang_addrlist(struct mount *mp, struct netexport *nep,
register int i;
struct radix_node *rn;
struct sockaddr *saddr, *smask = 0;
struct domain *dom;
int off;
int error;
/*
@ -163,46 +170,39 @@ vfs_hang_addrlist(struct mount *mp, struct netexport *nep,
if (smask->sa_len > argp->ex_masklen)
smask->sa_len = argp->ex_masklen;
}
i = saddr->sa_family;
if ((rnh = nep->ne_rtable[i]) == NULL) {
/*
* Seems silly to initialize every AF when most are not used,
* do so on demand here
*/
for (dom = domains; dom; dom = dom->dom_next) {
KASSERT(((i == AF_INET) || (i == AF_INET6)),
("unexpected protocol in vfs_hang_addrlist"));
if (dom->dom_family == i && dom->dom_rtattach) {
/*
* XXX MRT
* The INET and INET6 domains know the
* offset already. We don't need to send it
* So we just use it as a flag to say that
* we are or are not setting up a real routing
* table. Only IP and IPV6 need have this
* be 0 so all other protocols can stay the
* same (ABI compatible).
*/
dom->dom_rtattach(
(void **) &nep->ne_rtable[i], 0);
break;
}
rnh = NULL;
switch (saddr->sa_family) {
#ifdef INET
case AF_INET:
if ((rnh = nep->ne4) == NULL) {
off = offsetof(struct sockaddr_in, sin_addr) << 3;
rnh = vfs_create_addrlist_af(&nep->ne4, off);
}
if ((rnh = nep->ne_rtable[i]) == NULL) {
error = ENOBUFS;
vfs_mount_error(mp, "%s %s %d",
"Unable to initialize radix node head ",
"for address family", i);
goto out;
break;
#endif
#ifdef INET6
case AF_INET6:
if ((rnh = nep->ne6) == NULL) {
off = offsetof(struct sockaddr_in6, sin6_addr) << 3;
rnh = vfs_create_addrlist_af(&nep->ne6, off);
}
break;
#endif
}
if (rnh == NULL) {
error = ENOBUFS;
vfs_mount_error(mp, "%s %s %d",
"Unable to initialize radix node head ",
"for address family", saddr->sa_family);
goto out;
}
RADIX_NODE_HEAD_LOCK(rnh);
rn = (*rnh->rnh_addaddr)(saddr, smask, rnh, np->netc_rnodes);
RADIX_NODE_HEAD_UNLOCK(rnh);
if (rn == NULL || np != (struct netcred *)rn) { /* already exists */
error = EPERM;
vfs_mount_error(mp, "Invalid radix node head, rn: %p %p",
rn, np);
vfs_mount_error(mp,
"netcred already exists for given addr/mask");
goto out;
}
np->netc_exflags = argp->ex_flags;
@ -237,26 +237,43 @@ vfs_free_netcred(struct radix_node *rn, void *w)
return (0);
}
static struct radix_node_head *
vfs_create_addrlist_af(struct radix_node_head **prnh, int off)
{
if (rn_inithead((void **)prnh, off) == 0)
return (NULL);
RADIX_NODE_HEAD_LOCK_INIT(*prnh);
return (*prnh);
}
static void
vfs_free_addrlist_af(struct radix_node_head **prnh)
{
struct radix_node_head *rnh;
rnh = *prnh;
RADIX_NODE_HEAD_LOCK(rnh);
(*rnh->rnh_walktree) (rnh, vfs_free_netcred, rnh);
RADIX_NODE_HEAD_UNLOCK(rnh);
RADIX_NODE_HEAD_DESTROY(rnh);
free(rnh, M_RTABLE);
prnh = NULL;
}
/*
* Free the net address hash lists that are hanging off the mount points.
*/
static void
vfs_free_addrlist(struct netexport *nep)
{
int i;
struct radix_node_head *rnh;
struct ucred *cred;
for (i = 0; i <= AF_MAX; i++) {
if ((rnh = nep->ne_rtable[i])) {
RADIX_NODE_HEAD_LOCK(rnh);
(*rnh->rnh_walktree) (rnh, vfs_free_netcred, rnh);
RADIX_NODE_HEAD_UNLOCK(rnh);
RADIX_NODE_HEAD_DESTROY(rnh);
free(rnh, M_RTABLE);
nep->ne_rtable[i] = NULL; /* not SMP safe XXX */
}
}
if (nep->ne4 != NULL)
vfs_free_addrlist_af(&nep->ne4);
if (nep->ne6 != NULL)
vfs_free_addrlist_af(&nep->ne6);
cred = nep->ne_defexported.netc_anon;
if (cred != NULL)
crfree(cred);
@ -439,7 +456,15 @@ vfs_export_lookup(struct mount *mp, struct sockaddr *nam)
*/
if (nam != NULL) {
saddr = nam;
rnh = nep->ne_rtable[saddr->sa_family];
rnh = NULL;
switch (saddr->sa_family) {
case AF_INET:
rnh = nep->ne4;
break;
case AF_INET6:
rnh = nep->ne6;
break;
}
if (rnh != NULL) {
RADIX_NODE_HEAD_RLOCK(rnh);
np = (struct netcred *)

View File

@ -341,23 +341,12 @@ in_inithead(void **head, int off)
{
struct radix_node_head *rnh;
/* XXX MRT
* This can be called from vfs_export.c too in which case 'off'
* will be 0. We know the correct value so just use that and
* return directly if it was 0.
* This is a hack that replaces an even worse hack on a bad hack
* on a bad design. After RELENG_7 this should be fixed but that
* will change the ABI, so for now do it this way.
*/
if (!rn_inithead(head, 32))
return 0;
rnh = *head;
RADIX_NODE_HEAD_LOCK_INIT(rnh);
if (off == 0) /* XXX MRT see above */
return 1; /* only do the rest for a real routing table */
rnh->rnh_addaddr = in_addroute;
in_setmatchfunc(rnh, V_drop_redirect);
rnh->rnh_close = in_clsroute;

View File

@ -254,10 +254,6 @@ in6_mtutimo(void *rock)
/*
* Initialize our routing tree.
* XXX MRT When off == 0, we are being called from vfs_export.c
* so just set up their table and leave. (we know what the correct
* value should be so just use that).. FIX AFTER RELENG_7 is MFC'd
* see also comments in in_inithead() vfs_export.c and domain.h
*/
static VNET_DEFINE(int, _in6_rt_was_here);
#define V__in6_rt_was_here VNET(_in6_rt_was_here)
@ -268,14 +264,11 @@ in6_inithead(void **head, int off)
struct radix_node_head *rnh;
if (!rn_inithead(head, offsetof(struct sockaddr_in6, sin6_addr) << 3))
return 0; /* See above */
return (0);
rnh = *head;
RADIX_NODE_HEAD_LOCK_INIT(rnh);
if (off == 0) /* See above */
return 1; /* only do the rest for the real thing */
rnh->rnh_addaddr = in6_addroute;
if (V__in6_rt_was_here == 0) {
@ -284,7 +277,7 @@ in6_inithead(void **head, int off)
V__in6_rt_was_here = 1;
}
return 1;
return (1);
}
#ifdef VIMAGE

View File

@ -61,12 +61,6 @@ struct domain {
int (*dom_rtdetach) /* clean up routing table */
(void **, int);
int dom_rtoffset; /* an arg to rtattach, in bits */
/* XXX MRT.
* rtoffset May be 0 if the domain supplies its own rtattach(),
* in which case, a 0 indicates it's being called from
* vfs_export.c (HACK) Only for AF_INET{,6} at this time.
* Temporary ABI compat hack.. fix post RELENG_7
*/
int dom_maxrtkey; /* for routing layer */
void *(*dom_ifattach)(struct ifnet *);
void (*dom_ifdetach)(struct ifnet *, void *);