70d85b1cf3
Refactor of /dev/random device. Main points include: * Userland seeding is no longer used. This auto-seeds at boot time on PC/Desktop setups; this may need some tweeking and intelligence from those folks setting up embedded boxes, but the work is believed to be minimal. * An entropy cache is written to /entropy (even during installation) and the kernel uses this at next boot. * An entropy file written to /boot/entropy can be loaded by loader(8) * Hardware sources such as rdrand are fed into Yarrow, and are no longer available raw. ------------------------------------------------------------------------ r256240 | des | 2013-10-09 21:14:16 +0100 (Wed, 09 Oct 2013) | 4 lines Add a RANDOM_RWFILE option and hide the entropy cache code behind it. Rename YARROW_RNG and FORTUNA_RNG to RANDOM_YARROW and RANDOM_FORTUNA. Add the RANDOM_* options to LINT. ------------------------------------------------------------------------ r256239 | des | 2013-10-09 21:12:59 +0100 (Wed, 09 Oct 2013) | 2 lines Define RANDOM_PURE_RNDTEST for rndtest(4). ------------------------------------------------------------------------ r256204 | des | 2013-10-09 18:51:38 +0100 (Wed, 09 Oct 2013) | 2 lines staticize struct random_hardware_source ------------------------------------------------------------------------ r256203 | markm | 2013-10-09 18:50:36 +0100 (Wed, 09 Oct 2013) | 2 lines Wrap some policy-rich code in 'if NOTYET' until we can thresh out what it really needs to do. ------------------------------------------------------------------------ r256184 | des | 2013-10-09 10:13:12 +0100 (Wed, 09 Oct 2013) | 2 lines Re-add /dev/urandom for compatibility purposes. ------------------------------------------------------------------------ r256182 | des | 2013-10-09 10:11:14 +0100 (Wed, 09 Oct 2013) | 3 lines Add missing include guards and move the existing ones out of the implementation namespace. ------------------------------------------------------------------------ r256168 | markm | 2013-10-08 23:14:07 +0100 (Tue, 08 Oct 2013) | 10 lines Fix some just-noticed problems: o Allow this to work with "nodevice random" by fixing where the MALLOC pool is defined. o Fix the explicit reseed code. This was correct as submitted, but in the project branch doesn't need to set the "seeded" bit as this is done correctly in the "unblock" function. o Remove some debug ifdeffing. o Adjust comments. ------------------------------------------------------------------------ r256159 | markm | 2013-10-08 19:48:11 +0100 (Tue, 08 Oct 2013) | 6 lines Time to eat crow for me. I replaced the sx_* locks that Arthur used with regular mutexes; this turned out the be the wrong thing to do as the locks need to be sleepable. Revert this folly. # Submitted by: Arthur Mesh <arthurmesh@gmail.com> (In original diff) ------------------------------------------------------------------------ r256138 | des | 2013-10-08 12:05:26 +0100 (Tue, 08 Oct 2013) | 10 lines Add YARROW_RNG and FORTUNA_RNG to sys/conf/options. Add a SYSINIT that forces a reseed during proc0 setup, which happens fairly late in the boot process. Add a RANDOM_DEBUG option which enables some debugging printf()s. Add a new RANDOM_ATTACH entropy source which harvests entropy from the get_cyclecount() delta across each call to a device attach method. ------------------------------------------------------------------------ r256135 | markm | 2013-10-08 07:54:52 +0100 (Tue, 08 Oct 2013) | 8 lines Debugging. My attempt at EVENTHANDLER(multiuser) was a failure; use EVENTHANDLER(mountroot) instead. This means we can't count on /var being present, so something will need to be done about harvesting /var/db/entropy/... . Some policy now needs to be sorted out, and a pre-sync cache needs to be written, but apart from that we are now ready to go. Over to review. ------------------------------------------------------------------------ r256094 | markm | 2013-10-06 23:45:02 +0100 (Sun, 06 Oct 2013) | 8 lines Snapshot. Looking pretty good; this mostly works now. New code includes: * Read cached entropy at startup, both from files and from loader(8) preloaded entropy. Failures are soft, but announced. Untested. * Use EVENTHANDLER to do above just before we go multiuser. Untested. ------------------------------------------------------------------------ r256088 | markm | 2013-10-06 14:01:42 +0100 (Sun, 06 Oct 2013) | 2 lines Fix up the man page for random(4). This mainly removes no-longer-relevant details about HW RNGs, reseeding explicitly and user-supplied entropy. ------------------------------------------------------------------------ r256087 | markm | 2013-10-06 13:43:42 +0100 (Sun, 06 Oct 2013) | 6 lines As userland writing to /dev/random is no more, remove the "better than nothing" bootstrap mode. Add SWI harvesting to the mix. My box seeds Yarrow by itself in a few seconds! YMMV; more to follow. ------------------------------------------------------------------------ r256086 | markm | 2013-10-06 13:40:32 +0100 (Sun, 06 Oct 2013) | 11 lines Debug run. This now works, except that the "live" sources haven't been tested. With all sources turned on, this unlocks itself in a couple of seconds! That is no my box, and there is no guarantee that this will be the case everywhere. * Cut debug prints. * Use the same locks/mutexes all the way through. * Be a tad more conservative about entropy estimates. ------------------------------------------------------------------------ r256084 | markm | 2013-10-06 13:35:29 +0100 (Sun, 06 Oct 2013) | 5 lines Don't use the "real" assembler mnemonics; older compilers may not understand them (like when building CURRENT on 9.x). # Submitted by: Konstantin Belousov <kostikbel@gmail.com> ------------------------------------------------------------------------ r256081 | markm | 2013-10-06 10:55:28 +0100 (Sun, 06 Oct 2013) | 12 lines SNAPSHOT. Simplify the malloc pools; We only need one for this device. Simplify the harvest queue. Marginally improve the entropy pool hashing, making it a bit faster in the process. Connect up the hardware "live" source harvesting. This is simplistic for now, and will need to be made rate-adaptive. All of the above passes a compile test but needs to be debugged. ------------------------------------------------------------------------ r256042 | markm | 2013-10-04 07:55:06 +0100 (Fri, 04 Oct 2013) | 25 lines Snapshot. This passes the build test, but has not yet been finished or debugged. Contains: * Refactor the hardware RNG CPU instruction sources to feed into the software mixer. This is unfinished. The actual harvesting needs to be sorted out. Modified by me (see below). * Remove 'frac' parameter from random_harvest(). This was never used and adds extra code for no good reason. * Remove device write entropy harvesting. This provided a weak attack vector, was not very good at bootstrapping the device. To follow will be a replacement explicit reseed knob. * Separate out all the RANDOM_PURE sources into separate harvest entities. This adds some secuity in the case where more than one is present. * Review all the code and fix anything obviously messy or inconsistent. Address som review concerns while I'm here, like rename the pseudo-rng to 'dummy'. # Submitted by: Arthur Mesh <arthurmesh@gmail.com> (the first item) ------------------------------------------------------------------------ r255319 | markm | 2013-09-06 18:51:52 +0100 (Fri, 06 Sep 2013) | 4 lines Yarrow wants entropy estimations to be conservative; the usual idea is that if you are certain you have N bits of entropy, you declare N/2. ------------------------------------------------------------------------ r255075 | markm | 2013-08-30 18:47:53 +0100 (Fri, 30 Aug 2013) | 4 lines Remove short-lived idea; thread to harvest (eg) RDRAND enropy into the usual harvest queues. It was a nifty idea, but too heavyweight. # Submitted by: Arthur Mesh <arthurmesh@gmail.com> ------------------------------------------------------------------------ r255071 | markm | 2013-08-30 12:42:57 +0100 (Fri, 30 Aug 2013) | 4 lines Separate out the Software RNG entropy harvesting queue and thread into its own files. # Submitted by: Arthur Mesh <arthurmesh@gmail.com> ------------------------------------------------------------------------ r254934 | markm | 2013-08-26 20:07:03 +0100 (Mon, 26 Aug 2013) | 2 lines Remove the short-lived namei experiment. ------------------------------------------------------------------------ r254928 | markm | 2013-08-26 19:35:21 +0100 (Mon, 26 Aug 2013) | 2 lines Snapshot; Do some running repairs on entropy harvesting. More needs to follow. ------------------------------------------------------------------------ r254927 | markm | 2013-08-26 19:29:51 +0100 (Mon, 26 Aug 2013) | 15 lines Snapshot of current work; 1) Clean up namespace; only use "Yarrow" where it is Yarrow-specific or close enough to the Yarrow algorithm. For the rest use a neutral name. 2) Tidy up headers; put private stuff in private places. More could be done here. 3) Streamline the hashing/encryption; no need for a 256-bit counter; 128 bits will last for long enough. There are bits of debug code lying around; these will be removed at a later stage. ------------------------------------------------------------------------ r254784 | markm | 2013-08-24 14:54:56 +0100 (Sat, 24 Aug 2013) | 39 lines 1) example (partially humorous random_adaptor, that I call "EXAMPLE") * It's not meant to be used in a real system, it's there to show how the basics of how to create interfaces for random_adaptors. Perhaps it should belong in a manual page 2) Move probe.c's functionality in to random_adaptors.c * rename random_ident_hardware() to random_adaptor_choose() 3) Introduce a new way to choose (or select) random_adaptors via tunable "rngs_want" It's a list of comma separated names of adaptors, ordered by preferences. I.e.: rngs_want="yarrow,rdrand" Such setting would cause yarrow to be preferred to rdrand. If neither of them are available (or registered), then system will default to something reasonable (currently yarrow). If yarrow is not present, then we fall back to the adaptor that's first on the list of registered adaptors. 4) Introduce a way where RNGs can play a role of entropy source. This is mostly useful for HW rngs. The way I envision this is that every HW RNG will use this functionality by default. Functionality to disable this is also present. I have an example of how to use this in random_adaptor_example.c (see modload event, and init function) 5) fix kern.random.adaptors from kern.random.adaptors: yarrowpanicblock to kern.random.adaptors: yarrow,panic,block 6) add kern.random.active_adaptor to indicate currently selected adaptor: root@freebsd04:~ # sysctl kern.random.active_adaptor kern.random.active_adaptor: yarrow # Submitted by: Arthur Mesh <arthurmesh@gmail.com> Submitted by: Dag-Erling Smørgrav <des@FreeBSD.org>, Arthur Mesh <arthurmesh@gmail.com> Reviewed by: des@FreeBSD.org Approved by: re (delphij) Approved by: secteam (des,delphij)
861 lines
20 KiB
C
861 lines
20 KiB
C
/*
|
|
* ng_iface.c
|
|
*/
|
|
|
|
/*-
|
|
* Copyright (c) 1996-1999 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.
|
|
*
|
|
* Author: Archie Cobbs <archie@freebsd.org>
|
|
*
|
|
* $FreeBSD$
|
|
* $Whistle: ng_iface.c,v 1.33 1999/11/01 09:24:51 julian Exp $
|
|
*/
|
|
|
|
/*
|
|
* This node is also a system networking interface. It has
|
|
* a hook for each protocol (IP, AppleTalk, IPX, etc). Packets
|
|
* are simply relayed between the interface and the hooks.
|
|
*
|
|
* Interfaces are named ng0, ng1, etc. New nodes take the
|
|
* first available interface name.
|
|
*
|
|
* This node also includes Berkeley packet filter support.
|
|
*/
|
|
|
|
#include "opt_atalk.h"
|
|
#include "opt_inet.h"
|
|
#include "opt_inet6.h"
|
|
#include "opt_ipx.h"
|
|
|
|
#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/proc.h>
|
|
#include <sys/random.h>
|
|
#include <sys/sockio.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/syslog.h>
|
|
#include <sys/libkern.h>
|
|
|
|
#include <net/if.h>
|
|
#include <net/if_types.h>
|
|
#include <net/bpf.h>
|
|
#include <net/netisr.h>
|
|
#include <net/route.h>
|
|
#include <net/vnet.h>
|
|
|
|
#include <netinet/in.h>
|
|
|
|
#include <netgraph/ng_message.h>
|
|
#include <netgraph/netgraph.h>
|
|
#include <netgraph/ng_parse.h>
|
|
#include <netgraph/ng_iface.h>
|
|
#include <netgraph/ng_cisco.h>
|
|
|
|
#ifdef NG_SEPARATE_MALLOC
|
|
static MALLOC_DEFINE(M_NETGRAPH_IFACE, "netgraph_iface", "netgraph iface node");
|
|
#else
|
|
#define M_NETGRAPH_IFACE M_NETGRAPH
|
|
#endif
|
|
|
|
/* This struct describes one address family */
|
|
struct iffam {
|
|
sa_family_t family; /* Address family */
|
|
const char *hookname; /* Name for hook */
|
|
};
|
|
typedef const struct iffam *iffam_p;
|
|
|
|
/* List of address families supported by our interface */
|
|
const static struct iffam gFamilies[] = {
|
|
{ AF_INET, NG_IFACE_HOOK_INET },
|
|
{ AF_INET6, NG_IFACE_HOOK_INET6 },
|
|
{ AF_APPLETALK, NG_IFACE_HOOK_ATALK },
|
|
{ AF_IPX, NG_IFACE_HOOK_IPX },
|
|
{ AF_ATM, NG_IFACE_HOOK_ATM },
|
|
{ AF_NATM, NG_IFACE_HOOK_NATM },
|
|
};
|
|
#define NUM_FAMILIES (sizeof(gFamilies) / sizeof(*gFamilies))
|
|
|
|
/* Node private data */
|
|
struct ng_iface_private {
|
|
struct ifnet *ifp; /* Our interface */
|
|
int unit; /* Interface unit number */
|
|
node_p node; /* Our netgraph node */
|
|
hook_p hooks[NUM_FAMILIES]; /* Hook for each address family */
|
|
};
|
|
typedef struct ng_iface_private *priv_p;
|
|
|
|
/* Interface methods */
|
|
static void ng_iface_start(struct ifnet *ifp);
|
|
static int ng_iface_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data);
|
|
static int ng_iface_output(struct ifnet *ifp, struct mbuf *m0,
|
|
const struct sockaddr *dst, struct route *ro);
|
|
static void ng_iface_bpftap(struct ifnet *ifp,
|
|
struct mbuf *m, sa_family_t family);
|
|
static int ng_iface_send(struct ifnet *ifp, struct mbuf *m,
|
|
sa_family_t sa);
|
|
#ifdef DEBUG
|
|
static void ng_iface_print_ioctl(struct ifnet *ifp, int cmd, caddr_t data);
|
|
#endif
|
|
|
|
/* Netgraph methods */
|
|
static int ng_iface_mod_event(module_t, int, void *);
|
|
static ng_constructor_t ng_iface_constructor;
|
|
static ng_rcvmsg_t ng_iface_rcvmsg;
|
|
static ng_shutdown_t ng_iface_shutdown;
|
|
static ng_newhook_t ng_iface_newhook;
|
|
static ng_rcvdata_t ng_iface_rcvdata;
|
|
static ng_disconnect_t ng_iface_disconnect;
|
|
|
|
/* Helper stuff */
|
|
static iffam_p get_iffam_from_af(sa_family_t family);
|
|
static iffam_p get_iffam_from_hook(priv_p priv, hook_p hook);
|
|
static iffam_p get_iffam_from_name(const char *name);
|
|
static hook_p *get_hook_from_iffam(priv_p priv, iffam_p iffam);
|
|
|
|
/* Parse type for struct ng_cisco_ipaddr */
|
|
static const struct ng_parse_struct_field ng_cisco_ipaddr_type_fields[]
|
|
= NG_CISCO_IPADDR_TYPE_INFO;
|
|
static const struct ng_parse_type ng_cisco_ipaddr_type = {
|
|
&ng_parse_struct_type,
|
|
&ng_cisco_ipaddr_type_fields
|
|
};
|
|
|
|
/* List of commands and how to convert arguments to/from ASCII */
|
|
static const struct ng_cmdlist ng_iface_cmds[] = {
|
|
{
|
|
NGM_IFACE_COOKIE,
|
|
NGM_IFACE_GET_IFNAME,
|
|
"getifname",
|
|
NULL,
|
|
&ng_parse_string_type
|
|
},
|
|
{
|
|
NGM_IFACE_COOKIE,
|
|
NGM_IFACE_POINT2POINT,
|
|
"point2point",
|
|
NULL,
|
|
NULL
|
|
},
|
|
{
|
|
NGM_IFACE_COOKIE,
|
|
NGM_IFACE_BROADCAST,
|
|
"broadcast",
|
|
NULL,
|
|
NULL
|
|
},
|
|
{
|
|
NGM_CISCO_COOKIE,
|
|
NGM_CISCO_GET_IPADDR,
|
|
"getipaddr",
|
|
NULL,
|
|
&ng_cisco_ipaddr_type
|
|
},
|
|
{
|
|
NGM_IFACE_COOKIE,
|
|
NGM_IFACE_GET_IFINDEX,
|
|
"getifindex",
|
|
NULL,
|
|
&ng_parse_uint32_type
|
|
},
|
|
{ 0 }
|
|
};
|
|
|
|
/* Node type descriptor */
|
|
static struct ng_type typestruct = {
|
|
.version = NG_ABI_VERSION,
|
|
.name = NG_IFACE_NODE_TYPE,
|
|
.mod_event = ng_iface_mod_event,
|
|
.constructor = ng_iface_constructor,
|
|
.rcvmsg = ng_iface_rcvmsg,
|
|
.shutdown = ng_iface_shutdown,
|
|
.newhook = ng_iface_newhook,
|
|
.rcvdata = ng_iface_rcvdata,
|
|
.disconnect = ng_iface_disconnect,
|
|
.cmdlist = ng_iface_cmds,
|
|
};
|
|
NETGRAPH_INIT(iface, &typestruct);
|
|
|
|
static VNET_DEFINE(struct unrhdr *, ng_iface_unit);
|
|
#define V_ng_iface_unit VNET(ng_iface_unit)
|
|
|
|
/************************************************************************
|
|
HELPER STUFF
|
|
************************************************************************/
|
|
|
|
/*
|
|
* Get the family descriptor from the family ID
|
|
*/
|
|
static __inline iffam_p
|
|
get_iffam_from_af(sa_family_t family)
|
|
{
|
|
iffam_p iffam;
|
|
int k;
|
|
|
|
for (k = 0; k < NUM_FAMILIES; k++) {
|
|
iffam = &gFamilies[k];
|
|
if (iffam->family == family)
|
|
return (iffam);
|
|
}
|
|
return (NULL);
|
|
}
|
|
|
|
/*
|
|
* Get the family descriptor from the hook
|
|
*/
|
|
static __inline iffam_p
|
|
get_iffam_from_hook(priv_p priv, hook_p hook)
|
|
{
|
|
int k;
|
|
|
|
for (k = 0; k < NUM_FAMILIES; k++)
|
|
if (priv->hooks[k] == hook)
|
|
return (&gFamilies[k]);
|
|
return (NULL);
|
|
}
|
|
|
|
/*
|
|
* Get the hook from the iffam descriptor
|
|
*/
|
|
|
|
static __inline hook_p *
|
|
get_hook_from_iffam(priv_p priv, iffam_p iffam)
|
|
{
|
|
return (&priv->hooks[iffam - gFamilies]);
|
|
}
|
|
|
|
/*
|
|
* Get the iffam descriptor from the name
|
|
*/
|
|
static __inline iffam_p
|
|
get_iffam_from_name(const char *name)
|
|
{
|
|
iffam_p iffam;
|
|
int k;
|
|
|
|
for (k = 0; k < NUM_FAMILIES; k++) {
|
|
iffam = &gFamilies[k];
|
|
if (!strcmp(iffam->hookname, name))
|
|
return (iffam);
|
|
}
|
|
return (NULL);
|
|
}
|
|
|
|
/************************************************************************
|
|
INTERFACE STUFF
|
|
************************************************************************/
|
|
|
|
/*
|
|
* Process an ioctl for the virtual interface
|
|
*/
|
|
static int
|
|
ng_iface_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
|
|
{
|
|
struct ifreq *const ifr = (struct ifreq *) data;
|
|
int error = 0;
|
|
|
|
#ifdef DEBUG
|
|
ng_iface_print_ioctl(ifp, command, data);
|
|
#endif
|
|
switch (command) {
|
|
|
|
/* These two are mostly handled at a higher layer */
|
|
case SIOCSIFADDR:
|
|
ifp->if_flags |= IFF_UP;
|
|
ifp->if_drv_flags |= IFF_DRV_RUNNING;
|
|
ifp->if_drv_flags &= ~(IFF_DRV_OACTIVE);
|
|
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_drv_flags & IFF_DRV_RUNNING)) {
|
|
ifp->if_drv_flags &= ~(IFF_DRV_OACTIVE);
|
|
ifp->if_drv_flags |= IFF_DRV_RUNNING;
|
|
}
|
|
} else {
|
|
if (ifp->if_drv_flags & IFF_DRV_RUNNING)
|
|
ifp->if_drv_flags &= ~(IFF_DRV_RUNNING |
|
|
IFF_DRV_OACTIVE);
|
|
}
|
|
break;
|
|
|
|
/* Set the interface MTU */
|
|
case SIOCSIFMTU:
|
|
if (ifr->ifr_mtu > NG_IFACE_MTU_MAX
|
|
|| ifr->ifr_mtu < NG_IFACE_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;
|
|
}
|
|
return (error);
|
|
}
|
|
|
|
/*
|
|
* This routine is called to deliver a packet out the interface.
|
|
* We simply look at the address family and relay the packet to
|
|
* the corresponding hook, if it exists and is connected.
|
|
*/
|
|
|
|
static int
|
|
ng_iface_output(struct ifnet *ifp, struct mbuf *m,
|
|
const struct sockaddr *dst, struct route *ro)
|
|
{
|
|
struct m_tag *mtag;
|
|
uint32_t af;
|
|
int error;
|
|
|
|
/* Check interface flags */
|
|
if (!((ifp->if_flags & IFF_UP) &&
|
|
(ifp->if_drv_flags & IFF_DRV_RUNNING))) {
|
|
m_freem(m);
|
|
return (ENETDOWN);
|
|
}
|
|
|
|
/* Protect from deadly infinite recursion. */
|
|
mtag = NULL;
|
|
while ((mtag = m_tag_locate(m, MTAG_NGIF, MTAG_NGIF_CALLED, mtag))) {
|
|
if (*(struct ifnet **)(mtag + 1) == ifp) {
|
|
log(LOG_NOTICE, "Loop detected on %s\n", ifp->if_xname);
|
|
m_freem(m);
|
|
return (EDEADLK);
|
|
}
|
|
}
|
|
mtag = m_tag_alloc(MTAG_NGIF, MTAG_NGIF_CALLED, sizeof(struct ifnet *),
|
|
M_NOWAIT);
|
|
if (mtag == NULL) {
|
|
m_freem(m);
|
|
return (ENOMEM);
|
|
}
|
|
*(struct ifnet **)(mtag + 1) = ifp;
|
|
m_tag_prepend(m, mtag);
|
|
|
|
/* BPF writes need to be handled specially. */
|
|
if (dst->sa_family == AF_UNSPEC)
|
|
bcopy(dst->sa_data, &af, sizeof(af));
|
|
else
|
|
af = dst->sa_family;
|
|
|
|
/* Berkeley packet filter */
|
|
ng_iface_bpftap(ifp, m, af);
|
|
|
|
if (ALTQ_IS_ENABLED(&ifp->if_snd)) {
|
|
M_PREPEND(m, sizeof(sa_family_t), M_NOWAIT);
|
|
if (m == NULL) {
|
|
IFQ_LOCK(&ifp->if_snd);
|
|
IFQ_INC_DROPS(&ifp->if_snd);
|
|
IFQ_UNLOCK(&ifp->if_snd);
|
|
ifp->if_oerrors++;
|
|
return (ENOBUFS);
|
|
}
|
|
*(sa_family_t *)m->m_data = af;
|
|
error = (ifp->if_transmit)(ifp, m);
|
|
} else
|
|
error = ng_iface_send(ifp, m, af);
|
|
|
|
return (error);
|
|
}
|
|
|
|
/*
|
|
* Start method is used only when ALTQ is enabled.
|
|
*/
|
|
static void
|
|
ng_iface_start(struct ifnet *ifp)
|
|
{
|
|
struct mbuf *m;
|
|
sa_family_t sa;
|
|
|
|
KASSERT(ALTQ_IS_ENABLED(&ifp->if_snd), ("%s without ALTQ", __func__));
|
|
|
|
for(;;) {
|
|
IFQ_DRV_DEQUEUE(&ifp->if_snd, m);
|
|
if (m == NULL)
|
|
break;
|
|
sa = *mtod(m, sa_family_t *);
|
|
m_adj(m, sizeof(sa_family_t));
|
|
ng_iface_send(ifp, m, sa);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Flash a packet by the BPF (requires prepending 4 byte AF header)
|
|
* Note the phoney mbuf; this is OK because BPF treats it read-only.
|
|
*/
|
|
static void
|
|
ng_iface_bpftap(struct ifnet *ifp, struct mbuf *m, sa_family_t family)
|
|
{
|
|
KASSERT(family != AF_UNSPEC, ("%s: family=AF_UNSPEC", __func__));
|
|
if (bpf_peers_present(ifp->if_bpf)) {
|
|
int32_t family4 = (int32_t)family;
|
|
bpf_mtap2(ifp->if_bpf, &family4, sizeof(family4), m);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* This routine does actual delivery of the packet into the
|
|
* netgraph(4). It is called from ng_iface_start() and
|
|
* ng_iface_output().
|
|
*/
|
|
static int
|
|
ng_iface_send(struct ifnet *ifp, struct mbuf *m, sa_family_t sa)
|
|
{
|
|
const priv_p priv = (priv_p) ifp->if_softc;
|
|
const iffam_p iffam = get_iffam_from_af(sa);
|
|
int error;
|
|
int len;
|
|
|
|
/* Check address family to determine hook (if known) */
|
|
if (iffam == NULL) {
|
|
m_freem(m);
|
|
log(LOG_WARNING, "%s: can't handle af%d\n", ifp->if_xname, sa);
|
|
return (EAFNOSUPPORT);
|
|
}
|
|
|
|
/* Copy length before the mbuf gets invalidated. */
|
|
len = m->m_pkthdr.len;
|
|
|
|
/* Send packet. If hook is not connected, mbuf will get freed. */
|
|
NG_OUTBOUND_THREAD_REF();
|
|
NG_SEND_DATA_ONLY(error, *get_hook_from_iffam(priv, iffam), m);
|
|
NG_OUTBOUND_THREAD_UNREF();
|
|
|
|
/* Update stats. */
|
|
if (error == 0) {
|
|
ifp->if_obytes += len;
|
|
ifp->if_opackets++;
|
|
}
|
|
|
|
return (error);
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
/*
|
|
* Display an ioctl to the virtual interface
|
|
*/
|
|
|
|
static void
|
|
ng_iface_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: %s('%c', %d, char[%d])\n",
|
|
ifp->if_xname,
|
|
str,
|
|
IOCGROUP(command),
|
|
command & 0xff,
|
|
IOCPARM_LEN(command));
|
|
}
|
|
#endif /* DEBUG */
|
|
|
|
/************************************************************************
|
|
NETGRAPH NODE STUFF
|
|
************************************************************************/
|
|
|
|
/*
|
|
* Constructor for a node
|
|
*/
|
|
static int
|
|
ng_iface_constructor(node_p node)
|
|
{
|
|
struct ifnet *ifp;
|
|
priv_p priv;
|
|
|
|
/* Allocate node and interface private structures */
|
|
priv = malloc(sizeof(*priv), M_NETGRAPH_IFACE, M_WAITOK | M_ZERO);
|
|
ifp = if_alloc(IFT_PROPVIRTUAL);
|
|
if (ifp == NULL) {
|
|
free(priv, M_NETGRAPH_IFACE);
|
|
return (ENOMEM);
|
|
}
|
|
|
|
/* Link them together */
|
|
ifp->if_softc = priv;
|
|
priv->ifp = ifp;
|
|
|
|
/* Get an interface unit number */
|
|
priv->unit = alloc_unr(V_ng_iface_unit);
|
|
|
|
/* Link together node and private info */
|
|
NG_NODE_SET_PRIVATE(node, priv);
|
|
priv->node = node;
|
|
|
|
/* Initialize interface structure */
|
|
if_initname(ifp, NG_IFACE_IFACE_NAME, priv->unit);
|
|
ifp->if_output = ng_iface_output;
|
|
ifp->if_start = ng_iface_start;
|
|
ifp->if_ioctl = ng_iface_ioctl;
|
|
ifp->if_mtu = NG_IFACE_MTU_DEFAULT;
|
|
ifp->if_flags = (IFF_SIMPLEX|IFF_POINTOPOINT|IFF_NOARP|IFF_MULTICAST);
|
|
ifp->if_type = IFT_PROPVIRTUAL; /* XXX */
|
|
ifp->if_addrlen = 0; /* XXX */
|
|
ifp->if_hdrlen = 0; /* XXX */
|
|
ifp->if_baudrate = 64000; /* XXX */
|
|
IFQ_SET_MAXLEN(&ifp->if_snd, ifqmaxlen);
|
|
ifp->if_snd.ifq_drv_maxlen = ifqmaxlen;
|
|
IFQ_SET_READY(&ifp->if_snd);
|
|
|
|
/* Give this node the same name as the interface (if possible) */
|
|
if (ng_name_node(node, ifp->if_xname) != 0)
|
|
log(LOG_WARNING, "%s: can't acquire netgraph name\n",
|
|
ifp->if_xname);
|
|
|
|
/* Attach the interface */
|
|
if_attach(ifp);
|
|
bpfattach(ifp, DLT_NULL, sizeof(u_int32_t));
|
|
|
|
/* Done */
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Give our ok for a hook to be added
|
|
*/
|
|
static int
|
|
ng_iface_newhook(node_p node, hook_p hook, const char *name)
|
|
{
|
|
const iffam_p iffam = get_iffam_from_name(name);
|
|
hook_p *hookptr;
|
|
|
|
if (iffam == NULL)
|
|
return (EPFNOSUPPORT);
|
|
hookptr = get_hook_from_iffam(NG_NODE_PRIVATE(node), iffam);
|
|
if (*hookptr != NULL)
|
|
return (EISCONN);
|
|
*hookptr = hook;
|
|
NG_HOOK_HI_STACK(hook);
|
|
NG_HOOK_SET_TO_INBOUND(hook);
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Receive a control message
|
|
*/
|
|
static int
|
|
ng_iface_rcvmsg(node_p node, item_p item, hook_p lasthook)
|
|
{
|
|
const 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_IFACE_COOKIE:
|
|
switch (msg->header.cmd) {
|
|
case NGM_IFACE_GET_IFNAME:
|
|
NG_MKRESPONSE(resp, msg, IFNAMSIZ, M_NOWAIT);
|
|
if (resp == NULL) {
|
|
error = ENOMEM;
|
|
break;
|
|
}
|
|
strlcpy(resp->data, ifp->if_xname, IFNAMSIZ);
|
|
break;
|
|
|
|
case NGM_IFACE_POINT2POINT:
|
|
case NGM_IFACE_BROADCAST:
|
|
{
|
|
|
|
/* Deny request if interface is UP */
|
|
if ((ifp->if_flags & IFF_UP) != 0)
|
|
return (EBUSY);
|
|
|
|
/* Change flags */
|
|
switch (msg->header.cmd) {
|
|
case NGM_IFACE_POINT2POINT:
|
|
ifp->if_flags |= IFF_POINTOPOINT;
|
|
ifp->if_flags &= ~IFF_BROADCAST;
|
|
break;
|
|
case NGM_IFACE_BROADCAST:
|
|
ifp->if_flags &= ~IFF_POINTOPOINT;
|
|
ifp->if_flags |= IFF_BROADCAST;
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case NGM_IFACE_GET_IFINDEX:
|
|
NG_MKRESPONSE(resp, msg, sizeof(uint32_t), M_NOWAIT);
|
|
if (resp == NULL) {
|
|
error = ENOMEM;
|
|
break;
|
|
}
|
|
*((uint32_t *)resp->data) = priv->ifp->if_index;
|
|
break;
|
|
|
|
default:
|
|
error = EINVAL;
|
|
break;
|
|
}
|
|
break;
|
|
case NGM_CISCO_COOKIE:
|
|
switch (msg->header.cmd) {
|
|
case NGM_CISCO_GET_IPADDR: /* we understand this too */
|
|
{
|
|
struct ifaddr *ifa;
|
|
|
|
/* Return the first configured IP address */
|
|
if_addr_rlock(ifp);
|
|
TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
|
|
struct ng_cisco_ipaddr *ips;
|
|
|
|
if (ifa->ifa_addr->sa_family != AF_INET)
|
|
continue;
|
|
NG_MKRESPONSE(resp, msg, sizeof(ips), M_NOWAIT);
|
|
if (resp == NULL) {
|
|
error = ENOMEM;
|
|
break;
|
|
}
|
|
ips = (struct ng_cisco_ipaddr *)resp->data;
|
|
ips->ipaddr = ((struct sockaddr_in *)
|
|
ifa->ifa_addr)->sin_addr;
|
|
ips->netmask = ((struct sockaddr_in *)
|
|
ifa->ifa_netmask)->sin_addr;
|
|
break;
|
|
}
|
|
if_addr_runlock(ifp);
|
|
|
|
/* No IP addresses on this interface? */
|
|
if (ifa == NULL)
|
|
error = EADDRNOTAVAIL;
|
|
break;
|
|
}
|
|
default:
|
|
error = EINVAL;
|
|
break;
|
|
}
|
|
break;
|
|
case NGM_FLOW_COOKIE:
|
|
switch (msg->header.cmd) {
|
|
case NGM_LINK_IS_UP:
|
|
ifp->if_drv_flags |= IFF_DRV_RUNNING;
|
|
break;
|
|
case NGM_LINK_IS_DOWN:
|
|
ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
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 correct input routine.
|
|
*/
|
|
static int
|
|
ng_iface_rcvdata(hook_p hook, item_p item)
|
|
{
|
|
const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
|
|
const iffam_p iffam = get_iffam_from_hook(priv, hook);
|
|
struct ifnet *const ifp = priv->ifp;
|
|
struct mbuf *m;
|
|
int isr;
|
|
|
|
NGI_GET_M(item, m);
|
|
NG_FREE_ITEM(item);
|
|
/* Sanity checks */
|
|
KASSERT(iffam != NULL, ("%s: iffam", __func__));
|
|
M_ASSERTPKTHDR(m);
|
|
if ((ifp->if_flags & IFF_UP) == 0) {
|
|
NG_FREE_M(m);
|
|
return (ENETDOWN);
|
|
}
|
|
|
|
/* Update interface stats */
|
|
ifp->if_ipackets++;
|
|
ifp->if_ibytes += m->m_pkthdr.len;
|
|
|
|
/* Note receiving interface */
|
|
m->m_pkthdr.rcvif = ifp;
|
|
|
|
/* Berkeley packet filter */
|
|
ng_iface_bpftap(ifp, m, iffam->family);
|
|
|
|
/* Send packet */
|
|
switch (iffam->family) {
|
|
#ifdef INET
|
|
case AF_INET:
|
|
isr = NETISR_IP;
|
|
break;
|
|
#endif
|
|
#ifdef INET6
|
|
case AF_INET6:
|
|
isr = NETISR_IPV6;
|
|
break;
|
|
#endif
|
|
#ifdef IPX
|
|
case AF_IPX:
|
|
isr = NETISR_IPX;
|
|
break;
|
|
#endif
|
|
#ifdef NETATALK
|
|
case AF_APPLETALK:
|
|
isr = NETISR_ATALK2;
|
|
break;
|
|
#endif
|
|
default:
|
|
m_freem(m);
|
|
return (EAFNOSUPPORT);
|
|
}
|
|
if (harvest.point_to_point)
|
|
random_harvest(&(m->m_data), 12, 2, RANDOM_NET_NG);
|
|
M_SETFIB(m, ifp->if_fib);
|
|
netisr_dispatch(isr, m);
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Shutdown and remove the node and its associated interface.
|
|
*/
|
|
static int
|
|
ng_iface_shutdown(node_p node)
|
|
{
|
|
const priv_p priv = NG_NODE_PRIVATE(node);
|
|
|
|
/*
|
|
* The ifnet may be in a different vnet than the netgraph node,
|
|
* hence we have to change the current vnet context here.
|
|
*/
|
|
CURVNET_SET_QUIET(priv->ifp->if_vnet);
|
|
bpfdetach(priv->ifp);
|
|
if_detach(priv->ifp);
|
|
if_free(priv->ifp);
|
|
CURVNET_RESTORE();
|
|
priv->ifp = NULL;
|
|
free_unr(V_ng_iface_unit, priv->unit);
|
|
free(priv, M_NETGRAPH_IFACE);
|
|
NG_NODE_SET_PRIVATE(node, NULL);
|
|
NG_NODE_UNREF(node);
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Hook disconnection. Note that we do *not* shutdown when all
|
|
* hooks have been disconnected.
|
|
*/
|
|
static int
|
|
ng_iface_disconnect(hook_p hook)
|
|
{
|
|
const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
|
|
const iffam_p iffam = get_iffam_from_hook(priv, hook);
|
|
|
|
if (iffam == NULL)
|
|
panic("%s", __func__);
|
|
*get_hook_from_iffam(priv, iffam) = NULL;
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Handle loading and unloading for this node type.
|
|
*/
|
|
static int
|
|
ng_iface_mod_event(module_t mod, int event, void *data)
|
|
{
|
|
int error = 0;
|
|
|
|
switch (event) {
|
|
case MOD_LOAD:
|
|
case MOD_UNLOAD:
|
|
break;
|
|
default:
|
|
error = EOPNOTSUPP;
|
|
break;
|
|
}
|
|
return (error);
|
|
}
|
|
|
|
static void
|
|
vnet_ng_iface_init(const void *unused)
|
|
{
|
|
|
|
V_ng_iface_unit = new_unrhdr(0, 0xffff, NULL);
|
|
}
|
|
VNET_SYSINIT(vnet_ng_iface_init, SI_SUB_PSEUDO, SI_ORDER_ANY,
|
|
vnet_ng_iface_init, NULL);
|
|
|
|
static void
|
|
vnet_ng_iface_uninit(const void *unused)
|
|
{
|
|
|
|
delete_unrhdr(V_ng_iface_unit);
|
|
}
|
|
VNET_SYSUNINIT(vnet_ng_iface_uninit, SI_SUB_PSEUDO, SI_ORDER_ANY,
|
|
vnet_ng_iface_uninit, NULL);
|