reorganization to move ufsmount ops to be vnode ops;
[unix-history] / usr / src / sys / netinet / in.c
index e947bc7..05f83a7 100644 (file)
-/*     in.c    4.12    83/03/14        */
-
-#include "../h/param.h"
-#include "../h/mbuf.h"
-#include "../h/protosw.h"
-#include "../h/socket.h"
-#include "../h/socketvar.h"
-#include "../netinet/in.h"
-#include "../netinet/in_systm.h"
-#include "../net/if.h"
-#include "../net/route.h"
-#include "../net/af.h"
-
-#ifdef INET
-inet_hash(sin, hp)
-       register struct sockaddr_in *sin;
-       struct afhash *hp;
-{
-
-       hp->afh_nethash = in_netof(sin->sin_addr);
-       hp->afh_hosthash = ntohl(sin->sin_addr.s_addr);
-}
-
-inet_netmatch(sin1, sin2)
-       struct sockaddr_in *sin1, *sin2;
-{
+/*
+ * Copyright (c) 1982, 1986, 1991 Regents of the University of California.
+ * All rights reserved.
+ *
+ * %sccs.include.redist.c%
+ *
+ *     @(#)in.c        7.18 (Berkeley) %G%
+ */
 
 
-       return (in_netof(sin1->sin_addr) == in_netof(sin2->sin_addr));
-}
+#include "param.h"
+#include "ioctl.h"
+#include "errno.h"
+#include "mbuf.h"
+#include "socket.h"
+#include "socketvar.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
 /*
 /*
- * Formulate an Internet address from network + host.  Used in
- * building addresses stored in the ifnet structure.
+ * Formulate an Internet address from network + host.
  */
 struct in_addr
  */
 struct in_addr
-if_makeaddr(net, host)
-       int net, host;
+in_makeaddr(net, host)
+       u_long net, host;
 {
 {
+       register struct in_ifaddr *ia;
+       register u_long mask;
        u_long addr;
 
        u_long addr;
 
-       if (net < 128)
-               addr = (net << IN_CLASSA_NSHIFT) | host;
-       else if (net < 65536)
-               addr = (net << IN_CLASSB_NSHIFT) | host;
+       if (IN_CLASSA(net))
+               mask = IN_CLASSA_HOST;
+       else if (IN_CLASSB(net))
+               mask = IN_CLASSB_HOST;
        else
        else
-               addr = (net << IN_CLASSC_NSHIFT) | host;
-       addr = htonl(addr);
+               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);
 }
 
 /*
  * Return the network number from an internet address.
  */
        return (*(struct in_addr *)&addr);
 }
 
 /*
  * Return the network number from an internet address.
  */
