diff --git a/sys/conf/files b/sys/conf/files index 9cd9299f3298..1939ca17840a 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -674,6 +674,7 @@ netgraph/ng_bpf.c optional netgraph_bpf net/bpf_filter.c optional netgraph_bpf netgraph/ng_cisco.c optional netgraph_cisco netgraph/ng_echo.c optional netgraph_echo +netgraph/ng_ether.c optional netgraph_ether netgraph/ng_frame_relay.c optional netgraph_frame_relay netgraph/ng_hole.c optional netgraph_hole netgraph/ng_iface.c optional netgraph_iface diff --git a/sys/conf/options b/sys/conf/options index ad8363f0a40f..7b8552d3e07e 100644 --- a/sys/conf/options +++ b/sys/conf/options @@ -275,6 +275,7 @@ NETGRAPH_ASYNC opt_netgraph.h NETGRAPH_BPF opt_netgraph.h NETGRAPH_CISCO opt_netgraph.h NETGRAPH_ECHO opt_netgraph.h +NETGRAPH_ETHER opt_netgraph.h NETGRAPH_FRAME_RELAY opt_netgraph.h NETGRAPH_HOLE opt_netgraph.h NETGRAPH_IFACE opt_netgraph.h diff --git a/sys/net/ethernet.h b/sys/net/ethernet.h index 7295e5a40d3f..5bb3448f457a 100644 --- a/sys/net/ethernet.h +++ b/sys/net/ethernet.h @@ -80,7 +80,18 @@ struct ether_addr { #define ETHERMTU (ETHER_MAX_LEN-ETHER_HDR_LEN-ETHER_CRC_LEN) #define ETHERMIN (ETHER_MIN_LEN-ETHER_HDR_LEN-ETHER_CRC_LEN) -#ifndef _KERNEL +#ifdef _KERNEL + +extern void (*ng_ether_input_p)(struct ifnet *ifp, + struct mbuf **mp, struct ether_header *eh); +extern void (*ng_ether_input_orphan_p)(struct ifnet *ifp, + struct mbuf *m, struct ether_header *eh); +extern int (*ng_ether_output_p)(struct ifnet *ifp, struct mbuf **mp); +extern void (*ng_ether_attach_p)(struct ifnet *ifp); +extern void (*ng_ether_detach_p)(struct ifnet *ifp); + +#else /* _KERNEL */ + #include /* @@ -93,6 +104,7 @@ int ether_line __P((char *, struct ether_addr *, char *)); char *ether_ntoa __P((struct ether_addr *)); int ether_ntohost __P((char *, struct ether_addr *)); __END_DECLS -#endif + +#endif /* !_KERNEL */ #endif /* !_NET_ETHERNET_H_ */ diff --git a/sys/net/if.c b/sys/net/if.c index 2dce111ec22c..e33e86c064a5 100644 --- a/sys/net/if.c +++ b/sys/net/if.c @@ -54,6 +54,7 @@ #include #include #include +#include #include #include @@ -235,6 +236,17 @@ if_detach(ifp) s = splnet(); if_down(ifp); + /* + * Do any type-specific detach operation + */ + switch (ifp->if_type) { + case IFT_ETHER: + ether_ifdetach(ifp); + break; + default: + break; + } + /* * Remove address from ifnet_addrs[] and maybe decrement if_index. * Clean up all addresses. diff --git a/sys/net/if_arp.h b/sys/net/if_arp.h index 5bfbe4b65567..67609725678a 100644 --- a/sys/net/if_arp.h +++ b/sys/net/if_arp.h @@ -102,9 +102,7 @@ struct arpcom { struct ifnet ac_if; /* network-visible interface */ u_char ac_enaddr[6]; /* ethernet hardware address */ int ac_multicnt; /* length of ac_multiaddrs list */ -/* #ifdef NETGRAPH */ - void *ac_ng; /* hook to hang netgraph stuff off */ -/* #endif */ + void *ac_netgraph; /* ng_ether(4) netgraph node info */ }; extern u_char etherbroadcastaddr[6]; diff --git a/sys/net/if_ethersubr.c b/sys/net/if_ethersubr.c index 22101e77f132..2084aac174d2 100644 --- a/sys/net/if_ethersubr.c +++ b/sys/net/if_ethersubr.c @@ -57,6 +57,7 @@ #include #include #include +#include #if defined(INET) || defined(INET6) #include @@ -105,48 +106,21 @@ extern u_char aarp_org_code[3]; #include #endif /* NVLAN > 0 */ +/* netgraph node hooks for ng_ether(4) */ +void (*ng_ether_input_p)(struct ifnet *ifp, + struct mbuf **mp, struct ether_header *eh); +void (*ng_ether_input_orphan_p)(struct ifnet *ifp, + struct mbuf *m, struct ether_header *eh); +int (*ng_ether_output_p)(struct ifnet *ifp, struct mbuf **mp); +void (*ng_ether_attach_p)(struct ifnet *ifp); +void (*ng_ether_detach_p)(struct ifnet *ifp); + static int ether_resolvemulti __P((struct ifnet *, struct sockaddr **, struct sockaddr *)); 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 -#include -#include - -static void ngether_init(void* ignored); -static void ngether_send(struct arpcom *ac, - struct ether_header *eh, struct mbuf *m); -static ng_constructor_t ngether_constructor; -static ng_rcvmsg_t ngether_rcvmsg; -static ng_shutdown_t ngether_rmnode; -static ng_newhook_t ngether_newhook; -static ng_connect_t ngether_connect; -static ng_rcvdata_t ngether_rcvdata; -static ng_disconnect_t ngether_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, - NULL -}; - -#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. @@ -162,11 +136,11 @@ ether_output(ifp, m, dst, rt0) struct rtentry *rt0; { short type; - int s, error = 0, hdrcmplt = 0; + int error = 0, hdrcmplt = 0; u_char esrc[6], edst[6]; register struct rtentry *rt; register struct ether_header *eh; - int off, len = m->m_pkthdr.len, loop_copy = 0; + int off, loop_copy = 0; int hlen; /* link layer header lenght */ struct arpcom *ac = IFP2AC(ifp); @@ -249,7 +223,6 @@ ether_output(ifp, m, dst, rt0) struct llc llc; M_PREPEND(m, sizeof(struct llc), M_WAIT); - len += sizeof(struct llc); llc.llc_dsap = llc.llc_ssap = LLC_SNAP_LSAP; llc.llc_control = LLC_UI; bcopy(at_org_code, llc.llc_snap_org_code, sizeof(at_org_code)); @@ -367,6 +340,34 @@ ether_output(ifp, m, dst, rt0) } } + /* Handle ng_ether(4) processing, if any */ + if (ng_ether_output_p != NULL) { + if ((error = (*ng_ether_output_p)(ifp, &m)) != 0) { +bad: if (m != NULL) + m_freem(m); + return (error); + } + if (m == NULL) + return (0); + } + + /* Continue with link-layer output */ + return ether_output_frame(ifp, m); +} + +/* + * Ethernet link layer output routine to send a raw frame to the device. + * + * This assumes that the 14 byte Ethernet header is present and contiguous + * in the first mbuf (if BRIDGE'ing). + */ +int +ether_output_frame(ifp, m) + struct ifnet *ifp; + struct mbuf *m; +{ + int s, error = 0; + #ifdef BRIDGE if (do_bridge) { struct ether_header hdr; @@ -390,20 +391,16 @@ ether_output(ifp, m, dst, rt0) if (IF_QFULL(&ifp->if_snd)) { IF_DROP(&ifp->if_snd); splx(s); - senderr(ENOBUFS); + m_freem(m); + return (ENOBUFS); } + ifp->if_obytes += m->m_pkthdr.len; if (m->m_flags & M_MCAST) ifp->if_omcasts++; IF_ENQUEUE(&ifp->if_snd, m); if ((ifp->if_flags & IFF_OACTIVE) == 0) (*ifp->if_start)(ifp); splx(s); - ifp->if_obytes += len + sizeof (struct ether_header); - return (error); - -bad: - if (m) - m_freem(m); return (error); } @@ -411,19 +408,16 @@ ether_output(ifp, m, dst, rt0) * Process a received Ethernet packet; * the packet is in the mbuf chain m without * the ether header, which is provided separately. + * + * First we perform any link layer operations, then continue + * to the upper layers with ether_demux(). */ void ether_input(ifp, eh, m) struct ifnet *ifp; - register struct ether_header *eh; + struct ether_header *eh; struct mbuf *m; { - register struct ifqueue *inq; - u_short ether_type; - int s; -#if defined(NETATALK) - register struct llc *l; -#endif /* Check for a BPF tap */ if (ifp->if_bpf != NULL) { @@ -436,6 +430,13 @@ ether_input(ifp, eh, m) bpf_mtap(ifp, (struct mbuf *)&mh); } + /* Handle ng_ether(4) processing, if any */ + if (ng_ether_input_p != NULL) { + (*ng_ether_input_p)(ifp, &m, eh); + if (m == NULL) + return; + } + #ifdef BRIDGE /* Check for bridging mode */ if (do_bridge) { @@ -473,6 +474,26 @@ ether_input(ifp, eh, m) #ifdef BRIDGE recvLocal: #endif + /* Continue with upper layer processing */ + ether_demux(ifp, eh, m); +} + +/* + * Upper layer processing for a received Ethernet packet. + */ +void +ether_demux(ifp, eh, m) + struct ifnet *ifp; + struct ether_header *eh; + struct mbuf *m; +{ + struct ifqueue *inq; + u_short ether_type; + int s; +#if defined(NETATALK) + register struct llc *l; +#endif + /* Discard packet if interface is not up */ if ((ifp->if_flags & IFF_UP) == 0) { m_freem(m); @@ -491,16 +512,6 @@ 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) @@ -608,20 +619,18 @@ ether_input(ifp, eh, m) break; dropanyway: default: -#ifdef NETGRAPH - ngether_send(IFP2AC(ifp), eh, m); -#else /* NETGRAPH */ - m_freem(m); -#endif /* NETGRAPH */ + if (ng_ether_input_orphan_p != NULL) + (*ng_ether_input_orphan_p)(ifp, m, eh); + else + m_freem(m); return; } #else /* NETATALK */ -#ifdef NETGRAPH - ngether_send(IFP2AC(ifp), eh, m); -#else /* NETGRAPH */ - m_freem(m); -#endif /* NETGRAPH */ - return; + if (ng_ether_input_orphan_p != NULL) + (*ng_ether_input_orphan_p)(ifp, m, eh); + else + m_freem(m); + return; #endif /* NETATALK */ } @@ -660,12 +669,22 @@ ether_ifattach(ifp) sdl->sdl_type = IFT_ETHER; sdl->sdl_alen = ifp->if_addrlen; bcopy((IFP2AC(ifp))->ac_enaddr, LLADDR(sdl), ifp->if_addrlen); -#ifdef NETGRAPH - ngether_init(ifp); -#endif /* NETGRAPH */ #ifdef INET6 in6_ifattach_getifid(ifp); #endif + if (ng_ether_attach_p != NULL) + (*ng_ether_attach_p)(ifp); +} + +/* + * Perform common duties while detaching an Ethernet interface + */ +void +ether_ifdetach(ifp) + struct ifnet *ifp; +{ + if (ng_ether_detach_p != NULL) + (*ng_ether_detach_p)(ifp); } SYSCTL_DECL(_net_link); @@ -846,348 +865,3 @@ ether_resolvemulti(ifp, llsa, sa) } } -#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, hook_p lasthook) -{ - 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 mbuf **ret_m, meta_p *ret_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); - /* drop in the MAC address */ - eh = mtod(m, struct ether_header *); - bcopy(IFP2AC(ifp)->ac_enaddr, eh->ether_shost, 6); - /* - * 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) { - 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); - } - ifp->if_obytes += m->m_pkthdr.len; - if (m->m_flags & M_MCAST) - ifp->if_omcasts++; - IF_ENQUEUE(&ifp->if_snd, m); - if ((ifp->if_flags & IFF_OACTIVE) == 0) - (*ifp->if_start)(ifp); - splx(s); - return (error); - -bad: - NG_FREE_DATA(m, meta); - return (error); -} - -/* - * pass an mbuf out to the connected hook - * More complicated than just an m_prepend, as it tries to save later nodes - * from needing to do lots of m_pullups. - */ -static void -ngether_send(struct arpcom *ac, struct ether_header *eh, struct mbuf *m) -{ - int room; - node_p node = AC2NG(ac); - struct ether_header *eh2; - - if (node && LIST_FIRST(&(node->hooks))) { - /* - * Possibly the header is already on the front, - */ - eh2 = mtod(m, struct ether_header *) - 1; - if ( eh == eh2) { - /* - * This is the case so just move the markers back to - * re-include it. We lucked out. - * This allows us to avoid a yucky m_pullup - * in later nodes if it works. - */ - m->m_len += sizeof(*eh); - m->m_data -= sizeof(*eh); - m->m_pkthdr.len += sizeof(*eh); - } else { - /* - * Alternatively there may be room even though - * it is stored somewhere else. If so, copy it in. - * This only safe because we KNOW that this packet has - * just been generated by an ethernet card, so there - * are no aliases to the buffer. (unlike in outgoing - * packets). - * Nearly all ethernet cards will end up producing mbufs - * that fall into these cases. So we are not optimising - * contorted cases. - */ - - if (m->m_flags & M_EXT) { - room = (mtod(m, caddr_t) - m->m_ext.ext_buf); - if (room > m->m_ext.ext_size) /* garbage */ - room = 0; /* fail immediatly */ - } else { - room = (mtod(m, caddr_t) - m->m_pktdat); - } - if (room > sizeof (*eh)) { - /* we have room, just copy it and adjust */ - m->m_len += sizeof(*eh); - m->m_data -= sizeof(*eh); - m->m_pkthdr.len += sizeof(*eh); - } else { - /* - * Doing anything more is likely to get more - * expensive than it's worth.. - * it's probable that everything else is in one - * big lump. The next node will do an m_pullup() - * for exactly the amount of data it needs and - * hopefully everything after that will not - * need one. So let's just use M_PREPEND. - */ - M_PREPEND(m, sizeof (*eh), M_DONTWAIT); - if (m == NULL) - return; - } - bcopy ((caddr_t)eh, mtod(m, struct ether_header *), - 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 *************************************/ diff --git a/sys/net/if_var.h b/sys/net/if_var.h index 2ec532b29e0b..5234a583a04e 100644 --- a/sys/net/if_var.h +++ b/sys/net/if_var.h @@ -324,9 +324,12 @@ extern int if_index; extern struct ifaddr **ifnet_addrs; void ether_ifattach __P((struct ifnet *)); +void ether_ifdetach __P((struct ifnet *)); void ether_input __P((struct ifnet *, struct ether_header *, struct mbuf *)); +void ether_demux __P((struct ifnet *, struct ether_header *, struct mbuf *)); int ether_output __P((struct ifnet *, struct mbuf *, struct sockaddr *, struct rtentry *)); +int ether_output_frame __P((struct ifnet *, struct mbuf *)); int ether_ioctl __P((struct ifnet *, int, caddr_t)); int if_addmulti __P((struct ifnet *, struct sockaddr *, diff --git a/sys/netgraph/ng_ether.c b/sys/netgraph/ng_ether.c new file mode 100644 index 000000000000..1df202dbe5fb --- /dev/null +++ b/sys/netgraph/ng_ether.c @@ -0,0 +1,633 @@ + +/* + * ng_ether.c + * + * Copyright (c) 1996-2000 Whistle Communications, Inc. + * All rights reserved. + * + * Subject to the following obligations and disclaimer of warranty, use and + * redistribution of this software, in source or object code forms, with or + * without modifications are expressly permitted by Whistle Communications; + * provided, however, that: + * 1. Any and all reproductions of the source or object code must include the + * copyright notice above and the following disclaimer of warranties; and + * 2. No rights are granted, in any manner or form, to use Whistle + * Communications, Inc. trademarks, including the mark "WHISTLE + * COMMUNICATIONS" on advertising, endorsements, or otherwise except as + * such appears in the above copyright notice or in the software. + * + * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND + * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO + * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE, + * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. + * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY + * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS + * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE. + * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES + * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING + * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER 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 WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * Authors: Archie Cobbs + * Julian Elischer + * + * $FreeBSD$ + */ + +/* + * ng_ether(4) netgraph node type + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define IFP2NG(ifp) ((struct ng_node *)((struct arpcom *)(ifp))->ac_netgraph) + +/* Per-node private data */ +struct private { + struct ifnet *ifp; /* associated interface */ + hook_p upper; /* upper hook connection */ + hook_p lower; /* lower OR orphan hook connection */ + u_char lowerOrphan; /* whether lower is lower or orphan */ +}; +typedef struct private *priv_p; + +/* Functional hooks called from if_ethersubr.c */ +static void ng_ether_input(struct ifnet *ifp, + struct mbuf **mp, struct ether_header *eh); +static void ng_ether_input_orphan(struct ifnet *ifp, + struct mbuf *m, struct ether_header *eh); +static int ng_ether_output(struct ifnet *ifp, struct mbuf **mp); +static void ng_ether_attach(struct ifnet *ifp); +static void ng_ether_detach(struct ifnet *ifp); + +/* Other functions */ +static void ng_ether_input2(node_p node, + struct mbuf **mp, struct ether_header *eh); +static int ng_ether_glueback_header(struct mbuf **mp, + struct ether_header *eh); +static int ng_ether_rcv_lower(node_p node, struct mbuf *m, meta_p meta); +static int ng_ether_rcv_upper(node_p node, struct mbuf *m, meta_p meta); + +/* Netgraph node methods */ +static ng_constructor_t ng_ether_constructor; +static ng_rcvmsg_t ng_ether_rcvmsg; +static ng_shutdown_t ng_ether_rmnode; +static ng_newhook_t ng_ether_newhook; +static ng_rcvdata_t ng_ether_rcvdata; +static ng_disconnect_t ng_ether_disconnect; +static int ng_ether_mod_event(module_t mod, int event, void *data); + +/* List of commands and how to convert arguments to/from ASCII */ +static const struct ng_cmdlist ng_ether_cmdlist[] = { + { + NGM_ETHER_COOKIE, + NGM_ETHER_GET_IFNAME, + "getifname", + NULL, + &ng_parse_string_type + }, + { + NGM_ETHER_COOKIE, + NGM_ETHER_GET_IFINDEX, + "getifindex", + NULL, + &ng_parse_int32_type + }, + { 0 } +}; + +static struct ng_type ng_ether_typestruct = { + NG_VERSION, + NG_ETHER_NODE_TYPE, + ng_ether_mod_event, + ng_ether_constructor, + ng_ether_rcvmsg, + ng_ether_rmnode, + ng_ether_newhook, + NULL, + NULL, + ng_ether_rcvdata, + ng_ether_rcvdata, + ng_ether_disconnect, + ng_ether_cmdlist, +}; +NETGRAPH_INIT(ether, &ng_ether_typestruct); + +/****************************************************************** + ETHERNET FUNCTION HOOKS +******************************************************************/ + +/* + * Handle a packet that has come in on an interface. We get to + * look at it here before any upper layer protocols do. + * + * NOTE: this function will get called at splimp() + */ +static void +ng_ether_input(struct ifnet *ifp, + struct mbuf **mp, struct ether_header *eh) +{ + const node_p node = IFP2NG(ifp); + const priv_p priv = node->private; + + /* If "lower" hook not connected, let packet continue */ + if (priv->lower == NULL || priv->lowerOrphan) + return; + ng_ether_input2(node, mp, eh); +} + +/* + * Handle a packet that has come in on an interface, and which + * does not match any of our known protocols (an ``orphan''). + * + * NOTE: this function will get called at splimp() + */ +static void +ng_ether_input_orphan(struct ifnet *ifp, + struct mbuf *m, struct ether_header *eh) +{ + const node_p node = IFP2NG(ifp); + const priv_p priv = node->private; + + /* If "orphan" hook not connected, let packet continue */ + if (priv->lower == NULL || !priv->lowerOrphan) { + m_freem(m); + return; + } + ng_ether_input2(node, &m, eh); + if (m != NULL) + m_freem(m); +} + +/* + * Handle a packet that has come in on an interface. + * The Ethernet header has already been detached from the mbuf, + * so we have to put it back. + * + * NOTE: this function will get called at splimp() + */ +static void +ng_ether_input2(node_p node, struct mbuf **mp, struct ether_header *eh) +{ + const priv_p priv = node->private; + meta_p meta = NULL; + int error; + + /* Glue Ethernet header back on */ + if ((error = ng_ether_glueback_header(mp, eh)) != 0) + return; + + /* Send out lower/orphan hook */ + NG_SEND_DATAQ(error, priv->lower, *mp, meta); + + /* Any reflected packet must come later due to queuing */ + *mp = NULL; +} + +/* + * Handle a packet that is going out on an interface. + * The Ethernet header is already attached to the mbuf. + */ +static int +ng_ether_output(struct ifnet *ifp, struct mbuf **mp) +{ + const node_p node = IFP2NG(ifp); + const priv_p priv = node->private; + meta_p meta = NULL; + int error = 0; + + /* If "upper" hook not connected, let packet continue */ + if (priv->upper == NULL) + return (0); + + /* Send it out "upper" hook */ + NG_SEND_DATA_RET(error, priv->upper, *mp, meta); + + /* If we got a reflected packet back, handle it */ + if (error == 0 && *mp != NULL) { + error = ng_ether_rcv_upper(node, *mp, meta); + *mp = NULL; + } + return (error); +} + +/* + * A new Ethernet interface has been attached. + * Create a new node for it, etc. + */ +static void +ng_ether_attach(struct ifnet *ifp) +{ + char name[IFNAMSIZ + 1]; + priv_p priv; + node_p node; + + /* Create node */ + KASSERT(!IFP2NG(ifp), ("%s: node already exists?", __FUNCTION__)); + snprintf(name, sizeof(name), "%s%d", ifp->if_name, ifp->if_unit); + if (ng_make_node_common(&ng_ether_typestruct, &node) != 0) { + log(LOG_ERR, "%s: can't %s for %s\n", + __FUNCTION__, "create node", name); + return; + } + + /* Allocate private data */ + MALLOC(priv, priv_p, sizeof(*priv), M_NETGRAPH, M_NOWAIT); + if (priv == NULL) { + log(LOG_ERR, "%s: can't %s for %s\n", + __FUNCTION__, "allocate memory", name); + ng_unref(node); + return; + } + bzero(priv, sizeof(*priv)); + node->private = priv; + priv->ifp = ifp; + IFP2NG(ifp) = node; + + /* Try to give the node the same name as the interface */ + if (ng_name_node(node, name) != 0) { + log(LOG_WARNING, "%s: can't name node %s\n", + __FUNCTION__, name); + } +} + +/* + * An Ethernet interface is being detached. + * Destroy its node. + */ +static void +ng_ether_detach(struct ifnet *ifp) +{ + const node_p node = IFP2NG(ifp); + priv_p priv; + + if (node == NULL) /* no node (why not?), ignore */ + return; + ng_rmnode(node); /* break all links to other nodes */ + IFP2NG(ifp) = NULL; /* detach node from interface */ + priv = node->private; /* free node private info */ + bzero(priv, sizeof(*priv)); + FREE(priv, M_NETGRAPH); + node->private = NULL; + ng_unref(node); /* free node itself */ +} + +/* + * Optimization for gluing the Ethernet header back onto + * the front of an incoming packet. + */ +static int +ng_ether_glueback_header(struct mbuf **mp, struct ether_header *eh) +{ + struct mbuf *m = *mp; + uintfptr_t room; + int error = 0; + + /* + * Possibly the header is already on the front. + * If this is the case so just move the markers back + * to re-include it. We lucked out. + * This allows us to avoid a yucky m_pullup + * in later nodes if it works. + */ + if (eh == mtod(m, struct ether_header *) - 1) { + m->m_len += sizeof(*eh); + m->m_data -= sizeof(*eh); + m->m_pkthdr.len += sizeof(*eh); + goto done; + } + + /* + * Alternatively there may be room even though + * it is stored somewhere else. If so, copy it in. + * This only safe because we KNOW that this packet has + * just been generated by an ethernet card, so there are + * no aliases to the buffer (not so for outgoing packets). + * Nearly all ethernet cards will end up producing mbufs + * that fall into these cases. So we are not optimizing + * contorted cases. + */ + if ((m->m_flags & M_EXT) != 0) { + room = mtod(m, caddr_t) - m->m_ext.ext_buf; + if (room > m->m_ext.ext_size) /* garbage, fail immediately */ + room = 0; + } else + room = mtod(m, caddr_t) - m->m_pktdat; + + /* + * If we have room, just copy it and adjust + */ + if (room >= sizeof(*eh)) { + m->m_len += sizeof(*eh); + m->m_data -= sizeof(*eh); + m->m_pkthdr.len += sizeof(*eh); + goto copy; + } + + /* + * Doing anything more is likely to get more + * expensive than it's worth.. + * it's probable that everything else is in one + * big lump. The next node will do an m_pullup() + * for exactly the amount of data it needs and + * hopefully everything after that will not + * need one. So let's just use M_PREPEND. + */ + M_PREPEND(m, sizeof (*eh), M_DONTWAIT); + if (m == NULL) { + error = ENOBUFS; + goto done; + } + +copy: + /* Copy header and return (possibly new) mbuf */ + bcopy((caddr_t)eh, mtod(m, struct ether_header *), sizeof(*eh)); +done: + *mp = m; + return error; +} + +/****************************************************************** + NETGRAPH NODE METHODS +******************************************************************/ + +/* + * It is not possible or allowable to create a node of this type. + * Nodes get created when the interface is attached (or, when + * this node type's KLD is loaded). + */ +static int +ng_ether_constructor(node_p *nodep) +{ + return (EINVAL); +} + +/* + * Check for attaching a new hook. + */ +static int +ng_ether_newhook(node_p node, hook_p hook, const char *name) +{ + const priv_p priv = node->private; + u_char orphan = priv->lowerOrphan; + hook_p *hookptr; + + /* Divert hook is an alias for lower */ + if (strcmp(name, NG_ETHER_HOOK_DIVERT) == 0) + name = NG_ETHER_HOOK_LOWER; + + /* Which hook? */ + if (strcmp(name, NG_ETHER_HOOK_UPPER) == 0) + hookptr = &priv->upper; + else if (strcmp(name, NG_ETHER_HOOK_LOWER) == 0) { + hookptr = &priv->lower; + orphan = 0; + } else if (strcmp(name, NG_ETHER_HOOK_ORPHAN) == 0) { + hookptr = &priv->lower; + orphan = 1; + } else + return (EINVAL); + + /* Check if already connected (shouldn't be, but doesn't hurt) */ + if (*hookptr != NULL) + return (EISCONN); + + /* OK */ + *hookptr = hook; + priv->lowerOrphan = orphan; + return (0); +} + +/* + * Receive an incoming control message. + */ +static int +ng_ether_rcvmsg(node_p node, struct ng_mesg *msg, const char *retaddr, + struct ng_mesg **rptr, hook_p lasthook) +{ + const priv_p priv = node->private; + struct ng_mesg *resp = NULL; + int error = 0; + + switch (msg->header.typecookie) { + case NGM_ETHER_COOKIE: + switch (msg->header.cmd) { + case NGM_ETHER_GET_IFNAME: + NG_MKRESPONSE(resp, msg, IFNAMSIZ + 1, M_NOWAIT); + if (resp == NULL) { + error = ENOMEM; + break; + } + snprintf(resp->data, IFNAMSIZ + 1, + "%s%d", priv->ifp->if_name, priv->ifp->if_unit); + break; + case NGM_ETHER_GET_IFINDEX: + NG_MKRESPONSE(resp, msg, sizeof(u_int32_t), M_NOWAIT); + if (resp == NULL) { + error = ENOMEM; + break; + } + *((u_int32_t *)resp->data) = priv->ifp->if_index; + break; + default: + error = EINVAL; + break; + } + break; + default: + error = EINVAL; + break; + } + if (rptr) + *rptr = resp; + else if (resp != NULL) + FREE(resp, M_NETGRAPH); + FREE(msg, M_NETGRAPH); + return (error); +} + +/* + * Receive data on a hook. + */ +static int +ng_ether_rcvdata(hook_p hook, struct mbuf *m, meta_p meta, + struct mbuf **ret_m, meta_p *ret_meta) +{ + const node_p node = hook->node; + const priv_p priv = node->private; + + if (hook == priv->lower) + return ng_ether_rcv_lower(node, m, meta); + if (hook == priv->upper) + return ng_ether_rcv_upper(node, m, meta); + panic("%s: weird hook", __FUNCTION__); +} + +/* + * Handle an mbuf received on the "lower" hook. + */ +static int +ng_ether_rcv_lower(node_p node, struct mbuf *m, meta_p meta) +{ + const priv_p priv = node->private; + + /* Make sure header is fully pulled up */ + if (m->m_pkthdr.len < sizeof(struct ether_header)) { + NG_FREE_DATA(m, meta); + return (EINVAL); + } + if (m->m_len < sizeof(struct ether_header) + && (m = m_pullup(m, sizeof(struct ether_header))) == NULL) { + NG_FREE_META(meta); + return (ENOBUFS); + } + + /* Send it on its way */ + NG_FREE_META(meta); + return ether_output_frame(priv->ifp, m); +} + +/* + * Handle an mbuf received on the "upper" hook. + */ +static int +ng_ether_rcv_upper(node_p node, struct mbuf *m, meta_p meta) +{ + const priv_p priv = node->private; + struct ether_header *eh; + + /* Check length and pull off header */ + if (m->m_pkthdr.len < sizeof(*eh)) { + NG_FREE_DATA(m, meta); + return (EINVAL); + } + if (m->m_len < sizeof(*eh) && (m = m_pullup(m, sizeof(*eh))) == NULL) { + NG_FREE_META(meta); + return (ENOBUFS); + } + eh = mtod(m, struct ether_header *); + m->m_data += sizeof(*eh); + m->m_len -= sizeof(*eh); + m->m_pkthdr.len -= sizeof(*eh); + + /* Route packet back in */ + NG_FREE_META(meta); + ether_demux(priv->ifp, eh, m); + return (0); +} + +/* + * Shutdown node. This resets the node but does not remove it. + */ +static int +ng_ether_rmnode(node_p node) +{ + ng_cutlinks(node); + node->flags &= ~NG_INVALID; /* bounce back to life */ + return (0); +} + +/* + * Hook disconnection. + */ +static int +ng_ether_disconnect(hook_p hook) +{ + const priv_p priv = hook->node->private; + + if (hook == priv->upper) + priv->upper = NULL; + else if (hook == priv->lower) { + priv->lower = NULL; + priv->lowerOrphan = 0; + } else + panic("%s: weird hook", __FUNCTION__); + return (0); +} + +/****************************************************************** + INITIALIZATION +******************************************************************/ + +/* + * Handle loading and unloading for this node type. + */ +static int +ng_ether_mod_event(module_t mod, int event, void *data) +{ + struct ifnet *ifp; + int error = 0; + int s; + + s = splnet(); + switch (event) { + case MOD_LOAD: + + /* Register function hooks */ + if (ng_ether_attach_p != NULL) { + error = EEXIST; + break; + } + ng_ether_attach_p = ng_ether_attach; + ng_ether_detach_p = ng_ether_detach; + ng_ether_output_p = ng_ether_output; + ng_ether_input_p = ng_ether_input; + ng_ether_input_orphan_p = ng_ether_input_orphan; + + /* Create nodes for any already-existing Ethernet interfaces */ + TAILQ_FOREACH(ifp, &ifnet, if_link) { + if (ifp->if_type == IFT_ETHER) + ng_ether_attach(ifp); + } + break; + + case MOD_UNLOAD: + + /* + * Note that the base code won't try to unload us until + * all nodes have been removed, and that can't happen + * until all Ethernet interfaces are removed. In any + * case, we know there are no nodes left if the action + * is MOD_UNLOAD, so there's no need to detach any nodes. + */ + + /* Unregister function hooks */ + ng_ether_attach_p = NULL; + ng_ether_detach_p = NULL; + ng_ether_output_p = NULL; + ng_ether_input_p = NULL; + ng_ether_input_orphan_p = NULL; + break; + + default: + error = EOPNOTSUPP; + break; + } + splx(s); + return (error); +} + diff --git a/sys/netgraph/ng_ether.h b/sys/netgraph/ng_ether.h index f492361b6374..71c73a98ce20 100644 --- a/sys/netgraph/ng_ether.h +++ b/sys/netgraph/ng_ether.h @@ -45,18 +45,18 @@ /* Node type name and magic cookie */ #define NG_ETHER_NODE_TYPE "ether" -#define NGM_ETHER_COOKIE 917786904 +#define NGM_ETHER_COOKIE 917786905 /* Hook names */ -#define NG_ETHER_HOOK_ORPHAN "orphans" -#define NG_ETHER_HOOK_DIVERT "divert" +#define NG_ETHER_HOOK_LOWER "lower" /* connection to raw device */ +#define NG_ETHER_HOOK_UPPER "upper" /* connection to upper layers */ +#define NG_ETHER_HOOK_DIVERT "divert" /* alias for lower */ +#define NG_ETHER_HOOK_ORPHAN "orphans" /* like lower, unknowns only */ -/* For adding/removing Ethernet multicast addresses */ +/* Netgraph control messages */ enum { - NGM_ETHER_ADD_MULTICAST = 1, /* supply struct ether_addr */ - NGM_ETHER_DEL_MULTICAST, /* supply struct ether_addr */ - NGM_ETHER_GET_MULTICAST, /* returns array of struct ether_addr */ - NGM_ETHER_CLR_MULTICAST, /* clears all multicast addresses */ + NGM_ETHER_GET_IFNAME = 1, /* get the interface name */ + NGM_ETHER_GET_IFINDEX, /* get the interface global index # */ }; #endif /* _NETGRAPH_NG_ETHER_H_ */