Fold in the changes to support IP multicasting, from Jim Lowe et al.
[unix-history] / sys / net / if_ethersubr.c
index 6913c6f..de4a85f 100644 (file)
@@ -1,5 +1,5 @@
 /*
 /*
- * Copyright (c) 1982, 1989 Regents of the University of California.
+ * Copyright (c) 1982, 1989, 1993 Regents of the University of California.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -31,7 +31,7 @@
  * SUCH DAMAGE.
  *
  *     from: @(#)if_ethersubr.c        7.13 (Berkeley) 4/20/91
  * SUCH DAMAGE.
  *
  *     from: @(#)if_ethersubr.c        7.13 (Berkeley) 4/20/91
- *     $Id: if_ethersubr.c,v 1.3 1993/10/16 17:43:16 rgrimes Exp $
+ *     $Id: if_ethersubr.c,v 1.4 1993/11/25 01:34:02 wollman Exp $
  */
 
 #include "param.h"
  */
 
 #include "param.h"
@@ -110,22 +110,12 @@ ether_output(ifp, m0, dst, rt)
                idst = ((struct sockaddr_in *)dst)->sin_addr;
                if (!arpresolve(ac, m, &idst, edst, &usetrailers))
                        return (0);     /* if not yet resolved */
                idst = ((struct sockaddr_in *)dst)->sin_addr;
                if (!arpresolve(ac, m, &idst, edst, &usetrailers))
                        return (0);     /* if not yet resolved */
-               if ((ifp->if_flags & IFF_SIMPLEX) && (*edst & 1))
+               /* If broadcasting on a simplex interface, loopback a copy */
+               if ((m->m_flags & M_BCAST) && (ifp->if_flags & IFF_SIMPLEX))
                        mcopy = m_copy(m, 0, (int)M_COPYALL);
                off = m->m_pkthdr.len - m->m_len;
                        mcopy = m_copy(m, 0, (int)M_COPYALL);
                off = m->m_pkthdr.len - m->m_len;
-               if (usetrailers && off > 0 && (off & 0x1ff) == 0 &&
-                   (m->m_flags & M_EXT) == 0 &&
-                   m->m_data >= m->m_pktdat + 2 * sizeof (u_short)) {
-                       type = ETHERTYPE_TRAIL + (off>>9);
-                       m->m_data -= 2 * sizeof (u_short);
-                       m->m_len += 2 * sizeof (u_short);
-                       len += 2 * sizeof (u_short);
-                       *mtod(m, u_short *) = htons((u_short)ETHERTYPE_IP);
-                       *(mtod(m, u_short *) + 1) = htons((u_short)m->m_len);
-                       goto gottrailertype;
-               }
                type = ETHERTYPE_IP;
                type = ETHERTYPE_IP;
-               goto gottype;
+               break;
 #endif
 #ifdef NS
        case AF_NS:
 #endif
 #ifdef NS
        case AF_NS:
@@ -134,9 +124,10 @@ ether_output(ifp, m0, dst, rt)
                    (caddr_t)edst, sizeof (edst));
                if (!bcmp((caddr_t)edst, (caddr_t)&ns_thishost, sizeof(edst)))
                        return (looutput(ifp, m, dst, rt));
                    (caddr_t)edst, sizeof (edst));
                if (!bcmp((caddr_t)edst, (caddr_t)&ns_thishost, sizeof(edst)))
                        return (looutput(ifp, m, dst, rt));
-               if ((ifp->if_flags & IFF_SIMPLEX) && (*edst & 1))
+               /* If broadcasting on a simplex interface, loopback a copy */
+               if ((m->m_flags & M_BCAST) && (ifp->if_flags & IFF_SIMPLEX))
                        mcopy = m_copy(m, 0, (int)M_COPYALL);
                        mcopy = m_copy(m, 0, (int)M_COPYALL);
-               goto gottype;
+               break;
 #endif
 #ifdef ISO
        case AF_ISO: {
 #endif
 #ifdef ISO
        case AF_ISO: {
@@ -165,7 +156,8 @@ ether_output(ifp, m0, dst, rt)
                                        (char *)edst, &snpalen)) > 0)
                        goto bad; /* Not Resolved */
        iso_resolved:
                                        (char *)edst, &snpalen)) > 0)
                        goto bad; /* Not Resolved */
        iso_resolved:
