BSD 4_3_Reno release
[unix-history] / usr / src / sys / netinet / in.c
index 6f4e61f..3abd775 100644 (file)
@@ -2,19 +2,22 @@
  * Copyright (c) 1982, 1986 Regents of the University of California.
  * All rights reserved.
  *
  * Copyright (c) 1982, 1986 Regents of the University of California.
  * All rights reserved.
  *
- * Redistribution and use in source and binary forms are permitted
- * provided that the above copyright notice and this paragraph are
- * duplicated in all such forms and that any documentation,
- * advertising materials, and other materials related to such
- * distribution and use acknowledge that the software was developed
- * by the University of California, 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'' AND WITHOUT ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
- * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ * Redistribution is only permitted until one year after the first shipment
+ * of 4.4BSD by the Regents.  Otherwise, redistribution and use in source and
+ * binary forms are permitted provided that: (1) source distributions retain
+ * this entire copyright notice and comment, and (2) distributions including
+ * binaries display the following acknowledgement:  This product includes
+ * software developed by the University of California, Berkeley and its
+ * contributors'' in the documentation or other materials provided with the
+ * distribution and in all advertising materials mentioning features or use
+ * of this software.  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.
+ * THIS SOFTWARE IS PROVIDED AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  *
  *
- *     @(#)in.c        7.9 (Berkeley) 6/29/88
+ *     @(#)in.c        7.15 (Berkeley) 6/28/90
  */
 
 #include "param.h"
  */
 
 #include "param.h"
@@ -23,8 +26,6 @@
 #include "protosw.h"
 #include "socket.h"
 #include "socketvar.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 "user.h"
 #include "in_systm.h"
 #include "../net/if.h"
 #include "in_var.h"
 
 #ifdef INET
 #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));
-}
-
 /*
  * Formulate an Internet address from network + host.
  */
 /*
  * Formulate an Internet address from network + host.
  */
@@ -111,6 +91,53 @@ in_netof(in)
        return (net);
 }
 
        return (net);
 }
 
+/*
+ * Compute and save network mask as sockaddr from an internet address.
+ */
+in_sockmaskof(in, sockmask)
+       struct in_addr in;
+       register struct sockaddr_in *sockmask;
+{
+       register u_long net;
+       register u_long mask;
+    {
+       register u_long i = ntohl(in.s_addr);
+
+       if (i == 0)
+               net = 0, mask = 0;
+       else if (IN_CLASSA(i))
+               net = i & IN_CLASSA_NET, mask = IN_CLASSA_NET;
+       else if (IN_CLASSB(i))
+               net = i & IN_CLASSB_NET, mask = IN_CLASSB_NET;
+       else if (IN_CLASSC(i))
+               net = i & IN_CLASSC_NET, mask = IN_CLASSC_NET;
+       else
+               net = i, mask = -1;
+    }
+    {
+       register struct in_ifaddr *ia;
+       /*
+        * Check whether network is a subnet;
+        * if so, return subnet number.
+        */
+       for (ia = in_ifaddr; ia; ia = ia->ia_next)
+               if (net == ia->ia_net)
+                       mask =  ia->ia_subnetmask;
+    }
+    {
+       register char *cpbase = (char *)&(sockmask->sin_addr);
+       register char *cp = (char *)(1 + &(sockmask->sin_addr));
+
+       sockmask->sin_addr.s_addr = htonl(mask);
+       sockmask->sin_len = 0;
+       while (--cp >= cpbase)
+               if (*cp) {
+                       sockmask->sin_len = 1 + cp - (caddr_t)sockmask;
+                       break;
+               }
+    }
+}
+
 /*
  * Return the host portion of an internet address.
  */
 /*
  * Return the host portion of an internet address.
  */
@@ -209,9 +236,13 @@ 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;
+       register struct ifaddr *ifa;
+       struct in_ifaddr *oia;
+       struct in_aliasreq *ifra = (struct in_aliasreq *)data;
        struct mbuf *m;
        struct mbuf *m;
-       int error;
+       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.
@@ -223,11 +254,23 @@ 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 (error = suser(u.u_cred, &u.u_acflag))
+                       return (error);
 
                if (ifp == 0)
                        panic("in_control");
 
                if (ifp == 0)
                        panic("in_control");
@@ -248,82 +291,152 @@ in_control(so, cmd, data, ifp)
                                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 (error = suser(u.u_cred, &u.u_acflag))
