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

Reply via email to