Esempio n. 1
0
static int
agrether_ctor(struct agr_softc *sc, struct ifnet *ifp_port)
{
	struct ifnet *ifp = &sc->sc_if;
	struct ethercom *ec = (void *)ifp;
	struct agrether_private *priv;

	priv = malloc(sizeof(*priv), M_DEVBUF, M_NOWAIT | M_ZERO);
	if (!priv)
		return ENOMEM;

	agr_mc_init(sc, &priv->aep_multiaddrs);

	sc->sc_iftprivate = priv;
	/* inherit ports capabilities
	 * XXX this really needs to be the intersection of all
	 * ports capabilities, not just the latest port.
	 * Okay if ports are the same.
	 */
	ifp->if_capabilities = ifp_port->if_capabilities &
			(IFCAP_TSOv4 | IFCAP_TSOv6 |
			IFCAP_CSUM_IPv4_Tx | IFCAP_CSUM_IPv4_Rx |
			IFCAP_CSUM_TCPv4_Tx | IFCAP_CSUM_TCPv4_Rx |
			IFCAP_CSUM_UDPv4_Tx | IFCAP_CSUM_UDPv4_Rx |
			IFCAP_CSUM_TCPv6_Tx | IFCAP_CSUM_TCPv6_Rx |
			IFCAP_CSUM_UDPv6_Tx | IFCAP_CSUM_UDPv6_Rx);

	ether_ifattach(ifp, CLLADDR(ifp_port->if_sadl));
	ec->ec_capabilities =
	    ETHERCAP_VLAN_MTU | ETHERCAP_VLAN_HWTAGGING | ETHERCAP_JUMBO_MTU;

	ieee8023ad_ctor(sc);

	return 0;
}
Esempio n. 2
0
/*
 * The helper functions make Andrew Brown's interface really
 * shine.  It makes possible to create value on the fly whether
 * the sysctl value is read or written.
 *
 * As shown as an example in the man page, the first step is to
 * create a copy of the node to have sysctl_lookup work on it.
 *
 * Here, we have more work to do than just a copy, since we have
 * to create the string.  The first step is to collect the actual
 * value of the node, which is a convenient pointer to the softc
 * of the interface.  From there we create the string and use it
 * as the value, but only for the *copy* of the node.
 *
 * Then we let sysctl_lookup do the magic, which consists in
 * setting oldp and newp as required by the operation.  When the
 * value is read, that means that the string will be copied to
 * the user, and when it is written, the new value will be copied
 * over in the addr array.
 *
 * If newp is NULL, the user was reading the value, so we don't
 * have anything else to do.  If a new value was written, we
 * have to check it.
 *
 * If it is incorrect, we can return an error and leave 'node' as
 * it is:  since it is a copy of the actual node, the change will
 * be forgotten.
 *
 * Upon a correct input, we commit the change to the ifnet
 * structure of our interface.
 */
static int
tap_sysctl_handler(SYSCTLFN_ARGS)
{
	struct sysctlnode node;
	struct tap_softc *sc;
	struct ifnet *ifp;
	int error;
	size_t len;
	char addr[3 * ETHER_ADDR_LEN];
	uint8_t enaddr[ETHER_ADDR_LEN];

	node = *rnode;
	sc = node.sysctl_data;
	ifp = &sc->sc_ec.ec_if;
	(void)ether_snprintf(addr, sizeof(addr), CLLADDR(ifp->if_sadl));
	node.sysctl_data = addr;
	error = sysctl_lookup(SYSCTLFN_CALL(&node));
	if (error || newp == NULL)
		return (error);

	len = strlen(addr);
	if (len < 11 || len > 17)
		return (EINVAL);

	/* Commit change */
	if (ether_aton_r(enaddr, sizeof(enaddr), addr) != 0)
		return (EINVAL);
	if_set_sadl(ifp, enaddr, ETHER_ADDR_LEN, false);
	return (error);
}
Esempio n. 3
0
int
atmresolve(struct rtentry *rt0, struct mbuf *m, const struct sockaddr *dst,
    struct atm_pseudohdr *desten /* OUT */)
{
	const struct sockaddr_dl *sdl;
	struct rtentry *rt = rt0;

	if (m->m_flags & (M_BCAST|M_MCAST)) {
		log(LOG_INFO, "atmresolve: BCAST/MCAST packet detected/dumped\n");
		goto bad;
	}

	if (rt == NULL) {
		rt = RTALLOC1(dst, 0);
		if (rt == NULL)
			goto bad; /* failed */
		if ((rt->rt_flags & RTF_GATEWAY) != 0 ||
		    /* XXX: are we using LLINFO? */
		    rt->rt_gateway->sa_family != AF_LINK) {
			rtfree(rt);
			goto bad;
		}
	}

	/*
	 * note that rt_gateway is a sockaddr_dl which contains the
	 * atm_pseudohdr data structure for this route.   we currently
	 * don't need any rt_llinfo info (but will if we want to support
	 * ATM ARP [c.f. if_ether.c]).
	 */

	sdl = satocsdl(rt->rt_gateway);

	/*
	 * Check the address family and length is valid, the address
	 * is resolved; otherwise, try to resolve.
	 */

	if (sdl->sdl_family == AF_LINK && sdl->sdl_alen == sizeof(*desten)) {
		memcpy(desten, CLLADDR(sdl), sdl->sdl_alen);
		if (rt != rt0)
			rtfree(rt);
		return (1);	/* ok, go for it! */
	}

	if (rt != rt0)
		rtfree(rt);

	/*
	 * we got an entry, but it doesn't have valid link address
	 * info in it (it is prob. the interface route, which has
	 * sdl_alen == 0).    dump packet.  (fall through to "bad").
	 */

bad:
	m_freem(m);
	return (0);
}
Esempio n. 4
0
/*
 * Do a hardware reset of the board, and upload the ethernet address again in
 * case the board forgets.
 */