+u_long
 in_netof(in)
        struct in_addr in;
 {
        register u_long i = ntohl(in.s_addr);
 in_netof(in)
        struct in_addr in;
 {
        register u_long i = ntohl(in.s_addr);
+       register u_long net;
+       register struct in_ifaddr *ia;
 
        if (IN_CLASSA(i))
 
        if (IN_CLASSA(i))
-               return (((i)&IN_CLASSA_NET) >> IN_CLASSA_NSHIFT);
+               net = i & IN_CLASSA_NET;
        else if (IN_CLASSB(i))
        else if (IN_CLASSB(i))
-               return (((i)&IN_CLASSB_NET) >> IN_CLASSB_NSHIFT);
+               net = i & IN_CLASSB_NET;
+       else if (IN_CLASSC(i))
+               net = i & IN_CLASSC_NET;
        else
        else
-               return (((i)&IN_CLASSC_NET) >> IN_CLASSC_NSHIFT);
+               return (0);
+
+       /*
+        * 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)
+                       return (i & ia->ia_subnetmask);
+       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.
  */
+u_long
 in_lnaof(in)
        struct in_addr in;
 {
        register u_long i = ntohl(in.s_addr);
 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
+int subnetsarelocal = SUBNETSARELOCAL;
+/*
+ * Return 1 if an internet address is for a ``local'' host
+ * (one to which we have a connection).  If subnetsarelocal
+ * is true, this includes other subnets of the local net.
+ * Otherwise, it includes only the directly-connected (sub)nets.
+ */
+in_localaddr(in)
+       struct in_addr in;
+{
+       register u_long i = ntohl(in.s_addr);
+       register struct in_ifaddr *ia;
+
+       if (subnetsarelocal) {
+               for (ia = in_ifaddr; ia; ia = ia->ia_next)
+                       if ((i & ia->ia_netmask) == ia->ia_net)
+                               return (1);
+       } else {
+               for (ia = in_ifaddr; ia; ia = ia->ia_next)
+                       if ((i & ia->ia_subnetmask) == ia->ia_subnet)
+                               return (1);
+       }
+       return (0);
+}
+
+/*
+ * Determine whether an IP address is in a reserved set of addresses
+ * that may not be forwarded, or whether datagrams to that destination
+ * may be forwarded.
+ */
+in_canforward(in)
+       struct in_addr in;
+{
+       register u_long i = ntohl(in.s_addr);
+       register u_long net;
+
+       if (IN_EXPERIMENTAL(i))
+               return (0);
+       if (IN_CLASSA(i)) {
+               net = i & IN_CLASSA_NET;
+               if (net == 0 || net == IN_LOOPBACKNET)
+                       return (0);
+       }
+       return (1);
+}
+
+int    in_interfaces;          /* number of external internet interfaces */
+extern struct ifnet loif;
+
+/*
+ * Generic internet control operations (ioctl's).
+ * Ifp is 0 if not an interface-specific ioctl.
+ */
+/* ARGSUSED */
+in_control(so, cmd, data, ifp)
+       struct socket *so;
+       int cmd;
+       caddr_t data;
+       register struct ifnet *ifp;
+{
+       register struct ifreq *ifr = (struct ifreq *)data;
+       register struct in_ifaddr *ia = 0;
+       register struct ifaddr *ifa;
+       struct in_ifaddr *oia;
+       struct in_aliasreq *ifra = (struct in_aliasreq *)data;
+       struct mbuf *m;
+       struct sockaddr_in oldaddr;
+       int error, hostIsNew, maskIsNew;
+       u_long i;
+
+       /*
+        * Find address for this interface, if it exists.
+        */
+       if (ifp)
+               for (ia = in_ifaddr; ia; ia = ia->ia_next)
+                       if (ia->ia_ifp == ifp)
+                               break;
+
+       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:
+               if ((so->so_state & SS_PRIV) == 0)
+                       return (EPERM);
+
+               if (ifp == 0)
+                       panic("in_control");
+               if (ia == (struct in_ifaddr *)0) {
+                       m = m_getclr(M_WAIT, MT_IFADDR);
+                       if (m == (struct mbuf *)NULL)
+                               return (ENOBUFS);
+                       if (ia = in_ifaddr) {
+                               for ( ; ia->ia_next; ia = ia->ia_next)
+                                       ;
+                               ia->ia_next = mtod(m, struct in_ifaddr *);
+                       } else
+                               in_ifaddr = mtod(m, struct in_ifaddr *);
+                       ia = mtod(m, struct in_ifaddr *);
+                       if (ifa = ifp->if_addrlist) {
+                               for ( ; ifa->ifa_next; ifa = ifa->ifa_next)
+                                       ;
+                               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;
+                       if (ifp != &loif)
+                               in_interfaces++;
+               }
+               break;
+
+       case SIOCSIFBRDADDR:
+               if ((so->so_state & SS_PRIV) == 0)
+                       return (EPERM);
+               /* FALLTHROUGH */
+
+       case SIOCGIFADDR:
+       case SIOCGIFNETMASK:
+       case SIOCGIFDSTADDR:
+       case SIOCGIFBRDADDR:
+               if (ia == (struct in_ifaddr *)0)
+                       return (EADDRNOTAVAIL);
+               break;
+
+       default:
+               return (EOPNOTSUPP);
+               break;
+       }
+       switch (cmd) {
+
+       case SIOCGIFADDR:
+               *((struct sockaddr_in *)&ifr->ifr_addr) = ia->ia_addr;
+               break;
+
+       case SIOCGIFBRDADDR:
+               if ((ifp->if_flags & IFF_BROADCAST) == 0)
+                       return (EINVAL);
+               *((struct sockaddr_in *)&ifr->ifr_dstaddr) = ia->ia_broadaddr;
+               break;
+
+       case SIOCGIFDSTADDR:
+               if ((ifp->if_flags & IFF_POINTOPOINT) == 0)
+                       return (EINVAL);
+               *((struct sockaddr_in *)&ifr->ifr_dstaddr) = ia->ia_dstaddr;
+               break;
+
+       case SIOCGIFNETMASK:
+               *((struct sockaddr_in *)&ifr->ifr_addr) = ia->ia_sockmask;
+               break;
+
+       case SIOCSIFDSTADDR:
+               if ((ifp->if_flags & IFF_POINTOPOINT) == 0)
+                       return (EINVAL);
+               oldaddr = ia->ia_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) {
+                       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);
+               ia->ia_broadaddr = *(struct sockaddr_in *)&ifr->ifr_broadaddr;
+               break;
+
+       case SIOCSIFADDR:
+               return (in_ifinit(ifp, ia,
+                   (struct sockaddr_in *) &ifr->ifr_addr, 1));
+
+       case SIOCSIFNETMASK:
+               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:
+               if (ifp == 0 || ifp->if_ioctl == 0)
+                       return (EOPNOTSUPP);
+               return ((*ifp->if_ioctl)(ifp, cmd, data));
+       }
+       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.
+ */
+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);
+       struct sockaddr_in oldaddr;
+       int s = splimp(), error, flags = RTF_UP;
+       int ether_output(), arp_rtrequest();
+
+       oldaddr = ia->ia_addr;
+       ia->ia_addr = *sin;
+       /*
+        * 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))) {
+               splx(s);
+               ia->ia_addr = oldaddr;
+               return (error);
+       }
+       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))
        if (IN_CLASSA(i))
-               return ((i)&IN_CLASSA_HOST);
+               ia->ia_netmask = IN_CLASSA_NET;
        else if (IN_CLASSB(i))
        else if (IN_CLASSB(i))
-               return ((i)&IN_CLASSB_HOST);
+               ia->ia_netmask = IN_CLASSB_NET;
        else
        else
-               return ((i)&IN_CLASSC_HOST);
+               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.
+        */
+       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;
+                       }
+       }
+       /*
+        * Add route for the network.
+        */
+       if (ifp->if_flags & IFF_BROADCAST) {
+               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));
+       } 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;
+       return (error);
 }
 
 /*
 }
 
 /*
- * Initialize an interface's routing
- * table entry according to the network.
- * INTERNET SPECIFIC.
+ * Return address info for specified internet network.
  */
  */
