📄 in6.c
字号:
{
if (ifa->ifa_addr->sa_family != AF_INET6)
continue;
if (!cmp)
break;
bcopy(IFA_IN6(ifa), &candidate, sizeof(candidate));
#ifndef SCOPEDROUTING
/*
* XXX: this is adhoc, but is necessary to allow
* a user to specify fe80::/64 (not /10) for a
* link-local address.
*/
if (IN6_IS_ADDR_LINKLOCAL(&candidate))
candidate.s6_addr16[1] = 0;
#endif
candidate.s6_addr32[0] &= mask.s6_addr32[0];
candidate.s6_addr32[1] &= mask.s6_addr32[1];
candidate.s6_addr32[2] &= mask.s6_addr32[2];
candidate.s6_addr32[3] &= mask.s6_addr32[3];
if (IN6_ARE_ADDR_EQUAL(&candidate, &match))
break;
}
if (!ifa)
return EADDRNOTAVAIL;
ia = ifa2ia6(ifa);
if (cmd == SIOCGLIFADDR) {
#ifndef SCOPEDROUTING
struct sockaddr_in6 *s6;
#endif
/* fill in the if_laddrreq structure */
bcopy(&ia->ia_addr, &iflr->addr, ia->ia_addr.sin6_len);
#ifndef SCOPEDROUTING /* XXX see above */
s6 = (struct sockaddr_in6 *)&iflr->addr;
if (IN6_IS_ADDR_LINKLOCAL(&s6->sin6_addr)) {
s6->sin6_addr.s6_addr16[1] = 0;
zoneid = in6_addr2zoneid(ifp, &s6->sin6_addr);
if (zoneid < 0) /* XXX: should not happen */
return(EINVAL);
s6->sin6_scope_id = zoneid;
}
#endif
if ((ifp->if_flags & IFF_POINTOPOINT) != 0) {
bcopy(&ia->ia_dstaddr, &iflr->dstaddr,
ia->ia_dstaddr.sin6_len);
#ifndef SCOPEDROUTING /* XXX see above */
s6 = (struct sockaddr_in6 *)&iflr->dstaddr;
if (IN6_IS_ADDR_LINKLOCAL(&s6->sin6_addr)) {
s6->sin6_addr.s6_addr16[1] = 0;
zoneid = in6_addr2zoneid(ifp,
&s6->sin6_addr);
if (zoneid < 0) /* XXX */
return(EINVAL);
s6->sin6_scope_id = zoneid;
}
#endif
} else
bzero(&iflr->dstaddr, sizeof(iflr->dstaddr));
iflr->prefixlen =
in6_mask2len(&ia->ia_prefixmask.sin6_addr,
NULL);
iflr->flags = ia->ia6_flags; /* XXX */
return 0;
} else {
struct in6_aliasreq ifra;
/* fill in6_aliasreq and do ioctl(SIOCDIFADDR_IN6) */
bzero(&ifra, sizeof(ifra));
bcopy(iflr->iflr_name, ifra.ifra_name,
sizeof(ifra.ifra_name));
bcopy(&ia->ia_addr, &ifra.ifra_addr,
ia->ia_addr.sin6_len);
if ((ifp->if_flags & IFF_POINTOPOINT) != 0) {
bcopy(&ia->ia_dstaddr, &ifra.ifra_dstaddr,
ia->ia_dstaddr.sin6_len);
} else {
bzero(&ifra.ifra_dstaddr,
sizeof(ifra.ifra_dstaddr));
}
bcopy(&ia->ia_prefixmask, &ifra.ifra_dstaddr,
ia->ia_prefixmask.sin6_len);
ifra.ifra_flags = ia->ia6_flags;
#if !defined(__bsdi__) && !(defined(__FreeBSD__) && __FreeBSD__ < 3)
return in6_control(so, SIOCDIFADDR_IN6, (caddr_t)&ifra,
ifp, p);
#else
return in6_control(so, SIOCDIFADDR_IN6, (caddr_t)&ifra,
ifp);
#endif
}
}
}
return EOPNOTSUPP; /* just for safety */
}
/*
* Initialize an interface's intetnet6 address
* and routing table entry.
*/
static int
in6_ifinit(ifp, ia, sin6, newhost)
struct ifnet *ifp;
struct in6_ifaddr *ia;
struct sockaddr_in6 *sin6;
int newhost;
{
int error = 0, plen, ifacount = 0;
int s = splimp();
struct ifaddr *ifa;
/*
* Give the interface a chance to initialize
* if this is its first address,
* and to validate the address if necessary.
*/
#if defined(__bsdi__) || (defined(__FreeBSD__) && __FreeBSD__ < 3)
for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next)
#elif defined(__FreeBSD__) && __FreeBSD__ >= 4
TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list)
#else
for (ifa = ifp->if_addrlist.tqh_first; ifa;
ifa = ifa->ifa_list.tqe_next)
#endif
{
if (ifa->ifa_addr == NULL)
continue; /* just for safety */
if (ifa->ifa_addr->sa_family != AF_INET6)
continue;
ifacount++;
}
ia->ia_addr = *sin6;
if (ifacount <= 1 && ifp->if_ioctl &&
(error = (*ifp->if_ioctl)(ifp, SIOCSIFADDR, (caddr_t)ia))) {
splx(s);
return(error);
}
splx(s);
ia->ia_ifa.ifa_metric = ifp->if_metric;
/* we could do in(6)_socktrim here, but just omit it at this moment. */
/*
* Special case:
* If the destination address is specified for a point-to-point
* interface, install a route to the destination as an interface
* direct route.
*/
plen = in6_mask2len(&ia->ia_prefixmask.sin6_addr, NULL); /* XXX */
if (plen == 128 && ia->ia_dstaddr.sin6_family == AF_INET6) {
if ((error = rtinit(&(ia->ia_ifa), (int)RTM_ADD,
RTF_UP | RTF_HOST)) != 0)
return(error);
ia->ia_flags |= IFA_ROUTE;
}
if (plen < 128) {
/*
* The RTF_CLONING flag is necessary for in6_is_ifloop_auto().
*/
ia->ia_ifa.ifa_flags |= RTF_CLONING;
}
/* Add ownaddr as loopback rtentry, if necessary (ex. on p2p link). */
if (newhost) {
/* set the rtrequest function to create llinfo */
ia->ia_ifa.ifa_rtrequest = nd6_rtrequest;
in6_ifaddloop(&(ia->ia_ifa));
}
#if !(defined(__FreeBSD__) && __FreeBSD__ >= 3)
if (ifp->if_flags & IFF_MULTICAST)
in6_restoremkludge(ia, ifp);
#endif
return(error);
}
#if !(defined(__FreeBSD__) && __FreeBSD__ >= 3)
/*
* Multicast address kludge:
* If there were any multicast addresses attached to this interface address,
* either move them to another address on this interface, or save them until
* such time as this interface is reconfigured for IPv6.
*/
void
in6_savemkludge(oia)
struct in6_ifaddr *oia;
{
struct in6_ifaddr *ia;
struct in6_multi *in6m, *next;
IFP_TO_IA6(oia->ia_ifp, ia);
if (ia) { /* there is another address */
for (in6m = oia->ia6_multiaddrs.lh_first; in6m; in6m = next){
next = in6m->in6m_entry.le_next;
IFAFREE(&in6m->in6m_ia->ia_ifa);
IFAREF(&ia->ia_ifa);
in6m->in6m_ia = ia;
LIST_INSERT_HEAD(&ia->ia6_multiaddrs, in6m, in6m_entry);
}
} else { /* last address on this if deleted, save */
struct multi6_kludge *mk;
for (mk = in6_mk.lh_first; mk; mk = mk->mk_entry.le_next) {
if (mk->mk_ifp == oia->ia_ifp)
break;
}
if (mk == NULL) /* this should not happen! */
panic("in6_savemkludge: no kludge space");
for (in6m = oia->ia6_multiaddrs.lh_first; in6m; in6m = next){
next = in6m->in6m_entry.le_next;
IFAFREE(&in6m->in6m_ia->ia_ifa); /* release reference */
in6m->in6m_ia = NULL;
LIST_INSERT_HEAD(&mk->mk_head, in6m, in6m_entry);
}
}
}
/*
* Continuation of multicast address hack:
* If there was a multicast group list previously saved for this interface,
* then we re-attach it to the first address configured on the i/f.
*/
void
in6_restoremkludge(ia, ifp)
struct in6_ifaddr *ia;
struct ifnet *ifp;
{
struct multi6_kludge *mk;
for (mk = in6_mk.lh_first; mk; mk = mk->mk_entry.le_next) {
if (mk->mk_ifp == ifp) {
struct in6_multi *in6m, *next;
for (in6m = mk->mk_head.lh_first; in6m; in6m = next) {
next = in6m->in6m_entry.le_next;
in6m->in6m_ia = ia;
IFAREF(&ia->ia_ifa);
LIST_INSERT_HEAD(&ia->ia6_multiaddrs,
in6m, in6m_entry);
}
LIST_INIT(&mk->mk_head);
break;
}
}
}
/*
* Allocate space for the kludge at interface initialization time.
* Formerly, we dynamically allocated the space in in6_savemkludge() with
* malloc(M_WAITOK). However, it was wrong since the function could be called
* under an interrupt context (software timer on address lifetime expiration).
* Also, we cannot just give up allocating the strucutre, since the group
* membership structure is very complex and we need to keep it anyway.
* Of course, this function MUST NOT be called under an interrupt context.
* Specifically, it is expected to be called only from in6_ifattach(), though
* it is a global function.
*/
void
in6_createmkludge(ifp)
struct ifnet *ifp;
{
struct multi6_kludge *mk;
for (mk = in6_mk.lh_first; mk; mk = mk->mk_entry.le_next) {
/* If we've already had one, do not allocate. */
if (mk->mk_ifp == ifp)
return;
}
mk = malloc(sizeof(*mk), M_IPMADDR, M_WAITOK);
bzero(mk, sizeof(*mk));
LIST_INIT(&mk->mk_head);
mk->mk_ifp = ifp;
LIST_INSERT_HEAD(&in6_mk, mk, mk_entry);
}
void
in6_purgemkludge(ifp)
struct ifnet *ifp;
{
struct multi6_kludge *mk;
struct in6_multi *in6m;
for (mk = in6_mk.lh_first; mk; mk = mk->mk_entry.le_next) {
if (mk->mk_ifp != ifp)
continue;
/* leave from all multicast groups joined */
while ((in6m = LIST_FIRST(&mk->mk_head)) != NULL)
in6_delmulti(in6m);
LIST_REMOVE(mk, mk_entry);
free(mk, M_IPMADDR);
break;
}
}
/*
* Add an address to the list of IP6 multicast addresses for a
* given interface.
*/
struct in6_multi *
in6_addmulti(maddr6, ifp, errorp)
struct in6_addr *maddr6;
struct ifnet *ifp;
int *errorp;
{
struct in6_ifaddr *ia;
struct in6_ifreq ifr;
struct in6_multi *in6m;
#ifdef __NetBSD__
int s = splsoftnet();
#else
int s = splnet();
#endif
*errorp = 0;
/*
* See if address already in list.
*/
IN6_LOOKUP_MULTI(*maddr6, ifp, in6m);
if (in6m != NULL) {
/*
* Found it; just increment the refrence count.
*/
in6m->in6m_refcount++;
} else {
/*
* New address; allocate a new multicast record
* and link it into the interface's multicast list.
*/
in6m = (struct in6_multi *)
malloc(sizeof(*in6m), M_IPMADDR, M_NOWAIT);
if (in6m == NULL) {
splx(s);
*errorp = ENOBUFS;
return(NULL);
}
in6m->in6m_addr = *maddr6;
in6m->in6m_ifp = ifp;
in6m->in6m_refcount = 1;
IFP_TO_IA6(ifp, ia);
if (ia == NULL) {
free(in6m, M_IPMADDR);
splx(s);
*errorp = EADDRNOTAVAIL; /* appropriate? */
return(NULL);
}
in6m->in6m_ia = ia;
IFAREF(&ia->ia_ifa); /* gain a reference */
LIST_INSERT_HEAD(&ia->ia6_multiaddrs, in6m, in6m_entry);
/*
* Ask the network driver to update its multicast reception
* filter appropriately for the new address.
*/
bzero(&ifr.ifr_addr, sizeof(struct sockaddr_in6));
ifr.ifr_addr.sin6_len = sizeof(struct sockaddr_in6);
ifr.ifr_addr.sin6_family = AF_INET6;
ifr.ifr_addr.sin6_addr = *maddr6;
if (ifp->if_ioctl == NULL)
*errorp = ENXIO; /* XXX: appropriate? */
else
*errorp = (*ifp->if_ioctl)(ifp, SIOCADDMULTI,
(caddr_t)&ifr);
if (*errorp) {
LIST_REMOVE(in6m, in6m_entry);
free(in6m, M_IPMADDR);
IFAFREE(&ia->ia_ifa);
splx(s);
return(NULL);
}
/*
* Let MLD6 know that we have joined a new IP6 multicast
* group.
*/
mld6_start_listening(in6m);
}
splx(s);
return(in6m);
}
/*
* Delete a multicast address record.
*/
void
in6_delmulti(in6m)
struct in6_multi *in6m;
{
struct in6_ifreq ifr;
#ifdef __NetBSD__
int s = splsoftnet();
#else
int s = splnet();
#endif
if (--in6m->in6m_refcount == 0) {
/*
* No remaining claims to this record; let MLD6 know
* that we are leaving the multicast group.
*/
mld6_stop_listening(in6m);
/*
* Unlink from list.
*/
LIST_REMOVE(in6m, in6m_entry);
if (in6m->in6m_ia) {
IFAFREE(&in6m->in6m_ia->ia_ifa); /* release reference */
}
/*
* Notify the network driver to update its multicast
* reception filter.
*/
bzero(&ifr.ifr_addr, sizeof(struct sockaddr_in6));
ifr.ifr_addr.sin6_len = sizeof(struct sockaddr_in6);
ifr.ifr_addr.sin6_family = AF_INET6;
ifr.ifr_addr.sin6_addr = in6m->in6m_addr;
(*in6m->in6m_ifp->if_ioctl)(in6m->in6m_ifp,
SIOCDELMULTI, (caddr_t)&ifr);
free(in6m, M_IPMADDR);
}
splx(s);
}
#else /* not FreeBSD3 */
/*
* Add an address to the list of IP6 multicast addresses for a
* given interface.
*/
struct in6_multi *
in6_addmulti(maddr6, ifp, errorp)
struct in6_addr *maddr6;
struct ifnet *ifp;
int *errorp;
{
struct in6_multi *in6m;
struct sockaddr_in6 sin6;
struct ifmultiaddr *ifma;
int s = splnet();
*errorp = 0;
/*
* Call generic routine to add membership or increment
* refcount. It wants addresses in the form of a sockaddr,
* so we build one here (being careful to zero the unused byte
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -