diff options
Diffstat (limited to 'sys/net/if_ethersubr.c')
-rw-r--r-- | sys/net/if_ethersubr.c | 349 |
1 files changed, 349 insertions, 0 deletions
diff --git a/sys/net/if_ethersubr.c b/sys/net/if_ethersubr.c index 9d59287..04e3ea8 100644 --- a/sys/net/if_ethersubr.c +++ b/sys/net/if_ethersubr.c @@ -38,6 +38,7 @@ #include "opt_inet.h" #include "opt_ipx.h" #include "opt_bdg.h" +#include "opt_netgraph.h" #include <sys/param.h> #include <sys/systm.h> @@ -117,6 +118,43 @@ u_char etherbroadcastaddr[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; #define senderr(e) do { error = (e); goto bad;} while (0) #define IFP2AC(IFP) ((struct arpcom *)IFP) +#ifdef NETGRAPH +#include <netgraph/ng_ether.h> +#include <netgraph/ng_message.h> +#include <netgraph/netgraph.h> + +static void ngether_init(void* ignored); +static void ngether_send(struct arpcom *ac, + struct ether_header *eh, struct mbuf *m); +static int ngether_constructor(node_p *nodep); +static int ngether_rcvmsg(node_p node, struct ng_mesg *msg, + const char *retaddr, struct ng_mesg **resp); +static int ngether_rmnode(node_p node); +static int ngether_newhook(node_p node, hook_p hook, const char *name); +/*static hook_p ngether_findhook(node_p node, char *name);*/ +static int ngether_connect(hook_p hook); /* already PARTLY linked */ +static int ngether_rcvdata(hook_p hook, struct mbuf *m, meta_p meta); +static int ngether_disconnect(hook_p hook); /* notify on disconnect */ + +static struct ng_type typestruct = { + NG_VERSION, + NG_ETHER_NODE_TYPE, + NULL, + ngether_constructor, + ngether_rcvmsg, + ngether_rmnode, + ngether_newhook, + NULL, + ngether_connect, + ngether_rcvdata, + ngether_rcvdata, + ngether_disconnect +}; + +#define AC2NG(AC) ((node_p)((AC)->ac_ng)) +#define NGEF_DIVERT NGF_TYPE1 /* all packets sent to netgraph */ +#endif /* NETGRAPH */ + /* * Ethernet output routine. * Encapsulate a packet of type family for the local net. @@ -456,6 +494,16 @@ ether_input(ifp, eh, m) ether_type = ntohs(eh->ether_type); +#ifdef NETGRAPH + { + struct arpcom *ac = IFP2AC(ifp); + if (AC2NG(ac) && (AC2NG(ac)->flags & NGEF_DIVERT)) { + ngether_send(ac, eh, m); + return; + } + } +#endif /* NETGRAPH */ + #if NVLAN > 0 if (ether_type == vlan_proto) { if (vlan_input(eh, m) < 0) @@ -640,11 +688,19 @@ ether_input(ifp, eh, m) #endif /* LLC */ dropanyway: default: +#ifdef NETGRAPH + ngether_send(IFP2AC(ifp), eh, m); +#else /* NETGRAPH */ m_freem(m); +#endif /* NETGRAPH */ return; } #else /* ISO || LLC || NETATALK */ +#ifdef NETGRAPH + ngether_send(IFP2AC(ifp), eh, m); +#else /* NETGRAPH */ m_freem(m); +#endif /* NETGRAPH */ return; #endif /* ISO || LLC || NETATALK */ } @@ -844,3 +900,296 @@ ether_resolvemulti(ifp, llsa, sa) return EAFNOSUPPORT; } } + +#ifdef NETGRAPH + +/*********************************************************************** + * This section contains the methods for the Netgraph interface + ***********************************************************************/ +/* It's Ascii-art time! + * The ifnet is the first part of the arpcom which must be + * the first part of the device's softc.. yuk. + * + * +--------------------------+-----+---------+ + * | struct ifnet (*ifp) | | | + * | | | | + * +--------------------------+ | | + * +--|[ac_ng] struct arpcom (*ac) | | + * | +--------------------------------+ | + * | | struct softc (*ifp->if_softc) (device) | + * | +------------------------------------------+ + * | ^ + * AC2NG() | + * | v + * | +----------------------+ + * | | [private] [flags] | + * +------>| struct ng_node | + * | [hooks] | ** we only allow one hook + * +----------------------+ + * ^ + * | + * v + * +-------------+ + * | [node] | + * | hook | + * | [private]|-- *unused* + * +-------------+ + */ + +/* + * called during interface attaching + */ +static void +ngether_init(void *ifpvoid) +{ + struct ifnet *ifp = ifpvoid; + struct arpcom *ac = IFP2AC(ifp); + static int ngether_done_init; + char namebuf[32]; + node_p node; + + /* + * we have found a node, make sure our 'type' is availabe. + */ + if (ngether_done_init == 0) { + if (ng_newtype(&typestruct)) { + printf("ngether install failed\n"); + return; + } + ngether_done_init = 1; + } + if (ng_make_node_common(&typestruct, &node) != 0) + return; + ac->ac_ng = node; + node->private = ifp; + sprintf(namebuf, "%s%d", ifp->if_name, ifp->if_unit); + ng_name_node(AC2NG(ac), namebuf); +} + +/* + * It is not possible or allowable to create a node of this type. + * If the hardware exists, it will already have created it. + */ +static int +ngether_constructor(node_p *nodep) +{ + return (EINVAL); +} + +/* + * Give our ok for a hook to be added... + * + * Allow one hook at a time (rawdata). + * It can eiteh rdivert everything or only unclaimed packets. + */ +static int +ngether_newhook(node_p node, hook_p hook, const char *name) +{ + + /* check if there is already a hook */ + if (LIST_FIRST(&(node->hooks))) + return(EISCONN); + /* + * Check for which mode hook we want. + */ + if (strcmp(name, NG_ETHER_HOOK_ORPHAN) != 0) { + if (strcmp(name, NG_ETHER_HOOK_DIVERT) != 0) { + return (EINVAL); + } + node->flags |= NGEF_DIVERT; + } else { + node->flags &= ~NGEF_DIVERT; + } + return (0); +} + +/* + * incoming messages. + * Just respond to the generic TEXT_STATUS message + */ +static int +ngether_rcvmsg(node_p node, + struct ng_mesg *msg, const char *retaddr, struct ng_mesg **resp) +{ + struct ifnet *ifp; + int error = 0; + + ifp = node->private; + switch (msg->header.typecookie) { + case NGM_ETHER_COOKIE: + error = EINVAL; + break; + case NGM_GENERIC_COOKIE: + switch(msg->header.cmd) { + case NGM_TEXT_STATUS: { + char *arg; + int pos = 0; + int resplen = sizeof(struct ng_mesg) + 512; + MALLOC(*resp, struct ng_mesg *, resplen, + M_NETGRAPH, M_NOWAIT); + if (*resp == NULL) { + error = ENOMEM; + break; + } + bzero(*resp, resplen); + arg = (*resp)->data; + + /* + * Put in the throughput information. + */ + pos = sprintf(arg, "%ld bytes in, %ld bytes out\n", + ifp->if_ibytes, ifp->if_obytes); + pos += sprintf(arg + pos, + "%ld output errors\n", + ifp->if_oerrors); + pos += sprintf(arg + pos, + "ierrors = %ld\n", + ifp->if_ierrors); + + (*resp)->header.version = NG_VERSION; + (*resp)->header.arglen = strlen(arg) + 1; + (*resp)->header.token = msg->header.token; + (*resp)->header.typecookie = NGM_ETHER_COOKIE; + (*resp)->header.cmd = msg->header.cmd; + strncpy((*resp)->header.cmdstr, "status", + NG_CMDSTRLEN); + } + break; + default: + error = EINVAL; + break; + } + break; + default: + error = EINVAL; + break; + } + free(msg, M_NETGRAPH); + return (error); +} + +/* + * Receive a completed ethernet packet. + * Queue it for output. + */ +static int +ngether_rcvdata(hook_p hook, struct mbuf *m, meta_p meta) +{ + struct ifnet *ifp; + int error = 0; + int s; + struct ether_header *eh; + + ifp = hook->node->private; + + if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) + senderr(ENETDOWN); + /* + * If a simplex interface, and the packet is being sent to our + * Ethernet address or a broadcast address, loopback a copy. + * XXX To make a simplex device behave exactly like a duplex + * device, we should copy in the case of sending to our own + * ethernet address (thus letting the original actually appear + * on the wire). However, we don't do that here for security + * reasons and compatibility with the original behavior. + */ + if (ifp->if_flags & IFF_SIMPLEX) { + eh = mtod(m, struct ether_header *); + if (m->m_flags & M_BCAST) { + struct mbuf *n = m_copy(m, 0, (int)M_COPYALL); + + ng_queue_data(hook, n, meta); + } else if (bcmp(eh->ether_dhost, + eh->ether_shost, ETHER_ADDR_LEN) == 0) { + ng_queue_data(hook, m, meta); + return (0); /* XXX */ + } + } + s = splimp(); + /* + * Queue message on interface, and start output if interface + * not yet active. + * XXX if we lookead at the priority in the meta data we could + * queue high priority items at the head. + */ + if (IF_QFULL(&ifp->if_snd)) { + IF_DROP(&ifp->if_snd); + splx(s); + senderr(ENOBUFS); + } + IF_ENQUEUE(&ifp->if_snd, m); + if ((ifp->if_flags & IFF_OACTIVE) == 0) + (*ifp->if_start)(ifp); + splx(s); + ifp->if_obytes += m->m_pkthdr.len; + if (m->m_flags & M_MCAST) + ifp->if_omcasts++; + return (error); + +bad: + NG_FREE_DATA(m, meta); + return (error); +} + +/* + * pass an mbuf out to the connected hook + */ +static void +ngether_send(struct arpcom *ac, struct ether_header *eh, struct mbuf *m) +{ + node_p node = AC2NG(ac); + struct ether_header *eh2; + + if (node && LIST_FIRST(&(node->hooks))) { + M_PREPEND(m, sizeof(*eh), M_DONTWAIT); + if (m == 0) + return; + eh2 = mtod(m, struct ether_header *); + /* + * Possibly 'eh' already points + * to the right place. + */ + if (eh2 != eh) + bcopy(eh, eh2, sizeof(*eh)); + ng_queue_data(LIST_FIRST(&(node->hooks)), m, NULL); + } else { + m_freem(m); + } +} +/* + * do local shutdown processing.. + * This node will refuse to go away, unless the hardware says to.. + * don't unref the node, or remove our name. just clear our links up. + */ +static int +ngether_rmnode(node_p node) +{ + ng_cutlinks(node); + node->flags &= ~NG_INVALID; /* bounce back to life */ + return (0); +} + +/* already linked */ +static int +ngether_connect(hook_p hook) +{ + /* be really amiable and just say "YUP that's OK by me! " */ + return (0); +} + +/* + * notify on hook disconnection (destruction) + * + * For this type, removal of the last lins no effect. The interface can run + * independently. + * Since we have no per-hook information, this is rather simple. + */ +static int +ngether_disconnect(hook_p hook) +{ + hook->node->flags &= ~NGEF_DIVERT; + return (0); +} +#endif /* NETGRAPH */ + +/********************************** END *************************************/ |