Hi!
My previous change to vlan(4) allows to change the vlandev and vlan id
on-the-fly without re-creating the vlan interface.
Unfortunately, I figured out that carp(4) on vlan stops working if you
change the vlandev (vlan parent). The problem is that the vlan
interface forgets about its multicast and promisc mode settings that
were configured before.
To reproduce it:
hosta# ifconfig vlan1 vlandev em0 up
hosta# ifconfig carp1 carpdev vlan1 vhid 1 192.168.1.240/24 up
hostb$ ping 192.168.1.240
...replies...
hosta# ifconfig vlan1 vlandev em1 # something else
hosta# ifconfig vlan1 vlandev em0 # turn back to em0
hostb$ ping 192.168.1.240
...no replies...
The attached diff does the following:
- move any configured multicast addresses to the new vlandev
- keep the promisc mode and set it on the vlandev, if configured
- remove promisc mode from the vlandev if promisc vlan is unconfigured
- enforce a link state change to trigger carp and userland
I would like to get this diff into the release or my previous vlan
reconfigure diff needs to be backed out, I guess.
Comments?
reyk
Index: if_vlan.c
===================================================================
RCS file: /cvs/src/sys/net/if_vlan.c,v
retrieving revision 1.86
diff -u -p -r1.86 if_vlan.c
--- if_vlan.c 3 Jan 2011 11:18:13 -0000 1.86
+++ if_vlan.c 16 Feb 2011 13:09:38 -0000
@@ -85,7 +85,7 @@ LIST_HEAD(vlan_taghash, ifvlan) *vlan_ta
void vlan_start(struct ifnet *ifp);
int vlan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t addr);
-int vlan_unconfig(struct ifnet *ifp);
+int vlan_unconfig(struct ifnet *ifp, struct ifnet *newp);
int vlan_config(struct ifvlan *, struct ifnet *, u_int16_t);
void vlan_vlandev_state(void *);
void vlanattach(int count);
@@ -93,6 +93,7 @@ int vlan_set_promisc(struct ifnet *ifp);
int vlan_ether_addmulti(struct ifvlan *, struct ifreq *);
int vlan_ether_delmulti(struct ifvlan *, struct ifreq *);
void vlan_ether_purgemulti(struct ifvlan *);
+void vlan_ether_resetmulti(struct ifvlan *, struct ifnet *);
int vlan_clone_create(struct if_clone *, int);
int vlan_clone_destroy(struct ifnet *);
void vlan_ifdetach(void *);
@@ -163,7 +164,7 @@ vlan_clone_destroy(struct ifnet *ifp)
{
struct ifvlan *ifv = ifp->if_softc;
- vlan_unconfig(ifp);
+ vlan_unconfig(ifp, NULL);
ether_ifdetach(ifp);
if_detach(ifp);
@@ -359,6 +360,7 @@ vlan_config(struct ifvlan *ifv, struct i
struct ifaddr *ifa1, *ifa2;
struct sockaddr_dl *sdl1, *sdl2;
struct vlan_taghash *tagh;
+ u_int flags;
int s;
if (p->if_type != IFT_ETHER)
@@ -366,8 +368,9 @@ vlan_config(struct ifvlan *ifv, struct i
if (ifv->ifv_p == p && ifv->ifv_tag == tag) /* noop */
return (0);
- /* Reset the interface */
- vlan_unconfig(&ifv->ifv_if);
+ /* Remember existing interface flags and reset the interface */
+ flags = ifv->ifv_flags;
+ vlan_unconfig(&ifv->ifv_if, p);
ifv->ifv_p = p;
@@ -389,6 +392,12 @@ vlan_config(struct ifvlan *ifv, struct i
ifv->ifv_if.if_flags = p->if_flags &
(IFF_UP | IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST);
+ /* Reset promisc mode on the interface and its parent */
+ if (flags & IFVF_PROMISC) {
+ ifv->ifv_if.if_flags |= IFF_PROMISC;
+ vlan_set_promisc(&ifv->ifv_if);
+ }
+
/*
* Inherit the if_type from the parent. This allows us to
* participate in bridges of that type.
@@ -458,7 +467,7 @@ vlan_config(struct ifvlan *ifv, struct i
}
int
-vlan_unconfig(struct ifnet *ifp)
+vlan_unconfig(struct ifnet *ifp, struct ifnet *newp)
{
struct ifaddr *ifa;
struct sockaddr_dl *sdl;
@@ -471,6 +480,12 @@ vlan_unconfig(struct ifnet *ifp)
if (p == NULL)
return 0;
+ /* Unset promisc mode on the interface and its parent */
+ if (ifv->ifv_flags & IFVF_PROMISC) {
+ ifp->if_flags &= ~IFF_PROMISC;
+ vlan_set_promisc(ifp);
+ }
+
s = splnet();
LIST_REMOVE(ifv, ifv_list);
if (ifv->lh_cookie != NULL)
@@ -478,6 +493,11 @@ vlan_unconfig(struct ifnet *ifp)
/* The cookie is NULL if disestablished externally */
if (ifv->dh_cookie != NULL)
hook_disestablish(p->if_detachhooks, ifv->dh_cookie);
+ /* Reset link state */
+ if (newp != NULL) {
+ ifp->if_link_state = LINK_STATE_INVALID;
+ if_link_state_change(ifp);
+ }
splx(s);
/*
@@ -486,11 +506,12 @@ vlan_unconfig(struct ifnet *ifp)
* while we were alive and remove them from the parent's list
* as well.
*/
- vlan_ether_purgemulti(ifv);
+ vlan_ether_resetmulti(ifv, newp);
/* Disconnect from parent. */
ifv->ifv_p = NULL;
ifv->ifv_if.if_mtu = ETHERMTU;
+ ifv->ifv_flags = 0;
/* Clear our MAC address. */
ifa = ifnet_addrs[ifv->ifv_if.if_index];
@@ -606,7 +627,7 @@ vlan_ioctl(struct ifnet *ifp, u_long cmd
break;
if (vlr.vlr_parent[0] == '\0') {
s = splnet();
- vlan_unconfig(ifp);
+ vlan_unconfig(ifp, NULL);
if (ifp->if_flags & IFF_UP)
if_down(ifp);
ifp->if_flags &= ~IFF_RUNNING;
@@ -810,5 +831,38 @@ vlan_ether_purgemulti(struct ifvlan *ifv
(void)(*ifp->if_ioctl)(ifp, SIOCDELMULTI, (caddr_t)ifr);
LIST_REMOVE(mc, mc_entries);
free(mc, M_DEVBUF);
+ }
+}
+
+void
+vlan_ether_resetmulti(struct ifvlan *ifv, struct ifnet *p)
+{
+ struct ifnet *ifp = ifv->ifv_p; /* Parent. */
+ struct vlan_mc_entry *mc;
+ union {
+ struct ifreq ifreq;
+ struct {
+ char ifr_name[IFNAMSIZ];
+ struct sockaddr_storage ifr_ss;
+ } ifreq_storage;
+ } ifreq;
+ struct ifreq *ifr = &ifreq.ifreq;
+
+ if (p == NULL) {
+ vlan_ether_purgemulti(ifv);
+ return;
+ } else if (ifp == p)
+ return;
+
+ LIST_FOREACH(mc, &ifv->vlan_mc_listhead, mc_entries) {
+ memcpy(&ifr->ifr_addr, &mc->mc_addr, mc->mc_addr.ss_len);
+
+ /* Remove from the old parent */
+ memcpy(ifr->ifr_name, ifp->if_xname, IFNAMSIZ);
+ (void)(*ifp->if_ioctl)(ifp, SIOCDELMULTI, (caddr_t)ifr);
+
+ /* Try to add to the new parent */
+ memcpy(ifr->ifr_name, p->if_xname, IFNAMSIZ);
+ (void)(*p->if_ioctl)(p, SIOCADDMULTI, (caddr_t)ifr);
}
}