static inline void
el_hardreset(struct el_softc *sc)
{
	bus_space_tag_t iot = sc->sc_iot;
	bus_space_handle_t ioh = sc->sc_ioh;
	int i;

	bus_space_write_1(iot, ioh, EL_AC, EL_AC_RESET);
	delay(5);
	bus_space_write_1(iot, ioh, EL_AC, 0);

	for (i = 0; i < ETHER_ADDR_LEN; i++)
		bus_space_write_1(iot, ioh, i,
		    CLLADDR(sc->sc_ethercom.ec_if.if_sadl)[i]);
}
Esempio n. 5
0
static void 
camprogram(struct sn_softc *sc)
{
	struct ether_multistep step;
	struct ether_multi *enm;
	struct ifnet *ifp;
	int	timeout;
	int	mcount = 0;

	caminitialise(sc);

	ifp = &sc->sc_if;

	/* Always load our own address first. */
	camentry(sc, mcount, CLLADDR(ifp->if_sadl));
	mcount++;

	/* Assume we won't need allmulti bit. */
	ifp->if_flags &= ~IFF_ALLMULTI;

	/* Loop through multicast addresses */
	ETHER_FIRST_MULTI(step, &sc->sc_ethercom, enm);
	while (enm != NULL) {
		if (mcount == MAXCAM) {
			 ifp->if_flags |= IFF_ALLMULTI;
			 break;
		}

		if (memcmp(enm->enm_addrlo, enm->enm_addrhi,
		    sizeof(enm->enm_addrlo)) != 0) {
			/*
			 * SONIC's CAM is programmed with specific
			 * addresses. It has no way to specify a range.
			 * (Well, thats not exactly true. If the
			 * range is small one could program each addr
			 * within the range as a separate CAM entry)
			 */
			ifp->if_flags |= IFF_ALLMULTI;
			break;
		}

		/* program the CAM with the specified entry */
		camentry(sc, mcount, enm->enm_addrlo);
		mcount++;

		ETHER_NEXT_MULTI(step, enm);
	}

	NIC_PUT(sc, SNR_CDP, LOWER(sc->v_cda));
	NIC_PUT(sc, SNR_CDC, MAXCAM);
	NIC_PUT(sc, SNR_CR, CR_LCAM);
	wbflush();

	timeout = 10000;
	while ((NIC_GET(sc, SNR_CR) & CR_LCAM) && timeout--)
		delay(10);
	if (timeout == 0) {
		/* XXX */
		panic("%s: CAM initialisation failed",
		    device_xname(sc->sc_dev));
	}
	timeout = 10000;
	while (((NIC_GET(sc, SNR_ISR) & ISR_LCD) == 0) && timeout--)
		delay(10);

	if (NIC_GET(sc, SNR_ISR) & ISR_LCD)
		NIC_PUT(sc, SNR_ISR, ISR_LCD);
	else
		printf("%s: CAM initialisation without interrupt\n",
		    device_xname(sc->sc_dev));
}
Esempio n. 6
0
int
smsc_chip_init(struct smsc_softc *sc)
{
    int err;
    uint32_t reg_val;
    int burst_cap;

    /* Enter H/W config mode */
    smsc_write_reg(sc, SMSC_HW_CFG, SMSC_HW_CFG_LRST);

    if ((err = smsc_wait_for_bits(sc, SMSC_HW_CFG,
                                  SMSC_HW_CFG_LRST)) != 0) {
        smsc_warn_printf(sc, "timed-out waiting for reset to "
                         "complete\n");
        goto init_failed;
    }

    /* Reset the PHY */
    smsc_write_reg(sc, SMSC_PM_CTRL, SMSC_PM_CTRL_PHY_RST);

    if ((err = smsc_wait_for_bits(sc, SMSC_PM_CTRL,
                                  SMSC_PM_CTRL_PHY_RST) != 0)) {
        smsc_warn_printf(sc, "timed-out waiting for phy reset to "
                         "complete\n");
        goto init_failed;
    }
    usbd_delay_ms(sc->sc_udev, 40);

    /* Set the mac address */
    struct ifnet *ifp = &sc->sc_ec.ec_if;
    const char *eaddr = CLLADDR(ifp->if_sadl);
    if ((err = smsc_setmacaddress(sc, eaddr)) != 0) {
        smsc_warn_printf(sc, "failed to set the MAC address\n");
        goto init_failed;
    }

    /*
     * Don't know what the HW_CFG_BIR bit is, but following the reset
     * sequence as used in the Linux driver.
     */
    if ((err = smsc_read_reg(sc, SMSC_HW_CFG, &reg_val)) != 0) {
        smsc_warn_printf(sc, "failed to read HW_CFG: %d\n", err);
        goto init_failed;
    }
    reg_val |= SMSC_HW_CFG_BIR;
    smsc_write_reg(sc, SMSC_HW_CFG, reg_val);

    /*
     * There is a so called 'turbo mode' that the linux driver supports, it
     * seems to allow you to jam multiple frames per Rx transaction.
     * By default this driver supports that and therefore allows multiple
     * frames per USB transfer.
     *
     * The xfer buffer size needs to reflect this as well, therefore based
     * on the calculations in the Linux driver the RX bufsize is set to
     * 18944,
     *     bufsz = (16 * 1024 + 5 * 512)
     *
     * Burst capability is the number of URBs that can be in a burst of
     * data/ethernet frames.
     */

    if (sc->sc_udev->speed == USB_SPEED_HIGH)
        burst_cap = 37;
    else
        burst_cap = 128;

    smsc_write_reg(sc, SMSC_BURST_CAP, burst_cap);

    /* Set the default bulk in delay (magic value from Linux driver) */
    smsc_write_reg(sc, SMSC_BULK_IN_DLY, 0x00002000);

    /*
     * Initialise the RX interface
     */
    if ((err = smsc_read_reg(sc, SMSC_HW_CFG, &reg_val)) < 0) {
        smsc_warn_printf(sc, "failed to read HW_CFG: (err = %d)\n",
                         err);
        goto init_failed;
    }

    /*
     * The following settings are used for 'turbo mode', a.k.a multiple
     * frames per Rx transaction (again info taken form Linux driver).
     */
    reg_val |= (SMSC_HW_CFG_MEF | SMSC_HW_CFG_BCE);

    /*
     * set Rx data offset to ETHER_ALIGN which will make the IP header
     * align on a word boundary.
     */
    reg_val |= ETHER_ALIGN << SMSC_HW_CFG_RXDOFF_SHIFT;

    smsc_write_reg(sc, SMSC_HW_CFG, reg_val);

    /* Clear the status register ? */
    smsc_write_reg(sc, SMSC_INTR_STATUS, 0xffffffff);

    /* Read and display the revision register */
    if ((err = smsc_read_reg(sc, SMSC_ID_REV, &sc->sc_rev_id)) < 0) {
        smsc_warn_printf(sc, "failed to read ID_REV (err = %d)\n", err);
        goto init_failed;
    }

    /* GPIO/LED setup */
    reg_val = SMSC_LED_GPIO_CFG_SPD_LED | SMSC_LED_GPIO_CFG_LNK_LED |
              SMSC_LED_GPIO_CFG_FDX_LED;
    smsc_write_reg(sc, SMSC_LED_GPIO_CFG, reg_val);

    /*
     * Initialise the TX interface
     */
    smsc_write_reg(sc, SMSC_FLOW, 0);

    smsc_write_reg(sc, SMSC_AFC_CFG, AFC_CFG_DEFAULT);

    /* Read the current MAC configuration */
    if ((err = smsc_read_reg(sc, SMSC_MAC_CSR, &sc->sc_mac_csr)) < 0) {
        smsc_warn_printf(sc, "failed to read MAC_CSR (err=%d)\n", err);
        goto init_failed;
    }

    /* disable pad stripping, collides with checksum offload */
    sc->sc_mac_csr &= ~SMSC_MAC_CSR_PADSTR;

    /* Vlan */
    smsc_write_reg(sc, SMSC_VLAN1, (uint32_t)ETHERTYPE_VLAN);

    /*
     * Start TX
     */
    sc->sc_mac_csr |= SMSC_MAC_CSR_TXEN;
    smsc_write_reg(sc, SMSC_MAC_CSR, sc->sc_mac_csr);
    smsc_write_reg(sc, SMSC_TX_CFG, SMSC_TX_CFG_ON);

    /*
     * Start RX
     */
    sc->sc_mac_csr |= SMSC_MAC_CSR_RXEN;
    smsc_write_reg(sc, SMSC_MAC_CSR, sc->sc_mac_csr);

    return (0);

init_failed:
    smsc_err_printf(sc, "smsc_chip_init failed (err=%d)\n", err);
    return (err);
}
Esempio n. 7
0
void
atm_rtrequest(int req, struct rtentry *rt, const struct rt_addrinfo *info)
{
	struct sockaddr *gate = rt->rt_gateway;
	struct atm_pseudoioctl api;
#ifdef NATM
	const struct sockaddr_in *sin;
	struct natmpcb *npcb = NULL;
	const struct atm_pseudohdr *aph;
#endif
	const struct ifnet *ifp = rt->rt_ifp;
	uint8_t namelen = strlen(ifp->if_xname);
	uint8_t addrlen = ifp->if_addrlen;

	if (rt->rt_flags & RTF_GATEWAY)   /* link level requests only */
		return;

	switch (req) {

	case RTM_RESOLVE: /* resolve: only happens when cloning */
		printf("atm_rtrequest: RTM_RESOLVE request detected?\n");
		break;

	case RTM_ADD:

		/*
		 * route added by a command (e.g. ifconfig, route, arp...).
		 *
		 * first check to see if this is not a host route, in which
		 * case we are being called via "ifconfig" to set the address.
		 */

		if ((rt->rt_flags & RTF_HOST) == 0) {
			union {
				struct sockaddr sa;
				struct sockaddr_dl sdl;
				struct sockaddr_storage ss;
			} u;

			sockaddr_dl_init(&u.sdl, sizeof(u.ss),
			    ifp->if_index, ifp->if_type,
			    NULL, namelen, NULL, addrlen);
			rt_setgate(rt, &u.sa);
			gate = rt->rt_gateway;
			break;
		}

		if ((rt->rt_flags & RTF_CLONING) != 0) {
			printf("atm_rtrequest: cloning route detected?\n");
			break;
		}
		if (gate->sa_family != AF_LINK ||
		    gate->sa_len < sockaddr_dl_measure(namelen, addrlen)) {
			log(LOG_DEBUG, "atm_rtrequest: bad gateway value\n");
			break;
		}

#ifdef DIAGNOSTIC
		if (rt->rt_ifp->if_ioctl == NULL) panic("atm null ioctl");
#endif

#ifdef NATM
		/*
		 * let native ATM know we are using this VCI/VPI
		 * (i.e. reserve it)
		 */
		sin = satocsin(rt_getkey(rt));
		if (sin->sin_family != AF_INET)
			goto failed;
		aph = (const struct atm_pseudohdr *)CLLADDR(satosdl(gate));
		npcb = npcb_add(NULL, rt->rt_ifp, ATM_PH_VCI(aph),
						ATM_PH_VPI(aph));
		if (npcb == NULL)
			goto failed;
		npcb->npcb_flags |= NPCB_IP;
		npcb->ipaddr.s_addr = sin->sin_addr.s_addr;
		/* XXX: move npcb to llinfo when ATM ARP is ready */
		rt->rt_llinfo = (void *) npcb;
		rt->rt_flags |= RTF_LLINFO;
#endif
		/*
		 * let the lower level know this circuit is active
		 */
		memcpy(&api.aph, CLLADDR(satocsdl(gate)), sizeof(api.aph));
		api.rxhand = NULL;
		if (rt->rt_ifp->if_ioctl(rt->rt_ifp, SIOCATMENA, &api) != 0) {
			printf("atm: couldn't add VC\n");
			goto failed;
		}

		satosdl(gate)->sdl_type = rt->rt_ifp->if_type;
		satosdl(gate)->sdl_index = rt->rt_ifp->if_index;

		break;

failed:
#ifdef NATM
		if (npcb) {
			npcb_free(npcb, NPCB_DESTROY);
			rt->rt_llinfo = NULL;
			rt->rt_flags &= ~RTF_LLINFO;
		}
#endif
		rtrequest(RTM_DELETE, rt_getkey(rt), NULL,
			rt_mask(rt), 0, NULL);
		break;

	case RTM_DELETE:

#ifdef NATM
		/*
		 * tell native ATM we are done with this VC
		 */

		if (rt->rt_flags & RTF_LLINFO) {
			npcb_free((struct natmpcb *)rt->rt_llinfo,
								NPCB_DESTROY);
			rt->rt_llinfo = NULL;
			rt->rt_flags &= ~RTF_LLINFO;
		}
#endif
		/*
		 * tell the lower layer to disable this circuit
		 */

		memcpy(&api.aph, CLLADDR(satocsdl(gate)), sizeof(api.aph));
		api.rxhand = NULL;
		(void)rt->rt_ifp->if_ioctl(rt->rt_ifp, SIOCATMDIS, &api);

		break;
	}
}
Esempio n. 8
0
int
manage_link(int fd)
{
	char *p, *e, *cp;
	char ifname[IF_NAMESIZE];
	ssize_t bytes;
	struct rt_msghdr *rtm;
	struct if_announcemsghdr *ifan;
	struct if_msghdr *ifm;
	struct ifa_msghdr *ifam;
	struct sockaddr *sa, *rti_info[RTAX_MAX];
	int len;
	struct sockaddr_dl sdl;
#ifdef INET
	struct rt rt;
#endif
#if defined(INET6) && !defined(LISTEN_DAD)
	struct in6_addr ia6;
	struct sockaddr_in6 *sin6;
	int ifa_flags;
#endif

	for (;;) {
		if (ioctl(fd, FIONREAD, &len) == -1)
			return -1;
		if (link_buflen < len) {
			p = realloc(link_buf, len);
			if (p == NULL)
				return -1;
			link_buf = p;
			link_buflen = len;
		}
		bytes = read(fd, link_buf, link_buflen);
		if (bytes == -1) {
			if (errno == EAGAIN)
				return 0;
			if (errno == EINTR)
				continue;
			return -1;
		}
		e = link_buf + bytes;
		for (p = link_buf; p < e; p += rtm->rtm_msglen) {
			rtm = (struct rt_msghdr *)(void *)p;
			// Ignore messages generated by us
			if (rtm->rtm_pid == getpid())
				break;
			switch(rtm->rtm_type) {
#ifdef RTM_IFANNOUNCE
			case RTM_IFANNOUNCE:
				ifan = (struct if_announcemsghdr *)(void *)p;
				switch(ifan->ifan_what) {
				case IFAN_ARRIVAL:
					handle_interface(1, ifan->ifan_name);
					break;
				case IFAN_DEPARTURE:
					handle_interface(-1, ifan->ifan_name);
					break;
				}
				break;
#endif
			case RTM_IFINFO:
				ifm = (struct if_msghdr *)(void *)p;
				memset(ifname, 0, sizeof(ifname));
				if (!(if_indextoname(ifm->ifm_index, ifname)))
					break;
				switch (ifm->ifm_data.ifi_link_state) {
				case LINK_STATE_DOWN:
					len = LINK_DOWN;
					break;
				case LINK_STATE_UP:
					len = LINK_UP;
					break;
				default:
					/* handle_carrier will re-load
					 * the interface flags and check for
					 * IFF_RUNNING as some drivers that
					 * don't handle link state also don't
					 * set IFF_RUNNING when this routing
					 * message is generated.
					 * As such, it is a race ...*/
					len = LINK_UNKNOWN;
					break;
				}
				handle_carrier(len, ifm->ifm_flags, ifname);
				break;
			case RTM_DELETE:
				if (~rtm->rtm_addrs &
				    (RTA_DST | RTA_GATEWAY | RTA_NETMASK))
					break;
				cp = (char *)(void *)(rtm + 1);
				sa = (struct sockaddr *)(void *)cp;
				if (sa->sa_family != AF_INET)
					break;
#ifdef INET
				get_addrs(rtm->rtm_addrs, cp, rti_info);
				memset(&rt, 0, sizeof(rt));
				rt.iface = NULL;
				COPYOUT(rt.dest, rti_info[RTAX_DST]);
				COPYOUT(rt.net, rti_info[RTAX_NETMASK]);
				COPYOUT(rt.gate, rti_info[RTAX_GATEWAY]);
				ipv4_routedeleted(&rt);
#endif
				break;
#ifdef RTM_CHGADDR
			case RTM_CHGADDR:	/* FALLTHROUGH */
#endif
			case RTM_DELADDR:	/* FALLTHROUGH */
			case RTM_NEWADDR:
				ifam = (struct ifa_msghdr *)(void *)p;
				if (!if_indextoname(ifam->ifam_index, ifname))
					break;
				cp = (char *)(void *)(ifam + 1);
				get_addrs(ifam->ifam_addrs, cp, rti_info);
				if (rti_info[RTAX_IFA] == NULL)
					break;
				switch (rti_info[RTAX_IFA]->sa_family) {
				case AF_LINK:
#ifdef RTM_CHGADDR
					if (rtm->rtm_type != RTM_CHGADDR)
						break;
#else
					if (rtm->rtm_type != RTM_NEWADDR)
						break;
#endif
					memcpy(&sdl, rti_info[RTAX_IFA],
					    rti_info[RTAX_IFA]->sa_len);
					handle_hwaddr(ifname,
					    (const unsigned char*)CLLADDR(&sdl),
					    sdl.sdl_alen);
					break;
#ifdef INET
				case AF_INET:
				case 255: /* FIXME: Why 255? */
					COPYOUT(rt.dest, rti_info[RTAX_IFA]);
					COPYOUT(rt.net, rti_info[RTAX_NETMASK]);
					COPYOUT(rt.gate, rti_info[RTAX_BRD]);
					ipv4_handleifa(rtm->rtm_type,
					    NULL, ifname,
					    &rt.dest, &rt.net, &rt.gate);
					break;
#endif
#if defined(INET6) && !defined(LISTEN_DAD)
				case AF_INET6:
					sin6 = (struct sockaddr_in6*)(void *)
					    rti_info[RTAX_IFA];
					memcpy(ia6.s6_addr,
					    sin6->sin6_addr.s6_addr,
					    sizeof(ia6.s6_addr));
					if (rtm->rtm_type == RTM_NEWADDR) {
						ifa_flags = in6_addr_flags(
								ifname,
								&ia6);
						if (ifa_flags == -1)
							break;
					} else
						ifa_flags = 0;
					ipv6_handleifa(rtm->rtm_type, NULL,
					    ifname, &ia6, ifa_flags);
					break;
#endif
				}
				break;
			}
		}
	}
}
Esempio n. 9
0
/*
 * Get interface identifier for the specified interface.
 *
 * in6 - upper 64bits are preserved
 */
