BSD 4_4 release
[unix-history] / usr / src / sys / netinet / in.c
index dc7f8cd..cee7e8a 100644 (file)
@@ -1,81 +1,55 @@
 /*
 /*
- * Copyright (c) 1982, 1986 Regents of the University of California.
- * All rights reserved.
+ * Copyright (c) 1982, 1986, 1991, 1993
+ *     The Regents of the University of California.  All rights reserved.
  *
  *
- * Redistribution and use in source and binary forms are permitted
- * provided that this notice is preserved and that due credit is given
- * to the University of California at Berkeley. The name of the University
- * may not be used to endorse or promote products derived from this
- * software without specific prior written permission. This software
- * is provided ``as is'' without express or implied warranty.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
  *
  *
- *     @(#)in.c        7.8 (Berkeley) %G%
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *     @(#)in.c        8.1 (Berkeley) 6/10/93
  */
 
  */
 
-#include "param.h"
-#include "ioctl.h"
-#include "mbuf.h"
-#include "protosw.h"
-#include "socket.h"
-#include "socketvar.h"
-#include "uio.h"
-#include "dir.h"
-#include "user.h"
-#include "in_systm.h"
-#include "../net/if.h"
-#include "../net/route.h"
-#include "../net/af.h"
-#include "in.h"
-#include "in_var.h"
-
-#ifdef INET
-inet_hash(sin, hp)
-       register struct sockaddr_in *sin;
-       struct afhash *hp;
-{
-       register u_long n;
-
-       n = in_netof(sin->sin_addr);
-       if (n)
-           while ((n & 0xff) == 0)
-               n >>= 8;
-       hp->afh_nethash = n;
-       hp->afh_hosthash = ntohl(sin->sin_addr.s_addr);
-}
-
-inet_netmatch(sin1, sin2)
-       struct sockaddr_in *sin1, *sin2;
-{
-
-       return (in_netof(sin1->sin_addr) == in_netof(sin2->sin_addr));
-}
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/errno.h>
+#include <sys/malloc.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
 
 
-/*
- * Formulate an Internet address from network + host.
- */
-struct in_addr
-in_makeaddr(net, host)
-       u_long net, host;
-{
-       register struct in_ifaddr *ia;
-       register u_long mask;
-       u_long addr;
+#include <net/if.h>
+#include <net/route.h>
+#include <net/af.h>
 
 
-       if (IN_CLASSA(net))
-               mask = IN_CLASSA_HOST;
-       else if (IN_CLASSB(net))
-               mask = IN_CLASSB_HOST;
-       else
-               mask = IN_CLASSC_HOST;
-       for (ia = in_ifaddr; ia; ia = ia->ia_next)
-               if ((ia->ia_netmask & net) == ia->ia_net) {
-                       mask = ~ia->ia_subnetmask;
-                       break;
-               }
-       addr = htonl(net | (host & mask));
-       return (*(struct in_addr *)&addr);
-}
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/in_var.h>
+#include <netinet/if_ether.h>
 
 
+#ifdef INET
 /*
  * Return the network number from an internet address.
  */
 /*
  * Return the network number from an internet address.
  */
@@ -93,6 +67,8 @@ in_netof(in)
                net = i & IN_CLASSB_NET;
        else if (IN_CLASSC(i))
                net = i & IN_CLASSC_NET;
                net = i & IN_CLASSB_NET;
        else if (IN_CLASSC(i))
                net = i & IN_CLASSC_NET;
+       else if (IN_CLASSD(i))
+               net = i & IN_CLASSD_NET;
        else
                return (0);
 
        else
                return (0);
 
@@ -106,39 +82,6 @@ in_netof(in)
        return (net);
 }
 
        return (net);
 }
 
-/*
- * Return the host portion of an internet address.
- */
-u_long
-in_lnaof(in)
-       struct in_addr in;
-{
-       register u_long i = ntohl(in.s_addr);
-       register u_long net, host;
-       register struct in_ifaddr *ia;
-
-       if (IN_CLASSA(i)) {
-               net = i & IN_CLASSA_NET;
-               host = i & IN_CLASSA_HOST;
-       } else if (IN_CLASSB(i)) {
-               net = i & IN_CLASSB_NET;
-               host = i & IN_CLASSB_HOST;
-       } else if (IN_CLASSC(i)) {
-               net = i & IN_CLASSC_NET;
-               host = i & IN_CLASSC_HOST;
-       } else
-               return (i);
-
-       /*
-        * Check whether network is a subnet;
-        * if so, use the modified interpretation of `host'.
-        */
-       for (ia = in_ifaddr; ia; ia = ia->ia_next)
-               if (net == ia->ia_net)
-                       return (host &~ ia->ia_subnetmask);
-       return (host);
-}
-
 #ifndef SUBNETSARELOCAL
 #define        SUBNETSARELOCAL 1
 #endif
 #ifndef SUBNETSARELOCAL
 #define        SUBNETSARELOCAL 1
 #endif
@@ -178,16 +121,34 @@ in_canforward(in)
        register u_long i = ntohl(in.s_addr);
        register u_long net;
 
        register u_long i = ntohl(in.s_addr);
        register u_long net;
 
-       if (IN_EXPERIMENTAL(i))
+       if (IN_EXPERIMENTAL(i) || IN_MULTICAST(i))
                return (0);
        if (IN_CLASSA(i)) {
                net = i & IN_CLASSA_NET;
                return (0);
        if (IN_CLASSA(i)) {
                net = i & IN_CLASSA_NET;
-               if (net == 0 || net == IN_LOOPBACKNET)
+               if (net == 0 || net == (IN_LOOPBACKNET << IN_CLASSA_NSHIFT))
                        return (0);
        }
        return (1);
 }
 
                        return (0);
        }
        return (1);
 }
 
+/*
+ * Trim a mask in a sockaddr
+ */
+void
+in_socktrim(ap)
+struct sockaddr_in *ap;
+{
+    register char *cplim = (char *) &ap->sin_addr;
+    register char *cp = (char *) (&ap->sin_addr + 1);
+
+    ap->sin_len = 0;
+    while (--cp > cplim)
+        if (*cp) {
+           (ap)->sin_len = cp - (char *) (ap) + 1;
+           break;
+       }
+}
+
 int    in_interfaces;          /* number of external internet interfaces */
 extern struct ifnet loif;
 
 int    in_interfaces;          /* number of external internet interfaces */
 extern struct ifnet loif;
 
@@ -204,9 +165,12 @@ in_control(so, cmd, data, ifp)
 {
        register struct ifreq *ifr = (struct ifreq *)data;
        register struct in_ifaddr *ia = 0;
 {
        register struct ifreq *ifr = (struct ifreq *)data;
        register struct in_ifaddr *ia = 0;
-       struct ifaddr *ifa;
-       struct mbuf *m;
-       int error;
+       register struct ifaddr *ifa;
+       struct in_ifaddr *oia;
+       struct in_aliasreq *ifra = (struct in_aliasreq *)data;
+       struct sockaddr_in oldaddr;
+       int error, hostIsNew, maskIsNew;
+       u_long i;
 
        /*
         * Find address for this interface, if it exists.
 
        /*
         * Find address for this interface, if it exists.
@@ -218,107 +182,188 @@ in_control(so, cmd, data, ifp)
 
        switch (cmd) {
 
 
        switch (cmd) {
 
+       case SIOCAIFADDR:
+       case SIOCDIFADDR:
+               if (ifra->ifra_addr.sin_family == AF_INET)
+                   for (oia = ia; ia; ia = ia->ia_next) {
+                       if (ia->ia_ifp == ifp  &&
+                           ia->ia_addr.sin_addr.s_addr ==
+                               ifra->ifra_addr.sin_addr.s_addr)
+                           break;
+               }
+               if (cmd == SIOCDIFADDR && ia == 0)
+                       return (EADDRNOTAVAIL);
+               /* FALLTHROUGH */
        case SIOCSIFADDR:
        case SIOCSIFNETMASK:
        case SIOCSIFDSTADDR:
        case SIOCSIFADDR:
        case SIOCSIFNETMASK:
        case SIOCSIFDSTADDR:
-               if (!suser())
-                       return (u.u_error);
+               if ((so->so_state & SS_PRIV) == 0)
+                       return (EPERM);
 
                if (ifp == 0)
                        panic("in_control");
                if (ia == (struct in_ifaddr *)0) {
 
                if (ifp == 0)
                        panic("in_control");
                if (ia == (struct in_ifaddr *)0) {
-                       m = m_getclr(M_WAIT, MT_IFADDR);
-                       if (m == (struct mbuf *)NULL)
+                       oia = (struct in_ifaddr *)
+                               malloc(sizeof *oia, M_IFADDR, M_WAITOK);
+                       if (oia == (struct in_ifaddr *)NULL)
                                return (ENOBUFS);
                                return (ENOBUFS);
+                       bzero((caddr_t)oia, sizeof *oia);
                        if (ia = in_ifaddr) {
                                for ( ; ia->ia_next; ia = ia->ia_next)
                        if (ia = in_ifaddr) {
                                for ( ; ia->ia_next; ia = ia->ia_next)
-                                       ;
-                               ia->ia_next = mtod(m, struct in_ifaddr *);
+                                       continue;
+                               ia->ia_next = oia;
                        } else
                        } else
-                               in_ifaddr = mtod(m, struct in_ifaddr *);
-                       ia = mtod(m, struct in_ifaddr *);
+                               in_ifaddr = oia;
+                       ia = oia;
                        if (ifa = ifp->if_addrlist) {
                                for ( ; ifa->ifa_next; ifa = ifa->ifa_next)
                        if (ifa = ifp->if_addrlist) {
                                for ( ; ifa->ifa_next; ifa = ifa->ifa_next)
-                                       ;
+                                       continue;
                                ifa->ifa_next = (struct ifaddr *) ia;
                        } else
                                ifp->if_addrlist = (struct ifaddr *) ia;
                                ifa->ifa_next = (struct ifaddr *) ia;
                        } else
                                ifp->if_addrlist = (struct ifaddr *) ia;
+                       ia->ia_ifa.ifa_addr = (struct sockaddr *)&ia->ia_addr;
+                       ia->ia_ifa.ifa_dstaddr
+                                       = (struct sockaddr *)&ia->ia_dstaddr;
+                       ia->ia_ifa.ifa_netmask
+                                       = (struct sockaddr *)&ia->ia_sockmask;
+                       ia->ia_sockmask.sin_len = 8;
+                       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;
                        ia->ia_ifp = ifp;
-                       IA_SIN(ia)->sin_family = AF_INET;
                        if (ifp != &loif)
                                in_interfaces++;
                }
                break;
 
        case SIOCSIFBRDADDR:
                        if (ifp != &loif)
                                in_interfaces++;
                }
                break;
 
        case SIOCSIFBRDADDR:
-               if (!suser())
-                       return (u.u_error);
+               if ((so->so_state & SS_PRIV) == 0)
+                       return (EPERM);
                /* FALLTHROUGH */
 
                /* FALLTHROUGH */
 
-       default:
+       case SIOCGIFADDR:
+       case SIOCGIFNETMASK:
+       case SIOCGIFDSTADDR:
+       case SIOCGIFBRDADDR:
                if (ia == (struct in_ifaddr *)0)
                        return (EADDRNOTAVAIL);
                break;
        }
                if (ia == (struct in_ifaddr *)0)
                        return (EADDRNOTAVAIL);
                break;
        }
-
        switch (cmd) {
 
        case SIOCGIFADDR:
        switch (cmd) {
 
        case SIOCGIFADDR:
-               ifr->ifr_addr = ia->ia_addr;
+               *((struct sockaddr_in *)&ifr->ifr_addr) = ia->ia_addr;
                break;
 
        case SIOCGIFBRDADDR:
                if ((ifp->if_flags & IFF_BROADCAST) == 0)
                        return (EINVAL);
                break;
 
        case SIOCGIFBRDADDR:
                if ((ifp->if_flags & IFF_BROADCAST) == 0)
                        return (EINVAL);
-               ifr->ifr_dstaddr = ia->ia_broadaddr;
+               *((struct sockaddr_in *)&ifr->ifr_dstaddr) = ia->ia_broadaddr;
                break;
 
        case SIOCGIFDSTADDR:
                if ((ifp->if_flags & IFF_POINTOPOINT) == 0)
                        return (EINVAL);
                break;
 
        case SIOCGIFDSTADDR:
                if ((ifp->if_flags & IFF_POINTOPOINT) == 0)
                        return (EINVAL);
-               ifr->ifr_dstaddr = ia->ia_dstaddr;
+               *((struct sockaddr_in *)&ifr->ifr_dstaddr) = ia->ia_dstaddr;
                break;
 
        case SIOCGIFNETMASK:
                break;
 
        case SIOCGIFNETMASK:
-#define        satosin(sa)     ((struct sockaddr_in *)(sa))
-               satosin(&ifr->ifr_addr)->sin_family = AF_INET;
-               satosin(&ifr->ifr_addr)->sin_addr.s_addr = htonl(ia->ia_subnetmask);
+               *((struct sockaddr_in *)&ifr->ifr_addr) = ia->ia_sockmask;
                break;
 
        case SIOCSIFDSTADDR:
                break;
 
        case SIOCSIFDSTADDR:
-           {
-               struct sockaddr oldaddr;
-
                if ((ifp->if_flags & IFF_POINTOPOINT) == 0)
                        return (EINVAL);
                oldaddr = ia->ia_dstaddr;
                if ((ifp->if_flags & IFF_POINTOPOINT) == 0)
                        return (EINVAL);
                oldaddr = ia->ia_dstaddr;
-               ia->ia_dstaddr = ifr->ifr_dstaddr;
-               if (ifp->if_ioctl &&
-                   (error = (*ifp->if_ioctl)(ifp, SIOCSIFDSTADDR, ia))) {
+               ia->ia_dstaddr = *(struct sockaddr_in *)&ifr->ifr_dstaddr;
+               if (ifp->if_ioctl && (error = (*ifp->if_ioctl)
+                                       (ifp, SIOCSIFDSTADDR, (caddr_t)ia))) {
                        ia->ia_dstaddr = oldaddr;
                        return (error);
                }
                if (ia->ia_flags & IFA_ROUTE) {
                        ia->ia_dstaddr = oldaddr;
                        return (error);
                }
                if (ia->ia_flags & IFA_ROUTE) {
-                       rtinit(&oldaddr, &ia->ia_addr, (int)SIOCDELRT,
-                           RTF_HOST);
-                       rtinit(&ia->ia_dstaddr, &ia->ia_addr, (int)SIOCADDRT,
-                           RTF_HOST|RTF_UP);
+                       ia->ia_ifa.ifa_dstaddr = (struct sockaddr *)&oldaddr;
+                       rtinit(&(ia->ia_ifa), (int)RTM_DELETE, RTF_HOST);
+                       ia->ia_ifa.ifa_dstaddr =
+                                       (struct sockaddr *)&ia->ia_dstaddr;
+                       rtinit(&(ia->ia_ifa), (int)RTM_ADD, RTF_HOST|RTF_UP);
                }
                }
-           }
                break;
 
        case SIOCSIFBRDADDR:
                if ((ifp->if_flags & IFF_BROADCAST) == 0)
                        return (EINVAL);
                break;
 
        case SIOCSIFBRDADDR:
                if ((ifp->if_flags & IFF_BROADCAST) == 0)
                        return (EINVAL);
-               ia->ia_broadaddr = ifr->ifr_broadaddr;
+               ia->ia_broadaddr = *(struct sockaddr_in *)&ifr->ifr_broadaddr;
                break;
 
        case SIOCSIFADDR:
                return (in_ifinit(ifp, ia,
                break;
 
        case SIOCSIFADDR:
                return (in_ifinit(ifp, ia,
-                   (struct sockaddr_in *) &ifr->ifr_addr));
+                   (struct sockaddr_in *) &ifr->ifr_addr, 1));
 
        case SIOCSIFNETMASK:
 
        case SIOCSIFNETMASK:
-               ia->ia_subnetmask = ntohl(satosin(&ifr->ifr_addr)->sin_addr.s_addr);
+               i = ifra->ifra_addr.sin_addr.s_addr;
+               ia->ia_subnetmask = ntohl(ia->ia_sockmask.sin_addr.s_addr = i);
+               break;
+
+       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_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; /* We lie; but the effect's the same */
+               }
+               if (ifra->ifra_addr.sin_family == AF_INET &&
+                   (hostIsNew || maskIsNew))
+                       error = in_ifinit(ifp, ia, &ifra->ifra_addr, 0);
+               if ((ifp->if_flags & IFF_BROADCAST) &&
+                   (ifra->ifra_broadaddr.sin_family == AF_INET))
+                       ia->ia_broadaddr = ifra->ifra_broadaddr;
+               return (error);
+
+       case SIOCDIFADDR:
+               in_ifscrub(ifp, ia);
+               if ((ifa = ifp->if_addrlist) == (struct ifaddr *)ia)
+                       ifp->if_addrlist = ifa->ifa_next;
+               else {
+                       while (ifa->ifa_next &&
+                              (ifa->ifa_next != (struct ifaddr *)ia))
+                                   ifa = ifa->ifa_next;
+                       if (ifa->ifa_next)
+                               ifa->ifa_next = ((struct ifaddr *)ia)->ifa_next;
+                       else
+                               printf("Couldn't unlink inifaddr from ifp\n");
+               }
+               oia = ia;
+               if (oia == (ia = in_ifaddr))
+                       in_ifaddr = ia->ia_next;
+               else {
+                       while (ia->ia_next && (ia->ia_next != oia))
+                               ia = ia->ia_next;
+                       if (ia->ia_next)
+                               ia->ia_next = oia->ia_next;
+                       else
+                               printf("Didn't unlink inifadr from list\n");
+               }
+               IFAFREE((&oia->ia_ifa));
                break;
 
        default:
                break;
 
        default:
@@ -329,52 +374,60 @@ in_control(so, cmd, data, ifp)
        return (0);
 }
 
        return (0);
 }
 
+/*
+ * Delete any existing route for an interface.
+ */
+void
+in_ifscrub(ifp, ia)
+       register struct ifnet *ifp;
+       register struct in_ifaddr *ia;
+{
+
+       if ((ia->ia_flags & IFA_ROUTE) == 0)
+               return;
+       if (ifp->if_flags & (IFF_LOOPBACK|IFF_POINTOPOINT))
+               rtinit(&(ia->ia_ifa), (int)RTM_DELETE, RTF_HOST);
+       else
+               rtinit(&(ia->ia_ifa), (int)RTM_DELETE, 0);
+       ia->ia_flags &= ~IFA_ROUTE;
+}
+
 /*
  * Initialize an interface's internet address
  * and routing table entry.
  */
 /*
  * Initialize an interface's internet address
  * and routing table entry.
  */
-in_ifinit(ifp, ia, sin)
+in_ifinit(ifp, ia, sin, scrub)
        register struct ifnet *ifp;
        register struct in_ifaddr *ia;
        struct sockaddr_in *sin;
        register struct ifnet *ifp;
        register struct in_ifaddr *ia;
        struct sockaddr_in *sin;
+       int scrub;
 {
        register u_long i = ntohl(sin->sin_addr.s_addr);
 {
        register u_long i = ntohl(sin->sin_addr.s_addr);
-       struct sockaddr oldaddr;
-       struct sockaddr_in netaddr;
-       int s = splimp(), error;
+       struct sockaddr_in oldaddr;
+       int s = splimp(), flags = RTF_UP, error, ether_output();
 
        oldaddr = ia->ia_addr;
 
        oldaddr = ia->ia_addr;
-       ia->ia_addr = *(struct sockaddr *)sin;
-
+       ia->ia_addr = *sin;
        /*
         * Give the interface a chance to initialize
         * if this is its first address,
         * and to validate the address if necessary.
         */
        /*
         * Give the interface a chance to initialize
         * if this is its first address,
         * and to validate the address if necessary.
         */
-       if (ifp->if_ioctl && (error = (*ifp->if_ioctl)(ifp, SIOCSIFADDR, ia))) {
+       if (ifp->if_ioctl &&
+           (error = (*ifp->if_ioctl)(ifp, SIOCSIFADDR, (caddr_t)ia))) {
                splx(s);
                ia->ia_addr = oldaddr;
                return (error);
        }
                splx(s);
                ia->ia_addr = oldaddr;
                return (error);
        }
-
-       /*
-        * Delete any previous route for an old address.
-        */
-       bzero((caddr_t)&netaddr, sizeof (netaddr));
-       netaddr.sin_family = AF_INET;
-       if (ia->ia_flags & IFA_ROUTE) {
-               if (ifp->if_flags & IFF_LOOPBACK)
-                       rtinit(&oldaddr, &oldaddr, (int)SIOCDELRT, RTF_HOST);
-               else if (ifp->if_flags & IFF_POINTOPOINT)
-                       rtinit(&ia->ia_dstaddr, &oldaddr, (int)SIOCDELRT,
-                           RTF_HOST);
-               else {
-                       netaddr.sin_addr = in_makeaddr(ia->ia_subnet,
-                           INADDR_ANY);
-                       rtinit((struct sockaddr *)&netaddr, &oldaddr, 
-                           (int)SIOCDELRT, 0);
-               }
-               ia->ia_flags &= ~IFA_ROUTE;
+       if (ifp->if_output == ether_output) { /* XXX: Another Kludge */
+               ia->ia_ifa.ifa_rtrequest = arp_rtrequest;
+               ia->ia_ifa.ifa_flags |= RTF_CLONING;
+       }
+       splx(s);
+       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;
        }
        if (IN_CLASSA(i))
                ia->ia_netmask = IN_CLASSA_NET;
@@ -382,79 +435,189 @@ in_ifinit(ifp, ia, sin)
                ia->ia_netmask = IN_CLASSB_NET;
        else
                ia->ia_netmask = IN_CLASSC_NET;
                ia->ia_netmask = IN_CLASSB_NET;
        else
                ia->ia_netmask = IN_CLASSC_NET;
-       ia->ia_net = i & ia->ia_netmask;
        /*
        /*
-        * The subnet mask includes at least the standard network part,
-        * but may already have been set to a larger value.
+        * The subnet mask usually includes at least the standard network part,
+        * but may may be smaller in the case of supernetting.
+        * If it is set, we believe it.
         */
         */
-       ia->ia_subnetmask |= ia->ia_netmask;
+       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;
        ia->ia_subnet = i & ia->ia_subnetmask;
+       in_socktrim(&ia->ia_sockmask);
+       /*
+        * Add route for the network.
+        */
+       ia->ia_ifa.ifa_metric = ifp->if_metric;
        if (ifp->if_flags & IFF_BROADCAST) {
        if (ifp->if_flags & IFF_BROADCAST) {
-               ia->ia_broadaddr.sa_family = AF_INET;
-               ((struct sockaddr_in *)(&ia->ia_broadaddr))->sin_addr =
-                       in_makeaddr(ia->ia_subnet, INADDR_BROADCAST);
+               ia->ia_broadaddr.sin_addr.s_addr =
+                       htonl(ia->ia_subnet | ~ia->ia_subnetmask);
                ia->ia_netbroadcast.s_addr =
                ia->ia_netbroadcast.s_addr =
-                   htonl(ia->ia_net | (INADDR_BROADCAST &~ ia->ia_netmask));
+                       htonl(ia->ia_net | ~ ia->ia_netmask);
+       } else if (ifp->if_flags & IFF_LOOPBACK) {
+               ia->ia_ifa.ifa_dstaddr = ia->ia_ifa.ifa_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 ((error = rtinit(&(ia->ia_ifa), (int)RTM_ADD, flags)) == 0)
+               ia->ia_flags |= IFA_ROUTE;
        /*
        /*
-        * Add route for the network.
+        * If the interface supports multicast, join the "all hosts"
+        * multicast group on that interface.
         */
         */
-       if (ifp->if_flags & IFF_LOOPBACK)
-               rtinit(&ia->ia_addr, &ia->ia_addr, (int)SIOCADDRT,
-                   RTF_HOST|RTF_UP);
-       else if (ifp->if_flags & IFF_POINTOPOINT)
-               rtinit(&ia->ia_dstaddr, &ia->ia_addr, (int)SIOCADDRT,
-                   RTF_HOST|RTF_UP);
-       else {
-               netaddr.sin_addr = in_makeaddr(ia->ia_subnet, INADDR_ANY);
-               rtinit((struct sockaddr *)&netaddr, &ia->ia_addr,
-                   (int)SIOCADDRT, RTF_UP);
+       if (ifp->if_flags & IFF_MULTICAST) {
+               struct in_addr addr;
+
+               addr.s_addr = htonl(INADDR_ALLHOSTS_GROUP);
+               in_addmulti(&addr, ifp);
        }
        }
-       ia->ia_flags |= IFA_ROUTE;
-       splx(s);
-       return (0);
+       return (error);
 }
 
 }
 
-/*
- * Return address info for specified internet network.
- */
-struct in_ifaddr *
-in_iaonnetof(net)
-       u_long net;
-{
-       register struct in_ifaddr *ia;
-
-       for (ia = in_ifaddr; ia; ia = ia->ia_next)
-               if (ia->ia_subnet == net)
-                       return (ia);
-       return ((struct in_ifaddr *)0);
-}
 
 /*
  * Return 1 if the address might be a local broadcast address.
  */
 
 /*
  * Return 1 if the address might be a local broadcast address.
  */
-in_broadcast(in)
+in_broadcast(in, ifp)
        struct in_addr in;
        struct in_addr in;
+        struct ifnet *ifp;
 {
 {
-       register struct in_ifaddr *ia;
+       register struct ifaddr *ifa;
        u_long t;
 
        u_long t;
 
+       if (in.s_addr == INADDR_BROADCAST ||
+           in.s_addr == INADDR_ANY)
+               return 1;
+       if ((ifp->if_flags & IFF_BROADCAST) == 0)
+               return 0;
+       t = ntohl(in.s_addr);
        /*
         * Look through the list of addresses for a match
         * with a broadcast address.
         */
        /*
         * Look through the list of addresses for a match
         * with a broadcast address.
         */
-       for (ia = in_ifaddr; ia; ia = ia->ia_next)
-           if (ia->ia_ifp->if_flags & IFF_BROADCAST) {
-               if (satosin(&ia->ia_broadaddr)->sin_addr.s_addr == in.s_addr)
-                    return (1);
+#define ia ((struct in_ifaddr *)ifa)
+       for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next)
+               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 ||
+                    /*
+                     * Check for old-style (host 0) broadcast.
+                     */
+                    t == ia->ia_subnet || t == ia->ia_net))
+                           return 1;
+       return (0);
+#undef ia
+}
+/*
+ * Add an address to the list of IP multicast addresses for a given interface.
+ */
+struct in_multi *
+in_addmulti(ap, ifp)
+       register struct in_addr *ap;
+       register struct ifnet *ifp;
+{
+       register struct in_multi *inm;
+       struct ifreq ifr;
+       struct in_ifaddr *ia;
+       int s = splnet();
+
+       /*
+        * See if address already in list.
+        */
+       IN_LOOKUP_MULTI(*ap, ifp, inm);
+       if (inm != NULL) {
                /*
                /*
-                * Check for old-style (host 0) broadcast.
+                * Found it; just increment the reference count.
                 */
                 */
-               if ((t = ntohl(in.s_addr)) == ia->ia_subnet || t == ia->ia_net)
-                   return (1);
+               ++inm->inm_refcount;
        }
        }
-       if (in.s_addr == INADDR_BROADCAST || in.s_addr == INADDR_ANY)
-               return (1);
-       return (0);
+       else {
+               /*
+                * New address; allocate a new multicast record
+                * and link it into the interface's multicast list.
+                */
+               inm = (struct in_multi *)malloc(sizeof(*inm),
+                   M_IPMADDR, M_NOWAIT);
+               if (inm == NULL) {
+                       splx(s);
+                       return (NULL);
+               }
+               inm->inm_addr = *ap;
+               inm->inm_ifp = ifp;
+               inm->inm_refcount = 1;
+               IFP_TO_IA(ifp, ia);
+               if (ia == NULL) {
+                       free(inm, M_IPMADDR);
+                       splx(s);
+                       return (NULL);
+               }
+               inm->inm_ia = ia;
+               inm->inm_next = ia->ia_multiaddrs;
+               ia->ia_multiaddrs = inm;
+               /*
+                * Ask the network driver to update its multicast reception
+                * filter appropriately for the new address.
+                */
+               ((struct sockaddr_in *)&ifr.ifr_addr)->sin_family = AF_INET;
+               ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr = *ap;
+               if ((ifp->if_ioctl == NULL) ||
+                   (*ifp->if_ioctl)(ifp, SIOCADDMULTI,(caddr_t)&ifr) != 0) {
+                       ia->ia_multiaddrs = inm->inm_next;
+                       free(inm, M_IPMADDR);
+                       splx(s);
+                       return (NULL);
+               }
+               /*
+                * Let IGMP know that we have joined a new IP multicast group.
+                */
+               igmp_joingroup(inm);
+       }
+       splx(s);
+       return (inm);
+}
+
+/*
+ * Delete a multicast address record.
+ */
+int
+in_delmulti(inm)
+       register struct in_multi *inm;
+{
+       register struct in_multi **p;
+       struct ifreq ifr;
+       int s = splnet();
+
+       if (--inm->inm_refcount == 0) {
+               /*
+                * No remaining claims to this record; let IGMP know that
+                * we are leaving the multicast group.
+                */
+               igmp_leavegroup(inm);
+               /*
+                * Unlink from list.
+                */
+               for (p = &inm->inm_ia->ia_multiaddrs;
+                    *p != inm;
+                    p = &(*p)->inm_next)
+                        continue;
+               *p = (*p)->inm_next;
+               /*
+                * Notify the network driver to update its multicast reception
+                * filter.
+                */
+               ((struct sockaddr_in *)&(ifr.ifr_addr))->sin_family = AF_INET;
+               ((struct sockaddr_in *)&(ifr.ifr_addr))->sin_addr =
+                                                               inm->inm_addr;
+               (*inm->inm_ifp->if_ioctl)(inm->inm_ifp, SIOCDELMULTI,
+                                                            (caddr_t)&ifr);
+               free(inm, M_IPMADDR);
+       }
+       splx(s);
 }
 #endif
 }
 #endif