default ttl 30 => 60
[unix-history] / usr / src / sys / netinet / if_ether.c
index 2453524..28dd53d 100644 (file)
@@ -1,17 +1,33 @@
 /*
 /*
- * Copyright (c) 1982, 1986 Regents of the University of California.
- * All rights reserved.  The Berkeley software License Agreement
- * specifies the terms and conditions for redistribution.
+ * Copyright (c) 1982, 1986, 1988 Regents of the University of California.
+ * All rights reserved.
  *
  *
- *     @(#)if_ether.c  7.1 (Berkeley) %G%
+ * 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.
+ *
+ *     @(#)if_ether.c  7.10 (Berkeley) %G%
  */
 
 /*
  * Ethernet address resolution protocol.
  */
 
 /*
  * Ethernet address resolution protocol.
+ * TODO:
+ *     run at splnet (add ARP protocol intr.)
+ *     link entries onto hash chains, keep free list
+ *     add "inuse/lock" bit (or ref. count) along with valid bit
  */
 
 #include "param.h"
 #include "systm.h"
  */
 
 #include "param.h"
 #include "systm.h"
+#include "malloc.h"
 #include "mbuf.h"
 #include "socket.h"
 #include "time.h"
 #include "mbuf.h"
 #include "socket.h"
 #include "time.h"
 #include "../net/if.h"
 #include "in.h"
 #include "in_systm.h"
 #include "../net/if.h"
 #include "in.h"
 #include "in_systm.h"
+#include "in_var.h"
 #include "ip.h"
 #include "if_ether.h"
 
 #include "ip.h"
 #include "if_ether.h"
 
+#ifdef GATEWAY
+#define        ARPTAB_BSIZ     16              /* bucket size */
+#define        ARPTAB_NB       37              /* number of buckets */
+#else
 #define        ARPTAB_BSIZ     9               /* bucket size */
 #define        ARPTAB_NB       19              /* number of buckets */
 #define        ARPTAB_BSIZ     9               /* bucket size */
 #define        ARPTAB_NB       19              /* number of buckets */
+#endif
 #define        ARPTAB_SIZE     (ARPTAB_BSIZ * ARPTAB_NB)
 struct arptab arptab[ARPTAB_SIZE];
 int    arptab_size = ARPTAB_SIZE;      /* for arp command */
 #define        ARPTAB_SIZE     (ARPTAB_BSIZ * ARPTAB_NB)
 struct arptab arptab[ARPTAB_SIZE];
 int    arptab_size = ARPTAB_SIZE;      /* for arp command */
@@ -56,7 +78,6 @@ int   arptab_size = ARPTAB_SIZE;      /* for arp command */
 #define        ARPT_KILLC      20      /* kill completed entry in 20 mins. */
 #define        ARPT_KILLI      3       /* kill incomplete entry in 3 minutes */
 
 #define        ARPT_KILLC      20      /* kill completed entry in 20 mins. */
 #define        ARPT_KILLI      3       /* kill incomplete entry in 3 minutes */
 
