Updates for vlan stuff:
- add support for devices that do vlan tag insertion/deletion in firmware - add multicast support - add vlan_unconfig() to complement vlan_config() - update ifconfig(8) to configure vlan interfaces (vlan tag and parent device) Also fix a small bug in ifconfig; sometimes sa_family is overwritten by ioctls. Reviewed by: wollman
This commit is contained in:
parent
170f96bb1a
commit
f731f10490
@ -26,7 +26,7 @@
|
||||
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* $Id: if_vlan.c,v 1.3 1998/08/23 03:07:10 wollman Exp $
|
||||
* $Id: if_vlan.c,v 1.9 1999/03/15 00:33:02 wpaul Exp $
|
||||
*/
|
||||
|
||||
/*
|
||||
@ -39,6 +39,18 @@
|
||||
* ether_output() left on our output queue queue when it calls
|
||||
* if_start(), rewrite them for use by the real outgoing interface,
|
||||
* and ask it to send them.
|
||||
*
|
||||
* XXX It's incorrect to assume that we must always kludge up
|
||||
* headers on the physical device's behalf: some devices support
|
||||
* VLAN tag insersion and extraction in firmware. For these cases,
|
||||
* one can change the behavior of the vlan interface by setting
|
||||
* the LINK0 flag on it (that is setting the vlan interface's LINK0
|
||||
* flag, _not_ the parent's LINK0 flag; we try to leave the parent
|
||||
* alone). If the interface as the LINK0 flag set, then it will
|
||||
* not modify the ethernet header on output because the parent
|
||||
* can do that for itself. On input, the parent can call vlan_input_tag()
|
||||
* directly in order to supply us with an incoming mbuf and the vlan
|
||||
* tag value that goes with it.
|
||||
*/
|
||||
|
||||
#include "vlan.h"
|
||||
@ -48,7 +60,9 @@
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/malloc.h>
|
||||
#include <sys/mbuf.h>
|
||||
#include <sys/queue.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/sockio.h>
|
||||
#include <sys/sysctl.h>
|
||||
@ -81,6 +95,61 @@ static struct ifvlan ifv_softc[NVLAN];
|
||||
static void vlan_start(struct ifnet *ifp);
|
||||
static void vlan_ifinit(void *foo);
|
||||
static int vlan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t addr);
|
||||
static int vlan_setmulti(struct ifnet *ifp);
|
||||
static int vlan_unconfig(struct ifnet *ifp);
|
||||
static int vlan_config(struct ifvlan *ifv, struct ifnet *p);
|
||||
|
||||
/*
|
||||
* Program our multicast filter. What we're actually doing is
|
||||
* programming the multicast filter of the parent. This has the
|
||||
* side effect of causing the parent interface to receive multicast
|
||||
* traffic that it doesn't really want, which ends up being discarded
|
||||
* later by the upper protocol layers. Unfortunately, there's no way
|
||||
* to avoid this: there really is only one physical interface.
|
||||
*/
|
||||
static int vlan_setmulti(struct ifnet *ifp)
|
||||
{
|
||||
struct ifnet *ifp_p;
|
||||
struct ifmultiaddr *ifma, *rifma = NULL;
|
||||
struct ifvlan *sc;
|
||||
struct vlan_mc_entry *mc = NULL;
|
||||
struct sockaddr_dl sdl;
|
||||
int error;
|
||||
|
||||
/* Find the parent. */
|
||||
sc = ifp->if_softc;
|
||||
ifp_p = sc->ifv_p;
|
||||
|
||||
sdl.sdl_len = ETHER_ADDR_LEN;
|
||||
sdl.sdl_family = AF_LINK;
|
||||
|
||||
/* First, remove any existing filter entries. */
|
||||
while(sc->vlan_mc_listhead.slh_first != NULL) {
|
||||
mc = sc->vlan_mc_listhead.slh_first;
|
||||
bcopy((char *)&mc->mc_addr, LLADDR(&sdl), ETHER_ADDR_LEN);
|
||||
error = if_delmulti(ifp_p, (struct sockaddr *)&sdl);
|
||||
if (error)
|
||||
return(error);
|
||||
SLIST_REMOVE_HEAD(&sc->vlan_mc_listhead, mc_entries);
|
||||
free(mc, M_DEVBUF);
|
||||
}
|
||||
|
||||
/* Now program new ones. */
|
||||
for (ifma = ifp->if_multiaddrs.lh_first;
|
||||
ifma != NULL;ifma = ifma->ifma_link.le_next) {
|
||||
if (ifma->ifma_addr->sa_family != AF_LINK)
|
||||
continue;
|
||||
mc = malloc(sizeof(struct vlan_mc_entry), M_DEVBUF, M_NOWAIT);
|
||||
bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr),
|
||||
(char *)&mc->mc_addr, ETHER_ADDR_LEN);
|
||||
SLIST_INSERT_HEAD(&sc->vlan_mc_listhead, mc, mc_entries);
|
||||
error = if_addmulti(ifp_p, (struct sockaddr *)&sdl, &rifma);
|
||||
if (error)
|
||||
return(error);
|
||||
}
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
static void
|
||||
vlaninit(void *dummy)
|
||||
@ -102,6 +171,7 @@ vlaninit(void *dummy)
|
||||
ifp->if_start = vlan_start;
|
||||
ifp->if_ioctl = vlan_ioctl;
|
||||
ifp->if_output = ether_output;
|
||||
ifp->if_snd.ifq_maxlen = ifqmaxlen;
|
||||
if_attach(ifp);
|
||||
ether_ifattach(ifp);
|
||||
#if NBPFILTER > 0
|
||||
@ -118,7 +188,7 @@ PSEUDO_SET(vlaninit, if_vlan);
|
||||
static void
|
||||
vlan_ifinit(void *foo)
|
||||
{
|
||||
;
|
||||
return;
|
||||
}
|
||||
|
||||
static void
|
||||
@ -142,22 +212,48 @@ vlan_start(struct ifnet *ifp)
|
||||
bpf_mtap(ifp, m);
|
||||
#endif /* NBPFILTER > 0 */
|
||||
|
||||
M_PREPEND(m, EVL_ENCAPLEN, M_DONTWAIT);
|
||||
if (m == 0)
|
||||
continue;
|
||||
/* M_PREPEND takes care of m_len, m_pkthdr.len for us */
|
||||
|
||||
/*
|
||||
* Transform the Ethernet header into an Ethernet header
|
||||
* with 802.1Q encapsulation.
|
||||
* If the LINK0 flag is set, it means the underlying interface
|
||||
* can do VLAN tag insertion itself and doesn't require us to
|
||||
* create a special header for it. In this case, we just pass
|
||||
* the packet along. However, we need some way to tell the
|
||||
* interface where the packet came from so that it knows how
|
||||
* to find the VLAN tag to use, so we set the rcvif in the
|
||||
* mbuf header to our ifnet.
|
||||
*
|
||||
* Note: we also set the M_PROTO1 flag in the mbuf to let
|
||||
* the parent driver know that the rcvif pointer is really
|
||||
* valid. We need to do this because sometimes mbufs will
|
||||
* be allocated by other parts of the system that contain
|
||||
* garbage in the rcvif pointer. Using the M_PROTO1 flag
|
||||
* lets the driver perform a proper sanity check and avoid
|
||||
* following potentially bogus rcvif pointers off into
|
||||
* never-never land.
|
||||
*/
|
||||
bcopy(mtod(m, char *) + EVL_ENCAPLEN, mtod(m, char *),
|
||||
sizeof(struct ether_header));
|
||||
evl = mtod(m, struct ether_vlan_header *);
|
||||
evl->evl_proto = evl->evl_encap_proto;
|
||||
evl->evl_encap_proto = htons(vlan_proto);
|
||||
evl->evl_tag = htons(ifv->ifv_tag);
|
||||
printf("vlan_start: %*D\n", sizeof *evl, (char *)evl, ":");
|
||||
if (ifp->if_flags & IFF_LINK0) {
|
||||
m->m_pkthdr.rcvif = ifp;
|
||||
m->m_flags |= M_PROTO1;
|
||||
} else {
|
||||
M_PREPEND(m, EVL_ENCAPLEN, M_DONTWAIT);
|
||||
if (m == 0)
|
||||
continue;
|
||||
/* M_PREPEND takes care of m_len, m_pkthdr.len for us */
|
||||
|
||||
/*
|
||||
* Transform the Ethernet header into an Ethernet header
|
||||
* with 802.1Q encapsulation.
|
||||
*/
|
||||
bcopy(mtod(m, char *) + EVL_ENCAPLEN, mtod(m, char *),
|
||||
sizeof(struct ether_header));
|
||||
evl = mtod(m, struct ether_vlan_header *);
|
||||
evl->evl_proto = evl->evl_encap_proto;
|
||||
evl->evl_encap_proto = htons(vlan_proto);
|
||||
evl->evl_tag = htons(ifv->ifv_tag);
|
||||
#ifdef DEBUG
|
||||
printf("vlan_start: %*D\n", sizeof *evl,
|
||||
(char *)evl, ":");
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* Send it, precisely as ether_output() would have.
|
||||
@ -166,12 +262,64 @@ vlan_start(struct ifnet *ifp)
|
||||
if (IF_QFULL(&p->if_snd)) {
|
||||
IF_DROP(&p->if_snd);
|
||||
/* XXX stats */
|
||||
ifp->if_oerrors++;
|
||||
m_freem(m);
|
||||
continue;
|
||||
}
|
||||
IF_ENQUEUE(&p->if_snd, m);
|
||||
if ((p->if_flags & IFF_OACTIVE) == 0)
|
||||
if ((p->if_flags & IFF_OACTIVE) == 0) {
|
||||
p->if_start(p);
|
||||
ifp->if_opackets++;
|
||||
}
|
||||
}
|
||||
ifp->if_flags &= ~IFF_OACTIVE;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void
|
||||
vlan_input_tag(struct ether_header *eh, struct mbuf *m, u_int16_t t)
|
||||
{
|
||||
int i;
|
||||
struct ifvlan *ifv;
|
||||
|
||||
for (i = 0; i < NVLAN; i++) {
|
||||
ifv = &ifv_softc[i];
|
||||
if (ifv->ifv_tag == t)
|
||||
break;
|
||||
}
|
||||
|
||||
if (i >= NVLAN || (ifv->ifv_if.if_flags & IFF_UP) == 0) {
|
||||
m_freem(m);
|
||||
ifv->ifv_p->if_data.ifi_noproto++;
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Having found a valid vlan interface corresponding to
|
||||
* the given source interface and vlan tag, run the
|
||||
* the real packet through ethert_input().
|
||||
*/
|
||||
m->m_pkthdr.rcvif = &ifv->ifv_if;
|
||||
|
||||
#if NBPFILTER > 0
|
||||
if (ifv->ifv_if.if_bpf) {
|
||||
/*
|
||||
* Do the usual BPF fakery. Note that we don't support
|
||||
* promiscuous mode here, since it would require the
|
||||
* drivers to know about VLANs and we're not ready for
|
||||
* that yet.
|
||||
*/
|
||||
struct mbuf m0;
|
||||
m0.m_next = m;
|
||||
m0.m_len = sizeof(struct ether_header);
|
||||
m0.m_data = (char *)eh;
|
||||
bpf_mtap(&ifv->ifv_if, &m0);
|
||||
}
|
||||
#endif
|
||||
ifv->ifv_if.if_ipackets++;
|
||||
ether_input(&ifv->ifv_if, eh, m);
|
||||
return;
|
||||
}
|
||||
|
||||
int
|
||||
@ -221,6 +369,7 @@ vlan_input(struct ether_header *eh, struct mbuf *m)
|
||||
bpf_mtap(&ifv->ifv_if, &m0);
|
||||
}
|
||||
#endif
|
||||
ifv->ifv_if.if_ipackets++;
|
||||
ether_input(&ifv->ifv_if, eh, m);
|
||||
return 0;
|
||||
}
|
||||
@ -242,9 +391,9 @@ vlan_config(struct ifvlan *ifv, struct ifnet *p)
|
||||
ifv->ifv_if.if_mtu = p->if_data.ifi_mtu - EVL_ENCAPLEN;
|
||||
|
||||
/*
|
||||
* NB: we don't support multicast at this point.
|
||||
* Preserve the state of the LINK0 flag for ourselves.
|
||||
*/
|
||||
ifv->ifv_if.if_flags = (p->if_flags & ~IFF_MULTICAST); /* XXX */
|
||||
ifv->ifv_if.if_flags = (p->if_flags & ~(IFF_LINK0));
|
||||
|
||||
/*
|
||||
* Set up our ``Ethernet address'' to reflect the underlying
|
||||
@ -261,6 +410,55 @@ vlan_config(struct ifvlan *ifv, struct ifnet *p)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
vlan_unconfig(struct ifnet *ifp)
|
||||
{
|
||||
struct ifaddr *ifa;
|
||||
struct sockaddr_dl *sdl;
|
||||
struct vlan_mc_entry *mc;
|
||||
struct ifvlan *ifv;
|
||||
struct ifnet *p;
|
||||
int error;
|
||||
|
||||
ifv = ifp->if_softc;
|
||||
p = ifv->ifv_p;
|
||||
|
||||
/*
|
||||
* Since the interface is being unconfigured, we need to
|
||||
* empty the list of multicast groups that we may have joined
|
||||
* while we were alive and remove them from the parent's list
|
||||
* as well.
|
||||
*/
|
||||
while(ifv->vlan_mc_listhead.slh_first != NULL) {
|
||||
struct sockaddr_dl sdl;
|
||||
|
||||
sdl.sdl_len = ETHER_ADDR_LEN;
|
||||
sdl.sdl_family = AF_LINK;
|
||||
mc = ifv->vlan_mc_listhead.slh_first;
|
||||
bcopy((char *)&mc->mc_addr, LLADDR(&sdl), ETHER_ADDR_LEN);
|
||||
error = if_delmulti(p, (struct sockaddr *)&sdl);
|
||||
error = if_delmulti(ifp, (struct sockaddr *)&sdl);
|
||||
if (error)
|
||||
return(error);
|
||||
SLIST_REMOVE_HEAD(&ifv->vlan_mc_listhead, mc_entries);
|
||||
free(mc, M_DEVBUF);
|
||||
}
|
||||
|
||||
/* Disconnect from parent. */
|
||||
ifv->ifv_p = NULL;
|
||||
ifv->ifv_if.if_mtu = ETHERMTU;
|
||||
|
||||
/* Clear our MAC address. */
|
||||
ifa = ifnet_addrs[ifv->ifv_if.if_index - 1];
|
||||
sdl = (struct sockaddr_dl *)ifa->ifa_addr;
|
||||
sdl->sdl_type = IFT_ETHER;
|
||||
sdl->sdl_alen = ETHER_ADDR_LEN;
|
||||
bzero(LLADDR(sdl), ETHER_ADDR_LEN);
|
||||
bzero(ifv->ifv_ac.ac_enaddr, ETHER_ADDR_LEN);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
vlan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
|
||||
{
|
||||
@ -303,6 +501,8 @@ vlan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
|
||||
case SIOCSIFMTU:
|
||||
/*
|
||||
* Set the interface MTU.
|
||||
* This is bogus. The underlying interface might support
|
||||
* jumbo frames.
|
||||
*/
|
||||
if (ifr->ifr_mtu > ETHERMTU) {
|
||||
error = EINVAL;
|
||||
@ -316,8 +516,9 @@ vlan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
|
||||
if (error)
|
||||
break;
|
||||
if (vlr.vlr_parent[0] == '\0') {
|
||||
ifv->ifv_p = 0;
|
||||
vlan_unconfig(ifp);
|
||||
if_down(ifp);
|
||||
ifp->if_flags = 0;
|
||||
break;
|
||||
}
|
||||
p = ifunit(vlr.vlr_parent);
|
||||
@ -343,17 +544,19 @@ vlan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
|
||||
|
||||
case SIOCSIFFLAGS:
|
||||
/*
|
||||
* We don't support all-multicast or promiscuous modes
|
||||
* We don't support promiscuous mode
|
||||
* right now because it would require help from the
|
||||
* underlying drivers, which hasn't been implemented.
|
||||
*/
|
||||
if (ifr->ifr_flags & (IFF_PROMISC|IFF_ALLMULTI)) {
|
||||
ifp->if_flags &= ~(IFF_PROMISC|IFF_ALLMULTI);
|
||||
if (ifr->ifr_flags & (IFF_PROMISC)) {
|
||||
ifp->if_flags &= ~(IFF_PROMISC);
|
||||
error = EINVAL;
|
||||
}
|
||||
break;
|
||||
|
||||
/* NB: this will reject multicast state changes */
|
||||
case SIOCADDMULTI:
|
||||
case SIOCDELMULTI:
|
||||
error = vlan_setmulti(ifp);
|
||||
break;
|
||||
default:
|
||||
error = EINVAL;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user