int
in6_get_hw_ifid(struct ifnet *ifp, struct in6_addr *in6)
{
	struct ifaddr *ifa;
	const struct sockaddr_dl *sdl = NULL, *tsdl;
	const char *addr;
	size_t addrlen;
	static u_int8_t allzero[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
	static u_int8_t allone[8] =
		{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };

	IFADDR_FOREACH(ifa, ifp) {
		if (ifa->ifa_addr->sa_family != AF_LINK)
			continue;
		tsdl = satocsdl(ifa->ifa_addr);
		if (tsdl == NULL || tsdl->sdl_alen == 0)
			continue;
		if (sdl == NULL || ifa == ifp->if_dl || ifa == ifp->if_hwdl)
			sdl = tsdl;
		if (ifa == ifp->if_hwdl)
			break;
	}

	if (sdl == NULL)
		return -1;

	addr = CLLADDR(sdl);
	addrlen = sdl->sdl_alen;

	switch (ifp->if_type) {
	case IFT_IEEE1394:
	case IFT_IEEE80211:
		/* IEEE1394 uses 16byte length address starting with EUI64 */
		if (addrlen > 8)
			addrlen = 8;
		break;
	default:
		break;
	}

	/* get EUI64 */
	switch (ifp->if_type) {
	/* IEEE802/EUI64 cases - what others? */
	case IFT_ETHER:
	case IFT_FDDI:
	case IFT_ATM:
	case IFT_IEEE1394:
	case IFT_IEEE80211:
		/* look at IEEE802/EUI64 only */
		if (addrlen != 8 && addrlen != 6)
			return -1;

		/*
		 * check for invalid MAC address - on bsdi, we see it a lot
		 * since wildboar configures all-zero MAC on pccard before
		 * card insertion.
		 */
		if (memcmp(addr, allzero, addrlen) == 0)
			return -1;
		if (memcmp(addr, allone, addrlen) == 0)
			return -1;

		/* make EUI64 address */
		if (addrlen == 8)
			memcpy(&in6->s6_addr[8], addr, 8);
		else if (addrlen == 6) {
			in6->s6_addr[8] = addr[0];
			in6->s6_addr[9] = addr[1];
			in6->s6_addr[10] = addr[2];
			in6->s6_addr[11] = 0xff;
			in6->s6_addr[12] = 0xfe;
			in6->s6_addr[13] = addr[3];
			in6->s6_addr[14] = addr[4];
			in6->s6_addr[15] = addr[5];
		}
		break;

	case IFT_ARCNET:
		if (addrlen != 1)
			return -1;
		if (!addr[0])
			return -1;

		memset(&in6->s6_addr[8], 0, 8);
		in6->s6_addr[15] = addr[0];

		/*
		 * due to insufficient bitwidth, we mark it local.
		 */
		in6->s6_addr[8] &= ~EUI64_GBIT;	/* g bit to "individual" */
		in6->s6_addr[8] |= EUI64_UBIT;	/* u bit to "local" */
		break;

	case IFT_GIF:
#ifdef IFT_STF
	case IFT_STF:
#endif
		/*
		 * RFC2893 says: "SHOULD use IPv4 address as ifid source".
		 * however, IPv4 address is not very suitable as unique
		 * identifier source (can be renumbered).
		 * we don't do this.
		 */
		return -1;

	default:
		return -1;
	}

	/* sanity check: g bit must not indicate "group" */
	if (EUI64_GROUP(in6))
		return -1;

	/* convert EUI64 into IPv6 interface identifier */
	EUI64_TO_IFID(in6);

	/*
	 * sanity check: ifid must not be all zero, avoid conflict with
	 * subnet router anycast
	 */
	if ((in6->s6_addr[8] & ~(EUI64_GBIT | EUI64_UBIT)) == 0x00 &&
	    memcmp(&in6->s6_addr[9], allzero, 7) == 0) {
		return -1;
	}

	return 0;
}
Esempio n. 10
0
static int
ieee1394_output(struct ifnet *ifp, struct mbuf *m0, const struct sockaddr *dst,
    const struct rtentry *rt)
{
	uint16_t etype = 0;
	struct mbuf *m;
	int hdrlen, error = 0;
	struct mbuf *mcopy = NULL;
	struct ieee1394_hwaddr *hwdst, baddr;
	const struct ieee1394_hwaddr *myaddr;
#ifdef INET
	struct arphdr *ah;
#endif /* INET */
	struct m_tag *mtag;
	int unicast;

	if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING))
		senderr(ENETDOWN);

	/*
	 * If the queueing discipline needs packet classification,
	 * do it before prepending link headers.
	 */
	IFQ_CLASSIFY(&ifp->if_snd, m0, dst->sa_family);

	/*
	 * For unicast, we make a tag to store the lladdr of the
	 * destination. This might not be the first time we have seen
	 * the packet (for instance, the arp code might be trying to
	 * re-send it after receiving an arp reply) so we only
	 * allocate a tag if there isn't one there already. For
	 * multicast, we will eventually use a different tag to store
	 * the channel number.
	 */
	unicast = !(m0->m_flags & (M_BCAST | M_MCAST));
	if (unicast) {
		mtag =
		    m_tag_find(m0, MTAG_FIREWIRE_HWADDR, NULL);
		if (!mtag) {
			mtag = m_tag_get(MTAG_FIREWIRE_HWADDR,
			    sizeof (struct ieee1394_hwaddr), M_NOWAIT);
			if (!mtag) {
				error = ENOMEM;
				goto bad;
			}
			m_tag_prepend(m0, mtag);
		}
		hwdst = (struct ieee1394_hwaddr *)(mtag + 1);
	} else {
		hwdst = &baddr;
	}

	switch (dst->sa_family) {
#ifdef INET
	case AF_INET:
		if (unicast &&
		    (error = arpresolve(ifp, rt, m0, dst, hwdst,
			sizeof(*hwdst))) != 0)
			return error == EWOULDBLOCK ? 0 : error;
		/* if broadcasting on a simplex interface, loopback a copy */
		if ((m0->m_flags & M_BCAST) && (ifp->if_flags & IFF_SIMPLEX))
			mcopy = m_copy(m0, 0, M_COPYALL);
		etype = htons(ETHERTYPE_IP);
		break;
	case AF_ARP:
		ah = mtod(m0, struct arphdr *);
		ah->ar_hrd = htons(ARPHRD_IEEE1394);
		etype = htons(ETHERTYPE_ARP);
		break;
#endif /* INET */
#ifdef INET6
	case AF_INET6:
		if (unicast && (!nd6_storelladdr(ifp, rt, m0, dst,
		    hwdst->iha_uid, IEEE1394_ADDR_LEN))) {
			/* something bad happened */
			return 0;
		}
		etype = htons(ETHERTYPE_IPV6);
		break;
#endif /* INET6 */

	case pseudo_AF_HDRCMPLT:
	case AF_UNSPEC:
		/* TODO? */
	default:
		printf("%s: can't handle af%d\n", ifp->if_xname,
		    dst->sa_family);
		senderr(EAFNOSUPPORT);
		break;
	}

	if (mcopy)
		looutput(ifp, mcopy, dst, rt);
	myaddr = (const struct ieee1394_hwaddr *)CLLADDR(ifp->if_sadl);
	if (ifp->if_bpf) {
		struct ieee1394_bpfhdr h;
		if (unicast)
			memcpy(h.ibh_dhost, hwdst->iha_uid, 8);
		else
			memcpy(h.ibh_dhost,
			    ((const struct ieee1394_hwaddr *)
			    ifp->if_broadcastaddr)->iha_uid, 8);
		memcpy(h.ibh_shost, myaddr->iha_uid, 8);
		h.ibh_type = etype;
		bpf_mtap2(ifp->if_bpf, &h, sizeof(h), m0);
	}
	if ((ifp->if_flags & IFF_SIMPLEX) &&
	    unicast &&
	    memcmp(hwdst, myaddr, IEEE1394_ADDR_LEN) == 0)
		return looutput(ifp, m0, dst, rt);

	/*
	 * XXX:
	 * The maximum possible rate depends on the topology.
	 * So the determination of maxrec and fragmentation should be
	 * called from the driver after probing the topology map.
	 */
	if (unicast) {
		hdrlen = IEEE1394_GASP_LEN;
		hwdst->iha_speed = 0;	/* XXX */
	} else
		hdrlen = 0;

	if (hwdst->iha_speed > myaddr->iha_speed)
		hwdst->iha_speed = myaddr->iha_speed;
	if (hwdst->iha_maxrec > myaddr->iha_maxrec)
		hwdst->iha_maxrec = myaddr->iha_maxrec;
	if (hwdst->iha_maxrec > (8 + hwdst->iha_speed))
		hwdst->iha_maxrec = 8 + hwdst->iha_speed;
	if (hwdst->iha_maxrec < 8)
			hwdst->iha_maxrec = 8;

	m0 = ieee1394_fragment(ifp, m0, (2<<hwdst->iha_maxrec) - hdrlen, etype);
	if (m0 == NULL)
		senderr(ENOBUFS);

	while ((m = m0) != NULL) {
		m0 = m->m_nextpkt;

		error = if_transmit_lock(ifp, m);
		if (error) {
			/* mbuf is already freed */
			goto bad;
		}
	}
	return 0;

  bad:
	while (m0 != NULL) {
		m = m0->m_nextpkt;
		m_freem(m0);
		m0 = m;
	}

	return error;
}
Esempio n. 11
0
/*
 * Create a setup packet and put in queue for sending.
 */