-u_char etherbroadcastaddr[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
 extern struct ifnet loif;
 
 /*
 extern struct ifnet loif;
 
 /*
@@ -92,10 +113,11 @@ arpwhohas(ac, addr)
        register struct ether_arp *ea;
        struct sockaddr sa;
 
        register struct ether_arp *ea;
        struct sockaddr sa;
 
-       if ((m = m_get(M_DONTWAIT, MT_DATA)) == NULL)
+       if ((m = m_gethdr(M_DONTWAIT, MT_DATA)) == NULL)
                return;
                return;
-       m->m_len = sizeof *ea;
-       m->m_off = MMAXOFF - m->m_len;
+       m->m_len = sizeof(*ea);
+       m->m_pkthdr.len = sizeof(*ea);
+       MH_ALIGN(m, sizeof(*ea));
        ea = mtod(m, struct ether_arp *);
        eh = (struct ether_header *)sa.sa_data;
        bzero((caddr_t)ea, sizeof (*ea));
        ea = mtod(m, struct ether_arp *);
        eh = (struct ether_header *)sa.sa_data;
        bzero((caddr_t)ea, sizeof (*ea));
@@ -113,9 +135,14 @@ arpwhohas(ac, addr)
           sizeof(ea->arp_spa));
        bcopy((caddr_t)addr, (caddr_t)ea->arp_tpa, sizeof(ea->arp_tpa));
        sa.sa_family = AF_UNSPEC;
           sizeof(ea->arp_spa));
        bcopy((caddr_t)addr, (caddr_t)ea->arp_tpa, sizeof(ea->arp_tpa));
        sa.sa_family = AF_UNSPEC;
+       sa.sa_len = sizeof(sa);
        (*ac->ac_if.if_output)(&ac->ac_if, m, &sa);
 }
 
        (*ac->ac_if.if_output)(&ac->ac_if, m, &sa);
 }
 
+int    useloopback = 1;        /* use loopback interface for local traffic */
+
+int    useloopback = 1;        /* use loopback interface for local traffic */
+
 /*
  * Resolve an IP address into an ethernet address.  If success, 
  * desten is filled in.  If there is no entry in arptab,
 /*
  * Resolve an IP address into an ethernet address.  If success, 
  * desten is filled in.  If there is no entry in arptab,
@@ -138,21 +165,33 @@ arpresolve(ac, m, destip, desten, usetrailers)
        int *usetrailers;
 {
        register struct arptab *at;
        int *usetrailers;
 {
        register struct arptab *at;
-       register struct ifnet *ifp;
        struct sockaddr_in sin;
        struct sockaddr_in sin;
-       int s, lna;
+       register struct in_ifaddr *ia;
+       u_long lna;
+       int s;
 
        *usetrailers = 0;
 
        *usetrailers = 0;
-       if (in_broadcast(*destip)) {    /* broadcast address */
+       if (m->m_flags & M_BCAST) {     /* broadcast */
                bcopy((caddr_t)etherbroadcastaddr, (caddr_t)desten,
                    sizeof(etherbroadcastaddr));
                return (1);
        }
        lna = in_lnaof(*destip);
                bcopy((caddr_t)etherbroadcastaddr, (caddr_t)desten,
                    sizeof(etherbroadcastaddr));
                return (1);
        }
        lna = in_lnaof(*destip);
-       ifp = &ac->ac_if;
        /* if for us, use software loopback driver if up */
        /* if for us, use software loopback driver if up */
