diff options
author | julian <julian@FreeBSD.org> | 2001-02-25 05:46:52 +0000 |
---|---|---|
committer | julian <julian@FreeBSD.org> | 2001-02-25 05:46:52 +0000 |
commit | ae2160cfc71040384273f3e9ad711c30dacff22e (patch) | |
tree | 41c14040ccab15d212481becd0e359b0831d0508 /sys/netgraph | |
parent | 2a3d8bdb3b5c2ab1ee8e2062400d36f55bf6febd (diff) | |
download | FreeBSD-src-ae2160cfc71040384273f3e9ad711c30dacff22e.zip FreeBSD-src-ae2160cfc71040384273f3e9ad711c30dacff22e.tar.gz |
Add a node that looks to all the word like an ethernet but delivers its
ehternet frames to a netgraph hook.
Submitted by: "Vitaly V. Belekhov" <vitaly@riss-telecom.ru>
translated to 5.0 by me. man page not yet written.
This node still needs a little work.. don't use yet. Not yet linked into
the build.
Diffstat (limited to 'sys/netgraph')
-rw-r--r-- | sys/netgraph/ng_eiface.c | 633 | ||||
-rw-r--r-- | sys/netgraph/ng_eiface.h | 100 |
2 files changed, 733 insertions, 0 deletions
diff --git a/sys/netgraph/ng_eiface.c b/sys/netgraph/ng_eiface.c new file mode 100644 index 0000000..eebb503 --- /dev/null +++ b/sys/netgraph/ng_eiface.c @@ -0,0 +1,633 @@ +/*- + * + * Copyright (c) 1999-2001, Vitaly V Belekhov + * 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 unmodified, 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$ + */ + + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/errno.h> +#include <sys/kernel.h> +#include <sys/malloc.h> +#include <sys/mbuf.h> +#include <sys/errno.h> +#include <sys/sockio.h> +#include <sys/socket.h> +#include <sys/syslog.h> + +#include <net/if.h> +#include <net/if_types.h> +#include <net/netisr.h> + +#include <netinet/in.h> +#include <netinet/if_ether.h> + +#include <netgraph/ng_message.h> +#include <netgraph/netgraph.h> +#include <netgraph/ng_parse.h> +#include <netgraph/ng_eiface.h> + +#include <net/bpf.h> +#include <net/ethernet.h> +#include <net/if_arp.h> + + +/* Node private data */ +struct ng_eiface_private { + struct ifnet *ifp; /* This interface */ + int unit; /* Interface unit number */ + struct arpcom arpcom; /* per-interface network data */ + node_p node; /* Our netgraph node */ + hook_p ether; /* Hook for ethernet stream */ +}; +typedef struct ng_eiface_private *priv_p; + +/* Interface methods */ +static void ng_eiface_init(void *xsc); +static void ng_eiface_start(struct ifnet *ifp); +static int ng_eiface_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data); +#ifdef DEBUG +static void ng_eiface_print_ioctl(struct ifnet *ifp, int cmd, caddr_t data); +#endif + +/* Netgraph methods */ +static ng_constructor_t ng_eiface_constructor; +static ng_rcvmsg_t ng_eiface_rcvmsg; +static ng_shutdown_t ng_eiface_rmnode; +static ng_newhook_t ng_eiface_newhook; +static ng_rcvdata_t ng_eiface_rcvdata; +static ng_connect_t ng_eiface_connect; +static ng_disconnect_t ng_eiface_disconnect; + +/* Node type descriptor */ +static struct ng_type typestruct = { + NG_VERSION, + NG_EIFACE_NODE_TYPE, + NULL, + ng_eiface_constructor, + ng_eiface_rcvmsg, + ng_eiface_rmnode, + ng_eiface_newhook, + NULL, + ng_eiface_connect, + ng_eiface_rcvdata, + ng_eiface_disconnect, + ng_eiface_cmdlist +}; +NETGRAPH_INIT(eiface, &typestruct); + +static char ng_eiface_ifname[] = NG_EIFACE_EIFACE_NAME; + +/* We keep a bitmap indicating which unit numbers are free. + One means the unit number is free, zero means it's taken. */ +static int *ng_eiface_units = NULL; +static int ng_eiface_units_len = 0; +static int ng_units_in_use = 0; + +#define UNITS_BITSPERWORD (sizeof(*ng_eiface_units) * NBBY) + + +/************************************************************************ + HELPER STUFF + ************************************************************************/ +/* + * Find the first free unit number for a new interface. + * Increase the size of the unit bitmap as necessary. + */ +static __inline__ int +ng_eiface_get_unit(int *unit) +{ + int index, bit; + + for (index = 0; index < ng_eiface_units_len + && ng_eiface_units[index] == 0; index++); + if (index == ng_eiface_units_len) { /* extend array */ + int i, *newarray, newlen; + + newlen = (2 * ng_eiface_units_len) + 4; + MALLOC(newarray, int *, newlen * sizeof(*ng_eiface_units), + M_NETGRAPH, M_NOWAIT); + if (newarray == NULL) + return (ENOMEM); + bcopy(ng_eiface_units, newarray, + ng_eiface_units_len * sizeof(*ng_eiface_units)); + for (i = ng_eiface_units_len; i < newlen; i++) + newarray[i] = ~0; + if (ng_eiface_units != NULL) + FREE(ng_eiface_units, M_NETGRAPH); + ng_eiface_units = newarray; + ng_eiface_units_len = newlen; + } + bit = ffs(ng_eiface_units[index]) - 1; + KASSERT(bit >= 0 && bit <= UNITS_BITSPERWORD - 1, + ("%s: word=%d bit=%d", __FUNCTION__, ng_eiface_units[index], bit)); + ng_eiface_units[index] &= ~(1 << bit); + *unit = (index * UNITS_BITSPERWORD) + bit; + ng_units_in_use++; + return (0); +} + +/* + * Free a no longer needed unit number. + */ +static __inline__ void +ng_eiface_free_unit(int unit) +{ + int index, bit; + + index = unit / UNITS_BITSPERWORD; + bit = unit % UNITS_BITSPERWORD; + KASSERT(index < ng_eiface_units_len, + ("%s: unit=%d len=%d", __FUNCTION__, unit, ng_eiface_units_len)); + KASSERT((ng_eiface_units[index] & (1 << bit)) == 0, + ("%s: unit=%d is free", __FUNCTION__, unit)); + ng_eiface_units[index] |= (1 << bit); + /* + * XXX We could think about reducing the size of ng_eiface_units[] + * XXX here if the last portion is all ones + * XXX At least free it if no more units. + * Needed if we are to eventually be able to unload. + */ + ng_units_in_use--; + if (ng_units_in_use == 0) { /* XXX make SMP safe */ + FREE(ng_eiface_units, M_NETGRAPH); + ng_eiface_units_len = 0; + ng_eiface_units = NULL; + } +} + +/************************************************************************ + INTERFACE STUFF + ************************************************************************/ + +/* + * Process an ioctl for the virtual interface + */ +static int +ng_eiface_ioctl(struct ifnet *ifp, u_long command, caddr_t data) +{ + struct ifreq *const ifr = (struct ifreq *)data; + int s, error = 0; + +#ifdef DEBUG + ng_eiface_print_ioctl(ifp, command, data); +#endif + s = splimp(); + switch (command) + { + /* These two are mostly handled at a higher layer */ + case SIOCSIFADDR: + error = ether_ioctl(ifp, command, data); + break; + case SIOCGIFADDR: + break; + + /* Set flags */ + case SIOCSIFFLAGS: + /* + * If the interface is marked up and stopped, then + * start it. If it is marked down and running, + * then stop it. + */ + if (ifr->ifr_flags & IFF_UP) { + if (!(ifp->if_flags & IFF_RUNNING)) { + ifp->if_flags &= ~(IFF_OACTIVE); + ifp->if_flags |= IFF_RUNNING; + } + } else { + if (ifp->if_flags & IFF_RUNNING) + ifp->if_flags + &= ~(IFF_RUNNING | IFF_OACTIVE); + } + break; + + /* Set the interface MTU */ + case SIOCSIFMTU: + if (ifr->ifr_mtu > NG_EIFACE_MTU_MAX + || ifr->ifr_mtu < NG_EIFACE_MTU_MIN) + error = EINVAL; + else + ifp->if_mtu = ifr->ifr_mtu; + break; + + /* Stuff that's not supported */ + case SIOCADDMULTI: + case SIOCDELMULTI: + error = 0; + break; + case SIOCSIFPHYS: + error = EOPNOTSUPP; + break; + + default: + error = EINVAL; + break; + } + (void)splx(s); + return (error); +} + +static void +ng_eiface_init(void *xsc) +{ + priv_p sc = xsc; + struct ifnet *ifp = sc->ifp; + int s; + + s = splimp(); + + ifp->if_flags |= IFF_RUNNING; + ifp->if_flags &= ~IFF_OACTIVE; + + splx(s); +} + +/* + * This routine is called to deliver a packet out the interface. We simply + * relay the packet to the ether hook, if it is connected. + */ +static void +ng_eiface_start(struct ifnet *ifp) +{ + const priv_p priv = (priv_p) ifp->if_softc; + int len, error = 0; + struct mbuf *m; + + /* Check interface flags */ + if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) != (IFF_UP | IFF_RUNNING)) + return; + + /* Don't do anything if output is active */ + if (ifp->if_flags & IFF_OACTIVE) + return; + + ifp->if_flags |= IFF_OACTIVE; + + /* + * Grab a packet to transmit. + */ + IF_DEQUEUE(&ifp->if_snd, m); + + /* If there's nothing to send, return. */ + if (m == NULL) { + ifp->if_flags &= ~IFF_OACTIVE; + return; + } + + /* Berkeley packet filter + * Pass packet to bpf if there is a listener. + */ + if (ifp->if_bpf) + bpf_mtap(ifp, m); + + /* Copy length before the mbuf gets invalidated */ + len = m->m_pkthdr.len; + + /* + * Send packet; if hook is not connected, mbuf will get + * freed. + */ + NG_SEND_DATA_ONLY(error, priv->ether, m); + + /* Update stats */ + if (error == 0) { + ifp->if_obytes += len; + ifp->if_opackets++; + } + ifp->if_flags &= ~IFF_OACTIVE; + return; +} + +#ifdef DEBUG +/* + * Display an ioctl to the virtual interface + */ + +static void +ng_eiface_print_ioctl(struct ifnet *ifp, int command, caddr_t data){ + char *str; + + switch (command & IOC_DIRMASK) + { + case IOC_VOID: + str = "IO"; + break; + case IOC_OUT: + str = "IOR"; + break; + case IOC_IN: + str = "IOW"; + break; + case IOC_INOUT: + str = "IORW"; + break; + default: + str = "IO??"; + } + log(LOG_DEBUG, "%s%d: %s('%c', %d, char[%d])\n", + ifp->if_name, ifp->if_unit, + str, + IOCGROUP(command), + command & 0xff, + IOCPARM_LEN(command)); +} +#endif /* DEBUG */ + +/************************************************************************ + NETGRAPH NODE STUFF + ************************************************************************/ + +/* + * Constructor for a node + */ +static int +ng_eiface_constructor(node_p node) +{ + struct ifnet *ifp; + priv_p priv; + int error = 0; + + /* Allocate node and interface private structures */ + MALLOC(priv, priv_p, sizeof(*priv), M_NETGRAPH, M_WAITOK); + if (priv == NULL) { + return (ENOMEM); + } + bzero(priv, sizeof(*priv)); + + ifp = &(priv->arpcom.ac_if); + + /* Link them together */ + ifp->if_softc = priv; + priv->ifp = ifp; + + /* Get an interface unit number */ + if ((error = ng_eiface_get_unit(&priv->unit)) != 0) { + FREE(priv, M_NETGRAPH); + return (error); + } + + /* Link together node and private info */ + NG_NODE_SET_PRIVATE(node, priv); + priv->node = node; + + /* Initialize interface structure */ + ifp->if_name = ng_eiface_ifname; + ifp->if_unit = priv->unit; + ifp->if_init = ng_eiface_init; + ifp->if_output = ether_output; + ifp->if_start = ng_eiface_start; + ifp->if_ioctl = ng_eiface_ioctl; + ifp->if_watchdog = NULL; + ifp->if_snd.ifq_maxlen = IFQ_MAXLEN; + ifp->if_flags = (IFF_SIMPLEX | IFF_BROADCAST | IFF_MULTICAST); + + /* + * Give this node name * bzero(ifname, sizeof(ifname)); + * sprintf(ifname, "if%s%d", ifp->if_name, ifp->if_unit); (void) + * ng_name_node(node, ifname); + */ + + /* Attach the interface */ + ether_ifattach(ifp, ETHER_BPF_SUPPORTED); + + /* Done */ + return (0); +} + +/* + * Give our ok for a hook to be added + */ +static int +ng_eiface_newhook(node_p node, hook_p hook, const char *name) +{ + priv_p priv = NG_NODE_PRIVATE(node); + + if (strcmp(name, NG_EIFACE_HOOK_ETHER)) + return (EPFNOSUPPORT); + if (priv->ether != NULL) + return (EISCONN); + priv->ether = hook; + NG_HOOK_SET_PRIVATE(hook, &priv->ether); + + return (0); +} + +/* + * Receive a control message + */ +static int +ng_eiface_rcvmsg(node_p node, item_p item, hook_p lasthook) +{ + priv_p priv = NG_NODE_PRIVATE(node); + struct ifnet *const ifp = priv->ifp; + struct ng_mesg *resp = NULL; + int error = 0; + struct ng_mesg *msg; + + NGI_GET_MSG(item, msg); + switch (msg->header.typecookie) { + case NGM_EIFACE_COOKIE: + switch (msg->header.cmd) { + case NGM_EIFACE_SET: + { + struct ng_eiface_par *eaddr; + + if (msg->header.arglen != sizeof(struct ng_eiface_par)){ + error = EINVAL; + break; + } + eaddr = (struct ng_eiface_par *)(msg->data); + + priv->arpcom.ac_enaddr[0] = eaddr->oct0; + priv->arpcom.ac_enaddr[1] = eaddr->oct1; + priv->arpcom.ac_enaddr[2] = eaddr->oct2; + priv->arpcom.ac_enaddr[3] = eaddr->oct3; + priv->arpcom.ac_enaddr[4] = eaddr->oct4; + priv->arpcom.ac_enaddr[5] = eaddr->oct5; + + break; + } + + case NGM_EIFACE_GET_IFNAME: + { + struct ng_eiface_ifname *arg; + + NG_MKRESPONSE(resp, msg, sizeof(*arg), M_NOWAIT); + if (resp == NULL) { + error = ENOMEM; + break; + } + arg = (struct ng_eiface_ifname *)resp->data; + sprintf(arg->ngif_name, + "%s%d", ifp->if_name, ifp->if_unit); + break; + } + + case NGM_EIFACE_GET_IFADDRS: + { + struct ifaddr *ifa; + caddr_t ptr; + int buflen; + +#define SA_SIZE(s) ((s)->sa_len<sizeof(*(s))? sizeof(*(s)):(s)->sa_len) + + /* Determine size of response and allocate it */ + buflen = 0; + TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) + buflen += SA_SIZE(ifa->ifa_addr); + NG_MKRESPONSE(resp, msg, buflen, M_NOWAIT); + if (resp == NULL) { + error = ENOMEM; + break; + } + /* Add addresses */ + ptr = resp->data; + TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { + const int len = SA_SIZE(ifa->ifa_addr); + + if (buflen < len) { + log(LOG_ERR, "%s%d: len changed?\n", + ifp->if_name, ifp->if_unit); + break; + } + bcopy(ifa->ifa_addr, ptr, len); + ptr += len; + buflen -= len; + } + break; +#undef SA_SIZE + } + + default: + error = EINVAL; + break; + } /* end of inner switch() */ + break; + default: + error = EINVAL; + break; + } + NG_RESPOND_MSG(error, node, item, resp); + NG_FREE_MSG(msg); + return (error); +} + +/* + * Recive data from a hook. Pass the packet to the ether_input routine. + */ +static int +ng_eiface_rcvdata(hook_p hook, item_p item) +{ + priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); + struct ifnet *const ifp = priv->ifp; + int s, error = 0; + struct ether_header *eh; + u_short ether_type; + struct mbuf *m; + + NGI_GET_M(item, m); + /* Meta-data ends its life here... */ + NG_FREE_ITEM(item); + + if (m == NULL) + { + printf("ng_eiface: mbuf is null.\n"); + return (EINVAL); + } + if (!(ifp->if_flags & IFF_UP)) { + return (ENETDOWN); + } + + /* Note receiving interface */ + m->m_pkthdr.rcvif = ifp; + + /* Update interface stats */ + ifp->if_ipackets++; + + eh = mtod(m, struct ether_header *); + ether_type = ntohs(eh->ether_type); + + s = splimp(); + m->m_pkthdr.len -= sizeof(*eh); + m->m_len -= sizeof(*eh); + if (m->m_len) { + m->m_data += sizeof(*eh); + } else { + if (ether_type == ETHERTYPE_ARP) { + m->m_len = m->m_next->m_len; + m->m_data = m->m_next->m_data; + } + } + splx(s); + + ether_input(ifp, eh, m); + + /* Done */ + return (error); +} + +/* + * the node. + */ +static int +ng_eiface_rmnode(node_p node) +{ + priv_p priv = NG_NODE_PRIVATE(node); + struct ifnet *const ifp = priv->ifp; + + ether_ifdetach(ifp, ETHER_BPF_SUPPORTED); + ng_eiface_free_unit(priv->unit); + FREE(priv, M_NETGRAPH); + NG_NODE_SET_PRIVATE(node, NULL); + NG_NODE_UNREF(node); + return (0); +} + + +/* + * This is called once we've already connected a new hook to the other node. + * It gives us a chance to balk at the last minute. + */ +static int +ng_eiface_connect(hook_p hook) +{ + /* be really amiable and just say "YUP that's OK by me! " */ + return (0); +} + +/* + * Hook disconnection + */ +static int +ng_eiface_disconnect(hook_p hook) +{ + priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); + + priv->ether = NULL; + return (0); +} diff --git a/sys/netgraph/ng_eiface.h b/sys/netgraph/ng_eiface.h new file mode 100644 index 0000000..493c0cb --- /dev/null +++ b/sys/netgraph/ng_eiface.h @@ -0,0 +1,100 @@ +/* + * ng_eiface.h + * + * Copyright (c) 1999-2001, Vitaly V Belekhov + * 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 unmodified, 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 _NETGRAPH_EIFACE_H_ +#define _NETGRAPH_EIFACE_H_ + +/* Node type name and magic cookie */ +#define NG_EIFACE_NODE_TYPE "eiface" +#define NGM_EIFACE_COOKIE 948105892 + +/* Interface base name */ +#define NG_EIFACE_EIFACE_NAME "nge" +#define NG_EIFACE_EIFACE_NAME_MAX 15 + +/* My hook names */ +#define NG_EIFACE_HOOK_ETHER "ether" + +/* MTU bounds */ +#define NG_EIFACE_MTU_MIN 72 +#define NG_EIFACE_MTU_MAX 2312 +#define NG_EIFACE_MTU_DEFAULT 1500 + +/* Netgraph commands */ +enum { + NGM_EIFACE_GET_IFNAME = 1, /* returns struct ng_eiface_ifname */ + NGM_EIFACE_GET_IFADDRS, /* returns list of addresses */ + NGM_EIFACE_SET, /* set ethernet address */ +}; + +struct ng_eiface_ifname { + char ngif_name[NG_EIFACE_EIFACE_NAME_MAX + 1]; +}; + +struct ng_eiface_par { + u_char oct0; + u_char oct1; + u_char oct2; + u_char oct3; + u_char oct4; + u_char oct5; +}; + +static const struct ng_parse_struct_info ng_eiface_par_fields = { + { + { "oct0", &ng_parse_int8_type }, + { "oct1", &ng_parse_int8_type }, + { "oct2", &ng_parse_int8_type }, + { "oct3", &ng_parse_int8_type }, + { "oct4", &ng_parse_int8_type }, + { "oct5", &ng_parse_int8_type }, + { NULL }, + } +}; + +static const struct ng_parse_type ng_eiface_par_type = { + &ng_parse_struct_type, + &ng_eiface_par_fields +}; + +static const struct ng_cmdlist ng_eiface_cmdlist[] = { + { + NGM_EIFACE_COOKIE, + NGM_EIFACE_SET, + "set", + &ng_eiface_par_type, + NULL + }, + { 0 } +}; + + +#endif /* _NETGRAPH_EIFACE_H_ */ |