summaryrefslogtreecommitdiffstats
path: root/sys/net
diff options
context:
space:
mode:
authordfr <dfr@FreeBSD.org>2004-06-13 10:54:36 +0000
committerdfr <dfr@FreeBSD.org>2004-06-13 10:54:36 +0000
commita1fa8042f5e49d1e07a282f98704243b2620baad (patch)
treef9ff797a413f188d7a3cfe59c9ce64b4963e6b1b /sys/net
parent394bdac7dcbab39d03e0a5cdac350d93c81e71d3 (diff)
downloadFreeBSD-src-a1fa8042f5e49d1e07a282f98704243b2620baad.zip
FreeBSD-src-a1fa8042f5e49d1e07a282f98704243b2620baad.tar.gz
Add a new driver to support IP over firewire. This driver is intended to
conform to the rfc2734 and rfc3146 standard for IP over firewire and should eventually supercede the fwe driver. Right now the broadcast channel number is hardwired and we don't support MCAP for multicast channel allocation - more infrastructure is required in the firewire code itself to fix these problems.
Diffstat (limited to 'sys/net')
-rw-r--r--sys/net/firewire.h141
-rw-r--r--sys/net/if_arp.h1
-rw-r--r--sys/net/if_fwsubr.c799
3 files changed, 941 insertions, 0 deletions
diff --git a/sys/net/firewire.h b/sys/net/firewire.h
new file mode 100644
index 0000000..86cd023
--- /dev/null
+++ b/sys/net/firewire.h
@@ -0,0 +1,141 @@
+/*-
+ * Copyright (c) 2004 Doug Rabson
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _NET_FIREWIRE_H_
+#define _NET_FIREWIRE_H_
+
+#define FW_ENCAP_UNFRAG 0
+#define FW_ENCAP_FIRST 1
+#define FW_ENCAP_LAST 2
+#define FW_ENCAP_NEXT 3
+
+union fw_encap {
+ uint32_t ul[2];
+ struct {
+#if BYTE_ORDER == BIG_ENDIAN
+ uint32_t lf :2;
+ uint32_t reserved :14;
+ uint32_t ether_type :16;
+#else
+ uint32_t ether_type :16;
+ uint32_t reserved :14;
+ uint32_t lf :2;
+#endif
+ } unfrag;
+ struct {
+#if BYTE_ORDER == BIG_ENDIAN
+ uint32_t lf :2;
+ uint32_t reserved1 :2;
+ uint32_t datagram_size :12
+ uint32_t ether_type :16;
+ uint32_t dgl :16;
+ uint32_t reserved2 :16;
+#else
+ uint32_t ether_type :16;
+ uint32_t datagram_size :12;
+ uint32_t reserved1 :2;
+ uint32_t lf :2;
+ uint32_t reserved2 :16;
+ uint32_t dgl :16;
+#endif
+ } firstfrag;
+ struct {
+#if BYTE_ORDER == BIG_ENDIAN
+ uint32_t lf :2;
+ uint32_t reserved1 :2;
+ uint32_t datagram_size :12
+ uint32_t reserved2 :4;
+ uint32_t fragment_offset :12;
+ uint32_t dgl :16;
+ uint32_t reserved3 :16;
+#else
+ uint32_t fragment_offset :12;
+ uint32_t reserved2 :4;
+ uint32_t datagram_size :12;
+ uint32_t reserved1 :2;
+ uint32_t lf :2;
+ uint32_t reserved3 :16;
+ uint32_t dgl :16;
+#endif
+ } nextfrag;
+};
+
+#define MTAG_FIREWIRE 1394
+#define MTAG_FIREWIRE_HWADDR 0
+#define MTAG_FIREWIRE_SENDER_EUID 1
+
+struct fw_hwaddr {
+ uint32_t sender_unique_ID_hi;
+ uint32_t sender_unique_ID_lo;
+ uint8_t sender_max_rec;
+ uint8_t sspd;
+ uint16_t sender_unicast_FIFO_hi;
+ uint32_t sender_unicast_FIFO_lo;
+};
+
+/*
+ * BPF wants to see one of these.
+ */
+struct fw_bpfhdr {
+ uint8_t firewire_dhost[8];
+ uint8_t firewire_shost[8];
+ uint16_t firewire_type;
+};
+
+#ifdef _KERNEL
+
+/*
+ * A structure to track the reassembly of a link-level fragmented
+ * datagram.
+ */
+struct fw_reass {
+ STAILQ_ENTRY(fw_reass) fr_link;
+ uint32_t fr_id; /* host+dgl */
+ struct mbuf *fr_frags; /* chain of frags */
+};
+STAILQ_HEAD(fw_reass_list, fw_reass);
+
+struct fw_com {
+ struct ifnet fc_if;
+ struct fw_hwaddr fc_hwaddr;
+ struct firewire_comm *fc_fc;
+ uint8_t fc_broadcast_channel;
+ uint8_t fc_speed; /* our speed */
+ uint16_t fc_node; /* our nodeid */
+ struct fw_reass_list fc_frags; /* partial datagrams */
+};
+
+extern void firewire_input(struct ifnet *ifp, struct mbuf *m, uint16_t src);
+extern void firewire_ifattach(struct ifnet *, struct fw_hwaddr *);
+extern void firewire_ifdetach(struct ifnet *);
+extern void firewire_busreset(struct ifnet *);
+extern int firewire_ioctl(struct ifnet *, int, caddr_t);
+
+#endif /* !_KERNEL */
+
+#endif /* !_NET_FIREWIRE_H_ */
diff --git a/sys/net/if_arp.h b/sys/net/if_arp.h
index 6cc3ce9..a9cdd42 100644
--- a/sys/net/if_arp.h
+++ b/sys/net/if_arp.h
@@ -49,6 +49,7 @@ struct arphdr {
#define ARPHRD_IEEE802 6 /* token-ring hardware format */
#define ARPHRD_ARCNET 7 /* arcnet hardware format */
#define ARPHRD_FRELAY 15 /* frame relay hardware format */
+#define ARPHRD_IEEE1394 24 /* firewire hardware format */
u_short ar_pro; /* format of protocol address */
u_char ar_hln; /* length of hardware address */
u_char ar_pln; /* length of protocol address */
diff --git a/sys/net/if_fwsubr.c b/sys/net/if_fwsubr.c
new file mode 100644
index 0000000..72390e9
--- /dev/null
+++ b/sys/net/if_fwsubr.c
@@ -0,0 +1,799 @@
+/*
+ * Copyright (c) 2004 Doug Rabson
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#include "opt_inet.h"
+#include "opt_inet6.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+
+#include <net/if.h>
+#include <net/netisr.h>
+#include <net/route.h>
+#include <net/if_llc.h>
+#include <net/if_dl.h>
+#include <net/if_types.h>
+#include <net/bpf.h>
+#include <net/firewire.h>
+
+#if defined(INET) || defined(INET6)
+#include <netinet/in.h>
+#include <netinet/in_var.h>
+#include <netinet/if_ether.h>
+#include <netinet/ip_fw.h>
+#include <netinet/ip_dummynet.h>
+#endif
+#ifdef INET6
+#include <netinet6/nd6.h>
+#endif
+
+#define IFP2FC(IFP) ((struct fw_com *)IFP)
+
+struct fw_hwaddr firewire_broadcastaddr = {
+ 0xffffffff,
+ 0xffffffff,
+ 0xff,
+ 0xff,
+ 0xffff,
+ 0xffffffff
+};
+
+static int
+firewire_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst,
+ struct rtentry *rt0)
+{
+ struct fw_com *fc = (struct fw_com *) ifp;
+ int error, type;
+ struct rtentry *rt;
+ struct m_tag *mtag;
+ union fw_encap *enc;
+ struct fw_hwaddr *destfw;
+ uint8_t speed;
+ uint16_t psize, fsize, dsize;
+ struct mbuf *mtail;
+ int unicast, dgl, foff;
+ static int next_dgl;
+
+ GIANT_REQUIRED;
+
+ if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) {
+ error = ENETDOWN;
+ goto bad;
+ }
+
+ error = rt_check(&rt, &rt0, dst);
+ if (error)
+ goto bad;
+
+ /*
+ * 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 = !(m->m_flags & (M_BCAST | M_MCAST));
+ if (unicast) {
+ mtag = m_tag_locate(m, MTAG_FIREWIRE, MTAG_FIREWIRE_HWADDR, NULL);
+ if (!mtag) {
+ mtag = m_tag_alloc(MTAG_FIREWIRE, MTAG_FIREWIRE_HWADDR,
+ sizeof (struct fw_hwaddr), M_NOWAIT);
+ if (!mtag) {
+ error = ENOMEM;
+ goto bad;
+ }
+ m_tag_prepend(m, mtag);
+ }
+ destfw = (struct fw_hwaddr *)(mtag + 1);
+ } else {
+ destfw = 0;
+ }
+
+ switch (dst->sa_family) {
+#ifdef AF_INET
+ case AF_INET:
+ /*
+ * Only bother with arp for unicast. Allocation of
+ * channels etc. for firewire is quite different and
+ * doesn't fit into the arp model.
+ */
+ if (unicast) {
+ error = arpresolve(ifp, rt, m, dst, (u_char *) destfw);
+ if (error)
+ return (error == EWOULDBLOCK ? 0 : error);
+ }
+ type = ETHERTYPE_IP;
+ break;
+
+ case AF_ARP:
+ {
+ struct arphdr *ah;
+ ah = mtod(m, struct arphdr *);
+ ah->ar_hrd = htons(ARPHRD_IEEE1394);
+ type = ETHERTYPE_ARP;
+ if (unicast)
+ *destfw = *(struct fw_hwaddr *) ar_tha(ah);
+
+ /*
+ * The standard arp code leaves a hole for the target
+ * hardware address which we need to close up.
+ */
+ bcopy(ar_tpa(ah), ar_tha(ah), ah->ar_pln);
+ m_adj(m, -ah->ar_hln);
+ break;
+ }
+#endif
+
+#ifdef INET6
+ case AF_INET6:
+ if (unicast) {
+ error = nd6_storelladdr(&fc->fc_if, rt, m, dst,
+ (u_char *) destfw);
+ if (error)
+ return (error);
+ }
+ type = ETHERTYPE_IPV6;
+ break;
+#endif
+
+ default:
+ if_printf(ifp, "can't handle af%d\n", dst->sa_family);
+ error = EAFNOSUPPORT;
+ goto bad;
+ }
+
+ /*
+ * Let BPF tap off a copy before we encapsulate.
+ */
+ if (ifp->if_bpf) {
+ struct fw_bpfhdr h;
+ if (unicast)
+ bcopy(destfw, h.firewire_dhost, 8);
+ else
+ bcopy(&firewire_broadcastaddr, h.firewire_dhost, 8);
+ bcopy(&fc->fc_hwaddr, h.firewire_shost, 8);
+ h.firewire_type = htons(type);
+ bpf_mtap2(ifp->if_bpf, &h, sizeof(h), m);
+ }
+
+ /*
+ * Punt on MCAP for now and send all multicast packets on the
+ * broadcast channel.
+ */
+ if (m->m_flags & M_MCAST)
+ m->m_flags |= M_BCAST;
+
+ /*
+ * Figure out what speed to use and what the largest supported
+ * packet size is. For unicast, this is the minimum of what we
+ * can speak and what they can hear. For broadcast, lets be
+ * conservative and use S100. We could possibly improve that
+ * by examining the bus manager's speed map or similar. We
+ * also reduce the packet size for broadcast to account for
+ * the GASP header.
+ */
+ if (unicast) {
+ speed = min(fc->fc_speed, destfw->sspd);
+ psize = min(512 << speed, 2 << destfw->sender_max_rec);
+ } else {
+ speed = 0;
+ psize = 512 - 2*sizeof(uint32_t);
+ }
+
+ /*
+ * Next, we encapsulate, possibly fragmenting the original
+ * datagram if it won't fit into a single packet.
+ */
+ if (m->m_pkthdr.len <= psize - sizeof(uint32_t)) {
+ /*
+ * No fragmentation is necessary.
+ */
+ M_PREPEND(m, sizeof(uint32_t), M_DONTWAIT);
+ if (!m) {
+ error = ENOBUFS;
+ goto bad;
+ }
+ enc = mtod(m, union fw_encap *);
+ enc->unfrag.ether_type = type;
+ enc->unfrag.lf = FW_ENCAP_UNFRAG;
+
+ /*
+ * Byte swap the encapsulation header manually.
+ */
+ enc->ul[0] = htonl(enc->ul[0]);
+
+ return (IF_HANDOFF(&ifp->if_snd, m, ifp) ? 0 : ENOBUFS);
+ } else {
+ /*
+ * Fragment the datagram, making sure to leave enough
+ * space for the encapsulation header in each packet.
+ */
+ fsize = psize - 2*sizeof(uint32_t);
+ dgl = next_dgl++;
+ dsize = m->m_pkthdr.len;
+ foff = 0;
+ while (m) {
+ if (m->m_pkthdr.len > fsize) {
+ /*
+ * Split off the tail segment from the
+ * datagram, copying our tags over.
+ */
+ mtail = m_split(m, fsize, M_DONTWAIT);
+ m_tag_copy_chain(mtail, m, M_NOWAIT);
+ } else {
+ mtail = 0;
+ }
+
+ /*
+ * Add our encapsulation header to this
+ * fragment and hand it off to the link.
+ */
+ M_PREPEND(m, 2*sizeof(uint32_t), M_DONTWAIT);
+ if (!m) {
+ error = ENOBUFS;
+ goto bad;
+ }
+ enc = mtod(m, union fw_encap *);
+ if (foff == 0) {
+ enc->firstfrag.lf = FW_ENCAP_FIRST;
+ enc->firstfrag.datagram_size = dsize - 1;
+ enc->firstfrag.ether_type = type;
+ enc->firstfrag.dgl = dgl;
+ } else {
+ if (mtail)
+ enc->nextfrag.lf = FW_ENCAP_NEXT;
+ else
+ enc->nextfrag.lf = FW_ENCAP_LAST;
+ enc->nextfrag.datagram_size = dsize - 1;
+ enc->nextfrag.fragment_offset = foff;
+ enc->nextfrag.dgl = dgl;
+ }
+ foff += m->m_pkthdr.len - 2*sizeof(uint32_t);
+
+ /*
+ * Byte swap the encapsulation header manually.
+ */
+ enc->ul[0] = htonl(enc->ul[0]);
+ enc->ul[1] = htonl(enc->ul[1]);
+
+ if (!IF_HANDOFF(&ifp->if_snd, m, ifp)) {
+ if (mtail)
+ m_freem(mtail);
+ return (ENOBUFS);
+ }
+
+ m = mtail;
+ }
+
+ return (0);
+ }
+
+bad:
+ if (m)
+ m_freem(m);
+ return (error);
+}
+
+static struct mbuf *
+firewire_input_fragment(struct fw_com *fc, struct mbuf *m, int src)
+{
+ union fw_encap *enc;
+ struct fw_reass *r;
+ struct mbuf *mf, *mprev;
+ int dsize;
+ int fstart, fend, start, end, islast;
+ uint32_t id;
+
+ GIANT_REQUIRED;
+
+ /*
+ * Find an existing reassembly buffer or create a new one.
+ */
+ enc = mtod(m, union fw_encap *);
+ id = enc->firstfrag.dgl | (src << 16);
+ STAILQ_FOREACH(r, &fc->fc_frags, fr_link)
+ if (r->fr_id == id)
+ break;
+ if (!r) {
+ r = malloc(sizeof(struct fw_reass), M_TEMP, M_NOWAIT);
+ if (!r) {
+ m_freem(m);
+ return 0;
+ }
+ r->fr_id = id;
+ r->fr_frags = 0;
+ STAILQ_INSERT_HEAD(&fc->fc_frags, r, fr_link);
+ }
+
+ /*
+ * If this fragment overlaps any other fragment, we must discard
+ * the partial reassembly and start again.
+ */
+ if (enc->firstfrag.lf == FW_ENCAP_FIRST)
+ fstart = 0;
+ else
+ fstart = enc->nextfrag.fragment_offset;
+ fend = fstart + m->m_pkthdr.len - 2*sizeof(uint32_t);
+ dsize = enc->nextfrag.datagram_size;
+ islast = (enc->nextfrag.lf == FW_ENCAP_LAST);
+
+ for (mf = r->fr_frags; mf; mf = mf->m_nextpkt) {
+ enc = mtod(mf, union fw_encap *);
+ if (enc->nextfrag.datagram_size != dsize) {
+ /*
+ * This fragment must be from a different
+ * packet.
+ */
+ goto bad;
+ }
+ if (enc->firstfrag.lf == FW_ENCAP_FIRST)
+ start = 0;
+ else
+ start = enc->nextfrag.fragment_offset;
+ end = start + mf->m_pkthdr.len - 2*sizeof(uint32_t);
+ if ((fstart < end && fend > start) ||
+ (islast && enc->nextfrag.lf == FW_ENCAP_LAST)) {
+ /*
+ * Overlap - discard reassembly buffer and start
+ * again with this fragment.
+ */
+ goto bad;
+ }
+ }
+
+ /*
+ * Find where to put this fragment in the list.
+ */
+ for (mf = r->fr_frags, mprev = NULL; mf;
+ mprev = mf, mf = mf->m_nextpkt) {
+ enc = mtod(mf, union fw_encap *);
+ if (enc->firstfrag.lf == FW_ENCAP_FIRST)
+ start = 0;
+ else
+ start = enc->nextfrag.fragment_offset;
+ if (start >= fend)
+ break;
+ }
+
+ /*
+ * If this is a last fragment and we are not adding at the end
+ * of the list, discard the buffer.
+ */
+ if (islast && mprev && mprev->m_nextpkt)
+ goto bad;
+
+ if (mprev) {
+ m->m_nextpkt = mprev->m_nextpkt;
+ mprev->m_nextpkt = m;
+
+ /*
+ * Coalesce forwards and see if we can make a whole
+ * datagram.
+ */
+ enc = mtod(mprev, union fw_encap *);
+ if (enc->firstfrag.lf == FW_ENCAP_FIRST)
+ start = 0;
+ else
+ start = enc->nextfrag.fragment_offset;
+ end = start + mprev->m_pkthdr.len - 2*sizeof(uint32_t);
+ while (end == fstart) {
+ /*
+ * Strip off the encap header from m and
+ * append it to mprev, freeing m.
+ */
+ m_adj(m, 2*sizeof(uint32_t));
+ mprev->m_nextpkt = m->m_nextpkt;
+ mprev->m_pkthdr.len += m->m_pkthdr.len;
+ m_cat(mprev, m);
+
+ if (mprev->m_pkthdr.len == dsize + 1 + 2*sizeof(uint32_t)) {
+ /*
+ * We have assembled a complete packet
+ * we must be finished. Make sure we have
+ * merged the whole chain.
+ */
+ STAILQ_REMOVE(&fc->fc_frags, r, fw_reass, fr_link);
+ free(r, M_TEMP);
+ m = mprev->m_nextpkt;
+ while (m) {
+ mf = m->m_nextpkt;
+ m_freem(m);
+ m = mf;
+ }
+ mprev->m_nextpkt = NULL;
+
+ return (mprev);
+ }
+
+ /*
+ * See if we can continue merging forwards.
+ */
+ end = fend;
+ m = mprev->m_nextpkt;
+ if (m) {
+ enc = mtod(m, union fw_encap *);
+ if (enc->firstfrag.lf == FW_ENCAP_FIRST)
+ fstart = 0;
+ else
+ fstart = enc->nextfrag.fragment_offset;
+ fend = fstart + m->m_pkthdr.len
+ - 2*sizeof(uint32_t);
+ } else {
+ break;
+ }
+ }
+ } else {
+ m->m_nextpkt = 0;
+ r->fr_frags = m;
+ }
+
+ return (0);
+
+bad:
+ while (r->fr_frags) {
+ mf = r->fr_frags;
+ r->fr_frags = mf->m_nextpkt;
+ m_freem(mf);
+ }
+ m->m_nextpkt = 0;
+ r->fr_frags = m;
+
+ return (0);
+}
+
+void
+firewire_input(struct ifnet *ifp, struct mbuf *m, uint16_t src)
+{
+ struct fw_com *fc = (struct fw_com *) ifp;
+ union fw_encap *enc;
+ int type, isr;
+
+ GIANT_REQUIRED;
+
+ /*
+ * The caller has already stripped off the packet header
+ * (stream or wreqb) and marked the mbuf's M_BCAST flag
+ * appropriately. We de-encapsulate the IP packet and pass it
+ * up the line after handling link-level fragmentation.
+ */
+ if (m->m_pkthdr.len < sizeof(uint32_t)) {
+ if_printf(ifp, "discarding frame without "
+ "encapsulation header (len %u pkt len %u)\n",
+ m->m_len, m->m_pkthdr.len);
+ }
+
+ m = m_pullup(m, sizeof(uint32_t));
+ enc = mtod(m, union fw_encap *);
+
+ /*
+ * Byte swap the encapsulation header manually.
+ */
+ enc->ul[0] = htonl(enc->ul[0]);
+
+ if (enc->unfrag.lf != 0) {
+ m = m_pullup(m, 2*sizeof(uint32_t));
+ if (!m)
+ return;
+ enc = mtod(m, union fw_encap *);
+ enc->ul[1] = htonl(enc->ul[1]);
+ m = firewire_input_fragment(fc, m, src);
+ if (!m)
+ return;
+ enc = mtod(m, union fw_encap *);
+ type = enc->firstfrag.ether_type;
+ m_adj(m, 2*sizeof(uint32_t));
+ } else {
+ type = enc->unfrag.ether_type;
+ m_adj(m, sizeof(uint32_t));
+ }
+
+ if (m->m_pkthdr.rcvif == NULL) {
+ if_printf(ifp, "discard frame w/o interface pointer\n");
+ ifp->if_ierrors++;
+ m_freem(m);
+ return;
+ }
+#ifdef DIAGNOSTIC
+ if (m->m_pkthdr.rcvif != ifp) {
+ if_printf(ifp, "Warning, frame marked as received on %s\n",
+ m->m_pkthdr.rcvif->if_xname);
+ }
+#endif
+
+#ifdef MAC
+ /*
+ * Tag the mbuf with an appropriate MAC label before any other
+ * consumers can get to it.
+ */
+ mac_create_mbuf_from_ifnet(ifp, m);
+#endif
+
+ /*
+ * Give bpf a chance at the packet. The link-level driver
+ * should have left us a tag with the EUID of the sender.
+ */
+ if (ifp->if_bpf) {
+ struct fw_bpfhdr h;
+ struct m_tag *mtag;
+
+ mtag = m_tag_locate(m, MTAG_FIREWIRE, MTAG_FIREWIRE_SENDER_EUID, 0);
+ if (mtag)
+ bcopy(mtag + 1, h.firewire_shost, 8);
+ else
+ bcopy(&firewire_broadcastaddr, h.firewire_dhost, 8);
+ bcopy(&fc->fc_hwaddr, h.firewire_dhost, 8);
+ h.firewire_type = htons(type);
+ bpf_mtap2(ifp->if_bpf, &h, sizeof(h), m);
+ }
+
+ if (ifp->if_flags & IFF_MONITOR) {
+ /*
+ * Interface marked for monitoring; discard packet.
+ */
+ m_freem(m);
+ return;
+ }
+
+ ifp->if_ibytes += m->m_pkthdr.len;
+
+ /* Discard packet if interface is not up */
+ if ((ifp->if_flags & IFF_UP) == 0) {
+ m_freem(m);
+ return;
+ }
+
+ if (m->m_flags & (M_BCAST|M_MCAST))
+ ifp->if_imcasts++;
+
+ switch (type) {
+#ifdef INET
+ case ETHERTYPE_IP:
+ if (ip_fastforward(m))
+ return;
+ isr = NETISR_IP;
+ break;
+
+ case ETHERTYPE_ARP:
+ {
+ struct arphdr *ah;
+ ah = mtod(m, struct arphdr *);
+
+ /*
+ * Adjust the arp packet to insert an empty tha slot.
+ */
+ m->m_len += ah->ar_hln;
+ m->m_pkthdr.len += ah->ar_hln;
+ bcopy(ar_tha(ah), ar_tpa(ah), ah->ar_pln);
+ isr = NETISR_ARP;
+ break;
+ }
+#endif
+
+#ifdef INET6
+ case ETHERTYPE_IPV6:
+ isr = NETISR_IPV6;
+ break;
+#endif
+
+ default:
+ m_freem(m);
+ return;
+ }
+
+ netisr_dispatch(isr, m);
+}
+
+int
+firewire_ioctl(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
+ default:
+ ifp->if_init(ifp->if_softc);
+ break;
+ }
+ break;
+
+ case SIOCGIFADDR:
+ {
+ struct sockaddr *sa;
+
+ sa = (struct sockaddr *) & ifr->ifr_data;
+ bcopy(&IFP2FC(ifp)->fc_hwaddr,
+ (caddr_t) sa->sa_data, sizeof(struct fw_hwaddr));
+ }
+ break;
+
+ case SIOCSIFMTU:
+ /*
+ * Set the interface MTU.
+ */
+ if (ifr->ifr_mtu > 1500) {
+ error = EINVAL;
+ } else {
+ ifp->if_mtu = ifr->ifr_mtu;
+ }
+ break;
+ default:
+ error = EINVAL; /* XXX netbsd has ENOTTY??? */
+ break;
+ }
+ return (error);
+}
+
+static int
+firewire_resolvemulti(struct ifnet *ifp, struct sockaddr **llsa,
+ struct sockaddr *sa)
+{
+#ifdef INET
+ struct sockaddr_in *sin;
+#endif
+#ifdef INET6
+ struct sockaddr_in6 *sin6;
+#endif
+
+ switch(sa->sa_family) {
+ case AF_LINK:
+ /*
+ * No mapping needed.
+ */
+ *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;
+ *llsa = 0;
+ 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;
+ }
+ if (!IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr))
+ return EADDRNOTAVAIL;
+ *llsa = 0;
+ return 0;
+#endif
+
+ default:
+ /*
+ * Well, the text isn't quite right, but it's the name
+ * that counts...
+ */
+ return EAFNOSUPPORT;
+ }
+}
+
+void
+firewire_ifattach(struct ifnet *ifp, struct fw_hwaddr *llc)
+{
+ struct fw_com *fc = (struct fw_com *) ifp;
+ struct ifaddr *ifa;
+ struct sockaddr_dl *sdl;
+ static const char* speeds[] = {
+ "S100", "S200", "S400", "S800",
+ "S1600", "S3200"
+ };
+
+ fc->fc_speed = llc->sspd;
+ STAILQ_INIT(&fc->fc_frags);
+
+ ifp->if_type = IFT_IEEE1394;
+ ifp->if_addrlen = sizeof(struct fw_hwaddr);
+ ifp->if_hdrlen = 0;
+ if_attach(ifp);
+ ifp->if_mtu = 1500; /* XXX */
+ ifp->if_output = firewire_output;
+ ifp->if_resolvemulti = firewire_resolvemulti;
+ ifp->if_broadcastaddr = (u_char *) &firewire_broadcastaddr;
+
+ ifa = ifaddr_byindex(ifp->if_index);
+ KASSERT(ifa != NULL, ("%s: no lladdr!\n", __func__));
+ sdl = (struct sockaddr_dl *)ifa->ifa_addr;
+ sdl->sdl_type = IFT_IEEE1394;
+ sdl->sdl_alen = ifp->if_addrlen;
+ bcopy(llc, LLADDR(sdl), ifp->if_addrlen);
+
+ bpfattach(ifp, DLT_APPLE_IP_OVER_IEEE1394,
+ sizeof(struct fw_hwaddr));
+
+ if_printf(ifp, "Firewire address: %8D @ 0x%04x%08x, %s, maxrec %d\n",
+ (uint8_t *) &llc->sender_unique_ID_hi, ":",
+ ntohs(llc->sender_unicast_FIFO_hi),
+ ntohl(llc->sender_unicast_FIFO_lo),
+ speeds[llc->sspd],
+ (2 << llc->sender_max_rec));
+}
+
+void
+firewire_ifdetach(struct ifnet *ifp)
+{
+ bpfdetach(ifp);
+ if_detach(ifp);
+}
+
+void
+firewire_busreset(struct ifnet *ifp)
+{
+ struct fw_com *fc = (struct fw_com *) ifp;
+ struct fw_reass *r;
+ struct mbuf *m;
+
+ /*
+ * Discard any partial datagrams since the host ids may have changed.
+ */
+ while ((r = STAILQ_FIRST(&fc->fc_frags))) {
+ STAILQ_REMOVE_HEAD(&fc->fc_frags, fr_link);
+ while (r->fr_frags) {
+ m = r->fr_frags;
+ r->fr_frags = m->m_nextpkt;
+ m_freem(m);
+ }
+ free(r, M_TEMP);
+ }
+}
OpenPOWER on IntegriCloud