On Tue, Dec 22, 2015 at 10:11:23PM +0100, Paul de Weerd wrote:
> Hi all,
>
> My ISP requires me to use different MAC addresses for internet and TV
> access. These services arrive at the demarc as two different ethernet
> VLANs, vlan4 (television) and vlan34 (internet), on one copper GE
> port.
>
> I can get a lease on vlan34 just fine, and have internet access. Then
> I change the MAC on my vlan4 interface:
>
> $ ifconfig vlan4 lladdr <some other MAC>
>
> However, dhclient doesn't give me a lease. To debug, I run tcpdump
> and try again: lo, an offer arrives.
>
> Turns out, vlan4 only works when the parent interface (em2, in my
> case) is in promiscuous mode. Seems to make sense from a naive
> point of view: the interface filters traffic that's destined for it's
> own MAC address, dropping traffic for other MACs. Is this a problem
> with the em(4) driver, with vlan(4) or elsewhere?
i think vlan.
the diff below allows a vlan interface to be configured with a
custom mac address. if that is done, itll mark itself as having a
custom lladdr and will in turn enable promisc on the parent interface
so those packets will actually end up coming into the kernel.
it supports setting the mac address while the vlan interface is
both up and down.
if you set the mac address on the vlan interface to 00:00:00:00:00:00,
itll treat that as removing the custom mac and will replace it with
the parents mac address.
lastly, this makes no effort to cope with the mac address of the
parent interface being changed at runtime.
Index: if_vlan_var.h
===================================================================
RCS file: /cvs/src/sys/net/if_vlan_var.h,v
retrieving revision 1.35
diff -u -p -r1.35 if_vlan_var.h
--- if_vlan_var.h 15 Apr 2016 04:34:10 -0000 1.35
+++ if_vlan_var.h 19 Apr 2016 13:30:31 -0000
@@ -81,7 +81,8 @@ struct ifvlan {
#define ifv_tag ifv_mib.ifvm_tag
#define ifv_prio ifv_mib.ifvm_prio
#define ifv_type ifv_mib.ifvm_type
-#define IFVF_PROMISC 0x01
+#define IFVF_PROMISC 0x01 /* the parent should be made promisc */
+#define IFVF_LLADDR 0x02 /* don't inherit the parents mac */
struct mbuf *vlan_inject(struct mbuf *, uint16_t, uint16_t);
#endif /* _KERNEL */
Index: if_vlan.c
===================================================================
RCS file: /cvs/src/sys/net/if_vlan.c,v
retrieving revision 1.162
diff -u -p -r1.162 if_vlan.c
--- if_vlan.c 15 Apr 2016 04:34:10 -0000 1.162
+++ if_vlan.c 19 Apr 2016 13:30:31 -0000
@@ -92,8 +92,6 @@ int vlan_up(struct ifvlan *);
int vlan_parent_up(struct ifvlan *, struct ifnet *);
int vlan_down(struct ifvlan *);
-int vlan_promisc(struct ifvlan *, int);
-
void vlan_ifdetach(void *);
void vlan_link_hook(void *);
void vlan_link_state(struct ifvlan *, u_char, u_int64_t);
@@ -107,6 +105,9 @@ int vlan_multi_del(struct ifvlan *, stru
void vlan_multi_apply(struct ifvlan *, struct ifnet *, u_long);
void vlan_multi_free(struct ifvlan *);
+int vlan_iff(struct ifvlan *);
+int vlan_setlladdr(struct ifvlan *, struct ifreq *);
+
int vlan_set_compat(struct ifnet *, struct ifreq *);
int vlan_get_compat(struct ifnet *, struct ifreq *);
@@ -432,6 +433,7 @@ vlan_parent_up(struct ifvlan *ifv, struc
if_ih_insert(ifp0, vlan_input, NULL);
return (0);
+
}
int
@@ -470,7 +472,8 @@ vlan_up(struct ifvlan *ifv)
/* parent is fine, let's prepare the ifv to handle packets */
ifp->if_hardmtu = hardmtu;
SET(ifp->if_flags, ifp0->if_flags & IFF_SIMPLEX);
- if_setlladdr(ifp, LLADDR(ifp0->if_sadl));
+ if (!ISSET(ifv->ifv_flags, IFVF_LLADDR))
+ if_setlladdr(ifp, LLADDR(ifp0->if_sadl));
if (ifv->ifv_type != ETHERTYPE_VLAN) {
/*
@@ -522,7 +525,8 @@ leave:
rw_exit(&vlan_tagh_lk);
scrub:
ifp->if_capabilities = 0;
- if_setlladdr(ifp, etheranyaddr);
+ if (!ISSET(ifv->ifv_flags, IFVF_LLADDR))
+ if_setlladdr(ifp, etheranyaddr);
CLR(ifp->if_flags, IFF_SIMPLEX);
ifp->if_hardmtu = 0xffff;
put:
@@ -564,7 +568,8 @@ vlan_down(struct ifvlan *ifv)
rw_exit_write(&vlan_tagh_lk);
ifp->if_capabilities = 0;
- if_setlladdr(ifp, etheranyaddr);
+ if (!ISSET(ifv->ifv_flags, IFVF_LLADDR))
+ if_setlladdr(ifp, etheranyaddr);
CLR(ifp->if_flags, IFF_SIMPLEX);
ifp->if_hardmtu = 0xffff;
@@ -617,29 +622,6 @@ vlan_link_state(struct ifvlan *ifv, u_ch
}
int
-vlan_promisc(struct ifvlan *ifv, int promisc)
-{
- struct ifnet *ifp0;
- int error = 0;
-
- if ((ISSET(ifv->ifv_flags, IFVF_PROMISC) ? 1 : 0) == promisc)
- return (0);
-
- ifp0 = if_get(ifv->ifv_ifp0);
- if (ifp0 != NULL) {
- error = ifpromisc(ifp0, promisc);
- }
- if_put(ifp0);
-
- if (error == 0) {
- CLR(ifv->ifv_flags, IFVF_PROMISC);
- SET(ifv->ifv_flags, promisc ? IFVF_PROMISC : 0);
- }
-
- return (error);
-}
-
-int
vlan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
{
struct ifvlan *ifv = ifp->if_softc;
@@ -655,14 +637,11 @@ vlan_ioctl(struct ifnet *ifp, u_long cmd
/* FALLTHROUGH */
case SIOCSIFFLAGS:
- error = vlan_promisc(ifv,
- ISSET(ifp->if_flags, IFF_PROMISC) ? 1 : 0);
- if (error != 0)
- break;
-
if (ISSET(ifp->if_flags, IFF_UP)) {
if (!ISSET(ifp->if_flags, IFF_RUNNING))
error = vlan_up(ifv);
+ else
+ error = ENETRESET;
} else {
if (ISSET(ifp->if_flags, IFF_RUNNING))
error = vlan_down(ifv);
@@ -749,6 +728,10 @@ vlan_ioctl(struct ifnet *ifp, u_long cmd
error = vlan_multi_del(ifv, ifr);
break;
+ case SIOCSIFLLADDR:
+ error = vlan_setlladdr(ifv, ifr);
+ break;
+
case SIOCSETVLAN:
error = vlan_set_compat(ifp, ifr);
break;
@@ -761,7 +744,71 @@ vlan_ioctl(struct ifnet *ifp, u_long cmd
break;
}
+ if (error == ENETRESET) {
+ if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) ==
+ (IFF_UP | IFF_RUNNING))
+ vlan_iff(ifv);
+ error = 0;
+ }
+
return error;
+}
+
+int
+vlan_iff(struct ifvlan *ifv)
+{
+ struct ifnet *ifp0;
+ int promisc = 0;
+ int error = 0;
+
+ if (ISSET(ifv->ifv_if.if_flags, IFF_PROMISC) ||
+ ISSET(ifv->ifv_flags, IFVF_LLADDR))
+ promisc = IFVF_PROMISC;
+
+ if (ISSET(ifv->ifv_flags, IFVF_PROMISC) == promisc)
+ return (0);
+
+ if (ISSET(ifv->ifv_if.if_flags, IFF_RUNNING)) {
+ ifp0 = if_get(ifv->ifv_ifp0);
+ if (ifp0 != NULL)
+ error = ifpromisc(ifp0, promisc);
+ if_put(ifp0);
+ }
+
+ if (error == 0) {
+ CLR(ifv->ifv_flags, IFVF_PROMISC);
+ SET(ifv->ifv_flags, promisc);
+ }
+
+ return (error);
+}
+
+int
+vlan_setlladdr(struct ifvlan *ifv, struct ifreq *ifr)
+{
+ struct ifnet *ifp = &ifv->ifv_if;;
+ struct ifnet *ifp0;
+ int flag = IFVF_LLADDR;
+
+ /* setting the mac addr to 00:00:00:00:00:00 means reset lladdr */
+ if (memcmp(ifr->ifr_addr.sa_data, etheranyaddr, ETHER_ADDR_LEN) == 0)
+ flag = 0;
+
+ if (ISSET(ifv->ifv_flags, IFVF_LLADDR) == flag)
+ return (0);
+
+ /* if we're up and the mac is reset, inherit the parents mac */
+ if (ISSET(ifp->if_flags, IFF_RUNNING) && flag == 0) {
+ ifp0 = if_get(ifv->ifv_ifp0);
+ if (ifp0 != NULL)
+ if_setlladdr(ifp, LLADDR(ifp0->if_sadl));
+ if_put(ifp0);
+ }
+
+ CLR(ifv->ifv_flags, IFVF_LLADDR);
+ SET(ifv->ifv_flags, flag);
+
+ return (ENETRESET);
}
int