pdq_ifsubr.c revision 21846
1/*-
2 * Copyright (c) 1995, 1996 Matt Thomas <matt@3am-software.com>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. The name of the author may not be used to endorse or promote products
11 *    derived from this software withough specific prior written permission
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
14 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
15 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
16 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
17 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
18 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
19 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
20 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
22 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23 *
24 * $Id: pdq_ifsubr.c,v 1.2 1997/01/17 23:54:32 joerg Exp $
25 *
26 */
27
28/*
29 * DEC PDQ FDDI Controller; code for BSD derived operating systems
30 *
31 *	This module provide bus independent BSD specific O/S functions.
32 *	(ie. it provides an ifnet interface to the rest of the system)
33 */
34
35
36#include <sys/param.h>
37#include <sys/kernel.h>
38#include <sys/mbuf.h>
39#include <sys/protosw.h>
40#include <sys/socket.h>
41#include <sys/ioctl.h>
42#include <sys/errno.h>
43#include <sys/malloc.h>
44#if defined(__bsdi__) || defined(__NetBSD__)
45#include <sys/device.h>
46#endif
47
48#include <net/if.h>
49#include <net/if_types.h>
50#include <net/if_dl.h>
51#include <net/route.h>
52
53#include "bpfilter.h"
54#if NBPFILTER > 0
55#include <net/bpf.h>
56#include <net/bpfdesc.h>
57#endif
58
59#ifdef INET
60#include <netinet/in.h>
61#include <netinet/in_systm.h>
62#include <netinet/in_var.h>
63#include <netinet/ip.h>
64#include <netinet/if_ether.h>
65#endif
66#if defined(__FreeBSD__)
67#include <netinet/if_fddi.h>
68#else
69#include <net/if_fddi.h>
70#endif
71
72#if defined(__bsdi__)
73#include <i386/isa/isavar.h>
74#endif
75
76#ifdef NS
77#include <netns/ns.h>
78#include <netns/ns_if.h>
79#endif
80
81#include <vm/vm.h>
82#include <vm/vm_kern.h>
83#include <vm/vm_param.h>
84
85#if defined(__FreeBSD__)
86#include <dev/pdq/pdqvar.h>
87#include <dev/pdq/pdqreg.h>
88#else
89#include "pdqvar.h"
90#include "pdqreg.h"
91#endif
92
93#if defined(__bsdi__) && _BSDI_VERSION < 199506 /* XXX */
94static void
95arp_ifinit(
96    struct arpcom *ac,
97    struct ifaddr *ifa)
98{
99    sc->sc_ac.ac_ipaddr = IA_SIN(ifa)->sin_addr;
100    arpwhohas(&sc->sc_ac, &IA_SIN(ifa)->sin_addr);
101#if _BSDI_VERSION >= 199401
102    ifa->ifa_rtrequest = arp_rtrequest;
103    ifa->ifa_flags |= RTF_CLONING;
104#endif
105#endif
106
107
108void
109pdq_ifinit(
110    pdq_softc_t *sc)
111{
112    if (sc->sc_if.if_flags & IFF_UP) {
113	sc->sc_if.if_flags |= IFF_RUNNING;
114	if (sc->sc_if.if_flags & IFF_PROMISC) {
115	    sc->sc_pdq->pdq_flags |= PDQ_PROMISC;
116	} else {
117	    sc->sc_pdq->pdq_flags &= ~PDQ_PROMISC;
118	}
119	if (sc->sc_if.if_flags & IFF_ALLMULTI) {
120	    sc->sc_pdq->pdq_flags |= PDQ_ALLMULTI;
121	} else {
122	    sc->sc_pdq->pdq_flags &= ~PDQ_ALLMULTI;
123	}
124	if (sc->sc_if.if_flags & IFF_LINK1) {
125	    sc->sc_pdq->pdq_flags |= PDQ_PASS_SMT;
126	} else {
127	    sc->sc_pdq->pdq_flags &= ~PDQ_PASS_SMT;
128	}
129	sc->sc_pdq->pdq_flags |= PDQ_RUNNING;
130	pdq_run(sc->sc_pdq);
131    } else {
132	sc->sc_if.if_flags &= ~IFF_RUNNING;
133	sc->sc_pdq->pdq_flags &= ~PDQ_RUNNING;
134	pdq_stop(sc->sc_pdq);
135    }
136}
137
138void
139pdq_ifwatchdog(
140    struct ifnet *ifp)
141{
142    /*
143     * No progress was made on the transmit queue for PDQ_OS_TX_TRANSMIT
144     * seconds.  Remove all queued packets.
145     */
146
147    ifp->if_flags &= ~IFF_OACTIVE;
148    ifp->if_timer = 0;
149    for (;;) {
150	struct mbuf *m;
151	IF_DEQUEUE(&ifp->if_snd, m);
152	if (m == NULL)
153	    return;
154	m_freem(m);
155    }
156}
157
158ifnet_ret_t
159pdq_ifstart(
160    struct ifnet *ifp)
161{
162    pdq_softc_t *sc = (pdq_softc_t *) ((caddr_t) ifp - offsetof(pdq_softc_t, sc_ac.ac_if));
163    struct ifqueue *ifq = &ifp->if_snd;
164    struct mbuf *m;
165    int tx = 0;
166
167    if ((ifp->if_flags & IFF_RUNNING) == 0)
168	return;
169
170    if (sc->sc_if.if_timer == 0)
171	sc->sc_if.if_timer = PDQ_OS_TX_TIMEOUT;
172
173    if ((sc->sc_pdq->pdq_flags & PDQ_TXOK) == 0) {
174	sc->sc_if.if_flags |= IFF_OACTIVE;
175	return;
176    }
177    for (;; tx = 1) {
178	IF_DEQUEUE(ifq, m);
179	if (m == NULL)
180	    break;
181
182	if (pdq_queue_transmit_data(sc->sc_pdq, m) == PDQ_FALSE) {
183	    ifp->if_flags |= IFF_OACTIVE;
184	    IF_PREPEND(ifq, m);
185	    break;
186	}
187    }
188    if (tx)
189	PDQ_DO_TYPE2_PRODUCER(sc->sc_pdq);
190}
191
192void
193pdq_os_receive_pdu(
194    pdq_t *pdq,
195    struct mbuf *m,
196    size_t pktlen)
197{
198    pdq_softc_t *sc = (pdq_softc_t *) pdq->pdq_os_ctx;
199    struct fddi_header *fh = mtod(m, struct fddi_header *);
200
201    sc->sc_if.if_ipackets++;
202#if NBPFILTER > 0
203    if (sc->sc_bpf != NULL)
204	PDQ_BPF_MTAP(sc, m);
205    if ((fh->fddi_fc & (FDDIFC_L|FDDIFC_F)) != FDDIFC_LLC_ASYNC) {
206	m_freem(m);
207	return;
208    }
209#endif
210
211    m->m_data += sizeof(struct fddi_header);
212    m->m_len  -= sizeof(struct fddi_header);
213    m->m_pkthdr.len = pktlen - sizeof(struct fddi_header);
214    m->m_pkthdr.rcvif = &sc->sc_if;
215    fddi_input(&sc->sc_if, fh, m);
216}
217
218void
219pdq_os_restart_transmitter(
220    pdq_t *pdq)
221{
222    pdq_softc_t *sc = (pdq_softc_t *) pdq->pdq_os_ctx;
223    sc->sc_if.if_flags &= ~IFF_OACTIVE;
224    if (sc->sc_if.if_snd.ifq_head != NULL) {
225	sc->sc_if.if_timer = PDQ_OS_TX_TIMEOUT;
226	pdq_ifstart(&sc->sc_if);
227    } else {
228	sc->sc_if.if_timer = 0;
229    }
230}
231
232void
233pdq_os_transmit_done(
234    pdq_t *pdq,
235    struct mbuf *m)
236{
237    pdq_softc_t *sc = (pdq_softc_t *) pdq->pdq_os_ctx;
238#if NBPFILTER > 0
239    if (sc->sc_bpf != NULL)
240	PDQ_BPF_MTAP(sc, m);
241#endif
242    m_freem(m);
243    sc->sc_if.if_opackets++;
244}
245
246void
247pdq_os_addr_fill(
248    pdq_t *pdq,
249    pdq_lanaddr_t *addr,
250    size_t num_addrs)
251{
252    pdq_softc_t *sc = (pdq_softc_t *) pdq->pdq_os_ctx;
253    struct ifmultiaddr *ifma;
254
255    for (ifma = sc->sc_if.if_multiaddrs.lh_first; ifma && num_addrs > 0;
256	 ifma = ifma->ifma_link.le_next) {
257	    char *mcaddr;
258	    if (ifma->ifma_addr->sa_family != AF_LINK)
259		    continue;
260	    mcaddr = LLADDR((struct sockaddr_dl *)ifma->ifma_addr);
261	    ((u_short *) addr->lanaddr_bytes)[0] = ((u_short *) mcaddr)[0];
262	    ((u_short *) addr->lanaddr_bytes)[1] = ((u_short *) mcaddr)[1];
263	    ((u_short *) addr->lanaddr_bytes)[2] = ((u_short *) mcaddr)[2];
264	    addr++;
265	    num_addrs--;
266    }
267}
268
269int
270pdq_ifioctl(
271    struct ifnet *ifp,
272    ioctl_cmd_t cmd,
273    caddr_t data)
274{
275    pdq_softc_t *sc = (pdq_softc_t *) ((caddr_t) ifp - offsetof(pdq_softc_t, sc_ac.ac_if));
276    int s, error = 0;
277
278    s = splimp();
279
280    switch (cmd) {
281	case SIOCSIFADDR: {
282	    struct ifaddr *ifa = (struct ifaddr *)data;
283
284	    ifp->if_flags |= IFF_UP;
285	    switch(ifa->ifa_addr->sa_family) {
286#if defined(INET)
287		case AF_INET: {
288		    pdq_ifinit(sc);
289		    arp_ifinit(&sc->sc_ac, ifa);
290		    break;
291		}
292#endif /* INET */
293
294#if defined(NS)
295		/* This magic copied from if_is.c; I don't use XNS,
296		 * so I have no way of telling if this actually
297		 * works or not.
298		 */
299		case AF_NS: {
300		    struct ns_addr *ina = &(IA_SNS(ifa)->sns_addr);
301		    if (ns_nullhost(*ina)) {
302			ina->x_host = *(union ns_host *)(sc->sc_ac.ac_enaddr);
303		    } else {
304			ifp->if_flags &= ~IFF_RUNNING;
305			bcopy((caddr_t)ina->x_host.c_host,
306			      (caddr_t)sc->sc_ac.ac_enaddr,
307			      sizeof sc->sc_ac.ac_enaddr);
308		    }
309
310		    pdq_ifinit(sc);
311		    break;
312		}
313#endif /* NS */
314
315		default: {
316		    pdq_ifinit(sc);
317		    break;
318		}
319	    }
320	    break;
321	}
322	case SIOCGIFADDR: {
323	    struct ifreq *ifr = (struct ifreq *)data;
324	    bcopy((caddr_t) sc->sc_ac.ac_enaddr,
325		  (caddr_t) ((struct sockaddr *)&ifr->ifr_data)->sa_data,
326		  6);
327	    break;
328	}
329
330	case SIOCSIFFLAGS: {
331	    pdq_ifinit(sc);
332	    break;
333	}
334
335	case SIOCADDMULTI:
336	case SIOCDELMULTI:
337		/*
338		 * Update multicast listeners
339		 */
340		if (sc->sc_if.if_flags & IFF_RUNNING)
341			pdq_run(sc->sc_pdq);
342		error = 0;
343		break;
344
345#if defined(SIOCSIFMTU)
346#if !defined(ifr_mtu)
347#define ifr_mtu ifr_metric
348#endif
349	case SIOCSIFMTU: {
350	    struct ifreq *ifr = (struct ifreq *)data;
351	    /*
352	     * Set the interface MTU.
353	     */
354	    if (ifr->ifr_mtu > FDDIMTU) {
355		error = EINVAL;
356		break;
357	    }
358	    ifp->if_mtu = ifr->ifr_mtu;
359	    break;
360	}
361#endif /* SIOCSIFMTU */
362
363	default: {
364	    error = EINVAL;
365	    break;
366	}
367    }
368
369    splx(s);
370    return error;
371}
372
373#ifndef IFF_NOTRAILERS
374#define	IFF_NOTRAILERS	0
375#endif
376
377void
378pdq_ifattach(
379    pdq_softc_t *sc,
380    ifnet_ret_t (*ifwatchdog)(int unit))
381{
382    struct ifnet *ifp = &sc->sc_if;
383
384    ifp->if_flags = IFF_BROADCAST|IFF_SIMPLEX|IFF_NOTRAILERS|IFF_MULTICAST;
385
386#if (defined(__FreeBSD__) && BSD >= 199506) || defined(__NetBSD__)
387    ifp->if_watchdog = pdq_ifwatchdog;
388#else
389    ifp->if_watchdog = ifwatchdog;
390#endif
391
392    ifp->if_ioctl = pdq_ifioctl;
393    ifp->if_output = fddi_output;
394    ifp->if_start = pdq_ifstart;
395#warning "Implement fddi_resolvemulti!"
396/*    ifp->if_resolvemulti = ether_resolvemulti; XXX */
397
398    if_attach(ifp);
399    fddi_ifattach(ifp);
400#if NBPFILTER > 0
401    PDQ_BPFATTACH(sc, DLT_FDDI, sizeof(struct fddi_header));
402#endif
403}
404