-if_rtinit(ifp, flags)
-       register struct ifnet *ifp;
-       int flags;
+struct in_ifaddr *
+in_iaonnetof(net)
+       u_long net;
 {
 {
-       struct sockaddr_in sin;
+       register struct in_ifaddr *ia;
 
 
-       if (ifp->if_flags & IFF_ROUTE)
-               return;
-       bzero((caddr_t)&sin, sizeof (sin));
-       sin.sin_family = AF_INET;
-       sin.sin_addr = if_makeaddr(ifp->if_net, INADDR_ANY);
-       rtinit((struct sockaddr *)&sin, &ifp->if_addr, flags);
+       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.
+ */
+in_broadcast(in)
+       struct in_addr in;
+{
+       register struct in_ifaddr *ia;
+       u_long t;
+
+       /*
+        * 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 (ia->ia_broadaddr.sin_addr.s_addr == in.s_addr)
+                    return (1);
+               /*
+                * Check for old-style (host 0) broadcast.
+                */
+               if ((t = ntohl(in.s_addr)) == ia->ia_subnet || t == ia->ia_net)
+                   return (1);
+       }
+       if (in.s_addr == INADDR_BROADCAST || in.s_addr == INADDR_ANY)
+               return (1);
+       return (0);
 }
 #endif
 }
 #endif