void
ze_setup(struct ze_softc *sc)
{
	struct ether_multi *enm;
	struct ether_multistep step;
	struct ze_cdata *zc = sc->sc_zedata;
	struct ifnet *ifp = &sc->sc_if;
	const u_int8_t *enaddr = CLLADDR(ifp->if_sadl);
	int j, idx, reg;

	if (sc->sc_inq == (TXDESCS - 1)) {
		sc->sc_setup = 1;
		return;
	}
	sc->sc_setup = 0;
	/*
	 * Init the setup packet with valid info.
	 */
	memset(zc->zc_setup, 0xff, sizeof(zc->zc_setup)); /* Broadcast */
	memcpy(zc->zc_setup, enaddr, ETHER_ADDR_LEN);

	/*
	 * Multicast handling. The SGEC can handle up to 16 direct
	 * ethernet addresses.
	 */
	j = 16;
	ifp->if_flags &= ~IFF_ALLMULTI;
	ETHER_FIRST_MULTI(step, &sc->sc_ec, enm);
	while (enm != NULL) {
		if (memcmp(enm->enm_addrlo, enm->enm_addrhi, 6)) {
			ifp->if_flags |= IFF_ALLMULTI;
			break;
		}
		memcpy(&zc->zc_setup[j], enm->enm_addrlo, ETHER_ADDR_LEN);
		j += 8;
		ETHER_NEXT_MULTI(step, enm);
		if ((enm != NULL)&& (j == 128)) {
			ifp->if_flags |= IFF_ALLMULTI;
			break;
		}
	}

	/*
	 * ALLMULTI implies PROMISC in this driver.
	 */
	if (ifp->if_flags & IFF_ALLMULTI)
		ifp->if_flags |= IFF_PROMISC;
	else if (ifp->if_pcount == 0)
		ifp->if_flags &= ~IFF_PROMISC;

	/*
	 * Fiddle with the receive logic.
	 */
	reg = ZE_RCSR(ZE_CSR6);
	DELAY(10);
	ZE_WCSR(ZE_CSR6, reg & ~ZE_NICSR6_SR); /* Stop rx */
	reg &= ~ZE_NICSR6_AF;
	if (ifp->if_flags & IFF_PROMISC)
		reg |= ZE_NICSR6_AF_PROM;
	else if (ifp->if_flags & IFF_ALLMULTI)
		reg |= ZE_NICSR6_AF_ALLM;
	DELAY(10);
	ZE_WCSR(ZE_CSR6, reg);
	/*
	 * Only send a setup packet if needed.
	 */
	if ((ifp->if_flags & (IFF_PROMISC|IFF_ALLMULTI)) == 0) {
		idx = sc->sc_nexttx;
		zc->zc_xmit[idx].ze_tdes1 = ZE_TDES1_DT_SETUP;
		zc->zc_xmit[idx].ze_bufsize = 128;
		zc->zc_xmit[idx].ze_bufaddr = sc->sc_pzedata->zc_setup;
		zc->zc_xmit[idx].ze_tdr = ZE_TDR_OW;

		if ((ZE_RCSR(ZE_CSR5) & ZE_NICSR5_TS) != ZE_NICSR5_TS_RUN)
			ZE_WCSR(ZE_CSR1, -1);

		sc->sc_inq++;
		if (++sc->sc_nexttx == TXDESCS)
			sc->sc_nexttx = 0;
	}
}
Esempio n. 12
0
struct if_head *
discover_interfaces(struct dhcpcd_ctx *ctx, int argc, char * const *argv)
{
	struct ifaddrs *ifaddrs, *ifa;
	char *p;
	int i, sdl_type;
	struct if_head *ifs;
	struct interface *ifp;
#ifdef __linux__
	char ifn[IF_NAMESIZE];
#endif
#ifdef INET
	const struct sockaddr_in *addr;
	const struct sockaddr_in *net;
	const struct sockaddr_in *dst;
#endif
#ifdef INET6
	const struct sockaddr_in6 *sin6;
	int ifa_flags;
#endif
#ifdef AF_LINK
	const struct sockaddr_dl *sdl;
#ifdef SIOCGIFPRIORITY
	struct ifreq ifr;
	int s_inet;
#endif
#ifdef IFLR_ACTIVE
	struct if_laddrreq iflr;
	int s_link;
#endif

#ifdef SIOCGIFPRIORITY
	if ((s_inet = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
		return NULL;
#endif
#ifdef IFLR_ACTIVE
	if ((s_link = socket(AF_LINK, SOCK_DGRAM, 0)) == -1) {
#ifdef SIOCGIFPRIORITY
		close(s_inet);
#endif
		return NULL;
	}
	memset(&iflr, 0, sizeof(iflr));
#endif
#elif AF_PACKET
	const struct sockaddr_ll *sll;
#endif

	if (getifaddrs(&ifaddrs) == -1)
		return NULL;
	ifs = malloc(sizeof(*ifs));
	if (ifs == NULL)
		return NULL;
	TAILQ_INIT(ifs);

	for (ifa = ifaddrs; ifa; ifa = ifa->ifa_next) {
		if (ifa->ifa_addr != NULL) {
#ifdef AF_LINK
			if (ifa->ifa_addr->sa_family != AF_LINK)
				continue;
#elif AF_PACKET
			if (ifa->ifa_addr->sa_family != AF_PACKET)
				continue;
#endif
		}

		/* Ensure that the interface name has settled */
		if (!dev_initialized(ctx, ifa->ifa_name))
			continue;

		/* It's possible for an interface to have >1 AF_LINK.
		 * For our purposes, we use the first one. */
		TAILQ_FOREACH(ifp, ifs, next) {
			if (strcmp(ifp->name, ifa->ifa_name) == 0)
				break;
		}
		if (ifp)
			continue;
		if (argc > 0) {
			for (i = 0; i < argc; i++) {
#ifdef __linux__
				/* Check the real interface name */
				strlcpy(ifn, argv[i], sizeof(ifn));
				p = strchr(ifn, ':');
				if (p)
					*p = '\0';
				if (strcmp(ifn, ifa->ifa_name) == 0)
					break;
#else
				if (strcmp(argv[i], ifa->ifa_name) == 0)
					break;
#endif
			}
			if (i == argc)
				continue;
			p = argv[i];
		} else {
			p = ifa->ifa_name;
			/* -1 means we're discovering against a specific
			 * interface, but we still need the below rules
			 * to apply. */
			if (argc == -1 && strcmp(argv[0], ifa->ifa_name) != 0)
				continue;
		}
		for (i = 0; i < ctx->ifdc; i++)
			if (!fnmatch(ctx->ifdv[i], p, 0))
				break;
		if (i < ctx->ifdc)
			continue;
		for (i = 0; i < ctx->ifac; i++)
			if (!fnmatch(ctx->ifav[i], p, 0))
				break;
		if (ctx->ifac && i == ctx->ifac)
			continue;

		if (if_vimaster(ifa->ifa_name) == 1) {
			syslog(argc ? LOG_ERR : LOG_DEBUG,
				"%s: is a Virtual Interface Master, skipping",
				ifa->ifa_name);
			continue;
		}

		ifp = calloc(1, sizeof(*ifp));
		if (ifp == NULL) {
			syslog(LOG_ERR, "%s: %m", __func__);
			break;
		}
		ifp->ctx = ctx;
		strlcpy(ifp->name, p, sizeof(ifp->name));
		ifp->flags = ifa->ifa_flags;

		/* Bring the interface up if not already */
		if (!(ifp->flags & IFF_UP)
#ifdef SIOCGIFMEDIA
		    && carrier_status(ifp) != LINK_UNKNOWN
#endif
		   )
		{
			if (up_interface(ifp) == 0)
				ctx->options |= DHCPCD_WAITUP;
			else
				syslog(LOG_ERR, "%s: up_interface: %m",
				    ifp->name);
		}

		sdl_type = 0;
		/* Don't allow loopback unless explicit */
		if (ifp->flags & IFF_LOOPBACK) {
			if (argc == 0 && ctx->ifac == 0) {
				free_interface(ifp);
				continue;
			}
		} else if (ifa->ifa_addr != NULL) {
#ifdef AF_LINK
			sdl = (const struct sockaddr_dl *)(void *)ifa->ifa_addr;

#ifdef IFLR_ACTIVE
			/* We need to check for active address */
			strlcpy(iflr.iflr_name, ifp->name,
			    sizeof(iflr.iflr_name));
			memcpy(&iflr.addr, ifa->ifa_addr,
			    MIN(ifa->ifa_addr->sa_len, sizeof(iflr.addr)));
			iflr.flags = IFLR_PREFIX;
			iflr.prefixlen = sdl->sdl_alen * NBBY;
			if (ioctl(s_link, SIOCGLIFADDR, &iflr) == -1 ||
			    !(iflr.flags & IFLR_ACTIVE))
			{
				free_interface(ifp);
				continue;
			}
#endif

			ifp->index = sdl->sdl_index;
			sdl_type = sdl->sdl_type;
			switch(sdl->sdl_type) {
			case IFT_BRIDGE: /* FALLTHROUGH */
			case IFT_L2VLAN: /* FALLTHOUGH */
			case IFT_L3IPVLAN: /* FALLTHROUGH */
			case IFT_ETHER:
				ifp->family = ARPHRD_ETHER;
				break;
			case IFT_IEEE1394:
				ifp->family = ARPHRD_IEEE1394;
				break;
#ifdef IFT_INFINIBAND
			case IFT_INFINIBAND:
				ifp->family = ARPHRD_INFINIBAND;
				break;
#endif
			}
			ifp->hwlen = sdl->sdl_alen;
#ifndef CLLADDR
#  define CLLADDR(s) ((const char *)((s)->sdl_data + (s)->sdl_nlen))
#endif
			memcpy(ifp->hwaddr, CLLADDR(sdl), ifp->hwlen);
#elif AF_PACKET
			sll = (const struct sockaddr_ll *)(void *)ifa->ifa_addr;
			ifp->index = sll->sll_ifindex;
			ifp->family = sdl_type = sll->sll_hatype;
			ifp->hwlen = sll->sll_halen;
			if (ifp->hwlen != 0)
				memcpy(ifp->hwaddr, sll->sll_addr, ifp->hwlen);
#endif
		}
#ifdef __linux__
		/* PPP addresses on Linux don't have hardware addresses */
		else
			ifp->index = if_nametoindex(ifp->name);
#endif

		/* We only work on ethernet by default */
		if (!(ifp->flags & IFF_POINTOPOINT) &&
		    ifp->family != ARPHRD_ETHER)
		{
			if (argc == 0 && ctx->ifac == 0) {
				free_interface(ifp);
				continue;
			}
			switch (ifp->family) {
			case ARPHRD_IEEE1394: /* FALLTHROUGH */
			case ARPHRD_INFINIBAND:
				/* We don't warn for supported families */
				break;
			default:
				syslog(LOG_WARNING,
				    "%s: unsupported interface type %.2x"
				    ", falling back to ethernet",
				    ifp->name, sdl_type);
				ifp->family = ARPHRD_ETHER;
				break;
			}
		}

		/* Handle any platform init for the interface */
		if (if_init(ifp) == -1) {
			syslog(LOG_ERR, "%s: if_init: %m", p);
			free_interface(ifp);
			continue;
		}

		/* Ensure that the MTU is big enough for DHCP */
		if (get_mtu(ifp->name) < MTU_MIN &&
		    set_mtu(ifp->name, MTU_MIN) == -1)
		{
			syslog(LOG_ERR, "%s: set_mtu: %m", p);
			free_interface(ifp);
			continue;
		}

#ifdef SIOCGIFPRIORITY
		/* Respect the interface priority */
		memset(&ifr, 0, sizeof(ifr));
		strlcpy(ifr.ifr_name, ifp->name, sizeof(ifr.ifr_name));
		if (ioctl(s_inet, SIOCGIFPRIORITY, &ifr) == 0)
			ifp->metric = ifr.ifr_metric;
#else
		/* We reserve the 100 range for virtual interfaces, if and when
		 * we can work them out. */
		ifp->metric = 200 + ifp->index;
		if (getifssid(ifp->name, ifp->ssid) != -1) {
			ifp->wireless = 1;
			ifp->metric += 100;
		}
#endif

		TAILQ_INSERT_TAIL(ifs, ifp, next);
	}

	for (ifa = ifaddrs; ifa; ifa = ifa->ifa_next) {
		if (ifa->ifa_addr == NULL)
			continue;
		switch(ifa->ifa_addr->sa_family) {
#ifdef INET
		case AF_INET:
			addr = (const struct sockaddr_in *)
			    (void *)ifa->ifa_addr;
			net = (const struct sockaddr_in *)
			    (void *)ifa->ifa_netmask;
			if (ifa->ifa_flags & IFF_POINTOPOINT)
				dst = (const struct sockaddr_in *)
				    (void *)ifa->ifa_dstaddr;
			else
				dst = NULL;
			ipv4_handleifa(ctx, RTM_NEWADDR, ifs, ifa->ifa_name,
				&addr->sin_addr,
				&net->sin_addr,
				dst ? &dst->sin_addr : NULL);
			break;
#endif
#ifdef INET6
		case AF_INET6:
			sin6 = (const struct sockaddr_in6 *)
			    (void *)ifa->ifa_addr;
			ifa_flags = in6_addr_flags(ifa->ifa_name,
			    &sin6->sin6_addr);
			if (ifa_flags != -1)
				ipv6_handleifa(ctx, RTM_NEWADDR, ifs,
				    ifa->ifa_name,
				    &sin6->sin6_addr, ifa_flags);
			break;
#endif
		}
	}

	freeifaddrs(ifaddrs);

#ifdef SIOCGIFPRIORITY
	close(s_inet);
#endif
#ifdef IFLR_ACTIVE
	close(s_link);
#endif

	return ifs;
}
Esempio n. 13
0
int
if_managelink(struct dhcpcd_ctx *ctx)
{
	/* route and ifwatchd like a msg buf size of 2048 */
	char msg[2048], *p, *e, *cp;
	ssize_t bytes;
	struct rt_msghdr *rtm;
	struct if_announcemsghdr *ifan;
	struct if_msghdr *ifm;
	struct ifa_msghdr *ifam;
	struct sockaddr *sa, *rti_info[RTAX_MAX];
	int len;
	struct sockaddr_dl sdl;
	struct interface *ifp;
#ifdef INET
	struct rt rt;
#endif
#ifdef INET6
	struct rt6 rt6;
	struct in6_addr ia6;
	struct sockaddr_in6 *sin6;
	int ifa_flags;
#endif

	bytes = read(ctx->link_fd, msg, sizeof(msg));
	if (bytes == -1)
		return -1;
	if (bytes == 0)
		return 0;
	e = msg + bytes;
	for (p = msg; p < e; p += rtm->rtm_msglen) {
		rtm = (struct rt_msghdr *)(void *)p;
		// Ignore messages generated by us
		if (rtm->rtm_pid == getpid())
			break;
		switch(rtm->rtm_type) {
#ifdef RTM_IFANNOUNCE
		case RTM_IFANNOUNCE:
			ifan = (struct if_announcemsghdr *)(void *)p;
			switch(ifan->ifan_what) {
			case IFAN_ARRIVAL:
				dhcpcd_handleinterface(ctx, 1,
				    ifan->ifan_name);
				break;
			case IFAN_DEPARTURE:
				dhcpcd_handleinterface(ctx, -1,
				    ifan->ifan_name);
				break;
			}
			break;
#endif
		case RTM_IFINFO:
			ifm = (struct if_msghdr *)(void *)p;
			if ((ifp = if_findindex(ctx, ifm->ifm_index)) == NULL)
				break;
			switch (ifm->ifm_data.ifi_link_state) {
			case LINK_STATE_DOWN:
				len = LINK_DOWN;
				break;
			case LINK_STATE_UP:
				len = LINK_UP;
				break;
			default:
				/* handle_carrier will re-load
				 * the interface flags and check for
				 * IFF_RUNNING as some drivers that
				 * don't handle link state also don't
				 * set IFF_RUNNING when this routing
				 * message is generated.
				 * As such, it is a race ...*/
				len = LINK_UNKNOWN;
				break;
			}
			dhcpcd_handlecarrier(ctx, len,
			    (unsigned int)ifm->ifm_flags, ifp->name);
			break;
		case RTM_DELETE:
			if (~rtm->rtm_addrs &
			    (RTA_DST | RTA_GATEWAY | RTA_NETMASK))
				break;
			cp = (char *)(void *)(rtm + 1);
			sa = (struct sockaddr *)(void *)cp;
			get_addrs(rtm->rtm_addrs, cp, rti_info);
			switch (sa->sa_family) {
#ifdef INET
			case AF_INET:
				memset(&rt, 0, sizeof(rt));
				rt.iface = NULL;
				COPYOUT(rt.dest, rti_info[RTAX_DST]);
				COPYOUT(rt.net, rti_info[RTAX_NETMASK]);
				COPYOUT(rt.gate, rti_info[RTAX_GATEWAY]);
				ipv4_routedeleted(ctx, &rt);
				break;
#endif
#ifdef INET6
			case AF_INET6:
				memset(&rt6, 0, sizeof(rt6));
				rt6.iface = NULL;
				COPYOUT6(rt6.dest, rti_info[RTAX_DST]);
				COPYOUT6(rt6.net, rti_info[RTAX_NETMASK]);
				COPYOUT6(rt6.gate, rti_info[RTAX_GATEWAY]);
				ipv6_routedeleted(ctx, &rt6);
				break;
#endif
			}
#ifdef RTM_CHGADDR
		case RTM_CHGADDR:	/* FALLTHROUGH */
#endif
		case RTM_DELADDR:	/* FALLTHROUGH */
		case RTM_NEWADDR:
			ifam = (struct ifa_msghdr *)(void *)p;
			if ((ifp = if_findindex(ctx, ifam->ifam_index)) == NULL)
				break;
			cp = (char *)(void *)(ifam + 1);
			get_addrs(ifam->ifam_addrs, cp, rti_info);
			if (rti_info[RTAX_IFA] == NULL)
				break;
			switch (rti_info[RTAX_IFA]->sa_family) {
			case AF_LINK:
#ifdef RTM_CHGADDR
				if (rtm->rtm_type != RTM_CHGADDR)
					break;
#else
				if (rtm->rtm_type != RTM_NEWADDR)
					break;
#endif
				memcpy(&sdl, rti_info[RTAX_IFA],
				    rti_info[RTAX_IFA]->sa_len);
				dhcpcd_handlehwaddr(ctx, ifp->name,
				    (const unsigned char*)CLLADDR(&sdl),
				    sdl.sdl_alen);
				break;
#ifdef INET
			case AF_INET:
			case 255: /* FIXME: Why 255? */
				COPYOUT(rt.dest, rti_info[RTAX_IFA]);
				COPYOUT(rt.net, rti_info[RTAX_NETMASK]);
				COPYOUT(rt.gate, rti_info[RTAX_BRD]);
				ipv4_handleifa(ctx, rtm->rtm_type,
				    NULL, ifp->name,
				    &rt.dest, &rt.net, &rt.gate);
				break;
#endif
#ifdef INET6
			case AF_INET6:
				sin6 = (struct sockaddr_in6*)(void *)
				    rti_info[RTAX_IFA];
				ia6 = sin6->sin6_addr;
#ifdef __KAME__
				if (IN6_IS_ADDR_LINKLOCAL(&ia6))
					ia6.s6_addr[2] = ia6.s6_addr[3] = '\0';
#endif
				if (rtm->rtm_type == RTM_NEWADDR) {
					ifa_flags = if_addrflags6(&ia6, ifp);
					if (ifa_flags == -1)
						break;
				} else
					ifa_flags = 0;
				ipv6_handleifa(ctx, rtm->rtm_type, NULL,
				    ifp->name, &ia6, ifa_flags);
				break;
#endif
			}
			break;
		}
	}

	return 0;
}
Esempio n. 14
0
static void
shmif_rcv(void *arg)
{
	struct ifnet *ifp = arg;
	struct shmif_sc *sc = ifp->if_softc;
	struct shmif_mem *busmem;
	struct mbuf *m = NULL;
	struct ether_header *eth;
	uint32_t nextpkt;
	bool wrap, passup;
	int error;
	const int align
	    = ALIGN(sizeof(struct ether_header)) - sizeof(struct ether_header);

 reup:
	mutex_enter(&sc->sc_mtx);
	while ((ifp->if_flags & IFF_RUNNING) == 0 && !sc->sc_dying)
		cv_wait(&sc->sc_cv, &sc->sc_mtx);
	mutex_exit(&sc->sc_mtx);

	busmem = sc->sc_busmem;

	while (ifp->if_flags & IFF_RUNNING) {
		struct shmif_pkthdr sp;

		if (m == NULL) {
			m = m_gethdr(M_WAIT, MT_DATA);
			MCLGET(m, M_WAIT);
			m->m_data += align;
		}

		DPRINTF(("waiting %d/%" PRIu64 "\n",
		    sc->sc_nextpacket, sc->sc_devgen));
		KASSERT(m->m_flags & M_EXT);

		shmif_lockbus(busmem);
		KASSERT(busmem->shm_magic == SHMIF_MAGIC);
		KASSERT(busmem->shm_gen >= sc->sc_devgen);

		/* need more data? */
		if (sc->sc_devgen == busmem->shm_gen && 
		    shmif_nextpktoff(busmem, busmem->shm_last)
		     == sc->sc_nextpacket) {
			shmif_unlockbus(busmem);
			error = 0;
			rumpcomp_shmif_watchwait(sc->sc_kq);
			if (__predict_false(error))
				printf("shmif_rcv: wait failed %d\n", error);
			membar_consumer();
			continue;
		}

		if (stillvalid_p(sc)) {
			nextpkt = sc->sc_nextpacket;
		} else {
			KASSERT(busmem->shm_gen > 0);
			nextpkt = busmem->shm_first;
			if (busmem->shm_first > busmem->shm_last)
				sc->sc_devgen = busmem->shm_gen - 1;
			else
				sc->sc_devgen = busmem->shm_gen;
			DPRINTF(("dev %p overrun, new data: %d/%" PRIu64 "\n",
			    sc, nextpkt, sc->sc_devgen));
		}

		/*
		 * If our read pointer is ahead the bus last write, our
		 * generation must be one behind.
		 */
		KASSERT(!(nextpkt > busmem->shm_last
		    && sc->sc_devgen == busmem->shm_gen));

		wrap = false;
		nextpkt = shmif_busread(busmem, &sp,
		    nextpkt, sizeof(sp), &wrap);
		KASSERT(sp.sp_len <= ETHERMTU + ETHER_HDR_LEN);
		nextpkt = shmif_busread(busmem, mtod(m, void *),
		    nextpkt, sp.sp_len, &wrap);

		DPRINTF(("shmif_rcv: read packet of length %d at %d\n",
		    sp.sp_len, nextpkt));

		sc->sc_nextpacket = nextpkt;
		shmif_unlockbus(sc->sc_busmem);

		if (wrap) {
			sc->sc_devgen++;
			DPRINTF(("dev %p generation now %" PRIu64 "\n",
			    sc, sc->sc_devgen));
		}

		/*
		 * Ignore packets too short to possibly be valid.
		 * This is hit at least for the first frame on a new bus.
		 */
		if (__predict_false(sp.sp_len < ETHER_HDR_LEN)) {
			DPRINTF(("shmif read packet len %d < ETHER_HDR_LEN\n",
			    sp.sp_len));
			continue;
		}

		m->m_len = m->m_pkthdr.len = sp.sp_len;
		m->m_pkthdr.rcvif = ifp;

		/*
		 * Test if we want to pass the packet upwards
		 */
		eth = mtod(m, struct ether_header *);
		if (memcmp(eth->ether_dhost, CLLADDR(ifp->if_sadl),
		    ETHER_ADDR_LEN) == 0) {
			passup = true;
		} else if (ETHER_IS_MULTICAST(eth->ether_dhost)) {
			passup = true;
		} else if (ifp->if_flags & IFF_PROMISC) {
			m->m_flags |= M_PROMISC;
			passup = true;
		} else {
			passup = false;
		}

		if (passup) {
			KERNEL_LOCK(1, NULL);
			bpf_mtap(ifp, m);
			ifp->if_input(ifp, m);
			KERNEL_UNLOCK_ONE(NULL);
			m = NULL;
		}
		/* else: reuse mbuf for a future packet */
	}
	m_freem(m);
	m = NULL;

	if (!sc->sc_dying)
		goto reup;

	kthread_exit(0);
}
Esempio n. 15
0
/*
 * Initialization of interface; clear recorded pending
 * operations, and reinitialize UNIBUS usage.
 */
int
ilinit(struct ifnet *ifp)
{
	struct il_softc *sc = ifp->if_softc;
	int s;

	if (sc->sc_flags & ILF_RUNNING)
		return 0;

	if ((ifp->if_flags & IFF_RUNNING) == 0) {
		if (if_ubainit(&sc->sc_ifuba,
		    device_private(device_parent(sc->sc_dev)),
		    ETHER_MAX_LEN)) {
			aprint_error_dev(sc->sc_dev, "can't initialize\n");
			sc->sc_if.if_flags &= ~IFF_UP;
			return 0;
		}
		sc->sc_ui.ui_size = sizeof(sc->sc_isu);
		sc->sc_ui.ui_vaddr = (void *)&sc->sc_isu;
		uballoc(device_private(device_parent(sc->sc_dev)), &sc->sc_ui, 0);
	}
	sc->sc_scaninterval = ILWATCHINTERVAL;
	ifp->if_timer = sc->sc_scaninterval;

	/*
	 * Turn off source address insertion (it's faster this way),
	 * and set board online.  Former doesn't work if board is
	 * already online (happens on ubareset), so we put it offline
	 * first.
	 */
	s = splnet();
	IL_WCSR(IL_CSR, ILC_RESET);
	if (ilwait(sc, "hardware diag")) {
		sc->sc_if.if_flags &= ~IFF_UP;
		goto out;
	}
	IL_WCSR(IL_CSR, ILC_CISA);
	while ((IL_RCSR(IL_CSR) & IL_CDONE) == 0)
		;
	/*
	 * If we must reprogram this board's physical ethernet
	 * address (as for secondary XNS interfaces), we do so
	 * before putting it on line, and starting receive requests.
	 * If you try this on an older 1010 board, it will total
	 * wedge the board.
	 */
	if (sc->sc_flags & ILF_SETADDR) {
		memcpy(&sc->sc_isu, CLLADDR(ifp->if_sadl), ETHER_ADDR_LEN);
		IL_WCSR(IL_BAR, LOWORD(sc->sc_ui.ui_baddr));
		IL_WCSR(IL_BCR, ETHER_ADDR_LEN);
		IL_WCSR(IL_CSR, ((sc->sc_ui.ui_baddr >> 2) & IL_EUA)|ILC_LDPA);
		if (ilwait(sc, "setaddr"))
			goto out;
		IL_WCSR(IL_BAR, LOWORD(sc->sc_ui.ui_baddr));
		IL_WCSR(IL_BCR, sizeof (struct il_stats));
		IL_WCSR(IL_CSR, ((sc->sc_ui.ui_baddr >> 2) & IL_EUA)|ILC_STAT);
		if (ilwait(sc, "verifying setaddr"))
			goto out;
		if (memcmp(sc->sc_stats.ils_addr,
		    CLLADDR(ifp->if_sadl), ETHER_ADDR_LEN) != 0) {
			aprint_error_dev(sc->sc_dev, "setaddr didn't work\n");
			goto out;
		}
	}
Esempio n. 16
0
Static void
kue_init(void *xsc)
{
	struct kue_softc	*sc = xsc;
	struct ifnet		*ifp = GET_IFP(sc);
	int			s;
	u_char			eaddr[ETHER_ADDR_LEN];

	DPRINTFN(5,("%s: %s: enter\n", USBDEVNAME(sc->kue_dev),__func__));

	if (ifp->if_flags & IFF_RUNNING)
		return;

	s = splnet();

	memcpy(eaddr, CLLADDR(ifp->if_sadl), sizeof(eaddr));
	/* Set MAC address */
	kue_ctl(sc, KUE_CTL_WRITE, KUE_CMD_SET_MAC, 0, eaddr, ETHER_ADDR_LEN);

	sc->kue_rxfilt = KUE_RXFILT_UNICAST | KUE_RXFILT_BROADCAST;

	 /* If we want promiscuous mode, set the allframes bit. */
	if (ifp->if_flags & IFF_PROMISC)
		sc->kue_rxfilt |= KUE_RXFILT_PROMISC;

	kue_setword(sc, KUE_CMD_SET_PKT_FILTER, sc->kue_rxfilt);

	/* I'm not sure how to tune these. */
#if 0
	/*
	 * Leave this one alone for now; setting it
	 * wrong causes lockups on some machines/controllers.
	 */
	kue_setword(sc, KUE_CMD_SET_SOFS, 1);
#endif
	kue_setword(sc, KUE_CMD_SET_URB_SIZE, 64);

	/* Init TX ring. */
	if (kue_tx_list_init(sc) == ENOBUFS) {
		printf("%s: tx list init failed\n", USBDEVNAME(sc->kue_dev));
		splx(s);
		return;
	}

	/* Init RX ring. */
	if (kue_rx_list_init(sc) == ENOBUFS) {
		printf("%s: rx list init failed\n", USBDEVNAME(sc->kue_dev));
		splx(s);
		return;
	}

	/* Load the multicast filter. */
	kue_setmulti(sc);

	if (sc->kue_ep[KUE_ENDPT_RX] == NULL) {
		if (kue_open_pipes(sc)) {
			splx(s);
			return;
		}
	}

	ifp->if_flags |= IFF_RUNNING;
	ifp->if_flags &= ~IFF_OACTIVE;

	splx(s);
}
Esempio n. 17
0
Static int
url_init(struct ifnet *ifp)
{
	struct url_softc *sc = ifp->if_softc;
	struct mii_data *mii = GET_MII(sc);
	const u_char *eaddr;
	int i, rc, s;

	DPRINTF(("%s: %s: enter\n", device_xname(sc->sc_dev), __func__));

	if (sc->sc_dying)
		return (EIO);

	s = splnet();

	/* Cancel pending I/O and free all TX/RX buffers */
	url_stop(ifp, 1);

	eaddr = CLLADDR(ifp->if_sadl);
	for (i = 0; i < ETHER_ADDR_LEN; i++)
		url_csr_write_1(sc, URL_IDR0 + i, eaddr[i]);

	/* Init transmission control register */
	URL_CLRBIT(sc, URL_TCR,
		   URL_TCR_TXRR1 | URL_TCR_TXRR0 |
		   URL_TCR_IFG1 | URL_TCR_IFG0 |
		   URL_TCR_NOCRC);

	/* Init receive control register */
	URL_SETBIT2(sc, URL_RCR, URL_RCR_TAIL | URL_RCR_AD);
	if (ifp->if_flags & IFF_BROADCAST)
		URL_SETBIT2(sc, URL_RCR, URL_RCR_AB);
	else
		URL_CLRBIT2(sc, URL_RCR, URL_RCR_AB);

	/* If we want promiscuous mode, accept all physical frames. */
	if (ifp->if_flags & IFF_PROMISC)
		URL_SETBIT2(sc, URL_RCR, URL_RCR_AAM|URL_RCR_AAP);
	else
		URL_CLRBIT2(sc, URL_RCR, URL_RCR_AAM|URL_RCR_AAP);


	/* Initialize transmit ring */
	if (url_tx_list_init(sc) == ENOBUFS) {
		printf("%s: tx list init failed\n", device_xname(sc->sc_dev));
		splx(s);
		return (EIO);
	}

	/* Initialize receive ring */
	if (url_rx_list_init(sc) == ENOBUFS) {
		printf("%s: rx list init failed\n", device_xname(sc->sc_dev));
		splx(s);
		return (EIO);
	}

	/* Load the multicast filter */
	url_setmulti(sc);

	/* Enable RX and TX */
	URL_SETBIT(sc, URL_CR, URL_CR_TE | URL_CR_RE);

	if ((rc = mii_mediachg(mii)) == ENXIO)
		rc = 0;
	else if (rc != 0)
		goto out;

	if (sc->sc_pipe_tx == NULL || sc->sc_pipe_rx == NULL) {
		if (url_openpipes(sc)) {
			splx(s);
			return (EIO);
		}
	}

	ifp->if_flags |= IFF_RUNNING;
	ifp->if_flags &= ~IFF_OACTIVE;

	callout_reset(&sc->sc_stat_ch, hz, url_tick, sc);

out:
	splx(s);
	return rc;
}
Esempio n. 18
0
int
rump_netconfig_auto_ipv6(const char *ifname)
{
	struct ifnet *ifp;
	int ifindex;
	struct socket *rsso = NULL;
	int rv = 0;
	int hoplimit = 255;
	struct mbuf *m_nam = NULL,
		    *m_outbuf = NULL;
	struct sockaddr_in6 *sin6;
	char *buf;
	struct nd_router_solicit rs;
	struct nd_opt_hdr opt;

	ifp = ifunit(ifname);
	if (ifp == NULL) {
		rv = ENXIO;
		goto out;
	}
	if (ifp->if_sadl->sdl_type != IFT_ETHER) {
		rv = EINVAL;
		goto out;
	}

	rv = socreate(PF_INET6, &rsso, SOCK_RAW, IPPROTO_ICMPV6, curlwp, NULL);
	if (rv != 0)
		goto out;
	ifindex = ifp->if_index;
	rv = so_setsockopt(curlwp, rsso, IPPROTO_IPV6, IPV6_MULTICAST_IF,
			&ifindex, sizeof ifindex);
	if (rv != 0)
		goto out;
	rv = so_setsockopt(curlwp, rsso, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
			&hoplimit, sizeof hoplimit);
	if (rv != 0)
		goto out;

	m_nam = m_get(M_WAIT, MT_SONAME);
	sin6 = mtod(m_nam, struct sockaddr_in6 *);
	sin6->sin6_len = m_nam->m_len = sizeof (*sin6);
	sin6->sin6_family = AF_INET6;
	netconfig_inet_pton6("ff02::2", &sin6->sin6_addr);

#define rslen (sizeof rs + sizeof opt + ETHER_ADDR_LEN)
	CTASSERT(rslen <= MCLBYTES);
	m_outbuf = m_gethdr(M_WAIT, MT_DATA);
	m_clget(m_outbuf, M_WAIT);
	m_outbuf->m_pkthdr.len = m_outbuf->m_len = rslen;


#if __NetBSD_Prereq__(7,99,31)
	m_set_rcvif(m_outbuf, NULL);
#else
	m_outbuf->m_pkthdr.rcvif = NULL;
#endif

#undef rslen
	buf = mtod(m_outbuf, char *);
	memset(&rs, 0, sizeof rs);
	rs.nd_rs_type = ND_ROUTER_SOLICIT;
	memset(&opt, 0, sizeof opt);
	opt.nd_opt_type = ND_OPT_SOURCE_LINKADDR;
	opt.nd_opt_len = 1; /* units of 8 octets */
	memcpy(buf, &rs, sizeof rs);
	buf += sizeof rs;
	memcpy(buf, &opt, sizeof opt);
	buf += sizeof opt;
	memcpy(buf, CLLADDR(ifp->if_sadl), ETHER_ADDR_LEN);

	ip6_accept_rtadv = 1;
	rv = rump_netconfig_ifup(ifname);
	if (rv != 0)
		goto out;
#if __NetBSD_Prereq__(7,99,12)
	rv = (*rsso->so_send)(rsso, (struct sockaddr *)sin6, NULL, m_outbuf,
			NULL, 0, curlwp);
#else
	rv = (*rsso->so_send)(rsso, m_nam, NULL, m_outbuf, NULL, 0, curlwp);
#endif
	if (rv == 0)
		/* *(so_send)() takes ownership of m_outbuf on success */
		m_outbuf = NULL;
	else
		goto out;

	rv = 0;
out:
	if (m_nam)
		m_freem(m_nam);
	if (m_outbuf)
		m_freem(m_outbuf);
	if (rsso)
		soclose(rsso);
	return rv;
}
Esempio n. 19
0
/*
 * How Command Block List Processing is done.
 * 
 * A running CBL is never manipulated. If there is a CBL already running,
 * further CMDs are deferred until the current list is done. A new list is
 * setup when the old one has finished.
 * This eases programming. To manipulate a running CBL it is necessary to
 * suspend the Command Unit to avoid race conditions. After a suspend
 * is sent we have to wait for an interrupt that ACKs the suspend. Then
 * we can manipulate the CBL and resume operation. I am not sure that this
 * is more effective than the current, much simpler approach. => KISS
 * See i82596CA data sheet page 26.
 * 
 * A CBL is running or on the way to be set up when (sc->sc_next_cb != 0).
 * 
 * A CBL may consist of TX CMDs, and _only_ TX CMDs.
 * A TX CBL is running or on the way to be set up when
 * ((sc->sc_next_cb != 0) && (sc->sc_next_tbd != 0)).
 * 
 * A CBL may consist of other non-TX CMDs like IAS or CONF, and _only_
 * non-TX CMDs.
 * 
 * This comes mostly through the way how an Ethernet driver works and
 * because running CBLs are not manipulated when they are on the way. If
 * if_start() is called there will be TX CMDs enqueued so we have a running
 * CBL and other CMDs from e.g. if_ioctl() will be deferred and vice versa.
 * 
 * The Multicast Setup Command is special. A MCS needs more space than
 * a single CB has. Actual space requirement depends on the length of the
 * multicast list. So we always defer MCS until other CBLs are finished,
 * then we setup a CONF CMD in the first CB. The CONF CMD is needed to
 * turn ALLMULTI on the hardware on or off. The MCS is the 2nd CB and may
 * use all the remaining space in the CBL and the Transmit Buffer Descriptor
 * List. (Therefore CBL and TBDL must be continuous in physical and virtual
 * memory. This is guaranteed through the definitions of the list offsets
 * in i82596reg.h and because it is only a single DMA segment used for all
 * lists.) When ALLMULTI is enabled via the CONF CMD, the MCS is run with
 * a multicast list length of 0, thus disabling the multicast filter.
 * A deferred MCS is signaled via ((sc->sc_flags & IEE_WANT_MCAST) != 0)
 */
void
iee_cb_setup(struct iee_softc *sc, uint32_t cmd)
{
	struct iee_cb *cb = SC_CB(sc, sc->sc_next_cb);
	struct ifnet *ifp = &sc->sc_ethercom.ec_if;
	struct ether_multistep step;
	struct ether_multi *enm;

	memset(cb, 0, sc->sc_cb_sz);
	cb->cb_cmd = cmd;
	switch (cmd & IEE_CB_CMD) {
	case IEE_CB_CMD_NOP:	/* NOP CMD */
		break;
	case IEE_CB_CMD_IAS:	/* Individual Address Setup */
		memcpy(__UNVOLATILE(cb->cb_ind_addr), CLLADDR(ifp->if_sadl),
		    ETHER_ADDR_LEN);
		break;
	case IEE_CB_CMD_CONF:	/* Configure */
		memcpy(__UNVOLATILE(cb->cb_cf), sc->sc_cf, sc->sc_cf[0]
		    & IEE_CF_0_CNT_M);
		break;
	case IEE_CB_CMD_MCS:	/* Multicast Setup */
		if (sc->sc_next_cb != 0) {
			sc->sc_flags |= IEE_WANT_MCAST;
			return;
		}
		sc->sc_flags &= ~IEE_WANT_MCAST;
		if ((sc->sc_cf[8] & IEE_CF_8_PRM) != 0) {
			/* Need no multicast filter in promisc mode. */
			iee_cb_setup(sc, IEE_CB_CMD_CONF | IEE_CB_S | IEE_CB_EL
			    | IEE_CB_I);
			return;
		}
		/* Leave room for a CONF CMD to en/dis-able ALLMULTI mode */
		cb = SC_CB(sc, sc->sc_next_cb + 1);
		cb->cb_cmd = cmd;
		cb->cb_mcast.mc_size = 0;
		ETHER_FIRST_MULTI(step, &sc->sc_ethercom, enm);
		while (enm != NULL) {
			if (memcmp(enm->enm_addrlo, enm->enm_addrhi,
			    ETHER_ADDR_LEN) != 0 || cb->cb_mcast.mc_size
			    * ETHER_ADDR_LEN + 2 * sc->sc_cb_sz >
			    sc->sc_cb_sz * IEE_NCB +
			    sc->sc_tbd_sz * IEE_NTBD * IEE_NCB) {
				cb->cb_mcast.mc_size = 0;
				break;
			}
			memcpy(__UNVOLATILE(&cb->cb_mcast.mc_addrs[
			    cb->cb_mcast.mc_size * ETHER_ADDR_LEN]),
			    enm->enm_addrlo, ETHER_ADDR_LEN);
			ETHER_NEXT_MULTI(step, enm);
			cb->cb_mcast.mc_size++;
		}
		if (cb->cb_mcast.mc_size == 0) {
			/* Can't do exact mcast filtering, do ALLMULTI mode. */
			ifp->if_flags |= IFF_ALLMULTI;
			sc->sc_cf[11] &= ~IEE_CF_11_MCALL;
		} else {
			/* disable ALLMULTI and load mcast list */
			ifp->if_flags &= ~IFF_ALLMULTI;
			sc->sc_cf[11] |= IEE_CF_11_MCALL;
			/* Mcast setup may need more than sc->sc_cb_sz bytes. */
			bus_dmamap_sync(sc->sc_dmat, sc->sc_shmem_map,
			    sc->sc_cb_off,
			    sc->sc_cb_sz * IEE_NCB +
			    sc->sc_tbd_sz * IEE_NTBD * IEE_NCB,
			    BUS_DMASYNC_PREWRITE);
		}
		iee_cb_setup(sc, IEE_CB_CMD_CONF);
		break;
	case IEE_CB_CMD_TR:	/* Transmit */
		cb->cb_transmit.tx_tbd_addr =
		    IEE_SWAPA32(IEE_PHYS_SHMEM(sc->sc_tbd_off
		    + sc->sc_tbd_sz * sc->sc_next_tbd));
		cb->cb_cmd |= IEE_CB_SF; /* Always use Flexible Mode. */
		break;
	case IEE_CB_CMD_TDR:	/* Time Domain Reflectometry */
		break;
	case IEE_CB_CMD_DUMP:	/* Dump */
		break;
	case IEE_CB_CMD_DIAG:	/* Diagnose */
		break;
	default:
		/* can't happen */
		break;
	}
	cb->cb_link_addr = IEE_SWAPA32(IEE_PHYS_SHMEM(sc->sc_cb_off +
	    sc->sc_cb_sz * (sc->sc_next_cb + 1)));
	IEE_CBSYNC(sc, sc->sc_next_cb,
	    BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE);
	sc->sc_next_cb++;
	ifp->if_timer = 5;
}