+                       return (error);
                /* 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;
-       }
 
 
+       default:
+               return (EOPNOTSUPP);
+               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;
+               ia->ia_dstaddr = *(struct sockaddr_in *)&ifr->ifr_dstaddr;
                if (ifp->if_ioctl &&
                    (error = (*ifp->if_ioctl)(ifp, SIOCSIFDSTADDR, ia))) {
                        ia->ia_dstaddr = oldaddr;
                        return (error);
                }
                if (ia->ia_flags & IFA_ROUTE) {
                if (ifp->if_ioctl &&
                    (error = (*ifp->if_ioctl)(ifp, SIOCSIFDSTADDR, ia))) {
                        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_addr;
+                       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");
+               }
+               (void) m_free(dtom(oia));
                break;
 
        default:
                break;
 
        default:
@@ -334,23 +447,38 @@ in_control(so, cmd, data, ifp)
        return (0);
 }
 
        return (0);
 }
 
+/*
+ * Delete any existing route for an interface.
+ */
+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 u_long i = ntohl(sin->sin_addr.s_addr);
        register struct ifnet *ifp;
        register struct in_ifaddr *ia;
        struct sockaddr_in *sin;
 {
        register u_long i = ntohl(sin->sin_addr.s_addr);
-       struct sockaddr oldaddr;
-       struct sockaddr_in netaddr;
+       struct sockaddr_in oldaddr;
        int s = splimp(), error;
 
        oldaddr = ia->ia_addr;
        int s = splimp(), error;
 
        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,
        /*
         * Give the interface a chance to initialize
         * if this is its first address,
@@ -361,25 +489,10 @@ in_ifinit(ifp, ia, sin)
                ia->ia_addr = oldaddr;
                return (error);
        }
                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 (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;
@@ -394,9 +507,19 @@ in_ifinit(ifp, ia, sin)
         */
        ia->ia_subnetmask |= ia->ia_netmask;
        ia->ia_subnet = i & ia->ia_subnetmask;
         */
        ia->ia_subnetmask |= ia->ia_netmask;
        ia->ia_subnet = i & ia->ia_subnetmask;
+       ia->ia_sockmask.sin_addr.s_addr = htonl(ia->ia_subnetmask);
+       {
+               register char *cp = (char *) (1 + &(ia->ia_sockmask.sin_addr));
+               register char *cpbase = (char *) &(ia->ia_sockmask.sin_addr);
+               while (--cp >= cpbase)
+                       if (*cp) {
+                               ia->ia_sockmask.sin_len =
+                                       1 + cp - (char *) &(ia->ia_sockmask);
+                               break;
+                       }
+       }
        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 =
+               ia->ia_broadaddr.sin_addr = 
                        in_makeaddr(ia->ia_subnet, INADDR_BROADCAST);
                ia->ia_netbroadcast.s_addr =
                    htonl(ia->ia_net | (INADDR_BROADCAST &~ ia->ia_netmask));
                        in_makeaddr(ia->ia_subnet, INADDR_BROADCAST);
                ia->ia_netbroadcast.s_addr =
                    htonl(ia->ia_net | (INADDR_BROADCAST &~ ia->ia_netmask));
@@ -404,16 +527,14 @@ in_ifinit(ifp, ia, sin)
        /*
         * Add route for the network.
         */
        /*
         * Add route for the network.
         */
-       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);
+       if (ifp->if_flags & IFF_LOOPBACK) {
+               ia->ia_ifa.ifa_dstaddr = ia->ia_ifa.ifa_addr;
+               rtinit(&(ia->ia_ifa), (int)RTM_ADD, RTF_HOST|RTF_UP);
+       } else if (ifp->if_flags & IFF_POINTOPOINT &&
+                ia->ia_dstaddr.sin_family == AF_INET)
+               rtinit(&(ia->ia_ifa), (int)RTM_ADD, RTF_HOST|RTF_UP);
        else {
        else {
-               netaddr.sin_addr = in_makeaddr(ia->ia_subnet, INADDR_ANY);
-               rtinit((struct sockaddr *)&netaddr, &ia->ia_addr,
-                   (int)SIOCADDRT, RTF_UP);
+               rtinit(&(ia->ia_ifa), (int)RTM_ADD, RTF_UP);
        }
        ia->ia_flags |= IFA_ROUTE;
        splx(s);
        }
        ia->ia_flags |= IFA_ROUTE;
        splx(s);
@@ -450,7 +571,7 @@ in_broadcast(in)
         */
        for (ia = in_ifaddr; ia; ia = ia->ia_next)
            if (ia->ia_ifp->if_flags & IFF_BROADCAST) {
         */
        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)
+               if (ia->ia_broadaddr.sin_addr.s_addr == in.s_addr)
                     return (1);
                /*
                 * Check for old-style (host 0) broadcast.
                     return (1);
                /*
                 * Check for old-style (host 0) broadcast.