diff options
author | zec <zec@FreeBSD.org> | 2011-05-24 14:10:33 +0000 |
---|---|---|
committer | zec <zec@FreeBSD.org> | 2011-05-24 14:10:33 +0000 |
commit | b3769a4355d61333f6b11fd0ccac65cddb54a3a8 (patch) | |
tree | 850c8da0ebcac46cb31c7bad7aec5c9ed1c12f81 /sys/netgraph/ng_eiface.c | |
parent | 7028e129fd10d1c1da25e7ac0581af6637d89df5 (diff) | |
download | FreeBSD-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.c | 71 |
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); } |