summaryrefslogtreecommitdiffstats
path: root/sys/netgraph/ng_eiface.c
diff options
context:
space:
mode:
authorzec <zec@FreeBSD.org>2011-05-24 14:10:33 +0000
committerzec <zec@FreeBSD.org>2011-05-24 14:10:33 +0000
commitb3769a4355d61333f6b11fd0ccac65cddb54a3a8 (patch)
tree850c8da0ebcac46cb31c7bad7aec5c9ed1c12f81 /sys/netgraph/ng_eiface.c
parent7028e129fd10d1c1da25e7ac0581af6637d89df5 (diff)
downloadFreeBSD-src-b3769a4355d61333f6b11fd0ccac65cddb54a3a8.zip
FreeBSD-src-b3769a4355d61333f6b11fd0ccac65cddb54a3a8.tar.gz
Provide fake link status information in an attempt to let ng_eiface(4)
virtual ifnets more realistically mimic physical ethernet interfaces. The main motivation behind this change is to allow for ng_eiface(4) interfaces to participate in STP if_bridge(4) configurations. When announcing link status changes, switch to the vnet to which the ifnet belongs, since it is possible for ng_eiface ifnets to be assigned to a vnet different from the one in which its netgraph node resides. MFC after: 3 days
Diffstat (limited to 'sys/netgraph/ng_eiface.c')
-rw-r--r--sys/netgraph/ng_eiface.c71
1 files changed, 70 insertions, 1 deletions
diff --git a/sys/netgraph/ng_eiface.c b/sys/netgraph/ng_eiface.c
index cbae38f..d761a6c 100644
--- a/sys/netgraph/ng_eiface.c
+++ b/sys/netgraph/ng_eiface.c
@@ -41,6 +41,7 @@
#include <sys/syslog.h>
#include <net/if.h>
+#include <net/if_media.h>
#include <net/if_types.h>
#include <net/netisr.h>
#include <net/route.h>
@@ -76,6 +77,8 @@ static const struct ng_cmdlist ng_eiface_cmdlist[] = {
/* Node private data */
struct ng_eiface_private {
struct ifnet *ifp; /* per-interface network data */
+ struct ifmedia media; /* (fake) media information */
+ int link_status; /* fake */
int unit; /* Interface unit number */
node_p node; /* Our netgraph node */
hook_p ether; /* Hook for ethernet stream */
@@ -127,6 +130,7 @@ static VNET_DEFINE(struct unrhdr *, ng_eiface_unit);
static int
ng_eiface_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
{
+ const priv_p priv = (priv_p)ifp->if_softc;
struct ifreq *const ifr = (struct ifreq *)data;
int s, error = 0;
@@ -170,6 +174,12 @@ ng_eiface_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
ifp->if_mtu = ifr->ifr_mtu;
break;
+ /* (Fake) media type manipulation */
+ case SIOCSIFMEDIA:
+ case SIOCGIFMEDIA:
+ error = ifmedia_ioctl(ifp, ifr, &priv->media, command);
+ break;
+
/* Stuff that's not supported */
case SIOCADDMULTI:
case SIOCDELMULTI:
@@ -280,7 +290,6 @@ ng_eiface_start2(node_p node, hook_p hook, void *arg1, int arg2)
static void
ng_eiface_start(struct ifnet *ifp)
{
-
const priv_p priv = (priv_p)ifp->if_softc;
/* Don't do anything if output is active */
@@ -328,6 +337,41 @@ ng_eiface_print_ioctl(struct ifnet *ifp, int command, caddr_t data)
}
#endif /* DEBUG */
+/*
+ * ifmedia stuff
+ */
+static int
+ng_eiface_mediachange(struct ifnet *ifp)
+{
+ const priv_p priv = (priv_p)ifp->if_softc;
+ struct ifmedia *ifm = &priv->media;
+
+ if (IFM_TYPE(ifm->ifm_media) != IFM_ETHER)
+ return (EINVAL);
+ if (IFM_SUBTYPE(ifm->ifm_media) == IFM_AUTO)
+ ifp->if_baudrate = ifmedia_baudrate(IFM_ETHER | IFM_1000_T);
+ else
+ ifp->if_baudrate = ifmedia_baudrate(ifm->ifm_media);
+
+ return (0);
+}
+
+static void
+ng_eiface_mediastatus(struct ifnet *ifp, struct ifmediareq *ifmr)
+{
+ const priv_p priv = (priv_p)ifp->if_softc;
+ struct ifmedia *ifm = &priv->media;
+
+ if (ifm->ifm_cur->ifm_media == (IFM_ETHER | IFM_AUTO) &&
+ (priv->link_status & IFM_ACTIVE))
+ ifmr->ifm_active = IFM_ETHER | IFM_1000_T | IFM_FDX;
+ else
+ ifmr->ifm_active = ifm->ifm_cur->ifm_media;
+ ifmr->ifm_status = priv->link_status;
+
+ return;
+}
+
/************************************************************************
NETGRAPH NODE STUFF
************************************************************************/
@@ -371,6 +415,18 @@ ng_eiface_constructor(node_p node)
ifp->if_flags = (IFF_SIMPLEX | IFF_BROADCAST | IFF_MULTICAST);
ifp->if_capabilities = IFCAP_VLAN_MTU | IFCAP_JUMBO_MTU;
ifp->if_capenable = IFCAP_VLAN_MTU | IFCAP_JUMBO_MTU;
+ ifmedia_init(&priv->media, 0, ng_eiface_mediachange,
+ ng_eiface_mediastatus);
+ ifmedia_add(&priv->media, IFM_ETHER | IFM_10_T, 0, NULL);
+ ifmedia_add(&priv->media, IFM_ETHER | IFM_10_T | IFM_FDX, 0, NULL);
+ ifmedia_add(&priv->media, IFM_ETHER | IFM_100_TX, 0, NULL);
+ ifmedia_add(&priv->media, IFM_ETHER | IFM_100_TX | IFM_FDX, 0, NULL);
+ ifmedia_add(&priv->media, IFM_ETHER | IFM_1000_T, 0, NULL);
+ ifmedia_add(&priv->media, IFM_ETHER | IFM_1000_T | IFM_FDX, 0, NULL);
+ ifmedia_add(&priv->media, IFM_ETHER | IFM_10G_T | IFM_FDX, 0, NULL);
+ ifmedia_add(&priv->media, IFM_ETHER | IFM_AUTO, 0, NULL);
+ ifmedia_set(&priv->media, IFM_ETHER | IFM_AUTO);
+ priv->link_status = IFM_AVALID;
/* Give this node the same name as the interface (if possible) */
if (ng_name_node(node, ifp->if_xname) != 0)
@@ -379,6 +435,7 @@ ng_eiface_constructor(node_p node)
/* Attach the interface */
ether_ifattach(ifp, eaddr);
+ ifp->if_baudrate = ifmedia_baudrate(IFM_ETHER | IFM_1000_T);
/* Done */
return (0);
@@ -401,7 +458,10 @@ ng_eiface_newhook(node_p node, hook_p hook, const char *name)
NG_HOOK_SET_PRIVATE(hook, &priv->ether);
NG_HOOK_SET_TO_INBOUND(hook);
+ priv->link_status |= IFM_ACTIVE;
+ CURVNET_SET_QUIET(ifp->if_vnet);
if_link_state_change(ifp, LINK_STATE_UP);
+ CURVNET_RESTORE();
return (0);
}
@@ -486,16 +546,20 @@ ng_eiface_rcvmsg(node_p node, item_p item, hook_p lasthook)
} /* end of inner switch() */
break;
case NGM_FLOW_COOKIE:
+ CURVNET_SET_QUIET(ifp->if_vnet);
switch (msg->header.cmd) {
case NGM_LINK_IS_UP:
+ priv->link_status |= IFM_ACTIVE;
if_link_state_change(ifp, LINK_STATE_UP);
break;
case NGM_LINK_IS_DOWN:
+ priv->link_status &= ~IFM_ACTIVE;
if_link_state_change(ifp, LINK_STATE_DOWN);
break;
default:
break;
}
+ CURVNET_RESTORE();
break;
default:
error = EINVAL;
@@ -557,6 +621,7 @@ ng_eiface_rmnode(node_p node)
* hence we have to change the current vnet context here.
*/
CURVNET_SET_QUIET(ifp->if_vnet);
+ ifmedia_removeall(&priv->media);
ether_ifdetach(ifp);
if_free(ifp);
CURVNET_RESTORE();
@@ -576,6 +641,10 @@ ng_eiface_disconnect(hook_p hook)
const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
priv->ether = NULL;
+ priv->link_status &= ~IFM_ACTIVE;
+ CURVNET_SET_QUIET(priv->ifp->if_vnet);
+ if_link_state_change(priv->ifp, LINK_STATE_DOWN);
+ CURVNET_RESTORE();
return (0);
}
OpenPOWER on IntegriCloud