diff options
author | 2011-02-18 17:06:45 +0000 | |
---|---|---|
committer | 2011-02-18 17:06:45 +0000 | |
commit | dc52d305246b2ac01358bfa75c618a09b5fef034 (patch) | |
tree | f37ab0e7a83435557dd7fd55e43754f209f370d5 | |
parent | An attempt to open an append-only file without O_APPEND results in EPERM. (diff) | |
download | wireguard-openbsd-dc52d305246b2ac01358bfa75c618a09b5fef034.tar.xz wireguard-openbsd-dc52d305246b2ac01358bfa75c618a09b5fef034.zip |
when changing the vlandev of a running vlan(4) interface, we also need to
move the configured multicast addresses and preserve the promisc mode
settings of the parent. this fixes an issue when carp stopped working on
a vlan after the vlandev was manually changed.
ok naddy@ phessler@
-rw-r--r-- | sys/net/if_vlan.c | 70 |
1 files changed, 62 insertions, 8 deletions
diff --git a/sys/net/if_vlan.c b/sys/net/if_vlan.c index 069fd152847..c7499600250 100644 --- a/sys/net/if_vlan.c +++ b/sys/net/if_vlan.c @@ -1,4 +1,4 @@ -/* $OpenBSD: if_vlan.c,v 1.86 2011/01/03 11:18:13 reyk Exp $ */ +/* $OpenBSD: if_vlan.c,v 1.87 2011/02/18 17:06:45 reyk Exp $ */ /* * Copyright 1998 Massachusetts Institute of Technology @@ -85,7 +85,7 @@ LIST_HEAD(vlan_taghash, ifvlan) *vlan_tagh, *svlan_tagh; 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 ifnet *p, u_int16_t tag) 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 ifnet *p, u_int16_t tag) 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 ifnet *p, u_int16_t tag) 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 ifnet *p, u_int16_t tag) } 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, caddr_t data) 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; @@ -812,3 +833,36 @@ vlan_ether_purgemulti(struct ifvlan *ifv) 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); + } +} |