#include "opt_bootp.h"
#include "opt_carp.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/sockio.h>
#include <sys/malloc.h>
#include <sys/proc.h>
#include <sys/caps.h>
#include <sys/msgport.h>
#include <sys/socket.h>
#include <sys/kernel.h>
#include <sys/sysctl.h>
#include <sys/thread2.h>
#include <net/if.h>
#include <net/if_types.h>
#include <net/route.h>
#include <net/netmsg2.h>
#include <net/netisr2.h>
#include <netinet/in.h>
#include <netinet/in_var.h>
#include <netinet/in_pcb.h>
#include <netinet/udp_var.h>
#include <netinet/igmp_var.h>
MALLOC_DEFINE(M_IPMADDR, "in_multi", "internet multicast address");
static int in_mask2len (struct in_addr *);
static void in_len2mask (struct in_addr *, int);
static int in_lifaddr_ioctl (u_long, caddr_t, struct ifnet *, struct thread *);
static void in_socktrim (struct sockaddr_in *);
static int in_ifinit(struct ifnet *, struct in_ifaddr *,
const struct sockaddr_in *, int);
static int in_control_internal(u_long, caddr_t, struct ifnet *,
struct thread *);
static int in_addprefix(struct in_ifaddr *, int);
static void in_scrubprefix(struct in_ifaddr *);
static int subnetsarelocal = 0;
SYSCTL_INT(_net_inet_ip, OID_AUTO, subnets_are_local, CTLFLAG_RW,
&subnetsarelocal, 0,
"Count all internet addresses of subnets of the local net as local");
static int broadcast_lowest = 0;
SYSCTL_INT(_net_inet_ip, OID_AUTO, broadcast_lowest, CTLFLAG_RW,
&broadcast_lowest, 0,
"Count all .0 subnets as broadcast");
struct in_multihead in_multihead;
extern struct inpcbinfo ripcbinfo;
int
in_localaddr(struct in_addr in)
{
u_long i = ntohl(in.s_addr);
struct in_ifaddr_container *iac;
struct in_ifaddr *ia;
if (subnetsarelocal) {
TAILQ_FOREACH(iac, &in_ifaddrheads[mycpuid], ia_link) {
ia = iac->ia;
if ((i & ia->ia_netmask) == ia->ia_net)
return (1);
}
} else {
TAILQ_FOREACH(iac, &in_ifaddrheads[mycpuid], ia_link) {
ia = iac->ia;
if ((i & ia->ia_subnetmask) == ia->ia_subnet)
return (1);
}
}
return (0);
}
int
in_canforward(struct in_addr in)
{
u_long i = ntohl(in.s_addr);
u_long net;
if (IN_EXPERIMENTAL(i) || IN_MULTICAST(i))
return (0);
if (IN_CLASSA(i)) {
net = i & IN_CLASSA_NET;
if (net == 0 || net == (IN_LOOPBACKNET << IN_CLASSA_NSHIFT))
return (0);
}
return (1);
}
static void
in_socktrim(struct sockaddr_in *ap)
{
char *cplim = (char *) &ap->sin_addr;
char *cp = (char *) (&ap->sin_addr + 1);
ap->sin_len = 0;
while (--cp >= cplim) {
if (*cp) {
(ap)->sin_len = cp - (char *) (ap) + 1;
break;
}
}
}
static int
in_mask2len(struct in_addr *mask)
{
int x, y;
u_char *p;
p = (u_char *)mask;
for (x = 0; x < sizeof *mask; x++) {
if (p[x] != 0xff)
break;
}
y = 0;
if (x < sizeof *mask) {
for (y = 0; y < 8; y++) {
if ((p[x] & (0x80 >> y)) == 0)
break;
}
}
return x * 8 + y;
}
static void
in_len2mask(struct in_addr *mask, int len)
{
int i;
u_char *p;
p = (u_char *)mask;
bzero(mask, sizeof *mask);
for (i = 0; i < len / 8; i++)
p[i] = 0xff;
if (len % 8)
p[i] = (0xff00 >> (len % 8)) & 0xff;
}
void
in_control_dispatch(netmsg_t msg)
{
int error;
error = in_control(msg->control.nm_cmd, msg->control.nm_data,
msg->control.nm_ifp, msg->control.nm_td);
lwkt_replymsg(&msg->lmsg, error);
}
static void
in_control_internal_dispatch(netmsg_t msg)
{
int error;
error = in_control_internal(msg->control.nm_cmd,
msg->control.nm_data,
msg->control.nm_ifp,
msg->control.nm_td);
lwkt_replymsg(&msg->lmsg, error);
}
int
in_control(u_long cmd, caddr_t data, struct ifnet *ifp, struct thread *td)
{
struct netmsg_pru_control msg;
switch (cmd) {
case SIOCALIFADDR:
case SIOCDLIFADDR:
case SIOCSIFDSTADDR:
case SIOCSIFBRDADDR:
case SIOCSIFADDR:
case SIOCSIFNETMASK:
case SIOCAIFADDR:
case SIOCDIFADDR:
case SIOCGIFALIAS:
netmsg_init(&msg.base, NULL, &curthread->td_msgport, 0,
in_control_internal_dispatch);
msg.nm_cmd = cmd;
msg.nm_data = data;
msg.nm_ifp = ifp;
msg.nm_td = td;
lwkt_domsg(netisr_cpuport(0), &msg.base.lmsg, 0);
return msg.base.lmsg.ms_error;
default:
return in_control_internal(cmd, data, ifp, td);
}
}
static void
in_ialink_dispatch(netmsg_t msg)
{
struct in_ifaddr *ia = msg->lmsg.u.ms_resultp;
struct ifaddr_container *ifac;
struct in_ifaddr_container *iac;
int cpu = mycpuid;
crit_enter();
ifac = &ia->ia_ifa.ifa_containers[cpu];
ASSERT_IFAC_VALID(ifac);
KASSERT((ifac->ifa_listmask & IFA_LIST_IN_IFADDRHEAD) == 0,
("ia is on in_ifaddrheads"));
ifac->ifa_listmask |= IFA_LIST_IN_IFADDRHEAD;
iac = &ifac->ifa_proto_u.u_in_ifac;
TAILQ_INSERT_TAIL(&in_ifaddrheads[cpu], iac, ia_link);
crit_exit();
netisr_forwardmsg_all(&msg->base, cpu + 1);
}
static void
in_iaunlink_dispatch(netmsg_t msg)
{
struct in_ifaddr *ia = msg->lmsg.u.ms_resultp;
struct ifaddr_container *ifac;
struct in_ifaddr_container *iac;
int cpu = mycpuid;
crit_enter();
ifac = &ia->ia_ifa.ifa_containers[cpu];
ASSERT_IFAC_VALID(ifac);
KASSERT(ifac->ifa_listmask & IFA_LIST_IN_IFADDRHEAD,
("ia is not on in_ifaddrheads"));
iac = &ifac->ifa_proto_u.u_in_ifac;
TAILQ_REMOVE(&in_ifaddrheads[cpu], iac, ia_link);
ifac->ifa_listmask &= ~IFA_LIST_IN_IFADDRHEAD;
crit_exit();
netisr_forwardmsg_all(&msg->base, cpu + 1);
}
static void
in_iahashins_dispatch(netmsg_t msg)
{
struct in_ifaddr *ia = msg->lmsg.u.ms_resultp;
struct ifaddr_container *ifac;
struct in_ifaddr_container *iac;
int cpu = mycpuid;
crit_enter();
ifac = &ia->ia_ifa.ifa_containers[cpu];
ASSERT_IFAC_VALID(ifac);
KASSERT((ifac->ifa_listmask & IFA_LIST_IN_IFADDRHASH) == 0,
("ia is on in_ifaddrhashtbls"));
ifac->ifa_listmask |= IFA_LIST_IN_IFADDRHASH;
iac = &ifac->ifa_proto_u.u_in_ifac;
LIST_INSERT_HEAD(INADDR_HASH(ia->ia_addr.sin_addr.s_addr),
iac, ia_hash);
crit_exit();
netisr_forwardmsg_all(&msg->base, cpu + 1);
}
static void
in_iahashrem_dispatch(netmsg_t msg)
{
struct in_ifaddr *ia = msg->lmsg.u.ms_resultp;
struct ifaddr_container *ifac;
struct in_ifaddr_container *iac;
int cpu = mycpuid;
crit_enter();
ifac = &ia->ia_ifa.ifa_containers[cpu];
ASSERT_IFAC_VALID(ifac);
KASSERT(ifac->ifa_listmask & IFA_LIST_IN_IFADDRHASH,
("ia is not on in_ifaddrhashtbls"));
iac = &ifac->ifa_proto_u.u_in_ifac;
LIST_REMOVE(iac, ia_hash);
ifac->ifa_listmask &= ~IFA_LIST_IN_IFADDRHASH;
crit_exit();
netisr_forwardmsg_all(&msg->base, cpu + 1);
}
static void
in_ialink(struct in_ifaddr *ia)
{
struct netmsg_base msg;
netmsg_init(&msg, NULL, &curthread->td_msgport,
0, in_ialink_dispatch);
msg.lmsg.u.ms_resultp = ia;
netisr_domsg(&msg, 0);
}
void
in_iaunlink(struct in_ifaddr *ia)
{
struct netmsg_base msg;
netmsg_init(&msg, NULL, &curthread->td_msgport,
0, in_iaunlink_dispatch);
msg.lmsg.u.ms_resultp = ia;
netisr_domsg(&msg, 0);
}
void
in_iahash_insert(struct in_ifaddr *ia)
{
struct netmsg_base msg;
netmsg_init(&msg, NULL, &curthread->td_msgport,
0, in_iahashins_dispatch);
msg.lmsg.u.ms_resultp = ia;
netisr_domsg(&msg, 0);
}
void
in_iahash_remove(struct in_ifaddr *ia)
{
struct netmsg_base msg;
netmsg_init(&msg, NULL, &curthread->td_msgport,
0, in_iahashrem_dispatch);
msg.lmsg.u.ms_resultp = ia;
netisr_domsg(&msg, 0);
}
static __inline struct in_ifaddr *
in_ianext(struct in_ifaddr *oia)
{
struct ifaddr_container *ifac;
struct in_ifaddr_container *iac;
ifac = &oia->ia_ifa.ifa_containers[mycpuid];
ASSERT_IFAC_VALID(ifac);
KASSERT(ifac->ifa_listmask & IFA_LIST_IN_IFADDRHEAD,
("ia is not on in_ifaddrheads"));
iac = &ifac->ifa_proto_u.u_in_ifac;
iac = TAILQ_NEXT(iac, ia_link);
if (iac != NULL)
return iac->ia;
else
return NULL;
}
static int
in_control_internal(u_long cmd, caddr_t data, struct ifnet *ifp,
struct thread *td)
{
struct ifreq *ifr = (struct ifreq *)data;
struct in_ifaddr *ia = NULL;
struct in_addr dst;
struct in_aliasreq *ifra = (struct in_aliasreq *)data;
struct ifaddr_container *ifac;
struct in_ifaddr_container *iac;
struct sockaddr_in oldaddr;
int hostIsNew, iaIsNew, maskIsNew, ifpWasUp;
int error = 0;
switch (cmd) {
case SIOCALIFADDR:
case SIOCDLIFADDR:
if (td && (error = caps_priv_check_td(td,
SYSCAP_RESTRICTEDROOT)) != 0)
{
return error;
}
case SIOCGLIFADDR:
if (!ifp)
return EINVAL;
return in_lifaddr_ioctl(cmd, data, ifp, td);
}
iaIsNew = 0;
ifpWasUp = 0;
if (ifp) {
struct in_ifaddr *iap;
dst = ((struct sockaddr_in *)&ifr->ifr_addr)->sin_addr;
LIST_FOREACH(iac, INADDR_HASH(dst.s_addr), ia_hash) {
iap = iac->ia;
if (iap->ia_ifp == ifp &&
iap->ia_addr.sin_addr.s_addr == dst.s_addr) {
ia = iap;
break;
}
}
if (ia == NULL) {
TAILQ_FOREACH(ifac, &ifp->if_addrheads[mycpuid],
ifa_link) {
iap = ifatoia(ifac->ifa);
if (iap->ia_addr.sin_family == AF_INET) {
ia = iap;
break;
}
}
}
if (ifp->if_flags & IFF_UP)
ifpWasUp = 1;
}
switch (cmd) {
case SIOCAIFADDR:
case SIOCDIFADDR:
case SIOCGIFALIAS:
if (ifp == NULL)
return (EADDRNOTAVAIL);
if (ifra->ifra_addr.sin_family == AF_INET) {
while (ia != NULL) {
if (ia->ia_ifp == ifp &&
ia->ia_addr.sin_addr.s_addr ==
ifra->ifra_addr.sin_addr.s_addr)
break;
ia = in_ianext(ia);
}
if ((ifp->if_flags & IFF_POINTOPOINT) &&
cmd == SIOCAIFADDR &&
ifra->ifra_dstaddr.sin_addr.s_addr == INADDR_ANY) {
return EDESTADDRREQ;
}
}
if ((cmd == SIOCDIFADDR || cmd == SIOCGIFALIAS) && ia == NULL)
return (EADDRNOTAVAIL);
if (cmd == SIOCGIFALIAS)
break;
case SIOCSIFADDR:
case SIOCSIFNETMASK:
case SIOCSIFDSTADDR:
if (td && (error = caps_priv_check_td(td,
SYSCAP_RESTRICTEDROOT)) != 0)
{
return error;
}
if (ifp == NULL)
return (EADDRNOTAVAIL);
if (cmd == SIOCSIFDSTADDR &&
(ifp->if_flags & IFF_POINTOPOINT) == 0)
return (EINVAL);
if (ia == NULL) {
struct ifaddr *ifa;
int i;
ia = ifa_create(sizeof(*ia));
ifa = &ia->ia_ifa;
for (i = 0; i < ncpus; ++i) {
ifac = &ifa->ifa_containers[i];
iac = &ifac->ifa_proto_u.u_in_ifac;
iac->ia = ia;
iac->ia_ifac = ifac;
}
ifa->ifa_addr = (struct sockaddr *)&ia->ia_addr;
ifa->ifa_dstaddr = (struct sockaddr *)&ia->ia_dstaddr;
ifa->ifa_netmask = (struct sockaddr *)&ia->ia_sockmask;
ia->ia_sockmask.sin_len = 8;
ia->ia_sockmask.sin_family = AF_INET;
if (ifp->if_flags & IFF_BROADCAST) {
ia->ia_broadaddr.sin_len = sizeof ia->ia_addr;
ia->ia_broadaddr.sin_family = AF_INET;
}
ia->ia_ifp = ifp;
iaIsNew = 1;
in_ialink(ia);
ifa_iflink(ifa, ifp, 1);
}
break;
case SIOCSIFBRDADDR:
if (td && (error = caps_priv_check_td(td,
SYSCAP_RESTRICTEDROOT)) != 0)
{
return error;
}
case SIOCGIFADDR:
case SIOCGIFNETMASK:
case SIOCGIFDSTADDR:
case SIOCGIFBRDADDR:
if (ia == NULL)
return (EADDRNOTAVAIL);
break;
}
switch (cmd) {
case SIOCGIFADDR:
*((struct sockaddr_in *)&ifr->ifr_addr) = ia->ia_addr;
return (0);
case SIOCGIFBRDADDR:
if ((ifp->if_flags & IFF_BROADCAST) == 0)
return (EINVAL);
*((struct sockaddr_in *)&ifr->ifr_dstaddr) = ia->ia_broadaddr;
return (0);
case SIOCGIFDSTADDR:
if ((ifp->if_flags & IFF_POINTOPOINT) == 0)
return (EINVAL);
*((struct sockaddr_in *)&ifr->ifr_dstaddr) = ia->ia_dstaddr;
return (0);
case SIOCGIFNETMASK:
*((struct sockaddr_in *)&ifr->ifr_addr) = ia->ia_sockmask;
return (0);
case SIOCSIFDSTADDR:
KKASSERT(ifp->if_flags & IFF_POINTOPOINT);
oldaddr = ia->ia_dstaddr;
ia->ia_dstaddr = *(struct sockaddr_in *)&ifr->ifr_dstaddr;
if (ifp->if_ioctl != NULL) {
ifnet_serialize_all(ifp);
error = ifp->if_ioctl(ifp, SIOCSIFDSTADDR, (caddr_t)ia,
td->td_proc->p_ucred);
ifnet_deserialize_all(ifp);
if (error) {
ia->ia_dstaddr = oldaddr;
return (error);
}
}
if (ia->ia_flags & IFA_ROUTE) {
ia->ia_ifa.ifa_dstaddr = (struct sockaddr *)&oldaddr;
rtinit(&ia->ia_ifa, RTM_DELETE, RTF_HOST);
ia->ia_ifa.ifa_dstaddr =
(struct sockaddr *)&ia->ia_dstaddr;
rtinit(&ia->ia_ifa, RTM_ADD, RTF_HOST | RTF_UP);
}
return (0);
case SIOCSIFBRDADDR:
if ((ifp->if_flags & IFF_BROADCAST) == 0)
return (EINVAL);
ia->ia_broadaddr = *(struct sockaddr_in *)&ifr->ifr_broadaddr;
return (0);
case SIOCSIFADDR:
error = in_ifinit(ifp, ia,
(const struct sockaddr_in *)&ifr->ifr_addr, 1);
if (error != 0 && iaIsNew)
break;
if (error == 0) {
EVENTHANDLER_INVOKE(ifaddr_event, ifp,
iaIsNew ? IFADDR_EVENT_ADD : IFADDR_EVENT_CHANGE,
&ia->ia_ifa);
}
if (!ifpWasUp && (ifp->if_flags & IFF_UP)) {
if_up(ifp);
}
return (0);
case SIOCSIFNETMASK:
ia->ia_sockmask.sin_addr = ifra->ifra_addr.sin_addr;
ia->ia_subnetmask = ntohl(ia->ia_sockmask.sin_addr.s_addr);
return (0);
case SIOCAIFADDR:
maskIsNew = 0;
hostIsNew = 1;
error = 0;
if (ia->ia_addr.sin_family == AF_INET) {
if (ifra->ifra_addr.sin_len == 0) {
ifra->ifra_addr = ia->ia_addr;
hostIsNew = 0;
} else if (ifra->ifra_addr.sin_addr.s_addr ==
ia->ia_addr.sin_addr.s_addr) {
hostIsNew = 0;
}
}
if (ifra->ifra_mask.sin_len) {
in_ifscrub(ifp, ia);
ia->ia_sockmask = ifra->ifra_mask;
ia->ia_sockmask.sin_family = AF_INET;
ia->ia_subnetmask =
ntohl(ia->ia_sockmask.sin_addr.s_addr);
maskIsNew = 1;
}
if ((ifp->if_flags & IFF_POINTOPOINT) &&
ifra->ifra_dstaddr.sin_family == AF_INET) {
in_ifscrub(ifp, ia);
ia->ia_dstaddr = ifra->ifra_dstaddr;
maskIsNew = 1;
}
if (ifra->ifra_addr.sin_family == AF_INET &&
(hostIsNew || maskIsNew))
error = in_ifinit(ifp, ia, &ifra->ifra_addr, 0);
if (error != 0 && iaIsNew)
break;
if ((ifp->if_flags & IFF_BROADCAST) &&
ifra->ifra_broadaddr.sin_family == AF_INET)
ia->ia_broadaddr = ifra->ifra_broadaddr;
if (error == 0) {
EVENTHANDLER_INVOKE(ifaddr_event, ifp,
iaIsNew ? IFADDR_EVENT_ADD : IFADDR_EVENT_CHANGE,
&ia->ia_ifa);
}
if (!ifpWasUp && (ifp->if_flags & IFF_UP)) {
if_up(ifp);
}
return (error);
case SIOCGIFALIAS:
ifra->ifra_mask = ia->ia_sockmask;
if ((ifp->if_flags & IFF_POINTOPOINT) &&
(ia->ia_dstaddr.sin_family == AF_INET))
ifra->ifra_dstaddr = ia->ia_dstaddr;
else if ((ifp->if_flags & IFF_BROADCAST) &&
(ia->ia_broadaddr.sin_family == AF_INET))
ifra->ifra_broadaddr = ia->ia_broadaddr;
else
memset(&ifra->ifra_broadaddr, 0,
sizeof(ifra->ifra_broadaddr));
return (0);
case SIOCDIFADDR:
in_ifscrub(ifp, ia);
in_ifadown(&ia->ia_ifa, 1);
EVENTHANDLER_INVOKE(ifaddr_event, ifp, IFADDR_EVENT_DELETE,
&ia->ia_ifa);
error = 0;
break;
default:
if (ifp == NULL || ifp->if_ioctl == NULL)
return (EOPNOTSUPP);
ifnet_serialize_all(ifp);
error = ifp->if_ioctl(ifp, cmd, data, td->td_proc->p_ucred);
ifnet_deserialize_all(ifp);
return (error);
}
KKASSERT(cmd == SIOCDIFADDR ||
((cmd == SIOCAIFADDR || cmd == SIOCSIFADDR) && iaIsNew));
ifa_ifunlink(&ia->ia_ifa, ifp);
in_iaunlink(ia);
if (cmd == SIOCDIFADDR) {
ifac = &ia->ia_ifa.ifa_containers[mycpuid];
if (ifac->ifa_listmask & IFA_LIST_IN_IFADDRHASH)
in_iahash_remove(ia);
}
#ifdef INVARIANTS
else {
ifac = &ia->ia_ifa.ifa_containers[mycpuid];
KASSERT((ifac->ifa_listmask & IFA_LIST_IN_IFADDRHASH) == 0,
("SIOC%cIFADDR failed on new ia, "
"but the new ia is still in hash table",
cmd == SIOCSIFADDR ? 'S' : 'A'));
}
#endif
ifa_destroy(&ia->ia_ifa);
if ((cmd == SIOCAIFADDR || cmd == SIOCSIFADDR) &&
!ifpWasUp && (ifp->if_flags & IFF_UP)) {
if_up(ifp);
}
return (error);
}
static int
in_lifaddr_ioctl(u_long cmd, caddr_t data, struct ifnet *ifp, struct thread *td)
{
struct if_laddrreq *iflr = (struct if_laddrreq *)data;
if (!data || !ifp) {
panic("invalid argument to in_lifaddr_ioctl");
}
switch (cmd) {
case SIOCGLIFADDR:
if ((iflr->flags & IFLR_PREFIX) == 0)
break;
case SIOCALIFADDR:
case SIOCDLIFADDR:
if (iflr->addr.ss_family != AF_INET)
return EINVAL;
if (iflr->addr.ss_len != sizeof(struct sockaddr_in))
return EINVAL;
if (iflr->dstaddr.ss_family &&
iflr->dstaddr.ss_family != AF_INET)
return EINVAL;
if (iflr->dstaddr.ss_family &&
iflr->dstaddr.ss_len != sizeof(struct sockaddr_in))
return EINVAL;
break;
default:
return EOPNOTSUPP;
}
if (sizeof(struct in_addr) * 8 < iflr->prefixlen)
return EINVAL;
switch (cmd) {
case SIOCALIFADDR:
{
struct in_aliasreq ifra;
if (iflr->flags & IFLR_PREFIX)
return EINVAL;
bzero(&ifra, sizeof ifra);
bcopy(iflr->iflr_name, ifra.ifra_name, sizeof ifra.ifra_name);
bcopy(&iflr->addr, &ifra.ifra_addr, iflr->addr.ss_len);
if (iflr->dstaddr.ss_family) {
bcopy(&iflr->dstaddr, &ifra.ifra_dstaddr,
iflr->dstaddr.ss_len);
}
ifra.ifra_mask.sin_family = AF_INET;
ifra.ifra_mask.sin_len = sizeof(struct sockaddr_in);
in_len2mask(&ifra.ifra_mask.sin_addr, iflr->prefixlen);
return in_control_internal(SIOCAIFADDR, (caddr_t)&ifra, ifp,
td);
}
case SIOCGLIFADDR:
case SIOCDLIFADDR:
{
struct ifaddr_container *ifac;
struct in_ifaddr *ia;
struct in_addr mask, candidate, match;
struct sockaddr_in *sin;
int cmp;
bzero(&mask, sizeof mask);
if (iflr->flags & IFLR_PREFIX) {
in_len2mask(&mask, iflr->prefixlen);
sin = (struct sockaddr_in *)&iflr->addr;
match.s_addr = sin->sin_addr.s_addr;
match.s_addr &= mask.s_addr;
if (match.s_addr != sin->sin_addr.s_addr)
return EINVAL;
cmp = 1;
} else {
if (cmd == SIOCGLIFADDR) {
match.s_addr = 0;
cmp = 0;
} else {
in_len2mask(&mask, 32);
sin = (struct sockaddr_in *)&iflr->addr;
match.s_addr = sin->sin_addr.s_addr;
cmp = 1;
}
}
TAILQ_FOREACH(ifac, &ifp->if_addrheads[mycpuid], ifa_link) {
struct ifaddr *ifa = ifac->ifa;
if (ifa->ifa_addr->sa_family != AF_INET6)
continue;
if (!cmp)
break;
candidate.s_addr =
((struct sockaddr_in *)&ifa->ifa_addr)->sin_addr.s_addr;
candidate.s_addr &= mask.s_addr;
if (candidate.s_addr == match.s_addr)
break;
}
if (ifac == NULL)
return EADDRNOTAVAIL;
ia = (struct in_ifaddr *)(ifac->ifa);
if (cmd == SIOCGLIFADDR) {
bcopy(&ia->ia_addr, &iflr->addr, ia->ia_addr.sin_len);
if ((ifp->if_flags & IFF_POINTOPOINT) != 0) {
bcopy(&ia->ia_dstaddr, &iflr->dstaddr,
ia->ia_dstaddr.sin_len);
} else
bzero(&iflr->dstaddr, sizeof iflr->dstaddr);
iflr->prefixlen =
in_mask2len(&ia->ia_sockmask.sin_addr);
iflr->flags = 0;
return 0;
} else {
struct in_aliasreq ifra;
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.sin_len);
if ((ifp->if_flags & IFF_POINTOPOINT) != 0) {
bcopy(&ia->ia_dstaddr, &ifra.ifra_dstaddr,
ia->ia_dstaddr.sin_len);
}
bcopy(&ia->ia_sockmask, &ifra.ifra_dstaddr,
ia->ia_sockmask.sin_len);
return in_control_internal(SIOCDIFADDR, (caddr_t)&ifra,
ifp, td);
}
}
}
return EOPNOTSUPP;
}
void
in_ifscrub(struct ifnet *ifp __unused, struct in_ifaddr *ia)
{
in_scrubprefix(ia);
}
static int
in_ifinit(struct ifnet *ifp, struct in_ifaddr *ia,
const struct sockaddr_in *sin, int scrub)
{
u_long i = ntohl(sin->sin_addr.s_addr);
struct sockaddr_in oldaddr;
struct ifaddr_container *ifac;
int flags = RTF_UP, error = 0;
int was_hash = 0;
ifac = &ia->ia_ifa.ifa_containers[mycpuid];
oldaddr = ia->ia_addr;
if (ifac->ifa_listmask & IFA_LIST_IN_IFADDRHASH) {
was_hash = 1;
in_iahash_remove(ia);
}
ia->ia_addr = *sin;
if (ia->ia_addr.sin_family == AF_INET)
in_iahash_insert(ia);
if (ifp->if_ioctl != NULL) {
ifnet_serialize_all(ifp);
error = ifp->if_ioctl(ifp, SIOCSIFADDR, (caddr_t)ia, NULL);
ifnet_deserialize_all(ifp);
if (error)
goto fail;
}
if (scrub) {
ia->ia_ifa.ifa_addr = (struct sockaddr *)&oldaddr;
in_ifscrub(ifp, ia);
ia->ia_ifa.ifa_addr = (struct sockaddr *)&ia->ia_addr;
}
if (IN_CLASSA(i))
ia->ia_netmask = IN_CLASSA_NET;
else if (IN_CLASSB(i))
ia->ia_netmask = IN_CLASSB_NET;
else
ia->ia_netmask = IN_CLASSC_NET;
if (ia->ia_subnetmask == 0) {
ia->ia_subnetmask = ia->ia_netmask;
ia->ia_sockmask.sin_addr.s_addr = htonl(ia->ia_subnetmask);
} else {
ia->ia_netmask &= ia->ia_subnetmask;
}
ia->ia_net = i & ia->ia_netmask;
ia->ia_subnet = i & ia->ia_subnetmask;
in_socktrim(&ia->ia_sockmask);
ia->ia_ifa.ifa_metric = ifp->if_metric;
if (ifp->if_flags & IFF_BROADCAST) {
if (ia->ia_subnetmask == IN_RFC3021_MASK) {
ia->ia_broadaddr.sin_addr.s_addr = INADDR_BROADCAST;
ia->ia_netbroadcast.s_addr = INADDR_BROADCAST;
} else {
ia->ia_broadaddr.sin_addr.s_addr =
htonl(ia->ia_subnet | ~ia->ia_subnetmask);
ia->ia_netbroadcast.s_addr =
htonl(ia->ia_net | ~ia->ia_netmask);
}
} else if (ifp->if_flags & IFF_LOOPBACK) {
ia->ia_dstaddr = ia->ia_addr;
flags |= RTF_HOST;
} else if (ifp->if_flags & IFF_POINTOPOINT) {
if (ia->ia_dstaddr.sin_family != AF_INET)
return (0);
flags |= RTF_HOST;
}
if (ia->ia_addr.sin_addr.s_addr != INADDR_ANY) {
error = in_addprefix(ia, flags);
if (error)
goto fail;
}
if (ifp->if_flags & IFF_MULTICAST) {
struct in_addr addr;
addr.s_addr = htonl(INADDR_ALLHOSTS_GROUP);
in_addmulti(&addr, ifp);
}
return (0);
fail:
if (ifac->ifa_listmask & IFA_LIST_IN_IFADDRHASH)
in_iahash_remove(ia);
ia->ia_addr = oldaddr;
if (was_hash)
in_iahash_insert(ia);
return (error);
}
#define rtinitflags(x) \
((((x)->ia_ifp->if_flags & (IFF_LOOPBACK | IFF_POINTOPOINT)) != 0) \
? RTF_HOST : 0)
static int
in_addprefix(struct in_ifaddr *target, int flags)
{
struct in_ifaddr_container *iac;
struct in_addr prefix, mask;
int error;
#ifdef CARP
if (target->ia_ifp->if_type == IFT_CARP)
return 0;
#endif
if ((target->ia_ifp->if_flags & IFF_LOOPBACK) == 0)
ifa_add_loopback_route(&target->ia_ifa,
(struct sockaddr *)&target->ia_addr);
mask = target->ia_sockmask.sin_addr;
if (flags & RTF_HOST) {
prefix = target->ia_dstaddr.sin_addr;
} else {
prefix = target->ia_addr.sin_addr;
prefix.s_addr &= mask.s_addr;
}
TAILQ_FOREACH(iac, &in_ifaddrheads[mycpuid], ia_link) {
struct in_ifaddr *ia = iac->ia;
struct in_addr p;
if (ia == target)
continue;
if ((ia->ia_flags & IFA_ROUTE) == 0)
continue;
if (rtinitflags(ia) != 0) {
p = ia->ia_dstaddr.sin_addr;
} else {
p = ia->ia_addr.sin_addr;
p.s_addr &= ia->ia_sockmask.sin_addr.s_addr;
}
if (prefix.s_addr != p.s_addr)
continue;
if (!(flags & RTF_HOST) && rtinitflags(ia) == 0 &&
mask.s_addr != ia->ia_sockmask.sin_addr.s_addr)
continue;
return 0;
}
error = rtinit(&target->ia_ifa, RTM_ADD, flags);
if (!error)
target->ia_flags |= IFA_ROUTE;
return error;
}
static void
in_scrubprefix(struct in_ifaddr *target)
{
struct in_ifaddr_container *iac;
struct in_addr prefix, mask;
int error;
#ifdef CARP
if (target->ia_ifp->if_type == IFT_CARP)
return;
#endif
if (target->ia_addr.sin_addr.s_addr != INADDR_ANY &&
(target->ia_ifp->if_flags & IFF_LOOPBACK) == 0) {
struct in_ifaddr *ia;
struct in_addr addr;
ifa_del_loopback_route(&target->ia_ifa,
(struct sockaddr *)&target->ia_addr);
addr = target->ia_addr.sin_addr;
LIST_FOREACH(iac, INADDR_HASH(addr.s_addr), ia_hash) {
ia = iac->ia;
if (ia != target &&
ia->ia_addr.sin_addr.s_addr == addr.s_addr) {
IFAREF(&ia->ia_ifa);
ifa_add_loopback_route(&ia->ia_ifa,
(struct sockaddr *)&target->ia_addr);
IFAFREE(&ia->ia_ifa);
break;
}
}
}
if ((target->ia_flags & IFA_ROUTE) == 0)
return;
mask = target->ia_sockmask.sin_addr;
if (rtinitflags(target) != 0) {
prefix = target->ia_dstaddr.sin_addr;
} else {
prefix = target->ia_addr.sin_addr;
prefix.s_addr &= mask.s_addr;
}
TAILQ_FOREACH(iac, &in_ifaddrheads[mycpuid], ia_link) {
struct in_ifaddr *ia = iac->ia;
struct in_addr p;
if (ia == target)
continue;
if (ia->ia_flags & IFA_ROUTE)
continue;
if ((ia->ia_ifp->if_flags & IFF_UP) == 0)
continue;
#ifdef CARP
if (ia->ia_ifp->if_type == IFT_CARP)
continue;
#endif
if (rtinitflags(ia) != 0) {
p = ia->ia_dstaddr.sin_addr;
} else {
p = ia->ia_addr.sin_addr;
p.s_addr &= ia->ia_sockmask.sin_addr.s_addr;
}
if (prefix.s_addr != p.s_addr)
continue;
rtinit(&target->ia_ifa, RTM_DELETE, rtinitflags(target));
target->ia_flags &= ~IFA_ROUTE;
error = rtinit(&ia->ia_ifa, RTM_ADD, rtinitflags(ia) | RTF_UP);
if (!error)
ia->ia_flags |= IFA_ROUTE;
return;
}
rtinit(&target->ia_ifa, RTM_DELETE, rtinitflags(target));
target->ia_flags &= ~IFA_ROUTE;
}
#undef rtinitflags
int
in_broadcast(struct in_addr in, struct ifnet *ifp)
{
struct ifaddr_container *ifac;
u_long t;
if (in.s_addr == INADDR_BROADCAST ||
in.s_addr == INADDR_ANY)
return 1;
if (ifp == NULL || (ifp->if_flags & IFF_BROADCAST) == 0)
return 0;
t = ntohl(in.s_addr);
#define ia ((struct in_ifaddr *)ifa)
TAILQ_FOREACH(ifac, &ifp->if_addrheads[mycpuid], ifa_link) {
struct ifaddr *ifa = ifac->ifa;
if (ifa->ifa_addr->sa_family == AF_INET &&
(in.s_addr == ia->ia_broadaddr.sin_addr.s_addr ||
in.s_addr == ia->ia_netbroadcast.s_addr ||
(broadcast_lowest && ia->ia_subnetmask != IN_RFC3021_MASK &&
(t == ia->ia_subnet || t == ia->ia_net))) &&
ia->ia_subnetmask != (u_long)0xffffffff)
return 1;
}
return (0);
#undef ia
}
struct in_multi *
in_addmulti(struct in_addr *ap, struct ifnet *ifp)
{
struct in_multi *inm;
int error;
struct sockaddr_in sin;
struct ifmultiaddr *ifma;
ASSERT_NETISR0;
bzero(&sin, sizeof sin);
sin.sin_family = AF_INET;
sin.sin_len = sizeof sin;
sin.sin_addr = *ap;
error = if_addmulti(ifp, (struct sockaddr *)&sin, &ifma);
if (error)
return NULL;
if (ifma->ifma_protospec != NULL)
return ifma->ifma_protospec;
inm = kmalloc(sizeof *inm, M_IPMADDR, M_INTWAIT | M_ZERO);
inm->inm_addr = *ap;
inm->inm_ifp = ifp;
inm->inm_ifma = ifma;
ifma->ifma_protospec = inm;
LIST_INSERT_HEAD(&in_multihead, inm, inm_link);
igmp_joingroup(inm);
return inm;
}
void
in_delmulti(struct in_multi *inm)
{
struct ifmultiaddr *ifma;
struct in_multi my_inm;
ASSERT_NETISR0;
ifma = inm->inm_ifma;
my_inm.inm_ifp = NULL ;
if (ifma->ifma_refcount == 1) {
my_inm = *inm ;
ifma->ifma_protospec = NULL;
LIST_REMOVE(inm, inm_link);
kfree(inm, M_IPMADDR);
}
if_delmulti(ifma->ifma_ifp, ifma->ifma_addr);
if (my_inm.inm_ifp != NULL)
igmp_leavegroup(&my_inm);
}
void
in_if_down(struct ifnet *ifp)
{
rt_purgecloned(ifp, AF_INET);
}
static void
in_ifdetach_dispatch(netmsg_t nmsg)
{
struct lwkt_msg *lmsg = &nmsg->lmsg;
struct ifnet *ifp = lmsg->u.ms_resultp;
int cpu;
in_pcbpurgeif0(&ripcbinfo, ifp);
for (cpu = 0; cpu < netisr_ncpus; ++cpu)
in_pcbpurgeif0(&udbinfo[cpu], ifp);
lwkt_replymsg(lmsg, 0);
}
void
in_ifdetach(struct ifnet *ifp)
{
struct netmsg_base nmsg;
struct lwkt_msg *lmsg = &nmsg.lmsg;
netmsg_init(&nmsg, NULL, &curthread->td_msgport, 0,
in_ifdetach_dispatch);
lmsg->u.ms_resultp = ifp;
lwkt_domsg(netisr_cpuport(0), lmsg, 0);
}