Changeset View
Standalone View
sys/netinet6/nd6_rtr.c
Show First 20 Lines • Show All 62 Lines • ▼ Show 20 Lines | |||||
#include <net/radix.h> | #include <net/radix.h> | ||||
#include <net/vnet.h> | #include <net/vnet.h> | ||||
#include <netinet/in.h> | #include <netinet/in.h> | ||||
#include <net/if_llatbl.h> | #include <net/if_llatbl.h> | ||||
#include <netinet6/in6_var.h> | #include <netinet6/in6_var.h> | ||||
#include <netinet6/in6_ifattach.h> | #include <netinet6/in6_ifattach.h> | ||||
#include <netinet/ip6.h> | #include <netinet/ip6.h> | ||||
#include <netinet/icmp6.h> | |||||
#include <netinet6/ip6_var.h> | #include <netinet6/ip6_var.h> | ||||
#include <netinet6/nd6.h> | #include <netinet6/nd6.h> | ||||
#include <netinet/icmp6.h> | #include <netinet/icmp6.h> | ||||
#include <netinet6/scope6_var.h> | #include <netinet6/scope6_var.h> | ||||
static struct nd_defrouter *defrtrlist_update(struct nd_defrouter *); | static struct nd_defrouter *defrtrlist_update(struct nd_defrouter *); | ||||
static int prelist_update(struct nd_prefixctl *, struct nd_defrouter *, | static int prelist_update(struct nd_prefixctl *, struct nd_defrouter *, | ||||
struct mbuf *, int); | struct mbuf *, int); | ||||
TAILQ_HEAD(nd6_drhead, nd_defrouter); | TAILQ_HEAD(nd6_drhead, nd_defrouter); | ||||
VNET_DEFINE_STATIC(struct nd6_drhead, nd6_defrouter); | VNET_DEFINE_STATIC(struct nd6_drhead, nd6_defrouter); | ||||
#define V_nd6_defrouter VNET(nd6_defrouter) | #define V_nd6_defrouter VNET(nd6_defrouter) | ||||
/* | |||||
* Global limit and per-VNET count for the maximum amount of default routers | |||||
* we accept in the list. | |||||
*/ | |||||
static int nd6_defrouter_max = 16; | |||||
VNET_DEFINE_STATIC(int, nd6_defrouter_cur); | |||||
#define V_nd6_defrouter_cur VNET(nd6_defrouter_cur) | |||||
/* | |||||
* Global limit and per-VNET count for the maximum amount of prefixes | |||||
* we accept in the list. | |||||
*/ | |||||
static int nd6_prefixes_max = 64; | |||||
VNET_DEFINE_STATIC(int, nd6_prefixes_cur); | |||||
#define V_nd6_prefixes_cur VNET(nd6_prefixes_cur) | |||||
VNET_DECLARE(int, nd6_recalc_reachtm_interval); | VNET_DECLARE(int, nd6_recalc_reachtm_interval); | ||||
#define V_nd6_recalc_reachtm_interval VNET(nd6_recalc_reachtm_interval) | #define V_nd6_recalc_reachtm_interval VNET(nd6_recalc_reachtm_interval) | ||||
VNET_DEFINE_STATIC(struct ifnet *, nd6_defifp); | VNET_DEFINE_STATIC(struct ifnet *, nd6_defifp); | ||||
VNET_DEFINE(int, nd6_defifindex); | VNET_DEFINE(int, nd6_defifindex); | ||||
#define V_nd6_defifp VNET(nd6_defifp) | #define V_nd6_defifp VNET(nd6_defifp) | ||||
VNET_DEFINE(int, ip6_use_tempaddr) = 0; | VNET_DEFINE(int, ip6_use_tempaddr) = 0; | ||||
VNET_DEFINE(int, ip6_desync_factor); | VNET_DEFINE(int, ip6_desync_factor); | ||||
VNET_DEFINE(u_int32_t, ip6_temp_preferred_lifetime) = DEF_TEMP_PREFERRED_LIFETIME; | VNET_DEFINE(u_int32_t, ip6_temp_preferred_lifetime) = DEF_TEMP_PREFERRED_LIFETIME; | ||||
VNET_DEFINE(u_int32_t, ip6_temp_valid_lifetime) = DEF_TEMP_VALID_LIFETIME; | VNET_DEFINE(u_int32_t, ip6_temp_valid_lifetime) = DEF_TEMP_VALID_LIFETIME; | ||||
VNET_DEFINE(int, ip6_temp_regen_advance) = TEMPADDR_REGEN_ADVANCE; | VNET_DEFINE(int, ip6_temp_regen_advance) = TEMPADDR_REGEN_ADVANCE; | ||||
#ifdef EXPERIMENTAL | #ifdef EXPERIMENTAL | ||||
VNET_DEFINE(int, nd6_ignore_ipv6_only_ra) = 1; | VNET_DEFINE(int, nd6_ignore_ipv6_only_ra) = 1; | ||||
#endif | #endif | ||||
SYSCTL_DECL(_net_inet6_icmp6); | SYSCTL_DECL(_net_inet6_icmp6); | ||||
/* RTPREF_MEDIUM has to be 0! */ | /* RTPREF_MEDIUM has to be 0! */ | ||||
#define RTPREF_HIGH 1 | #define RTPREF_HIGH 1 | ||||
markj: Aren't these per-VNET limits? | |||||
#define RTPREF_MEDIUM 0 | #define RTPREF_MEDIUM 0 | ||||
#define RTPREF_LOW (-1) | #define RTPREF_LOW (-1) | ||||
#define RTPREF_RESERVED (-2) | #define RTPREF_RESERVED (-2) | ||||
#define RTPREF_INVALID (-3) /* internal */ | #define RTPREF_INVALID (-3) /* internal */ | ||||
static void | static void | ||||
defrouter_ref(struct nd_defrouter *dr) | defrouter_ref(struct nd_defrouter *dr) | ||||
{ | { | ||||
refcount_acquire(&dr->refcnt); | refcount_acquire(&dr->refcnt); | ||||
} | } | ||||
void | void | ||||
defrouter_rele(struct nd_defrouter *dr) | defrouter_rele(struct nd_defrouter *dr) | ||||
{ | { | ||||
Not Done Inline ActionsSorry for commenting on it lately. There is a use case with router and many IPv6 interfaces that I'd like to discuss. As the size check is done in nd6_prelist_add(), it is traversed by both nd6_ra_input() and SIOCAIFADDR_IN6 codepath. The latter may result in inability to configure more than 64 IPv6-enabled interfaces by default. melifaro: Sorry for commenting on it lately. There is a use case with router and many IPv6 interfaces… | |||||
Not Done Inline ActionsIndeed, I think we should only apply the limit to prefixes added via router advertisements. I believe expiration time cannot be used to determine whether to count the prefix against the limit, since there is nothing disallowing routers from advertising prefixes with infinite lifetimes. For now we could add a new NDPRF_ flag to indicate whether the prefix was generated from a router advertisement. markj: Indeed, I think we should only apply the limit to prefixes added via router advertisements.
I… | |||||
if (refcount_release(&dr->refcnt)) | if (refcount_release(&dr->refcnt)) | ||||
free(dr, M_IP6NDP); | free(dr, M_IP6NDP); | ||||
} | } | ||||
Not Done Inline ActionsExtra newline. markj: Extra newline. | |||||
/* | /* | ||||
* Remove a router from the global list and optionally stash it in a | * Remove a router from the global list and optionally stash it in a | ||||
* caller-supplied queue. | * caller-supplied queue. | ||||
*/ | */ | ||||
static void | static void | ||||
defrouter_unlink(struct nd_defrouter *dr, struct nd6_drhead *drq) | defrouter_unlink(struct nd_defrouter *dr, struct nd6_drhead *drq) | ||||
{ | { | ||||
ND6_WLOCK_ASSERT(); | ND6_WLOCK_ASSERT(); | ||||
TAILQ_REMOVE(&V_nd6_defrouter, dr, dr_entry); | TAILQ_REMOVE(&V_nd6_defrouter, dr, dr_entry); | ||||
V_nd6_defrouter_cur--; | |||||
V_nd6_list_genid++; | V_nd6_list_genid++; | ||||
if (drq != NULL) | if (drq != NULL) | ||||
TAILQ_INSERT_TAIL(drq, dr, dr_entry); | TAILQ_INSERT_TAIL(drq, dr, dr_entry); | ||||
} | } | ||||
/* | /* | ||||
* Receive Router Solicitation Message - just for routers. | * Receive Router Solicitation Message - just for routers. | ||||
* Router solicitation/advertisement is mostly managed by userland program | * Router solicitation/advertisement is mostly managed by userland program | ||||
▲ Show 20 Lines • Show All 971 Lines • ▼ Show 20 Lines | if (!ND6_TRY_UPGRADE()) { | ||||
genid = V_nd6_list_genid; | genid = V_nd6_list_genid; | ||||
ND6_RUNLOCK(); | ND6_RUNLOCK(); | ||||
ND6_WLOCK(); | ND6_WLOCK(); | ||||
if (genid != V_nd6_list_genid) | if (genid != V_nd6_list_genid) | ||||
goto restart; | goto restart; | ||||
} | } | ||||
} | } | ||||
/* | |||||
* Do this check before possibly removing the router from the list. | |||||
* If the sysctl is set to 0 we accept an unrestricted amount of | |||||
* default routers. If people do not want default routers, they | |||||
* should use the -no_radr interface flags or sysctl. | |||||
*/ | |||||
if (nd6_defrouter_max > 0 && V_nd6_defrouter_cur >= nd6_defrouter_max) { | |||||
ND6_WUNLOCK(); | |||||
ICMP6STAT_INC(icp6s_overflowdefrtr); | |||||
return (NULL); | |||||
melifaroUnsubmitted Not Done Inline ActionsIf we have a an existing router dropping it preference (high -> low) in "overflow" condition, we would update dr in the beginning, but fail to maintain sort order for V_nd6_defrouter. Can we restrict this check to dr == NULL use case? melifaro: If we have a an existing router dropping it preference (high -> low) in "overflow" condition… | |||||
bzAuthorUnsubmitted Done Inline ActionsSorry it took me so long to update this. To much on the plate. bz: Sorry it took me so long to update this. To much on the plate. | |||||
} | |||||
V_nd6_defrouter_cur++; | |||||
if (dr != NULL) { | if (dr != NULL) { | ||||
/* | /* | ||||
* The preferred router may have changed, so relocate this | * The preferred router may have changed, so relocate this | ||||
* router. | * router. | ||||
*/ | */ | ||||
TAILQ_REMOVE(&V_nd6_defrouter, dr, dr_entry); | TAILQ_REMOVE(&V_nd6_defrouter, dr, dr_entry); | ||||
V_nd6_defrouter_cur--; | |||||
n = dr; | n = dr; | ||||
} else { | } else { | ||||
n = malloc(sizeof(*n), M_IP6NDP, M_NOWAIT | M_ZERO); | n = malloc(sizeof(*n), M_IP6NDP, M_NOWAIT | M_ZERO); | ||||
if (n == NULL) { | if (n == NULL) { | ||||
V_nd6_defrouter_cur--; | |||||
ND6_WUNLOCK(); | ND6_WUNLOCK(); | ||||
return (NULL); | return (NULL); | ||||
} | } | ||||
memcpy(n, new, sizeof(*n)); | memcpy(n, new, sizeof(*n)); | ||||
/* Initialize with an extra reference for the caller. */ | /* Initialize with an extra reference for the caller. */ | ||||
refcount_init(&n->refcnt, 2); | refcount_init(&n->refcnt, 2); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 245 Lines • ▼ Show 20 Lines | nd6_prelist_add(struct nd_prefixctl *pr, struct nd_defrouter *dr, | ||||
/* initialization */ | /* initialization */ | ||||
LIST_INIT(&new->ndpr_advrtrs); | LIST_INIT(&new->ndpr_advrtrs); | ||||
in6_prefixlen2mask(&new->ndpr_mask, new->ndpr_plen); | in6_prefixlen2mask(&new->ndpr_mask, new->ndpr_plen); | ||||
/* make prefix in the canonical form */ | /* make prefix in the canonical form */ | ||||
IN6_MASK_ADDR(&new->ndpr_prefix.sin6_addr, &new->ndpr_mask); | IN6_MASK_ADDR(&new->ndpr_prefix.sin6_addr, &new->ndpr_mask); | ||||
ND6_WLOCK(); | ND6_WLOCK(); | ||||
if (nd6_prefixes_max > 0 && V_nd6_prefixes_cur >= nd6_prefixes_max) { | |||||
ND6_WUNLOCK(); | |||||
ICMP6STAT_INC(icp6s_overflowprfx); | |||||
free(new, M_IP6NDP); | |||||
return (ENOBUFS); | |||||
} | |||||
LIST_INSERT_HEAD(&V_nd_prefix, new, ndpr_entry); | LIST_INSERT_HEAD(&V_nd_prefix, new, ndpr_entry); | ||||
V_nd6_prefixes_cur++; | |||||
V_nd6_list_genid++; | V_nd6_list_genid++; | ||||
ND6_WUNLOCK(); | ND6_WUNLOCK(); | ||||
/* ND_OPT_PI_FLAG_ONLINK processing */ | /* ND_OPT_PI_FLAG_ONLINK processing */ | ||||
if (new->ndpr_raf_onlink) { | if (new->ndpr_raf_onlink) { | ||||
struct epoch_tracker et; | struct epoch_tracker et; | ||||
ND6_ONLINK_LOCK(); | ND6_ONLINK_LOCK(); | ||||
Show All 24 Lines | |||||
*/ | */ | ||||
void | void | ||||
nd6_prefix_unlink(struct nd_prefix *pr, struct nd_prhead *list) | nd6_prefix_unlink(struct nd_prefix *pr, struct nd_prhead *list) | ||||
{ | { | ||||
ND6_WLOCK_ASSERT(); | ND6_WLOCK_ASSERT(); | ||||
LIST_REMOVE(pr, ndpr_entry); | LIST_REMOVE(pr, ndpr_entry); | ||||
V_nd6_prefixes_cur--; | |||||
V_nd6_list_genid++; | V_nd6_list_genid++; | ||||
if (list != NULL) | if (list != NULL) | ||||
LIST_INSERT_HEAD(list, pr, ndpr_entry); | LIST_INSERT_HEAD(list, pr, ndpr_entry); | ||||
} | } | ||||
/* | /* | ||||
* Free an unlinked prefix, first marking it off-link if necessary. | * Free an unlinked prefix, first marking it off-link if necessary. | ||||
*/ | */ | ||||
▲ Show 20 Lines • Show All 1,189 Lines • ▼ Show 20 Lines | nd6_sysctl_drlist(SYSCTL_HANDLER_ARGS) | ||||
} | } | ||||
ND6_RUNLOCK(); | ND6_RUNLOCK(); | ||||
return (error); | return (error); | ||||
} | } | ||||
SYSCTL_PROC(_net_inet6_icmp6, ICMPV6CTL_ND6_DRLIST, nd6_drlist, | SYSCTL_PROC(_net_inet6_icmp6, ICMPV6CTL_ND6_DRLIST, nd6_drlist, | ||||
CTLTYPE_OPAQUE | CTLFLAG_RD | CTLFLAG_MPSAFE, | CTLTYPE_OPAQUE | CTLFLAG_RD | CTLFLAG_MPSAFE, | ||||
NULL, 0, nd6_sysctl_drlist, "S,in6_defrouter", | NULL, 0, nd6_sysctl_drlist, "S,in6_defrouter", | ||||
"NDP default router list"); | "NDP default router list"); | ||||
/* | |||||
* These ones are not adjustable per-VNET to keep things simple and avoid | |||||
* VNETs allowing themselves to be a DoS target exhausting memory. Otherwise | |||||
* we need to make this a PROC as well and handle a global max cap per-vnet. | |||||
*/ | |||||
SYSCTL_INT(_net_inet6_icmp6, OID_AUTO, nd6_defrouter_max, | |||||
CTLFLAG_RW, &nd6_defrouter_max, 0, | |||||
"Maximum number of IPv6 default routers. 0 accept unlimited."); | |||||
SYSCTL_INT(_net_inet6_icmp6, OID_AUTO, nd6_prefixes_max, | |||||
CTLFLAG_RW, &nd6_prefixes_max, 0, | |||||
"Maximum number of IPv6 prefix list entries. 0 accept unlimited."); |
Aren't these per-VNET limits?