-               if ((ifp->if_flags & IFF_SIMPLEX) && (*edst & 1) &&
+               /* If broadcasting on a simplex interface, loopback a copy */
+               if ((m->m_flags & M_BCAST) && (ifp->if_flags & IFF_SIMPLEX)
                    (mcopy = m_copy(m, 0, (int)M_COPYALL))) {
                        M_PREPEND(mcopy, sizeof (*eh), M_DONTWAIT);
                        if (mcopy) {
                    (mcopy = m_copy(m, 0, (int)M_COPYALL))) {
                        M_PREPEND(mcopy, sizeof (*eh), M_DONTWAIT);
                        if (mcopy) {
@@ -191,7 +183,8 @@ ether_output(ifp, m0, dst, rt)
                                printf("%x ", edst[i] & 0xff);
                        printf("\n");
                ENDDEBUG
                                printf("%x ", edst[i] & 0xff);
                        printf("\n");
                ENDDEBUG
-               } goto gottype;
+               }
+               break;
 #endif ISO
 #ifdef RMP
        case AF_RMP:
 #endif ISO
 #ifdef RMP
        case AF_RMP:
@@ -208,7 +201,7 @@ ether_output(ifp, m0, dst, rt)
                eh = (struct ether_header *)dst->sa_data;
                bcopy((caddr_t)eh->ether_dhost, (caddr_t)edst, sizeof (edst));
                type = eh->ether_type;
                eh = (struct ether_header *)dst->sa_data;
                bcopy((caddr_t)eh->ether_dhost, (caddr_t)edst, sizeof (edst));
                type = eh->ether_type;
-               goto gottype;
+               break;
 
        default:
                printf("%s%d: can't handle af%d\n", ifp->if_name, ifp->if_unit,
 
        default:
                printf("%s%d: can't handle af%d\n", ifp->if_name, ifp->if_unit,
@@ -217,18 +210,6 @@ ether_output(ifp, m0, dst, rt)
                goto bad;
        }
 
                goto bad;
        }
 
-gottrailertype:
-       /*
-        * Packet to be sent as trailer: move first packet
-        * (control information) to end of chain.
-        */
-       while (m->m_next)
-               m = m->m_next;
-       m->m_next = m0;
-       m = m0->m_next;
-       m0->m_next = 0;
-
-gottype:
        if (mcopy)
                (void) looutput(ifp, mcopy, dst, rt);
        /*
        if (mcopy)
                (void) looutput(ifp, mcopy, dst, rt);
        /*
@@ -266,7 +247,7 @@ gottype:
                (*ifp->if_start)(ifp);
        splx(s);
        ifp->if_obytes += len + sizeof (struct ether_header);
                (*ifp->if_start)(ifp);
        splx(s);
        ifp->if_obytes += len + sizeof (struct ether_header);
-       if (edst[0] & 1)
+       if (m->m_flags & M_MCAST)
                ifp->if_omcasts++;
        return (error);
 
                ifp->if_omcasts++;
        return (error);
 
@@ -293,11 +274,13 @@ ether_input(ifp, eh, m)
 
        ifp->if_lastchange = time;
        ifp->if_ibytes += m->m_pkthdr.len + sizeof (*eh);
 
        ifp->if_lastchange = time;
        ifp->if_ibytes += m->m_pkthdr.len + sizeof (*eh);
+       if (eh->ether_dhost[0] & 1) {
        if (bcmp((caddr_t)etherbroadcastaddr, (caddr_t)eh->ether_dhost,
            sizeof(etherbroadcastaddr)) == 0)
                m->m_flags |= M_BCAST;
        if (bcmp((caddr_t)etherbroadcastaddr, (caddr_t)eh->ether_dhost,
            sizeof(etherbroadcastaddr)) == 0)
                m->m_flags |= M_BCAST;
-       else if (eh->ether_dhost[0] & 1)
+               else
                m->m_flags |= M_MCAST;
                m->m_flags |= M_MCAST;
+       }
        if (m->m_flags & (M_BCAST|M_MCAST))
                ifp->if_imcasts++;
 
        if (m->m_flags & (M_BCAST|M_MCAST))
                ifp->if_imcasts++;
 
@@ -421,3 +404,178 @@ ether_sprintf(ap)
        *--cp = 0;
        return (etherbuf);
 }
        *--cp = 0;
        return (etherbuf);
 }
+
+#ifdef MULTICAST
+u_char ether_ipmulticast_min[6] = { 0x01, 0x00, 0x5e, 0x00, 0x00, 0x00 };
+u_char ether_ipmulticast_max[6] = { 0x01, 0x00, 0x5e, 0x7f, 0xff, 0xff };
+
+/* XXX */
+#undef ac
+/*
+ * Add an Ethernet multicast address or range of addresses to the list for a
+ * given interface.
+ */
+int
+ether_addmulti(ifr, ac)
+       struct ifreq *ifr;
+       register struct arpcom *ac;
+{
+       register struct ether_multi *enm;
+       struct sockaddr_in *sin;
+       u_char addrlo[6];
+       u_char addrhi[6];
+       int s = splimp();
+
+       switch (ifr->ifr_addr.sa_family) {
+
+       case AF_UNSPEC:
+               bcopy(ifr->ifr_addr.sa_data, addrlo, 6);
+               bcopy(addrlo, addrhi, 6);
+               break;
+
+#ifdef INET
+       case AF_INET:
+               sin = (struct sockaddr_in *)&(ifr->ifr_addr);
+               if (sin->sin_addr.s_addr == INADDR_ANY) {
+                       /*
+                        * An IP address of INADDR_ANY means listen to all
+                        * of the Ethernet multicast addresses used for IP.
+                        * (This is for the sake of IP multicast routers.)
+                        */
+                       bcopy(ether_ipmulticast_min, addrlo, 6);
+                       bcopy(ether_ipmulticast_max, addrhi, 6);
+               }
+               else {
+                       ETHER_MAP_IP_MULTICAST(&sin->sin_addr, addrlo);
+                       bcopy(addrlo, addrhi, 6);
+               }
+               break;
+#endif
+
+       default:
+               splx(s);
+               return (EAFNOSUPPORT);
+       }
+
+       /*
+        * Verify that we have valid Ethernet multicast addresses.
+        */
+       if ((addrlo[0] & 0x01) != 1 || (addrhi[0] & 0x01) != 1) {
+               splx(s);
+               return (EINVAL);
+       }
+       /*
+        * See if the address range is already in the list.
+        */
+       ETHER_LOOKUP_MULTI(addrlo, addrhi, ac, enm);
+       if (enm != NULL) {
+               /*
+                * Found it; just increment the reference count.
+                */
+               ++enm->enm_refcount;
+               splx(s);
+               return (0);
+       }
+       /*
+        * New address or range; malloc a new multicast record
+        * and link it into the interface's multicast list.
+        */
+       enm = (struct ether_multi *)malloc(sizeof(*enm), M_IFMADDR, M_NOWAIT);
+       if (enm == NULL) {
+               splx(s);
+               return (ENOBUFS);
+       }
+       bcopy(addrlo, enm->enm_addrlo, 6);
+       bcopy(addrhi, enm->enm_addrhi, 6);
+       enm->enm_ac = ac;
+       enm->enm_refcount = 1;
+       enm->enm_next = ac->ac_multiaddrs;
+       ac->ac_multiaddrs = enm;
+       ac->ac_multicnt++;
+       splx(s);
+       /*
+        * Return ENETRESET to inform the driver that the list has changed
+        * and its reception filter should be adjusted accordingly.
+        */
+       return (ENETRESET);
+}
+
+/*
+ * Delete a multicast address record.
+ */
+int
+ether_delmulti(ifr, ac)
+       struct ifreq *ifr;
+       register struct arpcom *ac;
+{
+       register struct ether_multi *enm;
+       register struct ether_multi **p;
+       struct sockaddr_in *sin;
+       u_char addrlo[6];
+       u_char addrhi[6];
+       int s = splimp();
+
+       switch (ifr->ifr_addr.sa_family) {
+
+       case AF_UNSPEC:
+               bcopy(ifr->ifr_addr.sa_data, addrlo, 6);
+               bcopy(addrlo, addrhi, 6);
+               break;
+
+#ifdef INET
+       case AF_INET:
+               sin = (struct sockaddr_in *)&(ifr->ifr_addr);
+               if (sin->sin_addr.s_addr == INADDR_ANY) {
+                       /*
+                        * An IP address of INADDR_ANY means stop listening
+                        * to the range of Ethernet multicast addresses used
+                        * for IP.
+                        */
+                       bcopy(ether_ipmulticast_min, addrlo, 6);
+                       bcopy(ether_ipmulticast_max, addrhi, 6);
+               }
+               else {
+                       ETHER_MAP_IP_MULTICAST(&sin->sin_addr, addrlo);
+                       bcopy(addrlo, addrhi, 6);
+               }
+               break;
+#endif
+
+       default:
+               splx(s);
+               return (EAFNOSUPPORT);
+       }
+
+       /*
+        * Look up the address in our list.
+        */
+       ETHER_LOOKUP_MULTI(addrlo, addrhi, ac, enm);
+       if (enm == NULL) {
+               splx(s);
+               return (ENXIO);
+       }
+       if (--enm->enm_refcount != 0) {
+               /*
+                * Still some claims to this record.
+                */
+               splx(s);
+               return (0);
+       }
+       /*
+        * No remaining claims to this record; unlink and free it.
+        */
+       for (p = &enm->enm_ac->ac_multiaddrs;
+            *p != enm;
+            p = &(*p)->enm_next)
+               continue;
+       *p = (*p)->enm_next;
+       free(enm, M_IFMADDR);
+       ac->ac_multicnt--;
+       splx(s);
+       /*
+        * Return ENETRESET to inform the driver that the list has changed
+        * and its reception filter should be adjusted accordingly.
+        */
+       return (ENETRESET);
+}
+#endif