+static int vlan_unconfig(ifvlan_ref ifv, int need_to_wait);
+static int vlan_config(struct ifnet * ifp, struct ifnet * p, int tag);
+static void vlan_if_free(struct ifnet * ifp);
+static int vlan_remove(ifvlan_ref ifv, int need_to_wait);
+
+static struct if_clone vlan_cloner = IF_CLONE_INITIALIZER(VLANNAME,
+ vlan_clone_create,
+ vlan_clone_destroy,
+ 0,
+ IF_MAXUNIT);
+static void interface_link_event(struct ifnet * ifp, u_int32_t event_code);
+static void vlan_parent_link_event(struct ifnet * p,
+ u_int32_t event_code);
+
+static int ifvlan_new_mtu(ifvlan_ref ifv, int mtu);
+
+/**
+ ** ifvlan_ref routines
+ **/
+static void
+ifvlan_retain(ifvlan_ref ifv)
+{
+ if (ifv->ifv_signature != IFV_SIGNATURE) {
+ panic("ifvlan_retain: bad signature\n");
+ }
+ if (ifv->ifv_retain_count == 0) {
+ panic("ifvlan_retain: retain count is 0\n");
+ }
+ OSIncrementAtomic(&ifv->ifv_retain_count);
+}
+
+static void
+ifvlan_release(ifvlan_ref ifv)
+{
+ UInt32 old_retain_count;
+
+ if (ifv->ifv_signature != IFV_SIGNATURE) {
+ panic("ifvlan_release: bad signature\n");
+ }
+ old_retain_count = OSDecrementAtomic(&ifv->ifv_retain_count);
+ switch (old_retain_count) {
+ case 0:
+ panic("ifvlan_release: retain count is 0\n");
+ break;
+ case 1:
+ if (g_vlan->verbose) {
+ printf("ifvlan_release(%s)\n", ifv->ifv_name);
+ }
+ ifv->ifv_signature = 0;
+ FREE(ifv, M_VLAN);
+ break;
+ default:
+ break;
+ }
+ return;
+}
+
+static vlan_parent_ref
+ifvlan_get_vlan_parent_retained(ifvlan_ref ifv)
+{
+ vlan_parent_ref vlp = ifv->ifv_vlp;
+
+ if (vlan_parent_flags_detaching(vlp)) {
+ return (NULL);
+ }
+ vlan_parent_retain(vlp);
+ return (vlp);
+}
+
+/**
+ ** ifnet_* routines
+ **/
+
+static ifvlan_ref
+ifnet_get_ifvlan(struct ifnet * ifp)
+{
+ ifvlan_ref ifv;
+
+ ifv = (ifvlan_ref)ifnet_softc(ifp);
+ return (ifv);
+}
+
+static ifvlan_ref
+ifnet_get_ifvlan_retained(struct ifnet * ifp)
+{
+ ifvlan_ref ifv;
+
+ ifv = ifnet_get_ifvlan(ifp);
+ if (ifv == NULL) {
+ return (NULL);
+ }
+ if (ifvlan_flags_detaching(ifv)) {
+ return (NULL);
+ }
+ ifvlan_retain(ifv);
+ return (ifv);
+}
+
+static int
+ifnet_ifvlan_vlan_parent_ok(struct ifnet * ifp, ifvlan_ref ifv,
+ vlan_parent_ref vlp)
+{
+ ifvlan_ref check_ifv;
+
+ check_ifv = ifnet_get_ifvlan(ifp);
+ if (check_ifv != ifv || ifvlan_flags_detaching(ifv)) {
+ /* ifvlan_ref no longer valid */
+ return (FALSE);
+ }
+ if (ifv->ifv_vlp != vlp) {
+ /* vlan_parent no longer valid */
+ return (FALSE);
+ }
+ if (vlan_parent_flags_detaching(vlp)) {
+ /* parent is detaching */
+ return (FALSE);
+ }
+ return (TRUE);
+}
+
+/**
+ ** vlan, etc. routines
+ **/
+
+static int
+vlan_globals_init(void)
+{
+ vlan_globals_ref v;
+
+ vlan_assert_lock_not_held();
+
+ if (g_vlan != NULL) {
+ return (0);
+ }
+ v = _MALLOC(sizeof(*v), M_VLAN, M_WAITOK);
+ if (v != NULL) {
+ LIST_INIT(&v->parent_list);
+ v->verbose = 0;
+ }
+ vlan_lock();
+ if (g_vlan != NULL) {
+ vlan_unlock();
+ if (v != NULL) {
+ _FREE(v, M_VLAN);
+ }
+ return (0);
+ }
+ g_vlan = v;
+ vlan_unlock();
+ if (v == NULL) {
+ return (ENOMEM);
+ }
+ return (0);
+}
+
+static int
+siocgifdevmtu(struct ifnet * ifp, struct ifdevmtu * ifdm_p)
+{
+ struct ifreq ifr;
+ int error;
+
+ bzero(&ifr, sizeof(ifr));
+ error = ifnet_ioctl(ifp, 0,SIOCGIFDEVMTU, &ifr);
+ if (error == 0) {
+ *ifdm_p = ifr.ifr_devmtu;
+ }
+ return (error);
+}
+
+static int
+siocsifaltmtu(struct ifnet * ifp, int mtu)
+{
+ struct ifreq ifr;
+
+ bzero(&ifr, sizeof(ifr));
+ ifr.ifr_mtu = mtu;
+ return (ifnet_ioctl(ifp, 0, SIOCSIFALTMTU, &ifr));
+}
+
+static __inline__ void
+vlan_bpf_output(struct ifnet * ifp, struct mbuf * m,
+ bpf_packet_func func)
+{
+ if (func != NULL) {
+ (*func)(ifp, m);
+ }
+ return;
+}
+
+static __inline__ void
+vlan_bpf_input(struct ifnet * ifp, struct mbuf * m,
+ bpf_packet_func func, char * frame_header,
+ int frame_header_len, int encap_len)
+{
+ if (func != NULL) {
+ if (encap_len > 0) {
+ /* present the right header to bpf */
+ bcopy(frame_header, frame_header + encap_len, frame_header_len);
+ }
+ m->m_data -= frame_header_len;
+ m->m_len += frame_header_len;
+ (*func)(ifp, m);
+ m->m_data += frame_header_len;
+ m->m_len -= frame_header_len;
+ if (encap_len > 0) {
+ /* restore the header */
+ bcopy(frame_header + encap_len, frame_header, frame_header_len);
+ }
+ }
+ return;
+}
+
+/**
+ ** vlan_parent synchronization routines
+ **/
+static void
+vlan_parent_retain(vlan_parent_ref vlp)
+{
+ if (vlp->vlp_signature != VLP_SIGNATURE) {
+ panic("vlan_parent_retain: signature is bad\n");
+ }
+ if (vlp->vlp_retain_count == 0) {
+ panic("vlan_parent_retain: retain count is 0\n");
+ }
+ OSIncrementAtomic(&vlp->vlp_retain_count);
+}
+
+static void
+vlan_parent_release(vlan_parent_ref vlp)
+{
+ UInt32 old_retain_count;
+
+ if (vlp->vlp_signature != VLP_SIGNATURE) {
+ panic("vlan_parent_release: signature is bad\n");
+ }
+ old_retain_count = OSDecrementAtomic(&vlp->vlp_retain_count);
+ switch (old_retain_count) {
+ case 0:
+ panic("vlan_parent_release: retain count is 0\n");
+ break;
+ case 1:
+ if (g_vlan->verbose) {
+ struct ifnet * ifp = vlp->vlp_ifp;
+ printf("vlan_parent_release(%s%d)\n", ifnet_name(ifp),
+ ifnet_unit(ifp));
+ }
+ vlp->vlp_signature = 0;
+ FREE(vlp, M_VLAN);
+ break;
+ default:
+ break;
+ }
+ return;
+}
+
+/*
+ * Function: vlan_parent_wait
+ * Purpose:
+ * Allows a single thread to gain exclusive access to the vlan_parent
+ * data structure. Some operations take a long time to complete,
+ * and some have side-effects that we can't predict. Holding the
+ * vlan_lock() across such operations is not possible.
+ *
+ * Notes:
+ * Before calling, you must be holding the vlan_lock and have taken
+ * a reference on the vlan_parent_ref.
+ */
+static void
+vlan_parent_wait(vlan_parent_ref vlp, const char * msg)
+{
+ int waited = 0;
+
+ /* other add/remove/multicast-change in progress */
+ while (vlan_parent_flags_change_in_progress(vlp)) {
+ if (g_vlan->verbose) {
+ struct ifnet * ifp = vlp->vlp_ifp;
+
+ printf("%s%d: %s msleep\n", ifnet_name(ifp), ifnet_unit(ifp), msg);
+ }
+ waited = 1;
+ (void)msleep(vlp, vlan_lck_mtx, PZERO, msg, 0);
+ }
+ /* prevent other vlan parent remove/add from taking place */
+ vlan_parent_flags_set_change_in_progress(vlp);
+ if (g_vlan->verbose && waited) {
+ struct ifnet * ifp = vlp->vlp_ifp;
+
+ printf("%s%d: %s woke up\n", ifnet_name(ifp), ifnet_unit(ifp), msg);
+ }
+ return;
+}
+
+/*
+ * Function: vlan_parent_signal
+ * Purpose:
+ * Allows the thread that previously invoked vlan_parent_wait() to
+ * give up exclusive access to the vlan_parent data structure, and wake up
+ * any other threads waiting to access
+ * Notes:
+ * Before calling, you must be holding the vlan_lock and have taken
+ * a reference on the vlan_parent_ref.
+ */
+static void
+vlan_parent_signal(vlan_parent_ref vlp, const char * msg)
+{
+ vlan_parent_flags_clear_change_in_progress(vlp);
+ wakeup((caddr_t)vlp);
+ if (g_vlan->verbose) {
+ struct ifnet * ifp = vlp->vlp_ifp;
+
+ printf("%s%d: %s wakeup\n", ifnet_name(ifp), ifnet_unit(ifp), msg);
+ }
+ return;
+}