Index: stable/4/sys/dev/cm/smc90cx6.c =================================================================== --- stable/4/sys/dev/cm/smc90cx6.c (revision 109979) +++ stable/4/sys/dev/cm/smc90cx6.c (revision 109980) @@ -1,1071 +1,1072 @@ /* $NetBSD: smc90cx6.c,v 1.38 2001/07/07 15:57:53 thorpej Exp $ */ /* $FreeBSD$ */ /*- * Copyright (c) 1994, 1995, 1998 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Ignatios Souvatzis. * * 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 NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation 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 BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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. */ /* * Chip core driver for the SMC90c26 / SMC90c56 (and SMC90c66 in '56 * compatibility mode) boards */ /* #define CMSOFTCOPY */ #define CMRETRANSMIT /**/ -#undef CM_DEBUG +/* #define CM_DEBUG */ #include #include #include #include #include #include #include #include #include #include #include #include #if __FreeBSD_version < 500000 #include #endif #include #include #include #include #include #if 0 #if NBPFILTER > 0 #include #endif #endif #include #include MODULE_DEPEND(if_cm, arcnet, 1, 1, 1); /* these should be elsewhere */ #define ARC_MIN_LEN 1 #define ARC_MIN_FORBID_LEN 254 #define ARC_MAX_FORBID_LEN 256 #define ARC_MAX_LEN 508 #define ARC_ADDR_LEN 1 /* for watchdog timer. This should be more than enough. */ #define ARCTIMEOUT (5*IFNET_SLOWHZ) /* short notation */ #define GETREG(off) \ bus_space_read_1(rman_get_bustag((sc)->port_res), \ rman_get_bushandle((sc)->port_res), \ (off)) #define PUTREG(off, value) \ bus_space_write_1(rman_get_bustag((sc)->port_res), \ rman_get_bushandle((sc)->port_res), \ (off), (value)) #define GETMEM(off) \ bus_space_read_1(rman_get_bustag((sc)->mem_res), \ rman_get_bushandle((sc)->mem_res), \ (off)) #define PUTMEM(off, value) \ bus_space_write_1(rman_get_bustag((sc)->mem_res), \ rman_get_bushandle((sc)->mem_res), \ (off), (value)) devclass_t cm_devclass; /* * This currently uses 2 bufs for tx, 2 for rx * * New rx protocol: * * rx has a fillcount variable. If fillcount > (NRXBUF-1), * rx can be switched off from rx hard int. * Else rx is restarted on the other receiver. * rx soft int counts down. if it is == (NRXBUF-1), it restarts * the receiver. * To ensure packet ordering (we need that for 1201 later), we have a counter * which is incremented modulo 256 on each receive and a per buffer * variable, which is set to the counter on filling. The soft int can * compare both values to determine the older packet. * * Transmit direction: * * cm_start checks tx_fillcount * case 2: return * * else fill tx_act ^ 1 && inc tx_fillcount * * check tx_fillcount again. * case 2: set IFF_OACTIVE to stop arc_output from filling us. * case 1: start tx * * tint clears IFF_OCATIVE, decrements and checks tx_fillcount * case 1: start tx on tx_act ^ 1, softcall cm_start * case 0: softcall cm_start * * #define fill(i) get mbuf && copy mbuf to chip(i) */ void cm_init __P((void *)); void cm_reset __P((struct cm_softc *)); void cm_start __P((struct ifnet *)); int cm_ioctl __P((struct ifnet *, unsigned long, caddr_t)); void cm_watchdog __P((struct ifnet *)); void cm_srint __P((void *vsc)); static void cm_tint __P((struct cm_softc *, int)); void cm_reconwatch(void *); int cm_probe(dev) device_t dev; { int error; struct cm_softc *sc = device_get_softc(dev); error = cm_alloc_port(dev, 0, CM_IO_PORTS); if (error) return error; if (GETREG(CMSTAT) == 0xff) return ENXIO; error = cm_alloc_memory(dev, 0, 0x800); if (error) return error; return 0; } /* * Allocate a port resource with the given resource id. */ int cm_alloc_port(dev, rid, size) device_t dev; int rid; int size; { struct cm_softc *sc = device_get_softc(dev); struct resource *res; res = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0ul, ~0ul, size, RF_ACTIVE); if (res) { sc->port_rid = rid; sc->port_res = res; sc->port_used = size; return (0); } else { return (ENOENT); } } /* * Allocate a memory resource with the given resource id. */ int cm_alloc_memory(dev, rid, size) device_t dev; int rid; int size; { struct cm_softc *sc = device_get_softc(dev); struct resource *res; res = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, 0ul, ~0ul, size, RF_ACTIVE); if (res) { sc->mem_rid = rid; sc->mem_res = res; sc->mem_used = size; return (0); } else { return (ENOENT); } } /* * Allocate an irq resource with the given resource id. */ int cm_alloc_irq(dev, rid) device_t dev; int rid; { struct cm_softc *sc = device_get_softc(dev); struct resource *res; res = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0ul, ~0ul, 1, RF_ACTIVE); if (res) { sc->irq_rid = rid; sc->irq_res = res; return (0); } else { return (ENOENT); } } /* * Release all resources */ void cm_release_resources(dev) device_t dev; { struct cm_softc *sc = device_get_softc(dev); if (sc->port_res) { bus_deactivate_resource(dev, SYS_RES_IOPORT, sc->port_rid, sc->port_res); bus_release_resource(dev, SYS_RES_IOPORT, sc->port_rid, sc->port_res); sc->port_res = 0; } if (sc->mem_res) { bus_deactivate_resource(dev, SYS_RES_MEMORY, sc->mem_rid, sc->mem_res); bus_release_resource(dev, SYS_RES_MEMORY, sc->mem_rid, sc->mem_res); sc->mem_res = 0; } if (sc->irq_res) { bus_deactivate_resource(dev, SYS_RES_IRQ, sc->irq_rid, sc->irq_res); bus_release_resource(dev, SYS_RES_IRQ, sc->irq_rid, sc->irq_res); sc->irq_res = 0; } } int cm_attach(sc, unit) struct cm_softc *sc; int unit; { struct ifnet *ifp = &sc->sc_arccom.ac_if; int s; u_int8_t linkaddress; s = splhigh(); /* * read the arcnet address from the board */ GETREG(CMRESET); do { DELAY(200); } while (!(GETREG(CMSTAT) & CM_POR)); linkaddress = GETMEM(CMMACOFF); /* clear the int mask... */ sc->sc_intmask = 0; PUTREG(CMSTAT, 0); PUTREG(CMCMD, CM_CONF(CONF_LONG)); PUTREG(CMCMD, CM_CLR(CLR_POR|CLR_RECONFIG)); sc->sc_recontime = sc->sc_reconcount = 0; /* and reenable kernel int level */ splx(s); /* * set interface to stopped condition (reset) */ cm_stop(sc); if (!ifp->if_name) { ifp->if_softc = sc; ifp->if_unit = unit; ifp->if_name = "cm"; ifp->if_output = arc_output; ifp->if_start = cm_start; ifp->if_ioctl = cm_ioctl; ifp->if_watchdog = cm_watchdog; ifp->if_init = cm_init; /* XXX IFQ_SET_READY(&ifp->if_snd); */ ifp->if_snd.ifq_maxlen = IFQ_MAXLEN; ifp->if_timer = 0; ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX; arc_ifattach(ifp, linkaddress); #ifdef CMSOFTCOPY sc->sc_rxcookie = softintr_establish(IPL_SOFTNET, cm_srint, sc); sc->sc_txcookie = softintr_establish(IPL_SOFTNET, (void (*) __P((void *)))cm_start, ifp); #endif #if __FreeBSD_version < 500000 callout_init(&sc->sc_recon_ch); #else callout_init(&sc->sc_recon_ch, 0); #endif } - printf("%s%d: link addr 0x%02x(%d)\n", + printf("%s%d: link addr 0x%02x (%d)\n", ifp->if_name, ifp->if_unit, linkaddress, linkaddress); return 0; } /* * Initialize device * */ void cm_init(xsc) void *xsc; { struct cm_softc *sc = (struct cm_softc *)xsc; struct ifnet *ifp; int s; ifp = &sc->sc_arccom.ac_if; if ((ifp->if_flags & IFF_RUNNING) == 0) { s = splimp(); ifp->if_flags |= IFF_RUNNING; cm_reset(sc); cm_start(ifp); splx(s); } } /* * Reset the interface... * * this assumes that it is called inside a critical section... * */ void cm_reset(sc) struct cm_softc *sc; { struct ifnet *ifp; int linkaddress; ifp = &sc->sc_arccom.ac_if; #ifdef CM_DEBUG printf("%s%d: reset\n", ifp->if_name, ifp->if_unit); #endif /* stop and restart hardware */ GETREG(CMRESET); do { DELAY(200); } while (!(GETREG(CMSTAT) & CM_POR)); linkaddress = GETMEM(CMMACOFF); #if defined(CM_DEBUG) && (CM_DEBUG > 2) printf("%s%d: reset: card reset, link addr = 0x%02x (%d)\n", ifp->if_name, ifp->if_unit, linkaddress, linkaddress); #endif /* tell the routing level about the (possibly changed) link address */ arc_storelladdr(ifp, linkaddress); arc_frag_init(ifp); /* POR is NMI, but we need it below: */ sc->sc_intmask = CM_RECON|CM_POR; PUTREG(CMSTAT, sc->sc_intmask); PUTREG(CMCMD, CM_CONF(CONF_LONG)); #ifdef CM_DEBUG printf("%s%d: reset: chip configured, status=0x%02x\n", ifp->if_name, ifp->if_unit, GETREG(CMSTAT)); #endif PUTREG(CMCMD, CM_CLR(CLR_POR|CLR_RECONFIG)); #ifdef CM_DEBUG printf("%s%d: reset: bits cleared, status=0x%02x\n", ifp->if_name, ifp->if_unit, GETREG(CMSTAT)); #endif sc->sc_reconcount_excessive = ARC_EXCESSIVE_RECONS; /* start receiver */ sc->sc_intmask |= CM_RI; sc->sc_rx_fillcount = 0; sc->sc_rx_act = 2; PUTREG(CMCMD, CM_RXBC(2)); PUTREG(CMSTAT, sc->sc_intmask); #ifdef CM_DEBUG printf("%s%d: reset: started receiver, status=0x%02x\n", ifp->if_name, ifp->if_unit, GETREG(CMSTAT)); #endif /* and init transmitter status */ sc->sc_tx_act = 0; sc->sc_tx_fillcount = 0; ifp->if_flags |= IFF_RUNNING; ifp->if_flags &= ~IFF_OACTIVE; cm_start(ifp); } /* * Take interface offline */ void cm_stop(sc) struct cm_softc *sc; { /* Stop the interrupts */ PUTREG(CMSTAT, 0); /* Stop the interface */ GETREG(CMRESET); /* Stop watchdog timer */ sc->sc_arccom.ac_if.if_timer = 0; } /* * Start output on interface. Get another datagram to send * off the interface queue, and copy it to the * interface becore starting the output * * this assumes that it is called inside a critical section... * XXX hm... does it still? * */ void cm_start(ifp) struct ifnet *ifp; { struct cm_softc *sc = ifp->if_softc; struct mbuf *m,*mp; int cm_ram_ptr; int len, tlen, offset, s, buffer; #ifdef CMTIMINGS u_long copystart, lencopy, perbyte; #endif #if defined(CM_DEBUG) && (CM_DEBUG > 3) printf("%s%d: start(%p)\n", ifp->if_name, ifp->if_unit, ifp); #endif if ((ifp->if_flags & IFF_RUNNING) == 0) return; s = splimp(); if (sc->sc_tx_fillcount >= 2) { splx(s); return; } m = arc_frag_next(ifp); buffer = sc->sc_tx_act ^ 1; splx(s); if (m == 0) return; /* * If bpf is listening on this interface, let it * see the packet before we commit it to the wire * * (can't give the copy in A2060 card RAM to bpf, because * that RAM is just accessed as on every other byte) */ if (ifp->if_bpf) bpf_mtap(ifp, m); #ifdef CM_DEBUG if (m->m_len < ARC_HDRLEN) m = m_pullup(m, ARC_HDRLEN);/* gcc does structure padding */ printf("%s%d: start: filling %d from %d to %d type %d\n", ifp->if_name, ifp->if_unit, buffer, mtod(m, u_char *)[0], mtod(m, u_char *)[1], mtod(m, u_char *)[2]); #else if (m->m_len < 2) m = m_pullup(m, 2); #endif - cm_ram_ptr = buffer*512; + cm_ram_ptr = buffer * 512; if (m == 0) return; /* write the addresses to RAM and throw them away */ /* * Hardware does this: Yet Another Microsecond Saved. * (btw, timing code says usually 2 microseconds) * PUTMEM(cm_ram_ptr + 0, mtod(m, u_char *)[0]); */ PUTMEM(cm_ram_ptr + 1, mtod(m, u_char *)[1]); m_adj(m, 2); /* get total length left at this point */ tlen = m->m_pkthdr.len; if (tlen < ARC_MIN_FORBID_LEN) { offset = 256 - tlen; PUTMEM(cm_ram_ptr + 2, offset); } else { PUTMEM(cm_ram_ptr + 2, 0); if (tlen <= ARC_MAX_FORBID_LEN) offset = 255; /* !!! */ else { if (tlen > ARC_MAX_LEN) tlen = ARC_MAX_LEN; offset = 512 - tlen; } PUTMEM(cm_ram_ptr + 3, offset); } cm_ram_ptr += offset; /* lets loop through the mbuf chain */ for (mp = m; mp; mp = mp->m_next) { if ((len = mp->m_len)) { /* YAMS */ bus_space_write_region_1( rman_get_bustag(sc->mem_res), rman_get_bushandle(sc->mem_res), cm_ram_ptr, mtod(mp, caddr_t), len); cm_ram_ptr += len; } } sc->sc_broadcast[buffer] = (m->m_flags & M_BCAST) != 0; sc->sc_retransmits[buffer] = (m->m_flags & M_BCAST) ? 1 : 5; /* actually transmit the packet */ s = splimp(); if (++sc->sc_tx_fillcount > 1) { /* * We are filled up to the rim. No more bufs for the moment, * please. */ ifp->if_flags |= IFF_OACTIVE; } else { #ifdef CM_DEBUG printf("%s%d: start: starting transmitter on buffer %d\n", ifp->if_name, ifp->if_unit, buffer); #endif /* Transmitter was off, start it */ sc->sc_tx_act = buffer; /* * We still can accept another buf, so don't: * ifp->if_flags |= IFF_OACTIVE; */ sc->sc_intmask |= CM_TA; PUTREG(CMCMD, CM_TX(buffer)); PUTREG(CMSTAT, sc->sc_intmask); sc->sc_arccom.ac_if.if_timer = ARCTIMEOUT; } splx(s); m_freem(m); /* * After 10 times reading the docs, I realized * that in the case the receiver NAKs the buffer request, * the hardware retries till shutdown. * This is integrated now in the code above. */ return; } /* * Arcnet interface receiver soft interrupt: * get the stuff out of any filled buffer we find. */ void cm_srint(vsc) void *vsc; { struct cm_softc *sc = (struct cm_softc *)vsc; int buffer, len, offset, s, type; int cm_ram_ptr; struct mbuf *m; struct arc_header *ah; struct ifnet *ifp; ifp = &sc->sc_arccom.ac_if; s = splimp(); buffer = sc->sc_rx_act ^ 1; splx(s); /* Allocate header mbuf */ MGETHDR(m, M_DONTWAIT, MT_DATA); if (m == 0) { /* * in case s.th. goes wrong with mem, drop it * to make sure the receiver can be started again * count it as input error (we dont have any other * detectable) */ ifp->if_ierrors++; goto cleanup; } m->m_pkthdr.rcvif = ifp; /* * Align so that IP packet will be longword aligned. Here we * assume that m_data of new packet is longword aligned. * When implementing PHDS, we might have to change it to 2, * (2*sizeof(ulong) - CM_HDRNEWLEN)), packet type dependent. */ - cm_ram_ptr = buffer*512; + cm_ram_ptr = buffer * 512; offset = GETMEM(cm_ram_ptr + 2); if (offset) len = 256 - offset; else { offset = GETMEM(cm_ram_ptr + 3); len = 512 - offset; } /* * first +2 bytes for align fixup below * second +2 bytes are for src/dst addresses */ if ((len + 2 + 2) > MHLEN) { /* attach an mbuf cluster */ MCLGET(m, M_DONTWAIT); /* Insist on getting a cluster */ if ((m->m_flags & M_EXT) == 0) { ifp->if_ierrors++; goto cleanup; } } if (m == 0) { ifp->if_ierrors++; goto cleanup; } type = GETMEM(cm_ram_ptr + offset); m->m_data += 1 + arc_isphds(type); /* mbuf filled with ARCnet addresses */ m->m_pkthdr.len = m->m_len = len + 2; ah = mtod(m, struct arc_header *); ah->arc_shost = GETMEM(cm_ram_ptr + 0); ah->arc_dhost = GETMEM(cm_ram_ptr + 1); bus_space_read_region_1( rman_get_bustag(sc->mem_res), rman_get_bushandle(sc->mem_res), cm_ram_ptr + offset, mtod(m, u_char *) + 2, len); arc_input(ifp, m); m = NULL; ifp->if_ipackets++; cleanup: if (m != NULL) m_freem(m); /* mark buffer as invalid by source id 0 */ PUTMEM(buffer << 9, 0); s = splimp(); if (--sc->sc_rx_fillcount == 2 - 1) { /* was off, restart it on buffer just emptied */ sc->sc_rx_act = buffer; sc->sc_intmask |= CM_RI; /* this also clears the RI flag interupt: */ PUTREG(CMCMD, CM_RXBC(buffer)); PUTREG(CMSTAT, sc->sc_intmask); #ifdef CM_DEBUG printf("%s%d: srint: restarted rx on buf %d\n", ifp->if_name, ifp->if_unit, buffer); #endif } splx(s); } __inline static void cm_tint(sc, isr) struct cm_softc *sc; int isr; { struct ifnet *ifp; int buffer; #ifdef CMTIMINGS int clknow; #endif ifp = &(sc->sc_arccom.ac_if); buffer = sc->sc_tx_act; /* * retransmit code: * Normal situtations first for fast path: * If acknowledgement received ok or broadcast, we're ok. * else if */ if (isr & CM_TMA || sc->sc_broadcast[buffer]) sc->sc_arccom.ac_if.if_opackets++; #ifdef CMRETRANSMIT else if (ifp->if_flags & IFF_LINK2 && ifp->if_timer > 0 && --sc->sc_retransmits[buffer] > 0) { /* retransmit same buffer */ PUTREG(CMCMD, CM_TX(buffer)); return; } #endif else ifp->if_oerrors++; /* We know we can accept another buffer at this point. */ ifp->if_flags &= ~IFF_OACTIVE; if (--sc->sc_tx_fillcount > 0) { /* * start tx on other buffer. * This also clears the int flag */ buffer ^= 1; sc->sc_tx_act = buffer; /* * already given: * sc->sc_intmask |= CM_TA; * PUTREG(CMSTAT, sc->sc_intmask); */ PUTREG(CMCMD, CM_TX(buffer)); /* init watchdog timer */ ifp->if_timer = ARCTIMEOUT; #if defined(CM_DEBUG) && (CM_DEBUG > 1) printf("%s%d: tint: starting tx on buffer %d, status 0x%02x\n", ifp->if_name, ifp->if_unit, buffer, GETREG(CMSTAT)); #endif } else { /* have to disable TX interrupt */ sc->sc_intmask &= ~CM_TA; PUTREG(CMSTAT, sc->sc_intmask); /* ... and watchdog timer */ ifp->if_timer = 0; #ifdef CM_DEBUG printf("%s%d: tint: no more buffers to send, status 0x%02x\n", ifp->if_name, ifp->if_unit, GETREG(CMSTAT)); #endif } /* XXXX TODO */ #ifdef CMSOFTCOPY /* schedule soft int to fill a new buffer for us */ softintr_schedule(sc->sc_txcookie); #else /* call it directly */ cm_start(ifp); #endif } /* * Our interrupt routine */ void cmintr(arg) void *arg; { struct cm_softc *sc = arg; struct ifnet *ifp = &sc->sc_arccom.ac_if; u_char isr, maskedisr; int buffer; u_long newsec; isr = GETREG(CMSTAT); maskedisr = isr & sc->sc_intmask; if (!maskedisr) return; do { -#if defined(CM_DEBUG) && (CM_DEBUG>1) +#if defined(CM_DEBUG) && (CM_DEBUG > 1) printf("%s%d: intr: status 0x%02x, intmask 0x%02x\n", ifp->if_name, ifp->if_unit, isr, sc->sc_intmask); #endif if (maskedisr & CM_POR) { /* * XXX We should never see this. Don't bother to store * the address. * sc->sc_arccom.ac_anaddr = GETMEM(CMMACOFF); */ PUTREG(CMCMD, CM_CLR(CLR_POR)); log(LOG_WARNING, "%s%d: intr: got spurious power on reset int\n", ifp->if_name, ifp->if_unit); } if (maskedisr & CM_RECON) { /* * we dont need to: * PUTREG(CMCMD, CM_CONF(CONF_LONG)); */ PUTREG(CMCMD, CM_CLR(CLR_RECONFIG)); sc->sc_arccom.ac_if.if_collisions++; /* * If less than 2 seconds per reconfig: * If ARC_EXCESSIVE_RECONFIGS * since last burst, complain and set treshold for * warnings to ARC_EXCESSIVE_RECONS_REWARN. * * This allows for, e.g., new stations on the cable, or * cable switching as long as it is over after * (normally) 16 seconds. * * XXX TODO: check timeout bits in status word and * double time if necessary. */ callout_stop(&sc->sc_recon_ch); newsec = time_second; if ((newsec - sc->sc_recontime <= 2) && (++sc->sc_reconcount == ARC_EXCESSIVE_RECONS)) { log(LOG_WARNING, "%s%d: excessive token losses, " "cable problem?\n", ifp->if_name, ifp->if_unit); } sc->sc_recontime = newsec; callout_reset(&sc->sc_recon_ch, 15 * hz, cm_reconwatch, (void *)sc); } if (maskedisr & CM_RI) { #if defined(CM_DEBUG) && (CM_DEBUG > 1) printf("%s%d: intr: hard rint, act %d\n", ifp->if_name, ifp->if_unit, sc->sc_rx_act); #endif buffer = sc->sc_rx_act; /* look if buffer is marked invalid: */ - if (GETMEM(buffer*512) == 0) { + if (GETMEM(buffer * 512) == 0) { /* * invalid marked buffer (or illegally * configured sender) */ log(LOG_WARNING, "%s%d: spurious RX interupt or sender 0 " " (ignored)\n", ifp->if_name, ifp->if_unit); /* * restart receiver on same buffer. * XXX maybe better reset interface? */ PUTREG(CMCMD, CM_RXBC(buffer)); } else { if (++sc->sc_rx_fillcount > 1) { sc->sc_intmask &= ~CM_RI; PUTREG(CMSTAT, sc->sc_intmask); } else { buffer ^= 1; sc->sc_rx_act = buffer; /* * Start receiver on other receive * buffer. This also clears the RI * interupt flag. */ PUTREG(CMCMD, CM_RXBC(buffer)); /* in RX intr, so mask is ok for RX */ #ifdef CM_DEBUG printf("%s%d: strt rx for buf %d, " "stat 0x%02x\n", ifp->if_name, ifp->if_unit, sc->sc_rx_act, GETREG(CMSTAT)); #endif } #ifdef CMSOFTCOPY /* * this one starts a soft int to copy out * of the hw */ softintr_schedule(sc->sc_rxcookie); #else /* this one does the copy here */ cm_srint(sc); #endif } } if (maskedisr & CM_TA) { cm_tint(sc, isr); } isr = GETREG(CMSTAT); maskedisr = isr & sc->sc_intmask; } while (maskedisr); -#if defined(CM_DEBUG) && (CM_DEBUG>1) +#if defined(CM_DEBUG) && (CM_DEBUG > 1) printf("%s%d: intr (exit): status 0x%02x, intmask 0x%02x\n", ifp->if_name, ifp->if_unit, isr, sc->sc_intmask); #endif } void cm_reconwatch(arg) void *arg; { struct cm_softc *sc = arg; struct ifnet *ifp = &sc->sc_arccom.ac_if; if (sc->sc_reconcount >= ARC_EXCESSIVE_RECONS) { sc->sc_reconcount = 0; log(LOG_WARNING, "%s%d: token valid again.\n", ifp->if_name, ifp->if_unit); } sc->sc_reconcount = 0; } /* * Process an ioctl request. * This code needs some work - it looks pretty ugly. */ int cm_ioctl(ifp, command, data) struct ifnet *ifp; u_long command; caddr_t data; { struct cm_softc *sc; struct ifaddr *ifa; struct ifreq *ifr; int s, error; error = 0; sc = ifp->if_softc; ifa = (struct ifaddr *)data; ifr = (struct ifreq *)data; s = splimp(); #if defined(CM_DEBUG) && (CM_DEBUG > 2) printf("%s%d: ioctl() called, cmd = 0x%lx\n", ifp->if_name, ifp->if_unit, command); #endif switch (command) { case SIOCSIFADDR: + case SIOCGIFADDR: case SIOCADDMULTI: case SIOCDELMULTI: case SIOCSIFMTU: error = arc_ioctl(ifp, command, data); break; case SIOCSIFFLAGS: if ((ifp->if_flags & IFF_UP) == 0 && (ifp->if_flags & IFF_RUNNING) != 0) { /* * If interface is marked down and it is running, * then stop it. */ cm_stop(sc); ifp->if_flags &= ~IFF_RUNNING; } else if ((ifp->if_flags & IFF_UP) != 0 && (ifp->if_flags & IFF_RUNNING) == 0) { /* * If interface is marked up and it is stopped, then * start it. */ cm_init(sc); } break; default: error = EINVAL; break; } splx(s); return (error); } /* * watchdog routine for transmitter. * * We need this, because else a receiver whose hardware is alive, but whose * software has not enabled the Receiver, would make our hardware wait forever * Discovered this after 20 times reading the docs. * * Only thing we do is disable transmitter. We'll get an transmit timeout, * and the int handler will have to decide not to retransmit (in case * retransmission is implemented). * * This one assumes being called inside splimp() */ void cm_watchdog(ifp) struct ifnet *ifp; { struct cm_softc *sc = ifp->if_softc; PUTREG(CMCMD, CM_TXDIS); return; } Index: stable/4/sys/net/if.c =================================================================== --- stable/4/sys/net/if.c (revision 109979) +++ stable/4/sys/net/if.c (revision 109980) @@ -1,1651 +1,1653 @@ /* * Copyright (c) 1980, 1986, 1993 * The Regents of the University of California. All rights reserved. * * 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. * * 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. * * @(#)if.c 8.3 (Berkeley) 1/4/94 * $FreeBSD$ */ #include "opt_compat.h" #include "opt_inet6.h" #include "opt_inet.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(INET) || defined(INET6) /*XXX*/ #include #include #include #ifdef INET6 #include /* XXX: temporal workaround for fxp issue */ #include #include #endif #endif /* * System initialization */ static int ifconf __P((u_long, caddr_t)); static void ifinit __P((void *)); static void if_qflush __P((struct ifqueue *)); static void if_slowtimo __P((void *)); static void link_rtrequest __P((int, struct rtentry *, struct rt_addrinfo *)); static int if_rtdel __P((struct radix_node *, void *)); SYSINIT(interfaces, SI_SUB_PROTO_IF, SI_ORDER_FIRST, ifinit, NULL) MALLOC_DEFINE(M_IFADDR, "ifaddr", "interface address"); MALLOC_DEFINE(M_IFMADDR, "ether_multi", "link-level multicast address"); int ifqmaxlen = IFQ_MAXLEN; struct ifnethead ifnet; /* depend on static init XXX */ #ifdef INET6 /* * XXX: declare here to avoid to include many inet6 related files.. * should be more generalized? */ extern void nd6_setmtu __P((struct ifnet *)); #endif struct if_clone *if_clone_lookup __P((const char *, int *)); int if_clone_list __P((struct if_clonereq *)); LIST_HEAD(, if_clone) if_cloners = LIST_HEAD_INITIALIZER(if_cloners); int if_cloners_count; /* * Network interface utility routines. * * Routines with ifa_ifwith* names take sockaddr *'s as * parameters. */ /* ARGSUSED*/ void ifinit(dummy) void *dummy; { struct ifnet *ifp; int s; s = splimp(); TAILQ_FOREACH(ifp, &ifnet, if_link) { if (ifp->if_snd.ifq_maxlen == 0) { printf("%s%d XXX: driver didn't set ifq_maxlen\n", ifp->if_name, ifp->if_unit); ifp->if_snd.ifq_maxlen = ifqmaxlen; } } splx(s); if_slowtimo(0); } int if_index = 0; struct ifaddr **ifnet_addrs; struct ifnet **ifindex2ifnet = NULL; /* * Attach an interface to the * list of "active" interfaces. */ void if_attach(ifp) struct ifnet *ifp; { unsigned socksize, ifasize; int namelen, masklen; char workbuf[64]; register struct sockaddr_dl *sdl; register struct ifaddr *ifa; static int if_indexlim = 8; static int inited; if (!inited) { TAILQ_INIT(&ifnet); inited = 1; } TAILQ_INSERT_TAIL(&ifnet, ifp, if_link); ifp->if_index = ++if_index; /* * XXX - * The old code would work if the interface passed a pre-existing * chain of ifaddrs to this code. We don't trust our callers to * properly initialize the tailq, however, so we no longer allow * this unlikely case. */ TAILQ_INIT(&ifp->if_addrhead); TAILQ_INIT(&ifp->if_prefixhead); LIST_INIT(&ifp->if_multiaddrs); getmicrotime(&ifp->if_lastchange); if (ifnet_addrs == 0 || if_index >= if_indexlim) { unsigned n = (if_indexlim <<= 1) * sizeof(ifa); caddr_t q = malloc(n, M_IFADDR, M_WAITOK); bzero(q, n); if (ifnet_addrs) { bcopy((caddr_t)ifnet_addrs, (caddr_t)q, n/2); free((caddr_t)ifnet_addrs, M_IFADDR); } ifnet_addrs = (struct ifaddr **)q; /* grow ifindex2ifnet */ n = if_indexlim * sizeof(struct ifnet *); q = malloc(n, M_IFADDR, M_WAITOK); bzero(q, n); if (ifindex2ifnet) { bcopy((caddr_t)ifindex2ifnet, q, n/2); free((caddr_t)ifindex2ifnet, M_IFADDR); } ifindex2ifnet = (struct ifnet **)q; } ifindex2ifnet[if_index] = ifp; /* * create a Link Level name for this device */ namelen = snprintf(workbuf, sizeof(workbuf), "%s%d", ifp->if_name, ifp->if_unit); #define _offsetof(t, m) ((int)((caddr_t)&((t *)0)->m)) masklen = _offsetof(struct sockaddr_dl, sdl_data[0]) + namelen; socksize = masklen + ifp->if_addrlen; #define ROUNDUP(a) (1 + (((a) - 1) | (sizeof(long) - 1))) if (socksize < sizeof(*sdl)) socksize = sizeof(*sdl); socksize = ROUNDUP(socksize); ifasize = sizeof(*ifa) + 2 * socksize; ifa = (struct ifaddr *)malloc(ifasize, M_IFADDR, M_WAITOK); if (ifa) { bzero((caddr_t)ifa, ifasize); sdl = (struct sockaddr_dl *)(ifa + 1); sdl->sdl_len = socksize; sdl->sdl_family = AF_LINK; bcopy(workbuf, sdl->sdl_data, namelen); sdl->sdl_nlen = namelen; sdl->sdl_index = ifp->if_index; sdl->sdl_type = ifp->if_type; ifnet_addrs[if_index - 1] = ifa; ifa->ifa_ifp = ifp; ifa->ifa_rtrequest = link_rtrequest; ifa->ifa_addr = (struct sockaddr *)sdl; sdl = (struct sockaddr_dl *)(socksize + (caddr_t)sdl); ifa->ifa_netmask = (struct sockaddr *)sdl; sdl->sdl_len = masklen; while (namelen != 0) sdl->sdl_data[--namelen] = 0xff; TAILQ_INSERT_HEAD(&ifp->if_addrhead, ifa, ifa_link); } /* Announce the interface. */ rt_ifannouncemsg(ifp, IFAN_ARRIVAL); } /* * Detach an interface, removing it from the * list of "active" interfaces. */ void if_detach(ifp) struct ifnet *ifp; { struct ifaddr *ifa; struct radix_node_head *rnh; int s; int i; /* * Remove routes and flush queues. */ s = splnet(); if_down(ifp); /* * Remove address from ifnet_addrs[] and maybe decrement if_index. * Clean up all addresses. */ ifnet_addrs[ifp->if_index - 1] = 0; while (if_index > 0 && ifnet_addrs[if_index - 1] == 0) if_index--; for (ifa = TAILQ_FIRST(&ifp->if_addrhead); ifa; ifa = TAILQ_FIRST(&ifp->if_addrhead)) { #ifdef INET /* XXX: Ugly!! ad hoc just for INET */ if (ifa->ifa_addr && ifa->ifa_addr->sa_family == AF_INET) { struct ifaliasreq ifr; bzero(&ifr, sizeof(ifr)); ifr.ifra_addr = *ifa->ifa_addr; if (ifa->ifa_dstaddr) ifr.ifra_broadaddr = *ifa->ifa_dstaddr; if (in_control(NULL, SIOCDIFADDR, (caddr_t)&ifr, ifp, NULL) == 0) continue; } #endif /* INET */ #ifdef INET6 if (ifa->ifa_addr && ifa->ifa_addr->sa_family == AF_INET6) { in6_purgeaddr(ifa); /* ifp_addrhead is already updated */ continue; } #endif /* INET6 */ TAILQ_REMOVE(&ifp->if_addrhead, ifa, ifa_link); IFAFREE(ifa); } #ifdef INET6 /* * Remove all IPv6 kernel structs related to ifp. This should be done * before removing routing entries below, since IPv6 interface direct * routes are expected to be removed by the IPv6-specific kernel API. * Otherwise, the kernel will detect some inconsistency and bark it. */ in6_ifdetach(ifp); #endif /* * Delete all remaining routes using this interface * Unfortuneatly the only way to do this is to slog through * the entire routing table looking for routes which point * to this interface...oh well... */ for (i = 1; i <= AF_MAX; i++) { if ((rnh = rt_tables[i]) == NULL) continue; (void) rnh->rnh_walktree(rnh, if_rtdel, ifp); } /* Announce that the interface is gone. */ rt_ifannouncemsg(ifp, IFAN_DEPARTURE); TAILQ_REMOVE(&ifnet, ifp, if_link); splx(s); } /* * Delete Routes for a Network Interface * * Called for each routing entry via the rnh->rnh_walktree() call above * to delete all route entries referencing a detaching network interface. * * Arguments: * rn pointer to node in the routing table * arg argument passed to rnh->rnh_walktree() - detaching interface * * Returns: * 0 successful * errno failed - reason indicated * */ static int if_rtdel(rn, arg) struct radix_node *rn; void *arg; { struct rtentry *rt = (struct rtentry *)rn; struct ifnet *ifp = arg; int err; if (rt->rt_ifp == ifp) { /* * Protect (sorta) against walktree recursion problems * with cloned routes */ if ((rt->rt_flags & RTF_UP) == 0) return (0); err = rtrequest(RTM_DELETE, rt_key(rt), rt->rt_gateway, rt_mask(rt), rt->rt_flags, (struct rtentry **) NULL); if (err) { log(LOG_WARNING, "if_rtdel: error %d\n", err); } } return (0); } /* * Create a clone network interface. */ int if_clone_create(name, len) char *name; int len; { struct if_clone *ifc; char *dp; int wildcard; int unit; int err; ifc = if_clone_lookup(name, &unit); if (ifc == NULL) return (EINVAL); if (ifunit(name) != NULL) return (EEXIST); wildcard = (unit < 0); err = (*ifc->ifc_create)(ifc, &unit); if (err != 0) return (err); /* In the wildcard case, we need to update the name. */ if (wildcard) { for (dp = name; *dp != '\0'; dp++); if (snprintf(dp, len - (dp-name), "%d", unit) > len - (dp-name) - 1) { /* * This can only be a programmer error and * there's no straightforward way to recover if * it happens. */ panic("if_clone_create(): interface name too long"); } } return (0); } /* * Destroy a clone network interface. */ int if_clone_destroy(name) const char *name; { struct if_clone *ifc; struct ifnet *ifp; ifc = if_clone_lookup(name, NULL); if (ifc == NULL) return (EINVAL); ifp = ifunit(name); if (ifp == NULL) return (ENXIO); if (ifc->ifc_destroy == NULL) return (EOPNOTSUPP); (*ifc->ifc_destroy)(ifp); return (0); } /* * Look up a network interface cloner. */ struct if_clone * if_clone_lookup(name, unitp) const char *name; int *unitp; { struct if_clone *ifc; const char *cp; int i; for (ifc = LIST_FIRST(&if_cloners); ifc != NULL;) { for (cp = name, i = 0; i < ifc->ifc_namelen; i++, cp++) { if (ifc->ifc_name[i] != *cp) goto next_ifc; } goto found_name; next_ifc: ifc = LIST_NEXT(ifc, ifc_list); } /* No match. */ return ((struct if_clone *)NULL); found_name: if (*cp == '\0') { i = -1; } else { for (i = 0; *cp != '\0'; cp++) { if (*cp < '0' || *cp > '9') { /* Bogus unit number. */ return (NULL); } i = (i * 10) + (*cp - '0'); } } if (unitp != NULL) *unitp = i; return (ifc); } /* * Register a network interface cloner. */ void if_clone_attach(ifc) struct if_clone *ifc; { LIST_INSERT_HEAD(&if_cloners, ifc, ifc_list); if_cloners_count++; } /* * Unregister a network interface cloner. */ void if_clone_detach(ifc) struct if_clone *ifc; { LIST_REMOVE(ifc, ifc_list); if_cloners_count--; } /* * Provide list of interface cloners to userspace. */ int if_clone_list(ifcr) struct if_clonereq *ifcr; { char outbuf[IFNAMSIZ], *dst; struct if_clone *ifc; int count, error = 0; ifcr->ifcr_total = if_cloners_count; if ((dst = ifcr->ifcr_buffer) == NULL) { /* Just asking how many there are. */ return (0); } if (ifcr->ifcr_count < 0) return (EINVAL); count = (if_cloners_count < ifcr->ifcr_count) ? if_cloners_count : ifcr->ifcr_count; for (ifc = LIST_FIRST(&if_cloners); ifc != NULL && count != 0; ifc = LIST_NEXT(ifc, ifc_list), count--, dst += IFNAMSIZ) { strncpy(outbuf, ifc->ifc_name, IFNAMSIZ); outbuf[IFNAMSIZ - 1] = '\0'; /* sanity */ error = copyout(outbuf, dst, IFNAMSIZ); if (error) break; } return (error); } /* * Locate an interface based on a complete address. */ /*ARGSUSED*/ struct ifaddr * ifa_ifwithaddr(addr) register struct sockaddr *addr; { register struct ifnet *ifp; register struct ifaddr *ifa; #define equal(a1, a2) \ (bcmp((caddr_t)(a1), (caddr_t)(a2), ((struct sockaddr *)(a1))->sa_len) == 0) TAILQ_FOREACH(ifp, &ifnet, if_link) TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { if (ifa->ifa_addr->sa_family != addr->sa_family) continue; if (equal(addr, ifa->ifa_addr)) return (ifa); if ((ifp->if_flags & IFF_BROADCAST) && ifa->ifa_broadaddr && /* IP6 doesn't have broadcast */ ifa->ifa_broadaddr->sa_len != 0 && equal(ifa->ifa_broadaddr, addr)) return (ifa); } return ((struct ifaddr *)0); } /* * Locate the point to point interface with a given destination address. */ /*ARGSUSED*/ struct ifaddr * ifa_ifwithdstaddr(addr) register struct sockaddr *addr; { register struct ifnet *ifp; register struct ifaddr *ifa; TAILQ_FOREACH(ifp, &ifnet, if_link) if (ifp->if_flags & IFF_POINTOPOINT) TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { if (ifa->ifa_addr->sa_family != addr->sa_family) continue; if (ifa->ifa_dstaddr && equal(addr, ifa->ifa_dstaddr)) return (ifa); } return ((struct ifaddr *)0); } /* * Find an interface on a specific network. If many, choice * is most specific found. */ struct ifaddr * ifa_ifwithnet(addr) struct sockaddr *addr; { register struct ifnet *ifp; register struct ifaddr *ifa; struct ifaddr *ifa_maybe = (struct ifaddr *) 0; u_int af = addr->sa_family; char *addr_data = addr->sa_data, *cplim; /* * AF_LINK addresses can be looked up directly by their index number, * so do that if we can. */ if (af == AF_LINK) { register struct sockaddr_dl *sdl = (struct sockaddr_dl *)addr; if (sdl->sdl_index && sdl->sdl_index <= if_index) return (ifnet_addrs[sdl->sdl_index - 1]); } /* * Scan though each interface, looking for ones that have * addresses in this address family. */ TAILQ_FOREACH(ifp, &ifnet, if_link) { TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { register char *cp, *cp2, *cp3; if (ifa->ifa_addr->sa_family != af) next: continue; if (af == AF_INET && ifp->if_flags & IFF_POINTOPOINT) { /* * This is a bit broken as it doesn't * take into account that the remote end may * be a single node in the network we are * looking for. * The trouble is that we don't know the * netmask for the remote end. */ if (ifa->ifa_dstaddr != 0 && equal(addr, ifa->ifa_dstaddr)) return (ifa); } else { /* * if we have a special address handler, * then use it instead of the generic one. */ if (ifa->ifa_claim_addr) { if ((*ifa->ifa_claim_addr)(ifa, addr)) { return (ifa); } else { continue; } } /* * Scan all the bits in the ifa's address. * If a bit dissagrees with what we are * looking for, mask it with the netmask * to see if it really matters. * (A byte at a time) */ if (ifa->ifa_netmask == 0) continue; cp = addr_data; cp2 = ifa->ifa_addr->sa_data; cp3 = ifa->ifa_netmask->sa_data; cplim = ifa->ifa_netmask->sa_len + (char *)ifa->ifa_netmask; while (cp3 < cplim) if ((*cp++ ^ *cp2++) & *cp3++) goto next; /* next address! */ /* * If the netmask of what we just found * is more specific than what we had before * (if we had one) then remember the new one * before continuing to search * for an even better one. */ if (ifa_maybe == 0 || rn_refines((caddr_t)ifa->ifa_netmask, (caddr_t)ifa_maybe->ifa_netmask)) ifa_maybe = ifa; } } } return (ifa_maybe); } /* * Find an interface address specific to an interface best matching * a given address. */ struct ifaddr * ifaof_ifpforaddr(addr, ifp) struct sockaddr *addr; register struct ifnet *ifp; { register struct ifaddr *ifa; register char *cp, *cp2, *cp3; register char *cplim; struct ifaddr *ifa_maybe = 0; u_int af = addr->sa_family; if (af >= AF_MAX) return (0); TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { if (ifa->ifa_addr->sa_family != af) continue; if (ifa_maybe == 0) ifa_maybe = ifa; if (ifa->ifa_netmask == 0) { if (equal(addr, ifa->ifa_addr) || (ifa->ifa_dstaddr && equal(addr, ifa->ifa_dstaddr))) return (ifa); continue; } if (ifp->if_flags & IFF_POINTOPOINT) { if (equal(addr, ifa->ifa_dstaddr)) return (ifa); } else { cp = addr->sa_data; cp2 = ifa->ifa_addr->sa_data; cp3 = ifa->ifa_netmask->sa_data; cplim = ifa->ifa_netmask->sa_len + (char *)ifa->ifa_netmask; for (; cp3 < cplim; cp3++) if ((*cp++ ^ *cp2++) & *cp3) break; if (cp3 == cplim) return (ifa); } } return (ifa_maybe); } #include /* * Default action when installing a route with a Link Level gateway. * Lookup an appropriate real ifa to point to. * This should be moved to /sys/net/link.c eventually. */ static void link_rtrequest(cmd, rt, info) int cmd; register struct rtentry *rt; struct rt_addrinfo *info; { register struct ifaddr *ifa; struct sockaddr *dst; struct ifnet *ifp; if (cmd != RTM_ADD || ((ifa = rt->rt_ifa) == 0) || ((ifp = ifa->ifa_ifp) == 0) || ((dst = rt_key(rt)) == 0)) return; ifa = ifaof_ifpforaddr(dst, ifp); if (ifa) { IFAFREE(rt->rt_ifa); rt->rt_ifa = ifa; ifa->ifa_refcnt++; if (ifa->ifa_rtrequest && ifa->ifa_rtrequest != link_rtrequest) ifa->ifa_rtrequest(cmd, rt, info); } } /* * Mark an interface down and notify protocols of * the transition. * NOTE: must be called at splnet or eqivalent. */ void if_unroute(ifp, flag, fam) register struct ifnet *ifp; int flag, fam; { register struct ifaddr *ifa; ifp->if_flags &= ~flag; getmicrotime(&ifp->if_lastchange); TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) if (fam == PF_UNSPEC || (fam == ifa->ifa_addr->sa_family)) pfctlinput(PRC_IFDOWN, ifa->ifa_addr); if_qflush(&ifp->if_snd); rt_ifmsg(ifp); } /* * Mark an interface up and notify protocols of * the transition. * NOTE: must be called at splnet or eqivalent. */ void if_route(ifp, flag, fam) register struct ifnet *ifp; int flag, fam; { register struct ifaddr *ifa; ifp->if_flags |= flag; getmicrotime(&ifp->if_lastchange); TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) if (fam == PF_UNSPEC || (fam == ifa->ifa_addr->sa_family)) pfctlinput(PRC_IFUP, ifa->ifa_addr); rt_ifmsg(ifp); #ifdef INET6 in6_if_up(ifp); #endif } /* * Mark an interface down and notify protocols of * the transition. * NOTE: must be called at splnet or eqivalent. */ void if_down(ifp) register struct ifnet *ifp; { if_unroute(ifp, IFF_UP, AF_UNSPEC); } /* * Mark an interface up and notify protocols of * the transition. * NOTE: must be called at splnet or eqivalent. */ void if_up(ifp) register struct ifnet *ifp; { if_route(ifp, IFF_UP, AF_UNSPEC); } /* * Flush an interface queue. */ static void if_qflush(ifq) register struct ifqueue *ifq; { register struct mbuf *m, *n; n = ifq->ifq_head; while ((m = n) != 0) { n = m->m_act; m_freem(m); } ifq->ifq_head = 0; ifq->ifq_tail = 0; ifq->ifq_len = 0; } /* * Handle interface watchdog timer routines. Called * from softclock, we decrement timers (if set) and * call the appropriate interface routine on expiration. */ static void if_slowtimo(arg) void *arg; { register struct ifnet *ifp; int s = splimp(); TAILQ_FOREACH(ifp, &ifnet, if_link) { if (ifp->if_timer == 0 || --ifp->if_timer) continue; if (ifp->if_watchdog) (*ifp->if_watchdog)(ifp); } splx(s); timeout(if_slowtimo, (void *)0, hz / IFNET_SLOWHZ); } /* * Map interface name to * interface structure pointer. */ struct ifnet * ifunit(const char *name) { char namebuf[IFNAMSIZ + 1]; const char *cp; struct ifnet *ifp; int unit; unsigned len, m; char c; len = strlen(name); if (len < 2 || len > IFNAMSIZ) return NULL; cp = name + len - 1; c = *cp; if (c < '0' || c > '9') return NULL; /* trailing garbage */ unit = 0; m = 1; do { if (cp == name) return NULL; /* no interface name */ unit += (c - '0') * m; if (unit > 1000000) return NULL; /* number is unreasonable */ m *= 10; c = *--cp; } while (c >= '0' && c <= '9'); len = cp - name + 1; bcopy(name, namebuf, len); namebuf[len] = '\0'; /* * Now search all the interfaces for this name/number */ TAILQ_FOREACH(ifp, &ifnet, if_link) { if (strcmp(ifp->if_name, namebuf)) continue; if (unit == ifp->if_unit) break; } return (ifp); } /* * Map interface name in a sockaddr_dl to * interface structure pointer. */ struct ifnet * if_withname(sa) struct sockaddr *sa; { char ifname[IFNAMSIZ+1]; struct sockaddr_dl *sdl = (struct sockaddr_dl *)sa; if ( (sa->sa_family != AF_LINK) || (sdl->sdl_nlen == 0) || (sdl->sdl_nlen > IFNAMSIZ) ) return NULL; /* * ifunit wants a null-terminated name. It may not be null-terminated * in the sockaddr. We don't want to change the caller's sockaddr, * and there might not be room to put the trailing null anyway, so we * make a local copy that we know we can null terminate safely. */ bcopy(sdl->sdl_data, ifname, sdl->sdl_nlen); ifname[sdl->sdl_nlen] = '\0'; return ifunit(ifname); } /* * Interface ioctls. */ int ifioctl(so, cmd, data, p) struct socket *so; u_long cmd; caddr_t data; struct proc *p; { register struct ifnet *ifp; register struct ifreq *ifr; struct ifstat *ifs; int error; short oif_flags; int new_flags; switch (cmd) { case SIOCGIFCONF: case OSIOCGIFCONF: return (ifconf(cmd, data)); } ifr = (struct ifreq *)data; switch (cmd) { case SIOCIFCREATE: case SIOCIFDESTROY: if ((error = suser(p)) != 0) return (error); return ((cmd == SIOCIFCREATE) ? if_clone_create(ifr->ifr_name, sizeof(ifr->ifr_name)) : if_clone_destroy(ifr->ifr_name)); case SIOCIFGCLONERS: return (if_clone_list((struct if_clonereq *)data)); } ifp = ifunit(ifr->ifr_name); if (ifp == 0) return (ENXIO); switch (cmd) { case SIOCGIFFLAGS: ifr->ifr_flags = ifp->if_flags; ifr->ifr_flagshigh = ifp->if_ipending >> 16; break; case SIOCGIFCAP: ifr->ifr_reqcap = ifp->if_capabilities; ifr->ifr_curcap = ifp->if_capenable; break; case SIOCGIFMETRIC: ifr->ifr_metric = ifp->if_metric; break; case SIOCGIFMTU: ifr->ifr_mtu = ifp->if_mtu; break; case SIOCGIFPHYS: ifr->ifr_phys = ifp->if_physical; break; case SIOCSIFFLAGS: error = suser(p); if (error) return (error); new_flags = (ifr->ifr_flags & 0xffff) | (ifr->ifr_flagshigh << 16); if (ifp->if_flags & IFF_SMART) { /* Smart drivers twiddle their own routes */ } else if (ifp->if_flags & IFF_UP && (new_flags & IFF_UP) == 0) { int s = splimp(); if_down(ifp); splx(s); } else if (new_flags & IFF_UP && (ifp->if_flags & IFF_UP) == 0) { int s = splimp(); if_up(ifp); splx(s); } ifp->if_flags = (ifp->if_flags & IFF_CANTCHANGE) | (new_flags &~ IFF_CANTCHANGE); ifp->if_ipending = (ifp->if_ipending & IFF_CANTCHANGE) | (new_flags &~ IFF_CANTCHANGE); if (new_flags & IFF_PPROMISC) { /* Permanently promiscuous mode requested */ ifp->if_flags |= IFF_PROMISC; } else if (ifp->if_pcount == 0) { ifp->if_flags &= ~IFF_PROMISC; } if (ifp->if_ioctl) (void) (*ifp->if_ioctl)(ifp, cmd, data); getmicrotime(&ifp->if_lastchange); break; case SIOCSIFCAP: error = suser(p); if (error) return (error); if (ifr->ifr_reqcap & ~ifp->if_capabilities) return (EINVAL); (void) (*ifp->if_ioctl)(ifp, cmd, data); break; case SIOCSIFMETRIC: error = suser(p); if (error) return (error); ifp->if_metric = ifr->ifr_metric; getmicrotime(&ifp->if_lastchange); break; case SIOCSIFPHYS: error = suser(p); if (error) return error; if (!ifp->if_ioctl) return EOPNOTSUPP; error = (*ifp->if_ioctl)(ifp, cmd, data); if (error == 0) getmicrotime(&ifp->if_lastchange); return(error); case SIOCSIFMTU: { u_long oldmtu = ifp->if_mtu; error = suser(p); if (error) return (error); if (ifp->if_ioctl == NULL) return (EOPNOTSUPP); if (ifr->ifr_mtu < IF_MINMTU || ifr->ifr_mtu > IF_MAXMTU) return (EINVAL); error = (*ifp->if_ioctl)(ifp, cmd, data); if (error == 0) { getmicrotime(&ifp->if_lastchange); rt_ifmsg(ifp); } /* * If the link MTU changed, do network layer specific procedure. */ if (ifp->if_mtu != oldmtu) { #ifdef INET6 nd6_setmtu(ifp); #endif } return (error); } case SIOCADDMULTI: case SIOCDELMULTI: error = suser(p); if (error) return (error); /* Don't allow group membership on non-multicast interfaces. */ if ((ifp->if_flags & IFF_MULTICAST) == 0) return EOPNOTSUPP; /* Don't let users screw up protocols' entries. */ if (ifr->ifr_addr.sa_family != AF_LINK) return EINVAL; if (cmd == SIOCADDMULTI) { struct ifmultiaddr *ifma; error = if_addmulti(ifp, &ifr->ifr_addr, &ifma); } else { error = if_delmulti(ifp, &ifr->ifr_addr); } if (error == 0) getmicrotime(&ifp->if_lastchange); return error; case SIOCSIFPHYADDR: case SIOCDIFPHYADDR: #ifdef INET6 case SIOCSIFPHYADDR_IN6: #endif case SIOCSLIFPHYADDR: case SIOCSIFMEDIA: case SIOCSIFGENERIC: error = suser(p); if (error) return (error); if (ifp->if_ioctl == 0) return (EOPNOTSUPP); error = (*ifp->if_ioctl)(ifp, cmd, data); if (error == 0) getmicrotime(&ifp->if_lastchange); return error; case SIOCGIFSTATUS: ifs = (struct ifstat *)data; ifs->ascii[0] = '\0'; case SIOCGIFPSRCADDR: case SIOCGIFPDSTADDR: case SIOCGLIFPHYADDR: case SIOCGIFMEDIA: case SIOCGIFGENERIC: if (ifp->if_ioctl == 0) return (EOPNOTSUPP); return ((*ifp->if_ioctl)(ifp, cmd, data)); case SIOCSIFLLADDR: error = suser(p); if (error) return (error); return if_setlladdr(ifp, ifr->ifr_addr.sa_data, ifr->ifr_addr.sa_len); default: oif_flags = ifp->if_flags; if (so->so_proto == 0) return (EOPNOTSUPP); #ifndef COMPAT_43 error = ((*so->so_proto->pr_usrreqs->pru_control)(so, cmd, data, ifp, p)); #else { int ocmd = cmd; switch (cmd) { case SIOCSIFDSTADDR: case SIOCSIFADDR: case SIOCSIFBRDADDR: case SIOCSIFNETMASK: #if BYTE_ORDER != BIG_ENDIAN if (ifr->ifr_addr.sa_family == 0 && ifr->ifr_addr.sa_len < 16) { ifr->ifr_addr.sa_family = ifr->ifr_addr.sa_len; ifr->ifr_addr.sa_len = 16; } #else if (ifr->ifr_addr.sa_len == 0) ifr->ifr_addr.sa_len = 16; #endif break; case OSIOCGIFADDR: cmd = SIOCGIFADDR; break; case OSIOCGIFDSTADDR: cmd = SIOCGIFDSTADDR; break; case OSIOCGIFBRDADDR: cmd = SIOCGIFBRDADDR; break; case OSIOCGIFNETMASK: cmd = SIOCGIFNETMASK; } error = ((*so->so_proto->pr_usrreqs->pru_control)(so, cmd, data, ifp, p)); switch (ocmd) { case OSIOCGIFADDR: case OSIOCGIFDSTADDR: case OSIOCGIFBRDADDR: case OSIOCGIFNETMASK: *(u_short *)&ifr->ifr_addr = ifr->ifr_addr.sa_family; } } #endif /* COMPAT_43 */ if ((oif_flags ^ ifp->if_flags) & IFF_UP) { #ifdef INET6 DELAY(100);/* XXX: temporary workaround for fxp issue*/ if (ifp->if_flags & IFF_UP) { int s = splimp(); in6_if_up(ifp); splx(s); } #endif } return (error); } return (0); } /* * Set/clear promiscuous mode on interface ifp based on the truth value * of pswitch. The calls are reference counted so that only the first * "on" request actually has an effect, as does the final "off" request. * Results are undefined if the "off" and "on" requests are not matched. */ int ifpromisc(ifp, pswitch) struct ifnet *ifp; int pswitch; { struct ifreq ifr; int error; int oldflags; oldflags = ifp->if_flags; if (ifp->if_ipending & IFF_PPROMISC) { /* Do nothing if device is in permanently promiscuous mode */ ifp->if_pcount += pswitch ? 1 : -1; return (0); } if (pswitch) { /* * If the device is not configured up, we cannot put it in * promiscuous mode. */ if ((ifp->if_flags & IFF_UP) == 0) return (ENETDOWN); if (ifp->if_pcount++ != 0) return (0); ifp->if_flags |= IFF_PROMISC; log(LOG_INFO, "%s%d: promiscuous mode enabled\n", ifp->if_name, ifp->if_unit); } else { if (--ifp->if_pcount > 0) return (0); ifp->if_flags &= ~IFF_PROMISC; log(LOG_INFO, "%s%d: promiscuous mode disabled\n", ifp->if_name, ifp->if_unit); } ifr.ifr_flags = ifp->if_flags; ifr.ifr_flagshigh = ifp->if_ipending >> 16; error = (*ifp->if_ioctl)(ifp, SIOCSIFFLAGS, (caddr_t)&ifr); if (error == 0) rt_ifmsg(ifp); else ifp->if_flags = oldflags; return error; } /* * Return interface configuration * of system. List may be used * in later ioctl's (above) to get * other information. */ /*ARGSUSED*/ static int ifconf(cmd, data) u_long cmd; caddr_t data; { register struct ifconf *ifc = (struct ifconf *)data; register struct ifnet *ifp; register struct ifaddr *ifa; struct sockaddr *sa; struct ifreq ifr, *ifrp; int space = ifc->ifc_len, error = 0; ifrp = ifc->ifc_req; TAILQ_FOREACH(ifp, &ifnet, if_link) { char workbuf[64]; int ifnlen, addrs; if (space <= sizeof (ifr)) break; ifnlen = snprintf(workbuf, sizeof(workbuf), "%s%d", ifp->if_name, ifp->if_unit); if(ifnlen + 1 > sizeof ifr.ifr_name) { error = ENAMETOOLONG; break; } else { strcpy(ifr.ifr_name, workbuf); } addrs = 0; ifa = ifp->if_addrhead.tqh_first; TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { if (space <= sizeof(ifr)) break; sa = ifa->ifa_addr; if (curproc->p_prison && prison_if(curproc, sa)) continue; addrs++; #ifdef COMPAT_43 if (cmd == OSIOCGIFCONF) { struct osockaddr *osa = (struct osockaddr *)&ifr.ifr_addr; ifr.ifr_addr = *sa; osa->sa_family = sa->sa_family; error = copyout((caddr_t)&ifr, (caddr_t)ifrp, sizeof (ifr)); ifrp++; } else #endif if (sa->sa_len <= sizeof(*sa)) { ifr.ifr_addr = *sa; error = copyout((caddr_t)&ifr, (caddr_t)ifrp, sizeof (ifr)); ifrp++; } else { if (space < sizeof (ifr) + sa->sa_len - sizeof(*sa)) break; space -= sa->sa_len - sizeof(*sa); error = copyout((caddr_t)&ifr, (caddr_t)ifrp, sizeof (ifr.ifr_name)); if (error == 0) error = copyout((caddr_t)sa, (caddr_t)&ifrp->ifr_addr, sa->sa_len); ifrp = (struct ifreq *) (sa->sa_len + (caddr_t)&ifrp->ifr_addr); } if (error) break; space -= sizeof (ifr); } if (error) break; if (!addrs) { bzero((caddr_t)&ifr.ifr_addr, sizeof(ifr.ifr_addr)); error = copyout((caddr_t)&ifr, (caddr_t)ifrp, sizeof (ifr)); if (error) break; space -= sizeof (ifr); ifrp++; } } ifc->ifc_len -= space; return (error); } /* * Just like if_promisc(), but for all-multicast-reception mode. */ int if_allmulti(ifp, onswitch) struct ifnet *ifp; int onswitch; { int error = 0; int s = splimp(); struct ifreq ifr; if (onswitch) { if (ifp->if_amcount++ == 0) { ifp->if_flags |= IFF_ALLMULTI; ifr.ifr_flags = ifp->if_flags; ifr.ifr_flagshigh = ifp->if_ipending >> 16; error = ifp->if_ioctl(ifp, SIOCSIFFLAGS, (caddr_t)&ifr); } } else { if (ifp->if_amcount > 1) { ifp->if_amcount--; } else { ifp->if_amcount = 0; ifp->if_flags &= ~IFF_ALLMULTI; ifr.ifr_flags = ifp->if_flags; ifr.ifr_flagshigh = ifp->if_ipending >> 16; error = ifp->if_ioctl(ifp, SIOCSIFFLAGS, (caddr_t)&ifr); } } splx(s); if (error == 0) rt_ifmsg(ifp); return error; } /* * Add a multicast listenership to the interface in question. * The link layer provides a routine which converts */ int if_addmulti(ifp, sa, retifma) struct ifnet *ifp; /* interface to manipulate */ struct sockaddr *sa; /* address to add */ struct ifmultiaddr **retifma; { struct sockaddr *llsa, *dupsa; int error, s; struct ifmultiaddr *ifma; /* * If the matching multicast address already exists * then don't add a new one, just add a reference */ LIST_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { if (equal(sa, ifma->ifma_addr)) { ifma->ifma_refcount++; if (retifma) *retifma = ifma; return 0; } } /* * Give the link layer a chance to accept/reject it, and also * find out which AF_LINK address this maps to, if it isn't one * already. */ if (ifp->if_resolvemulti) { error = ifp->if_resolvemulti(ifp, &llsa, sa); if (error) return error; } else { llsa = 0; } MALLOC(ifma, struct ifmultiaddr *, sizeof *ifma, M_IFMADDR, M_WAITOK); MALLOC(dupsa, struct sockaddr *, sa->sa_len, M_IFMADDR, M_WAITOK); bcopy(sa, dupsa, sa->sa_len); ifma->ifma_addr = dupsa; ifma->ifma_lladdr = llsa; ifma->ifma_ifp = ifp; ifma->ifma_refcount = 1; ifma->ifma_protospec = 0; rt_newmaddrmsg(RTM_NEWMADDR, ifma); /* * Some network interfaces can scan the address list at * interrupt time; lock them out. */ s = splimp(); LIST_INSERT_HEAD(&ifp->if_multiaddrs, ifma, ifma_link); splx(s); *retifma = ifma; if (llsa != 0) { LIST_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { if (equal(ifma->ifma_addr, llsa)) break; } if (ifma) { ifma->ifma_refcount++; } else { MALLOC(ifma, struct ifmultiaddr *, sizeof *ifma, M_IFMADDR, M_WAITOK); MALLOC(dupsa, struct sockaddr *, llsa->sa_len, M_IFMADDR, M_WAITOK); bcopy(llsa, dupsa, llsa->sa_len); ifma->ifma_addr = dupsa; ifma->ifma_ifp = ifp; ifma->ifma_refcount = 1; s = splimp(); LIST_INSERT_HEAD(&ifp->if_multiaddrs, ifma, ifma_link); splx(s); } } /* * We are certain we have added something, so call down to the * interface to let them know about it. */ s = splimp(); ifp->if_ioctl(ifp, SIOCADDMULTI, 0); splx(s); return 0; } /* * Remove a reference to a multicast address on this interface. Yell * if the request does not match an existing membership. */ int if_delmulti(ifp, sa) struct ifnet *ifp; struct sockaddr *sa; { struct ifmultiaddr *ifma; int s; LIST_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) if (equal(sa, ifma->ifma_addr)) break; if (ifma == 0) return ENOENT; if (ifma->ifma_refcount > 1) { ifma->ifma_refcount--; return 0; } rt_newmaddrmsg(RTM_DELMADDR, ifma); sa = ifma->ifma_lladdr; s = splimp(); LIST_REMOVE(ifma, ifma_link); /* * Make sure the interface driver is notified * in the case of a link layer mcast group being left. */ if (ifma->ifma_addr->sa_family == AF_LINK && sa == 0) ifp->if_ioctl(ifp, SIOCDELMULTI, 0); splx(s); free(ifma->ifma_addr, M_IFMADDR); free(ifma, M_IFMADDR); if (sa == 0) return 0; /* * Now look for the link-layer address which corresponds to * this network address. It had been squirreled away in * ifma->ifma_lladdr for this purpose (so we don't have * to call ifp->if_resolvemulti() again), and we saved that * value in sa above. If some nasty deleted the * link-layer address out from underneath us, we can deal because * the address we stored was is not the same as the one which was * in the record for the link-layer address. (So we don't complain * in that case.) */ LIST_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) if (equal(sa, ifma->ifma_addr)) break; if (ifma == 0) return 0; if (ifma->ifma_refcount > 1) { ifma->ifma_refcount--; return 0; } s = splimp(); LIST_REMOVE(ifma, ifma_link); ifp->if_ioctl(ifp, SIOCDELMULTI, 0); splx(s); free(ifma->ifma_addr, M_IFMADDR); free(sa, M_IFMADDR); free(ifma, M_IFMADDR); return 0; } /* * Set the link layer address on an interface. * * At this time we only support certain types of interfaces, * and we don't allow the length of the address to change. */ int if_setlladdr(struct ifnet *ifp, const u_char *lladdr, int len) { struct sockaddr_dl *sdl; struct ifaddr *ifa; struct ifreq ifr; ifa = ifnet_addrs[ifp->if_index - 1]; if (ifa == NULL) return (EINVAL); sdl = (struct sockaddr_dl *)ifa->ifa_addr; if (sdl == NULL) return (EINVAL); if (len != sdl->sdl_alen) /* don't allow length to change */ return (EINVAL); switch (ifp->if_type) { case IFT_ETHER: /* these types use struct arpcom */ case IFT_FDDI: case IFT_XETHER: case IFT_ISO88025: case IFT_L2VLAN: bcopy(lladdr, ((struct arpcom *)ifp->if_softc)->ac_enaddr, len); + /* FALLTHROUGH */ + case IFT_ARCNET: bcopy(lladdr, LLADDR(sdl), len); break; default: return (ENODEV); } /* * If the interface is already up, we need * to re-init it in order to reprogram its * address filter. */ if ((ifp->if_flags & IFF_UP) != 0) { ifp->if_flags &= ~IFF_UP; ifr.ifr_flags = ifp->if_flags; ifr.ifr_flagshigh = ifp->if_ipending >> 16; (*ifp->if_ioctl)(ifp, SIOCSIFFLAGS, (caddr_t)&ifr); ifp->if_flags |= IFF_UP; ifr.ifr_flags = ifp->if_flags; ifr.ifr_flagshigh = ifp->if_ipending >> 16; (*ifp->if_ioctl)(ifp, SIOCSIFFLAGS, (caddr_t)&ifr); #ifdef INET /* * Also send gratuitous ARPs to notify other nodes about * the address change. */ TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { if (ifa->ifa_addr != NULL && ifa->ifa_addr->sa_family == AF_INET) arp_ifinit(ifp, ifa); } #endif } return (0); } struct ifmultiaddr * ifmaof_ifpforaddr(sa, ifp) struct sockaddr *sa; struct ifnet *ifp; { struct ifmultiaddr *ifma; LIST_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) if (equal(ifma->ifma_addr, sa)) break; return ifma; } SYSCTL_NODE(_net, PF_LINK, link, CTLFLAG_RW, 0, "Link layers"); SYSCTL_NODE(_net_link, 0, generic, CTLFLAG_RW, 0, "Generic link-management"); Index: stable/4/sys/net/if_arc.h =================================================================== --- stable/4/sys/net/if_arc.h (revision 109979) +++ stable/4/sys/net/if_arc.h (revision 109980) @@ -1,147 +1,146 @@ /* $NetBSD: if_arc.h,v 1.13 1999/11/19 20:41:19 thorpej Exp $ */ /* $FreeBSD$ */ /* * Copyright (c) 1982, 1986, 1993 * The Regents of the University of California. All rights reserved. * * 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. * * 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. * * from: NetBSD: if_ether.h,v 1.10 1994/06/29 06:37:55 cgd Exp * @(#)if_ether.h 8.1 (Berkeley) 6/10/93 */ #ifndef _NET_IF_ARC_H_ #define _NET_IF_ARC_H_ /* * Arcnet address - 1 octets * don't know who uses this. */ struct arc_addr { u_int8_t arc_addr_octet[1]; } __attribute__((__packed__)); /* * Structure of a 2.5MB/s Arcnet header. * as given to interface code. */ struct arc_header { u_int8_t arc_shost; u_int8_t arc_dhost; u_int8_t arc_type; /* * only present for newstyle encoding with LL fragmentation. * Don't use sizeof(anything), use ARC_HDR{,NEW}LEN instead. */ u_int8_t arc_flag; u_int16_t arc_seqid; /* * only present in exception packets (arc_flag == 0xff) */ u_int8_t arc_type2; /* same as arc_type */ u_int8_t arc_flag2; /* real flag value */ u_int16_t arc_seqid2; /* real seqid value */ } __attribute__((__packed__)); #define ARC_ADDR_LEN 1 #define ARC_HDRLEN 3 #define ARC_HDRNEWLEN 6 #define ARC_HDRNEWLEN_EXC 10 -/* these lengths are data link layer length - 2*ARC_ADDR_LEN */ +/* these lengths are data link layer length - 2 * ARC_ADDR_LEN */ #define ARC_MIN_LEN 1 #define ARC_MIN_FORBID_LEN 254 #define ARC_MAX_FORBID_LEN 256 #define ARC_MAX_LEN 508 +#define ARC_MAX_DATA 504 - /* RFC 1051 */ #define ARCTYPE_IP_OLD 240 /* IP protocol */ #define ARCTYPE_ARP_OLD 241 /* address resolution protocol */ /* RFC 1201 */ #define ARCTYPE_IP 212 /* IP protocol */ #define ARCTYPE_ARP 213 /* address resolution protocol */ #define ARCTYPE_REVARP 214 /* reverse addr resolution protocol */ #define ARCTYPE_ATALK 221 /* Appletalk */ #define ARCTYPE_BANIAN 247 /* Banyan Vines */ #define ARCTYPE_IPX 250 /* Novell IPX */ #define ARCTYPE_INET6 0xc4 /* IPng */ #define ARCTYPE_DIAGNOSE 0x80 /* as per ANSI/ATA 878.1 */ #define ARCMTU 507 #define ARCMIN 0 #define ARC_PHDS_MAXMTU 60480 struct arccom { struct ifnet ac_if; /* network-visible interface */ u_int16_t ac_seqid; /* seq. id used by PHDS encap. */ u_int8_t arc_shost; u_int8_t arc_dhost; u_int8_t arc_type; u_int8_t dummy0; u_int16_t dummy1; int sflag, fsflag, rsflag; struct mbuf *curr_frag; struct ac_frag { u_int8_t af_maxflag; /* from first packet */ u_int8_t af_lastseen; /* last split flag seen */ u_int16_t af_seqid; struct mbuf *af_packet; } ac_fragtab[256]; /* indexed by sender ll address */ }; #ifdef _KERNEL extern int arc_ipmtu; /* XXX new ip only, no RFC 1051! */ void arc_ifattach __P((struct ifnet *, u_int8_t)); void arc_ifdetach __P((struct ifnet *)); void arc_storelladdr __P((struct ifnet *, u_int8_t)); -char *arc_sprintf __P((u_int8_t *)); int arc_isphds __P((int)); void arc_input __P((struct ifnet *, struct mbuf *)); int arc_output __P((struct ifnet *, struct mbuf *, struct sockaddr *, struct rtentry *)); int arc_ioctl __P((struct ifnet *, int, caddr_t)); void arc_frag_init __P((struct ifnet *)); struct mbuf * arc_frag_next __P((struct ifnet *)); #endif #endif /* _NET_IF_ARC_H_ */ Index: stable/4/sys/net/if_arcsubr.c =================================================================== --- stable/4/sys/net/if_arcsubr.c (revision 109979) +++ stable/4/sys/net/if_arcsubr.c (revision 109980) @@ -1,774 +1,849 @@ /* $NetBSD: if_arcsubr.c,v 1.36 2001/06/14 05:44:23 itojun Exp $ */ /* $FreeBSD$ */ /* * Copyright (c) 1994, 1995 Ignatios Souvatzis * Copyright (c) 1982, 1989, 1993 * The Regents of the University of California. All rights reserved. * * 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. * * 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. * * from: NetBSD: if_ethersubr.c,v 1.9 1994/06/29 06:36:11 cgd Exp * @(#)if_ethersubr.c 8.1 (Berkeley) 6/10/93 * */ #include "opt_inet.h" #include "opt_inet6.h" +#include "opt_ipx.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(INET) || defined(INET6) #include #include #include #endif #ifdef INET6 #include #endif +#ifdef IPX +#include +#include +#endif + MODULE_VERSION(arcnet, 1); #define ARCNET_ALLOW_BROKEN_ARP static struct mbuf *arc_defrag __P((struct ifnet *, struct mbuf *)); +static int arc_resolvemulti __P((struct ifnet *, struct sockaddr **, + struct sockaddr *)); #define senderr(e) { error = (e); goto bad;} -#define SIN(s) ((struct sockaddr_in *)s) +#define SIN(s) ((struct sockaddr_in *)s) +#define SIPX(s) ((struct sockaddr_ipx *)s) /* * ARCnet output routine. * Encapsulate a packet of type family for the local net. * Assumes that ifp is actually pointer to arccom structure. */ int arc_output(ifp, m, dst, rt0) struct ifnet *ifp; struct mbuf *m; struct sockaddr *dst; struct rtentry *rt0; { - struct mbuf *mcopy; struct rtentry *rt; struct arccom *ac; struct arc_header *ah; - struct arphdr *arph; int error; u_int8_t atype, adst; -#if __FreeBSD_version < 500000 - int s; -#endif + int loop_copy = 0; if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) return(ENETDOWN); /* m, m1 aren't initialized yet */ error = 0; ac = (struct arccom *)ifp; - mcopy = NULL; if ((rt = rt0)) { if ((rt->rt_flags & RTF_UP) == 0) { if ((rt0 = rt = rtalloc1(dst, 1, 0UL))) rt->rt_refcnt--; else senderr(EHOSTUNREACH); } if (rt->rt_flags & RTF_GATEWAY) { if (rt->rt_gwroute == 0) goto lookup; if (((rt = rt->rt_gwroute)->rt_flags & RTF_UP) == 0) { rtfree(rt); rt = rt0; lookup: rt->rt_gwroute = rtalloc1(rt->rt_gateway, 1, 0UL); if ((rt = rt->rt_gwroute) == 0) senderr(EHOSTUNREACH); } } if (rt->rt_flags & RTF_REJECT) if (rt->rt_rmx.rmx_expire == 0 || time_second < rt->rt_rmx.rmx_expire) senderr(rt == rt0 ? EHOSTDOWN : EHOSTUNREACH); } switch (dst->sa_family) { #ifdef INET case AF_INET: /* * For now, use the simple IP addr -> ARCnet addr mapping */ if (m->m_flags & (M_BCAST|M_MCAST)) adst = arcbroadcastaddr; /* ARCnet broadcast address */ else if (ifp->if_flags & IFF_NOARP) adst = ntohl(SIN(dst)->sin_addr.s_addr) & 0xFF; else if (!arpresolve(ifp, rt, m, dst, &adst, rt0)) return 0; /* not resolved yet */ - /* If broadcasting on a simplex interface, loopback a copy */ - if ((m->m_flags & (M_BCAST|M_MCAST)) && - (ifp->if_flags & IFF_SIMPLEX)) - mcopy = m_copy(m, 0, (int)M_COPYALL); atype = (ifp->if_flags & IFF_LINK0) ? ARCTYPE_IP_OLD : ARCTYPE_IP; break; #endif #ifdef INET6 case AF_INET6: #ifdef OLDIP6OUTPUT if (!nd6_resolve(ifp, rt, m, dst, (u_char *)&adst)) return(0); /* if not yet resolves */ #else if (!nd6_storelladdr(ifp, rt, m, dst, (u_char *)&adst)) return(0); /* it must be impossible, but... */ #endif /* OLDIP6OUTPUT */ atype = ARCTYPE_INET6; break; #endif +#ifdef IPX + case AF_IPX: + adst = SIPX(dst)->sipx_addr.x_host.c_host[5]; + atype = ARCTYPE_IPX; + if (adst == 0xff) + adst = arcbroadcastaddr; + break; +#endif case AF_UNSPEC: + loop_copy = -1; ah = (struct arc_header *)dst->sa_data; adst = ah->arc_dhost; atype = ah->arc_type; if (atype == ARCTYPE_ARP) { atype = (ifp->if_flags & IFF_LINK0) ? ARCTYPE_ARP_OLD: ARCTYPE_ARP; #ifdef ARCNET_ALLOW_BROKEN_ARP /* * XXX It's not clear per RFC826 if this is needed, but * "assigned numbers" say this is wrong. * However, e.g., AmiTCP 3.0Beta used it... we make this * switchable for emergency cases. Not perfect, but... */ - arph = mtod(m, struct arphdr *); if (ifp->if_flags & IFF_LINK2) - arph->ar_pro = atype - 1; + mtod(m, struct arphdr *)->ar_pro = atype - 1; #endif } break; default: printf("%s%d: can't handle af%d\n", ifp->if_name, ifp->if_unit, dst->sa_family); senderr(EAFNOSUPPORT); } - if (mcopy) - (void) if_simloop(ifp, mcopy, dst->sa_family, 0); - M_PREPEND(m, ARC_HDRLEN, M_DONTWAIT); if (m == 0) senderr(ENOBUFS); ah = mtod(m, struct arc_header *); ah->arc_type = atype; ah->arc_dhost = adst; ah->arc_shost = *IF_LLADDR(ifp); + if ((ifp->if_flags & IFF_SIMPLEX) && (loop_copy != -1)) { + if ((m->m_flags & M_BCAST) || (loop_copy > 0)) { + struct mbuf *n = m_copy(m, 0, (int)M_COPYALL); + + (void) if_simloop(ifp, n, dst->sa_family, ARC_HDRLEN); + } else if (ah->arc_dhost == ah->arc_shost) { + (void) if_simloop(ifp, m, dst->sa_family, ARC_HDRLEN); + return (0); /* XXX */ + } + } + if (ifp->if_bpf) bpf_mtap(ifp, m); -#if __FreeBSD_version < 500000 - s = splimp(); - - /* - * Queue message on interface, and start output if interface - * not yet active. - */ - if (IF_QFULL(&ifp->if_snd)) { - IF_DROP(&ifp->if_snd); - splx(s); - senderr(ENOBUFS); - } - ifp->if_obytes += m->m_pkthdr.len; - IF_ENQUEUE(&ifp->if_snd, m); - if ((ifp->if_flags & IFF_OACTIVE) == 0) - (*ifp->if_start)(ifp); - splx(s); -#else if (!IF_HANDOFF(&ifp->if_snd, m, ifp)) { m = 0; senderr(ENOBUFS); } -#endif return (error); bad: if (m) m_freem(m); return (error); } void arc_frag_init(ifp) struct ifnet *ifp; { struct arccom *ac; ac = (struct arccom *)ifp; ac->curr_frag = 0; } struct mbuf * arc_frag_next(ifp) struct ifnet *ifp; { struct arccom *ac; struct mbuf *m; struct arc_header *ah; ac = (struct arccom *)ifp; if ((m = ac->curr_frag) == 0) { int tfrags; /* dequeue new packet */ IF_DEQUEUE(&ifp->if_snd, m); if (m == 0) return 0; ah = mtod(m, struct arc_header *); if (!arc_isphds(ah->arc_type)) return m; ++ac->ac_seqid; /* make the seqid unique */ - tfrags = (m->m_pkthdr.len + 503) / 504; + tfrags = (m->m_pkthdr.len + ARC_MAX_DATA - 1) / ARC_MAX_DATA; ac->fsflag = 2 * tfrags - 3; ac->sflag = 0; ac->rsflag = ac->fsflag; ac->arc_dhost = ah->arc_dhost; ac->arc_shost = ah->arc_shost; ac->arc_type = ah->arc_type; m_adj(m, ARC_HDRLEN); ac->curr_frag = m; } /* split out next fragment and return it */ if (ac->sflag < ac->fsflag) { /* we CAN'T have short packets here */ - ac->curr_frag = m_split(m, 504, M_DONTWAIT); + ac->curr_frag = m_split(m, ARC_MAX_DATA, M_DONTWAIT); if (ac->curr_frag == 0) { m_freem(m); return 0; } M_PREPEND(m, ARC_HDRNEWLEN, M_DONTWAIT); if (m == 0) { m_freem(ac->curr_frag); ac->curr_frag = 0; return 0; } ah = mtod(m, struct arc_header *); ah->arc_flag = ac->rsflag; ah->arc_seqid = ac->ac_seqid; ac->sflag += 2; ac->rsflag = ac->sflag; } else if ((m->m_pkthdr.len >= ARC_MIN_FORBID_LEN - ARC_HDRNEWLEN + 2) && (m->m_pkthdr.len <= ARC_MAX_FORBID_LEN - ARC_HDRNEWLEN + 2)) { ac->curr_frag = 0; M_PREPEND(m, ARC_HDRNEWLEN_EXC, M_DONTWAIT); if (m == 0) return 0; ah = mtod(m, struct arc_header *); ah->arc_flag = 0xFF; ah->arc_seqid = 0xFFFF; ah->arc_type2 = ac->arc_type; ah->arc_flag2 = ac->sflag; ah->arc_seqid2 = ac->ac_seqid; } else { ac->curr_frag = 0; M_PREPEND(m, ARC_HDRNEWLEN, M_DONTWAIT); if (m == 0) return 0; ah = mtod(m, struct arc_header *); ah->arc_flag = ac->sflag; ah->arc_seqid = ac->ac_seqid; } ah->arc_dhost = ac->arc_dhost; ah->arc_shost = ac->arc_shost; ah->arc_type = ac->arc_type; return m; } /* * Defragmenter. Returns mbuf if last packet found, else * NULL. frees imcoming mbuf as necessary. */ __inline struct mbuf * arc_defrag(ifp, m) struct ifnet *ifp; struct mbuf *m; { struct arc_header *ah, *ah1; struct arccom *ac; struct ac_frag *af; struct mbuf *m1; char *s; int newflen; u_char src,dst,typ; ac = (struct arccom *)ifp; if (m->m_len < ARC_HDRNEWLEN) { m = m_pullup(m, ARC_HDRNEWLEN); if (m == NULL) { ++ifp->if_ierrors; return NULL; } } ah = mtod(m, struct arc_header *); typ = ah->arc_type; if (!arc_isphds(typ)) return m; src = ah->arc_shost; dst = ah->arc_dhost; if (ah->arc_flag == 0xff) { m_adj(m, 4); if (m->m_len < ARC_HDRNEWLEN) { m = m_pullup(m, ARC_HDRNEWLEN); if (m == NULL) { ++ifp->if_ierrors; return NULL; } } ah = mtod(m, struct arc_header *); } af = &ac->ac_fragtab[src]; m1 = af->af_packet; s = "debug code error"; if (ah->arc_flag & 1) { /* * first fragment. We always initialize, which is * about the right thing to do, as we only want to * accept one fragmented packet per src at a time. */ if (m1 != NULL) m_freem(m1); af->af_packet = m; m1 = m; af->af_maxflag = ah->arc_flag; af->af_lastseen = 0; af->af_seqid = ah->arc_seqid; return NULL; /* notreached */ } else { /* check for unfragmented packet */ if (ah->arc_flag == 0) return m; /* do we have a first packet from that src? */ if (m1 == NULL) { s = "no first frag"; goto outofseq; } ah1 = mtod(m1, struct arc_header *); if (ah->arc_seqid != ah1->arc_seqid) { s = "seqid differs"; goto outofseq; } if (typ != ah1->arc_type) { s = "type differs"; goto outofseq; } if (dst != ah1->arc_dhost) { s = "dest host differs"; goto outofseq; } /* typ, seqid and dst are ok here. */ if (ah->arc_flag == af->af_lastseen) { m_freem(m); return NULL; } if (ah->arc_flag == af->af_lastseen + 2) { /* ok, this is next fragment */ af->af_lastseen = ah->arc_flag; m_adj(m,ARC_HDRNEWLEN); /* * m_cat might free the first mbuf (with pkthdr) * in 2nd chain; therefore: */ newflen = m->m_pkthdr.len; m_cat(m1,m); m1->m_pkthdr.len += newflen; /* is it the last one? */ if (af->af_lastseen > af->af_maxflag) { af->af_packet = NULL; return(m1); } else return NULL; } s = "other reason"; /* if all else fails, it is out of sequence, too */ } outofseq: if (m1) { m_freem(m1); af->af_packet = NULL; } if (m) m_freem(m); log(LOG_INFO,"%s%d: got out of seq. packet: %s\n", ifp->if_name, ifp->if_unit, s); return NULL; } /* * return 1 if Packet Header Definition Standard, else 0. * For now: old IP, old ARP aren't obviously. Lacking correct information, * we guess that besides new IP and new ARP also IPX and APPLETALK are PHDS. * (Apple and Novell corporations were involved, among others, in PHDS work). * Easiest is to assume that everybody else uses that, too. */ int arc_isphds(type) u_int8_t type; { return (type != ARCTYPE_IP_OLD && type != ARCTYPE_ARP_OLD && type != ARCTYPE_DIAGNOSE); } /* * Process a received Arcnet packet; * the packet is in the mbuf chain m with * the ARCnet header. */ void arc_input(ifp, m) struct ifnet *ifp; struct mbuf *m; { struct arc_header *ah; struct ifqueue *inq; u_int8_t atype; -#ifdef INET - struct arphdr *arph; -#endif -#if __FreeBSD_version < 500000 - int s; -#endif if ((ifp->if_flags & IFF_UP) == 0) { m_freem(m); return; } /* possibly defragment: */ m = arc_defrag(ifp, m); if (m == NULL) return; if (ifp->if_bpf) bpf_mtap(ifp, m); ah = mtod(m, struct arc_header *); + /* does this belong to us? */ + if ((ifp->if_flags & IFF_PROMISC) != 0 + && ah->arc_dhost != arcbroadcastaddr + && ah->arc_dhost != *IF_LLADDR(ifp)) { + m_freem(m); + return; + } ifp->if_ibytes += m->m_pkthdr.len; - if (arcbroadcastaddr == ah->arc_dhost) { + if (ah->arc_dhost == arcbroadcastaddr) { m->m_flags |= M_BCAST|M_MCAST; ifp->if_imcasts++; } atype = ah->arc_type; switch (atype) { #ifdef INET case ARCTYPE_IP: m_adj(m, ARC_HDRNEWLEN); + if (ipflow_fastforward(m)) + return; schednetisr(NETISR_IP); inq = &ipintrq; break; case ARCTYPE_IP_OLD: m_adj(m, ARC_HDRLEN); + if (ipflow_fastforward(m)) + return; schednetisr(NETISR_IP); inq = &ipintrq; break; case ARCTYPE_ARP: if (ifp->if_flags & IFF_NOARP) { /* Discard packet if ARP is disabled on interface */ m_freem(m); return; } m_adj(m, ARC_HDRNEWLEN); schednetisr(NETISR_ARP); inq = &arpintrq; #ifdef ARCNET_ALLOW_BROKEN_ARP mtod(m, struct arphdr *)->ar_pro = htons(ETHERTYPE_IP); #endif break; case ARCTYPE_ARP_OLD: if (ifp->if_flags & IFF_NOARP) { /* Discard packet if ARP is disabled on interface */ m_freem(m); return; } m_adj(m, ARC_HDRLEN); schednetisr(NETISR_ARP); inq = &arpintrq; - arph = mtod(m, struct arphdr *); #ifdef ARCNET_ALLOW_BROKEN_ARP mtod(m, struct arphdr *)->ar_pro = htons(ETHERTYPE_IP); #endif break; #endif #ifdef INET6 case ARCTYPE_INET6: m_adj(m, ARC_HDRNEWLEN); schednetisr(NETISR_IPV6); inq = &ip6intrq; break; #endif +#ifdef IPX + case ARCTYPE_IPX: + m_adj(m, ARC_HDRNEWLEN); + schednetisr(NETISR_IPX); + inq = &ipxintrq; + break; +#endif default: m_freem(m); return; } -#if __FreeBSD_version < 500000 - s = splimp(); - if (IF_QFULL(inq)) { - IF_DROP(inq); - m_freem(m); - } else - IF_ENQUEUE(inq, m); - splx(s); -#else IF_HANDOFF(inq, m, NULL); -#endif } /* - * Convert Arcnet address to printable (loggable) representation. - */ -static char digits[] = "0123456789abcdef"; -char * -arc_sprintf(ap) - u_int8_t *ap; -{ - static char arcbuf[3]; - char *cp = arcbuf; - - *cp++ = digits[*ap >> 4]; - *cp++ = digits[*ap++ & 0xf]; - *cp = 0; - return (arcbuf); -} - -/* * Register (new) link level address. */ void arc_storelladdr(ifp, lla) struct ifnet *ifp; u_int8_t lla; { *IF_LLADDR(ifp) = lla; } /* * Perform common duties while attaching to interface list */ void arc_ifattach(ifp, lla) struct ifnet *ifp; u_int8_t lla; { struct ifaddr *ifa; struct sockaddr_dl *sdl; struct arccom *ac; if_attach(ifp); ifp->if_type = IFT_ARCNET; ifp->if_addrlen = 1; ifp->if_hdrlen = ARC_HDRLEN; ifp->if_mtu = 1500; + ifp->if_resolvemulti = arc_resolvemulti; if (ifp->if_baudrate == 0) ifp->if_baudrate = 2500000; #if __FreeBSD_version < 500000 ifa = ifnet_addrs[ifp->if_index - 1]; #else ifa = ifaddr_byindex(ifp->if_index); #endif KASSERT(ifa != NULL, ("%s: no lladdr!\n", __FUNCTION__)); sdl = (struct sockaddr_dl *)ifa->ifa_addr; sdl->sdl_type = IFT_ARCNET; sdl->sdl_alen = ifp->if_addrlen; if (ifp->if_flags & IFF_BROADCAST) ifp->if_flags |= IFF_MULTICAST|IFF_ALLMULTI; ac = (struct arccom *)ifp; ac->ac_seqid = (time_second) & 0xFFFF; /* try to make seqid unique */ if (lla == 0) { /* XXX this message isn't entirely clear, to me -- cgd */ log(LOG_ERR,"%s%d: link address 0 reserved for broadcasts. Please change it and ifconfig %s%d down up\n", ifp->if_name, ifp->if_unit, ifp->if_name, ifp->if_unit); } arc_storelladdr(ifp, lla); bpfattach(ifp, DLT_ARCNET, ARC_HDRLEN); } void arc_ifdetach(ifp) struct ifnet *ifp; { bpfdetach(ifp); if_detach(ifp); } int arc_ioctl(ifp, command, data) struct ifnet *ifp; int command; caddr_t data; { struct ifaddr *ifa = (struct ifaddr *) data; struct ifreq *ifr = (struct ifreq *) data; int error = 0; switch (command) { case SIOCSIFADDR: ifp->if_flags |= IFF_UP; switch (ifa->ifa_addr->sa_family) { #ifdef INET case AF_INET: ifp->if_init(ifp->if_softc); /* before arpwhohas */ arp_ifinit(ifp, ifa); break; #endif +#ifdef IPX + /* + * XXX This code is probably wrong + */ + case AF_IPX: + { + struct ipx_addr *ina = &(IA_SIPX(ifa)->sipx_addr); + + if (ipx_nullhost(*ina)) + ina->x_host.c_host[5] = *IF_LLADDR(ifp); + else + arc_storelladdr(ifp, ina->x_host.c_host[5]); + + /* + * Set new address + */ + ifp->if_init(ifp->if_softc); + break; + } +#endif default: ifp->if_init(ifp->if_softc); break; } break; + case SIOCGIFADDR: + { + struct sockaddr *sa; + + sa = (struct sockaddr *) &ifr->ifr_data; + bcopy(IF_LLADDR(ifp), + (caddr_t) sa->sa_data, ARC_ADDR_LEN); + } + break; + case SIOCADDMULTI: case SIOCDELMULTI: if (ifr == NULL) error = EAFNOSUPPORT; else { switch (ifr->ifr_addr.sa_family) { case AF_INET: case AF_INET6: error = 0; break; default: error = EAFNOSUPPORT; break; } } break; case SIOCSIFMTU: /* * Set the interface MTU. * mtu can't be larger than ARCMTU for RFC1051 * and can't be larger than ARC_PHDS_MTU */ if (((ifp->if_flags & IFF_LINK0) && ifr->ifr_mtu > ARCMTU) || ifr->ifr_mtu > ARC_PHDS_MAXMTU) error = EINVAL; else ifp->if_mtu = ifr->ifr_mtu; break; + } -#if 0 - case SIOCGIFADDR: - { - struct sockaddr *sa; + return (error); +} - sa = (struct sockaddr *) & ifr->ifr_data; - bcopy(IFP2AC(ifp)->ac_enaddr, - (caddr_t) sa->sa_data, ETHER_ADDR_LEN); +/* based on ether_resolvemulti() */ +int +arc_resolvemulti(ifp, llsa, sa) + struct ifnet *ifp; + struct sockaddr **llsa; + struct sockaddr *sa; +{ + struct sockaddr_dl *sdl; + struct sockaddr_in *sin; +#ifdef INET6 + struct sockaddr_in6 *sin6; +#endif + + switch(sa->sa_family) { + case AF_LINK: + /* + * No mapping needed. Just check that it's a valid MC address. + */ + sdl = (struct sockaddr_dl *)sa; + if (*LLADDR(sdl) != arcbroadcastaddr) + return EADDRNOTAVAIL; + *llsa = 0; + return 0; +#ifdef INET + case AF_INET: + sin = (struct sockaddr_in *)sa; + if (!IN_MULTICAST(ntohl(sin->sin_addr.s_addr))) + return EADDRNOTAVAIL; + MALLOC(sdl, struct sockaddr_dl *, sizeof *sdl, M_IFMADDR, + M_WAITOK|M_ZERO); + sdl->sdl_len = sizeof *sdl; + sdl->sdl_family = AF_LINK; + sdl->sdl_index = ifp->if_index; + sdl->sdl_type = IFT_ARCNET; + sdl->sdl_alen = ARC_ADDR_LEN; + *LLADDR(sdl) = 0; + *llsa = (struct sockaddr *)sdl; + return 0; +#endif +#ifdef INET6 + case AF_INET6: + sin6 = (struct sockaddr_in6 *)sa; + if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) { + /* + * An IP6 address of 0 means listen to all + * of the Ethernet multicast address used for IP6. + * (This is used for multicast routers.) + */ + ifp->if_flags |= IFF_ALLMULTI; + *llsa = 0; + return 0; } - break; + if (!IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) + return EADDRNOTAVAIL; + MALLOC(sdl, struct sockaddr_dl *, sizeof *sdl, M_IFMADDR, + M_WAITOK|M_ZERO); + sdl->sdl_len = sizeof *sdl; + sdl->sdl_family = AF_LINK; + sdl->sdl_index = ifp->if_index; + sdl->sdl_type = IFT_ARCNET; + sdl->sdl_alen = ARC_ADDR_LEN; + *LLADDR(sdl) = 0; + *llsa = (struct sockaddr *)sdl; + return 0; #endif - } - return (error); + default: + /* + * Well, the text isn't quite right, but it's the name + * that counts... + */ + return EAFNOSUPPORT; + } }