mirror of
https://git.FreeBSD.org/src.git
synced 2024-12-13 10:02:38 +00:00
netinet6: factor interface addition code to the dedicated function
Summary: Move SIOCAIFADDR_IN6 (current "primary" ioctl to add an IPv6 interface address) handling code to the dedicated in6_addifaddr() function and make it a part of KPI. This allows in-kernel users to add/delete interfaces addresses without relying on ioctl interface. Subscribers: imp, ae, glebius Differential Revision: https://reviews.freebsd.org/D36713
This commit is contained in:
parent
ca7e6d7c29
commit
e437991fc9
@ -252,15 +252,15 @@ in6_control(struct socket *so, u_long cmd, void *data,
|
||||
struct in6_ifaddr *ia = NULL;
|
||||
struct in6_aliasreq *ifra = (struct in6_aliasreq *)data;
|
||||
struct sockaddr_in6 *sa6;
|
||||
int carp_attached = 0;
|
||||
int error;
|
||||
u_long ocmd = cmd;
|
||||
|
||||
/*
|
||||
* Compat to make pre-10.x ifconfig(8) operable.
|
||||
*/
|
||||
if (cmd == OSIOCAIFADDR_IN6)
|
||||
if (cmd == OSIOCAIFADDR_IN6) {
|
||||
cmd = SIOCAIFADDR_IN6;
|
||||
ifra->ifra_vhid = 0;
|
||||
}
|
||||
|
||||
switch (cmd) {
|
||||
case SIOCGETSGCNT_IN6:
|
||||
@ -560,142 +560,9 @@ in6_control(struct socket *so, u_long cmd, void *data,
|
||||
break;
|
||||
|
||||
case SIOCAIFADDR_IN6:
|
||||
{
|
||||
struct nd_prefixctl pr0;
|
||||
struct nd_prefix *pr;
|
||||
|
||||
/*
|
||||
* first, make or update the interface address structure,
|
||||
* and link it to the list.
|
||||
*/
|
||||
if ((error = in6_update_ifa(ifp, ifra, ia, 0)) != 0)
|
||||
goto out;
|
||||
if (ia != NULL) {
|
||||
if (ia->ia_ifa.ifa_carp)
|
||||
(*carp_detach_p)(&ia->ia_ifa, true);
|
||||
ifa_free(&ia->ia_ifa);
|
||||
}
|
||||
if ((ia = in6ifa_ifpwithaddr(ifp, &ifra->ifra_addr.sin6_addr))
|
||||
== NULL) {
|
||||
/*
|
||||
* this can happen when the user specify the 0 valid
|
||||
* lifetime.
|
||||
*/
|
||||
break;
|
||||
}
|
||||
|
||||
if (cmd == ocmd && ifra->ifra_vhid > 0) {
|
||||
if (carp_attach_p != NULL)
|
||||
error = (*carp_attach_p)(&ia->ia_ifa,
|
||||
ifra->ifra_vhid);
|
||||
else
|
||||
error = EPROTONOSUPPORT;
|
||||
if (error)
|
||||
goto out;
|
||||
else
|
||||
carp_attached = 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* then, make the prefix on-link on the interface.
|
||||
* XXX: we'd rather create the prefix before the address, but
|
||||
* we need at least one address to install the corresponding
|
||||
* interface route, so we configure the address first.
|
||||
*/
|
||||
|
||||
/*
|
||||
* convert mask to prefix length (prefixmask has already
|
||||
* been validated in in6_update_ifa().
|
||||
*/
|
||||
bzero(&pr0, sizeof(pr0));
|
||||
pr0.ndpr_ifp = ifp;
|
||||
pr0.ndpr_plen = in6_mask2len(&ifra->ifra_prefixmask.sin6_addr,
|
||||
NULL);
|
||||
if (pr0.ndpr_plen == 128) {
|
||||
/* we don't need to install a host route. */
|
||||
goto aifaddr_out;
|
||||
}
|
||||
pr0.ndpr_prefix = ifra->ifra_addr;
|
||||
/* apply the mask for safety. */
|
||||
IN6_MASK_ADDR(&pr0.ndpr_prefix.sin6_addr,
|
||||
&ifra->ifra_prefixmask.sin6_addr);
|
||||
|
||||
/*
|
||||
* XXX: since we don't have an API to set prefix (not address)
|
||||
* lifetimes, we just use the same lifetimes as addresses.
|
||||
* The (temporarily) installed lifetimes can be overridden by
|
||||
* later advertised RAs (when accept_rtadv is non 0), which is
|
||||
* an intended behavior.
|
||||
*/
|
||||
pr0.ndpr_raf_onlink = 1; /* should be configurable? */
|
||||
pr0.ndpr_raf_auto =
|
||||
((ifra->ifra_flags & IN6_IFF_AUTOCONF) != 0);
|
||||
pr0.ndpr_vltime = ifra->ifra_lifetime.ia6t_vltime;
|
||||
pr0.ndpr_pltime = ifra->ifra_lifetime.ia6t_pltime;
|
||||
|
||||
/* add the prefix if not yet. */
|
||||
if ((pr = nd6_prefix_lookup(&pr0)) == NULL) {
|
||||
/*
|
||||
* nd6_prelist_add will install the corresponding
|
||||
* interface route.
|
||||
*/
|
||||
if ((error = nd6_prelist_add(&pr0, NULL, &pr)) != 0) {
|
||||
if (carp_attached)
|
||||
(*carp_detach_p)(&ia->ia_ifa, false);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
/* relate the address to the prefix */
|
||||
if (ia->ia6_ndpr == NULL) {
|
||||
ia->ia6_ndpr = pr;
|
||||
pr->ndpr_addrcnt++;
|
||||
|
||||
/*
|
||||
* If this is the first autoconf address from the
|
||||
* prefix, create a temporary address as well
|
||||
* (when required).
|
||||
*/
|
||||
if ((ia->ia6_flags & IN6_IFF_AUTOCONF) &&
|
||||
V_ip6_use_tempaddr && pr->ndpr_addrcnt == 1) {
|
||||
int e;
|
||||
if ((e = in6_tmpifadd(ia, 1, 0)) != 0) {
|
||||
log(LOG_NOTICE, "in6_control: failed "
|
||||
"to create a temporary address, "
|
||||
"errno=%d\n", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
nd6_prefix_rele(pr);
|
||||
|
||||
/*
|
||||
* this might affect the status of autoconfigured addresses,
|
||||
* that is, this address might make other addresses detached.
|
||||
*/
|
||||
pfxlist_onlink_check();
|
||||
|
||||
aifaddr_out:
|
||||
/*
|
||||
* Try to clear the flag when a new IPv6 address is added
|
||||
* onto an IFDISABLED interface and it succeeds.
|
||||
*/
|
||||
if (ND_IFINFO(ifp)->flags & ND6_IFF_IFDISABLED) {
|
||||
struct in6_ndireq nd;
|
||||
|
||||
memset(&nd, 0, sizeof(nd));
|
||||
nd.ndi.flags = ND_IFINFO(ifp)->flags;
|
||||
nd.ndi.flags &= ~ND6_IFF_IFDISABLED;
|
||||
if (nd6_ioctl(SIOCSIFINFO_FLAGS, (caddr_t)&nd, ifp) < 0)
|
||||
log(LOG_NOTICE, "SIOCAIFADDR_IN6: "
|
||||
"SIOCSIFINFO_FLAGS for -ifdisabled "
|
||||
"failed.");
|
||||
/*
|
||||
* Ignore failure of clearing the flag intentionally.
|
||||
* The failure means address duplication was detected.
|
||||
*/
|
||||
}
|
||||
error = in6_addifaddr(ifp, ifra, ia);
|
||||
ia = NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
case SIOCDIFADDR_IN6:
|
||||
in6_purgeifaddr(ia);
|
||||
@ -1324,6 +1191,151 @@ ifa_is_p2p(struct in6_ifaddr *ia)
|
||||
return (false);
|
||||
}
|
||||
|
||||
int
|
||||
in6_addifaddr(struct ifnet *ifp, struct in6_aliasreq *ifra, struct in6_ifaddr *ia)
|
||||
{
|
||||
struct nd_prefixctl pr0;
|
||||
struct nd_prefix *pr;
|
||||
int carp_attached = 0;
|
||||
int error;
|
||||
|
||||
/*
|
||||
* first, make or update the interface address structure,
|
||||
* and link it to the list.
|
||||
*/
|
||||
if ((error = in6_update_ifa(ifp, ifra, ia, 0)) != 0)
|
||||
goto out;
|
||||
if (ia != NULL) {
|
||||
if (ia->ia_ifa.ifa_carp)
|
||||
(*carp_detach_p)(&ia->ia_ifa, true);
|
||||
ifa_free(&ia->ia_ifa);
|
||||
}
|
||||
if ((ia = in6ifa_ifpwithaddr(ifp, &ifra->ifra_addr.sin6_addr)) == NULL) {
|
||||
/*
|
||||
* this can happen when the user specify the 0 valid
|
||||
* lifetime.
|
||||
*/
|
||||
return (0);
|
||||
}
|
||||
|
||||
if (ifra->ifra_vhid > 0) {
|
||||
if (carp_attach_p != NULL)
|
||||
error = (*carp_attach_p)(&ia->ia_ifa,
|
||||
ifra->ifra_vhid);
|
||||
else
|
||||
error = EPROTONOSUPPORT;
|
||||
if (error)
|
||||
goto out;
|
||||
else
|
||||
carp_attached = 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* then, make the prefix on-link on the interface.
|
||||
* XXX: we'd rather create the prefix before the address, but
|
||||
* we need at least one address to install the corresponding
|
||||
* interface route, so we configure the address first.
|
||||
*/
|
||||
|
||||
/*
|
||||
* convert mask to prefix length (prefixmask has already
|
||||
* been validated in in6_update_ifa().
|
||||
*/
|
||||
bzero(&pr0, sizeof(pr0));
|
||||
pr0.ndpr_ifp = ifp;
|
||||
pr0.ndpr_plen = in6_mask2len(&ifra->ifra_prefixmask.sin6_addr,
|
||||
NULL);
|
||||
if (pr0.ndpr_plen == 128) {
|
||||
/* we don't need to install a host route. */
|
||||
goto aifaddr_out;
|
||||
}
|
||||
pr0.ndpr_prefix = ifra->ifra_addr;
|
||||
/* apply the mask for safety. */
|
||||
IN6_MASK_ADDR(&pr0.ndpr_prefix.sin6_addr,
|
||||
&ifra->ifra_prefixmask.sin6_addr);
|
||||
|
||||
/*
|
||||
* XXX: since we don't have an API to set prefix (not address)
|
||||
* lifetimes, we just use the same lifetimes as addresses.
|
||||
* The (temporarily) installed lifetimes can be overridden by
|
||||
* later advertised RAs (when accept_rtadv is non 0), which is
|
||||
* an intended behavior.
|
||||
*/
|
||||
pr0.ndpr_raf_onlink = 1; /* should be configurable? */
|
||||
pr0.ndpr_raf_auto =
|
||||
((ifra->ifra_flags & IN6_IFF_AUTOCONF) != 0);
|
||||
pr0.ndpr_vltime = ifra->ifra_lifetime.ia6t_vltime;
|
||||
pr0.ndpr_pltime = ifra->ifra_lifetime.ia6t_pltime;
|
||||
|
||||
/* add the prefix if not yet. */
|
||||
if ((pr = nd6_prefix_lookup(&pr0)) == NULL) {
|
||||
/*
|
||||
* nd6_prelist_add will install the corresponding
|
||||
* interface route.
|
||||
*/
|
||||
if ((error = nd6_prelist_add(&pr0, NULL, &pr)) != 0) {
|
||||
if (carp_attached)
|
||||
(*carp_detach_p)(&ia->ia_ifa, false);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
/* relate the address to the prefix */
|
||||
if (ia->ia6_ndpr == NULL) {
|
||||
ia->ia6_ndpr = pr;
|
||||
pr->ndpr_addrcnt++;
|
||||
|
||||
/*
|
||||
* If this is the first autoconf address from the
|
||||
* prefix, create a temporary address as well
|
||||
* (when required).
|
||||
*/
|
||||
if ((ia->ia6_flags & IN6_IFF_AUTOCONF) &&
|
||||
V_ip6_use_tempaddr && pr->ndpr_addrcnt == 1) {
|
||||
int e;
|
||||
if ((e = in6_tmpifadd(ia, 1, 0)) != 0) {
|
||||
log(LOG_NOTICE, "in6_control: failed "
|
||||
"to create a temporary address, "
|
||||
"errno=%d\n", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
nd6_prefix_rele(pr);
|
||||
|
||||
/*
|
||||
* this might affect the status of autoconfigured addresses,
|
||||
* that is, this address might make other addresses detached.
|
||||
*/
|
||||
pfxlist_onlink_check();
|
||||
|
||||
aifaddr_out:
|
||||
/*
|
||||
* Try to clear the flag when a new IPv6 address is added
|
||||
* onto an IFDISABLED interface and it succeeds.
|
||||
*/
|
||||
if (ND_IFINFO(ifp)->flags & ND6_IFF_IFDISABLED) {
|
||||
struct in6_ndireq nd;
|
||||
|
||||
memset(&nd, 0, sizeof(nd));
|
||||
nd.ndi.flags = ND_IFINFO(ifp)->flags;
|
||||
nd.ndi.flags &= ~ND6_IFF_IFDISABLED;
|
||||
if (nd6_ioctl(SIOCSIFINFO_FLAGS, (caddr_t)&nd, ifp) < 0)
|
||||
log(LOG_NOTICE, "SIOCAIFADDR_IN6: "
|
||||
"SIOCSIFINFO_FLAGS for -ifdisabled "
|
||||
"failed.");
|
||||
/*
|
||||
* Ignore failure of clearing the flag intentionally.
|
||||
* The failure means address duplication was detected.
|
||||
*/
|
||||
}
|
||||
error = 0;
|
||||
|
||||
out:
|
||||
if (ia != NULL)
|
||||
ifa_free(&ia->ia_ifa);
|
||||
return (error);
|
||||
}
|
||||
|
||||
void
|
||||
in6_purgeaddr(struct ifaddr *ifa)
|
||||
{
|
||||
|
@ -889,6 +889,7 @@ int in6_update_ifa(struct ifnet *, struct in6_aliasreq *,
|
||||
struct in6_ifaddr *, int);
|
||||
void in6_prepare_ifra(struct in6_aliasreq *, const struct in6_addr *,
|
||||
const struct in6_addr *);
|
||||
int in6_addifaddr(struct ifnet *, struct in6_aliasreq *, struct in6_ifaddr *);
|
||||
void in6_purgeaddr(struct ifaddr *);
|
||||
void in6_purgeifaddr(struct in6_ifaddr *);
|
||||
int in6if_do_dad(struct ifnet *);
|
||||
|
Loading…
Reference in New Issue
Block a user