-       if (destip->s_addr == ac->ac_ipaddr.s_addr) {
-               if (loif.if_flags & IFF_UP) {
+       for (ia = in_ifaddr; ia; ia = ia->ia_next)
+           if ((ia->ia_ifp == &ac->ac_if) &&
+               (destip->s_addr == ia->ia_addr.sin_addr.s_addr)) {
+               /*
+                * This test used to be
+                *      if (loif.if_flags & IFF_UP)
+                * It allowed local traffic to be forced
+                * through the hardware by configuring the loopback down.
+                * However, it causes problems during network configuration
+                * for boards that can't receive packets they send.
+                * It is now necessary to clear "useloopback"
+                * to force traffic out to the hardware.
+                */
+               if (useloopback) {
                        sin.sin_family = AF_INET;
                        sin.sin_addr = *destip;
                        (void) looutput(&loif, m, (struct sockaddr *)&sin);
                        sin.sin_family = AF_INET;
                        sin.sin_addr = *destip;
                        (void) looutput(&loif, m, (struct sockaddr *)&sin);
@@ -169,7 +208,7 @@ arpresolve(ac, m, destip, desten, usetrailers)
        s = splimp();
        ARPTAB_LOOK(at, destip->s_addr);
        if (at == 0) {                  /* not found */
        s = splimp();
        ARPTAB_LOOK(at, destip->s_addr);
        if (at == 0) {                  /* not found */
-               if (ifp->if_flags & IFF_NOARP) {
+               if (ac->ac_if.if_flags & IFF_NOARP) {
                        bcopy((caddr_t)ac->ac_enaddr, (caddr_t)desten, 3);
                        desten[3] = (lna >> 16) & 0x7f;
                        desten[4] = (lna >> 8) & 0xff;
                        bcopy((caddr_t)ac->ac_enaddr, (caddr_t)desten, 3);
                        desten[3] = (lna >> 16) & 0x7f;
                        desten[4] = (lna >> 8) & 0xff;
@@ -178,6 +217,8 @@ arpresolve(ac, m, destip, desten, usetrailers)
                        return (1);
                } else {
                        at = arptnew(destip);
                        return (1);
                } else {
                        at = arptnew(destip);
+                       if (at == 0)
+                               panic("arpresolve: no free entry");
                        at->at_hold = m;
                        arpwhohas(ac, destip);
                        splx(s);
                        at->at_hold = m;
                        arpwhohas(ac, destip);
                        splx(s);
@@ -220,7 +261,6 @@ arpinput(ac, m)
 
        if (ac->ac_if.if_flags & IFF_NOARP)
                goto out;
 
        if (ac->ac_if.if_flags & IFF_NOARP)
                goto out;
-       IF_ADJ(m);
        if (m->m_len < sizeof(struct arphdr))
                goto out;
        ar = mtod(m, struct arphdr *);
        if (m->m_len < sizeof(struct arphdr))
                goto out;
        ar = mtod(m, struct arphdr *);
@@ -264,20 +304,31 @@ in_arpinput(ac, m)
        register struct ether_arp *ea;
        struct ether_header *eh;
        register struct arptab *at;  /* same as "merge" flag */
        register struct ether_arp *ea;
        struct ether_header *eh;
        register struct arptab *at;  /* same as "merge" flag */
+       register struct in_ifaddr *ia;
+       struct in_ifaddr *maybe_ia = 0;
        struct mbuf *mcopy = 0;
        struct sockaddr_in sin;
        struct sockaddr sa;
        struct in_addr isaddr, itaddr, myaddr;
        struct mbuf *mcopy = 0;
        struct sockaddr_in sin;
        struct sockaddr sa;
        struct in_addr isaddr, itaddr, myaddr;
-       int proto, op;
+       int proto, op, s, completed = 0;
 
 
-       myaddr = ac->ac_ipaddr;
        ea = mtod(m, struct ether_arp *);
        proto = ntohs(ea->arp_pro);
        op = ntohs(ea->arp_op);
        ea = mtod(m, struct ether_arp *);
        proto = ntohs(ea->arp_pro);
        op = ntohs(ea->arp_op);
-       isaddr.s_addr = ((struct in_addr *)ea->arp_spa)->s_addr;
-       itaddr.s_addr = ((struct in_addr *)ea->arp_tpa)->s_addr;
+       bcopy((caddr_t)ea->arp_spa, (caddr_t)&isaddr, sizeof (isaddr));
+       bcopy((caddr_t)ea->arp_tpa, (caddr_t)&itaddr, sizeof (itaddr));
+       for (ia = in_ifaddr; ia; ia = ia->ia_next)
+               if (ia->ia_ifp == &ac->ac_if) {
+                       maybe_ia = ia;
+                       if ((itaddr.s_addr == ia->ia_addr.sin_addr.s_addr) ||
+                            (isaddr.s_addr == ia->ia_addr.sin_addr.s_addr))
+                               break;
+               }
+       if (maybe_ia == 0)
+               goto out;
+       myaddr = ia ? ia->ia_addr.sin_addr : maybe_ia->ia_addr.sin_addr;
        if (!bcmp((caddr_t)ea->arp_sha, (caddr_t)ac->ac_enaddr,
        if (!bcmp((caddr_t)ea->arp_sha, (caddr_t)ac->ac_enaddr,
-         sizeof (ea->arp_sha)))
+           sizeof (ea->arp_sha)))
                goto out;       /* it's from me, ignore it. */
        if (!bcmp((caddr_t)ea->arp_sha, (caddr_t)etherbroadcastaddr,
            sizeof (ea->arp_sha))) {
                goto out;       /* it's from me, ignore it. */
        if (!bcmp((caddr_t)ea->arp_sha, (caddr_t)etherbroadcastaddr,
            sizeof (ea->arp_sha))) {
@@ -287,18 +338,22 @@ in_arpinput(ac, m)
                goto out;
        }
        if (isaddr.s_addr == myaddr.s_addr) {
                goto out;
        }
        if (isaddr.s_addr == myaddr.s_addr) {
-               log(LOG_ERR, "%s: %s\n",
-                       "duplicate IP address!! sent from ethernet address",
-                       ether_sprintf(ea->arp_sha));
+               log(LOG_ERR,
+                  "duplicate IP address %x!! sent from ethernet address: %s\n",
+                  ntohl(isaddr.s_addr), ether_sprintf(ea->arp_sha));
                itaddr = myaddr;
                if (op == ARPOP_REQUEST)
                        goto reply;
                goto out;
        }
                itaddr = myaddr;
                if (op == ARPOP_REQUEST)
                        goto reply;
                goto out;
        }
+       s = splimp();
+       s = splimp();
        ARPTAB_LOOK(at, isaddr.s_addr);
        if (at) {
                bcopy((caddr_t)ea->arp_sha, (caddr_t)at->at_enaddr,
                    sizeof(ea->arp_sha));
        ARPTAB_LOOK(at, isaddr.s_addr);
        if (at) {
                bcopy((caddr_t)ea->arp_sha, (caddr_t)at->at_enaddr,
                    sizeof(ea->arp_sha));
+               if ((at->at_flags & ATF_COM) == 0)
+                       completed = 1;
                at->at_flags |= ATF_COM;
                if (at->at_hold) {
                        sin.sin_family = AF_INET;
                at->at_flags |= ATF_COM;
                if (at->at_hold) {
                        sin.sin_family = AF_INET;
@@ -310,11 +365,15 @@ in_arpinput(ac, m)
        }
        if (at == 0 && itaddr.s_addr == myaddr.s_addr) {
                /* ensure we have a table entry */
        }
        if (at == 0 && itaddr.s_addr == myaddr.s_addr) {
                /* ensure we have a table entry */
-               at = arptnew(&isaddr);
-               bcopy((caddr_t)ea->arp_sha, (caddr_t)at->at_enaddr,
-                   sizeof(ea->arp_sha));
-               at->at_flags |= ATF_COM;
+               if (at = arptnew(&isaddr)) {
+                       bcopy((caddr_t)ea->arp_sha, (caddr_t)at->at_enaddr,
+                           sizeof(ea->arp_sha));
+                       completed = 1;
+                       at->at_flags |= ATF_COM;
+               }
        }
        }
+       splx(s);
+       splx(s);
 reply:
        switch (proto) {
 
 reply:
        switch (proto) {
 
@@ -331,10 +390,13 @@ reply:
 
        case ETHERTYPE_IP:
                /*
 
        case ETHERTYPE_IP:
                /*
-                * Reply if this is an IP request, or if we want to send
-                * a trailer response.
+                * Reply if this is an IP request,
+                * or if we want to send a trailer response.
+                * Send the latter only to the IP response
+                * that completes the current ARP entry.
                 */
                 */
-               if (op != ARPOP_REQUEST && ac->ac_if.if_flags & IFF_NOTRAILERS)
+               if (op != ARPOP_REQUEST &&
+                   (completed == 0 || ac->ac_if.if_flags & IFF_NOTRAILERS))
                        goto out;
        }
        if (itaddr.s_addr == myaddr.s_addr) {
                        goto out;
        }
        if (itaddr.s_addr == myaddr.s_addr) {
@@ -375,6 +437,7 @@ reply:
            sizeof(eh->ether_dhost));
        eh->ether_type = ETHERTYPE_ARP;
        sa.sa_family = AF_UNSPEC;
            sizeof(eh->ether_dhost));
        eh->ether_type = ETHERTYPE_ARP;
        sa.sa_family = AF_UNSPEC;
+       sa.sa_len = sizeof(sa);
        (*ac->ac_if.if_output)(&ac->ac_if, m, &sa);
        if (mcopy) {
                ea = mtod(mcopy, struct ether_arp *);
        (*ac->ac_if.if_output)(&ac->ac_if, m, &sa);
        if (mcopy) {
                ea = mtod(mcopy, struct ether_arp *);
@@ -409,6 +472,7 @@ arptfree(at)
  * This always succeeds since no bucket can be completely filled
  * with permanent entries (except from arpioctl when testing whether
  * another permanent entry will fit).
  * This always succeeds since no bucket can be completely filled
  * with permanent entries (except from arpioctl when testing whether
  * another permanent entry will fit).
+ * MUST BE CALLED AT SPLIMP.
  */
 struct arptab *
 arptnew(addr)
  */
 struct arptab *
 arptnew(addr)
@@ -429,7 +493,7 @@ arptnew(addr)
                        goto out;        /* found an empty entry */
                if (at->at_flags & ATF_PERM)
                        continue;
                        goto out;        /* found an empty entry */
                if (at->at_flags & ATF_PERM)
                        continue;
-               if (at->at_timer > oldest) {
+               if ((int) at->at_timer > oldest) {
                        oldest = at->at_timer;
                        ato = at;
                }
                        oldest = at->at_timer;
                        ato = at;
                }
@@ -453,10 +517,21 @@ arpioctl(cmd, data)
        register struct sockaddr_in *sin;
        int s;
 
        register struct sockaddr_in *sin;
        int s;
 
+       sin = (struct sockaddr_in *)&ar->arp_ha;
+#if defined(COMPAT_43) && BYTE_ORDER != BIG_ENDIAN
+       if (sin->sin_family == 0 && sin->sin_len < 16)
+               sin->sin_family = sin->sin_len;
+#endif
+       sin->sin_len = sizeof(ar->arp_ha);
+       sin = (struct sockaddr_in *)&ar->arp_pa;
+#if defined(COMPAT_43) && BYTE_ORDER != BIG_ENDIAN
+       if (sin->sin_family == 0 && sin->sin_len < 16)
+               sin->sin_family = sin->sin_len;
+#endif
+       sin->sin_len = sizeof(ar->arp_pa);
        if (ar->arp_pa.sa_family != AF_INET ||
            ar->arp_ha.sa_family != AF_UNSPEC)
                return (EAFNOSUPPORT);
        if (ar->arp_pa.sa_family != AF_INET ||
            ar->arp_ha.sa_family != AF_UNSPEC)
                return (EAFNOSUPPORT);
-       sin = (struct sockaddr_in *)&ar->arp_pa;
        s = splimp();
        ARPTAB_LOOK(at, sin->sin_addr.s_addr);
        if (at == NULL) {               /* not found */
        s = splimp();
        ARPTAB_LOOK(at, sin->sin_addr.s_addr);
        if (at == NULL) {               /* not found */
@@ -474,6 +549,10 @@ arpioctl(cmd, data)
        case SIOCSARP:          /* set entry */
                if (at == NULL) {
                        at = arptnew(&sin->sin_addr);
        case SIOCSARP:          /* set entry */
                if (at == NULL) {
                        at = arptnew(&sin->sin_addr);
+                       if (at == NULL) {
+                               splx(s);
+                               return (EADDRNOTAVAIL);
+                       }
                        if (ar->arp_flags & ATF_PERM) {
                        /* never make all entries in a bucket permanent */
                                register struct arptab *tat;
                        if (ar->arp_flags & ATF_PERM) {
                        /* never make all entries in a bucket permanent */
                                register struct arptab *tat;
@@ -491,7 +570,7 @@ arpioctl(cmd, data)
                bcopy((caddr_t)ar->arp_ha.sa_data, (caddr_t)at->at_enaddr,
                    sizeof(at->at_enaddr));
                at->at_flags = ATF_COM | ATF_INUSE |
                bcopy((caddr_t)ar->arp_ha.sa_data, (caddr_t)at->at_enaddr,
                    sizeof(at->at_enaddr));
                at->at_flags = ATF_COM | ATF_INUSE |
-                       (ar->arp_flags & (ATF_PERM|ATF_PUBL));
+                       (ar->arp_flags & (ATF_PERM|ATF_PUBL|ATF_USETRAILERS));
                at->at_timer = 0;
                break;
 
                at->at_timer = 0;
                break;
 
@@ -500,32 +579,16 @@ arpioctl(cmd, data)
                break;
 
        case SIOCGARP:          /* get entry */
                break;
 
        case SIOCGARP:          /* get entry */
+       case OSIOCGARP:
                bcopy((caddr_t)at->at_enaddr, (caddr_t)ar->arp_ha.sa_data,
                    sizeof(at->at_enaddr));
                bcopy((caddr_t)at->at_enaddr, (caddr_t)ar->arp_ha.sa_data,
                    sizeof(at->at_enaddr));
+#ifdef COMPAT_43
+               if (cmd == OSIOCGARP)
+                       *(u_short *)&ar->arp_ha = ar->arp_ha.sa_family;
+#endif
                ar->arp_flags = at->at_flags;
                break;
        }
        splx(s);
        return (0);
 }
                ar->arp_flags = at->at_flags;
                break;
        }
        splx(s);
        return (0);
 }
-
-/*
- * Convert Ethernet address to printable (loggable) representation.
- */
-char *
-ether_sprintf(ap)
-       register u_char *ap;
-{
-       register i;
-       static char etherbuf[18];
-       register char *cp = etherbuf;
-       static char digits[] = "0123456789abcdef";
-
-       for (i = 0; i < 6; i++) {
-               *cp++ = digits[*ap >> 4];
-               *cp++ = digits[*ap++ & 0xf];
-               *cp++ = ':';
-       }
-       *--cp = 0;
-       return (etherbuf);
-}