Current Path : /sys/amd64/compile/hs32/modules/usr/src/sys/modules/mlx/@/amd64/compile/hs32/modules/usr/src/sys/modules/lmc/@/netatalk/ |
FreeBSD hs32.drive.ne.jp 9.1-RELEASE FreeBSD 9.1-RELEASE #1: Wed Jan 14 12:18:08 JST 2015 root@hs32.drive.ne.jp:/sys/amd64/compile/hs32 amd64 |
Current File : //sys/amd64/compile/hs32/modules/usr/src/sys/modules/mlx/@/amd64/compile/hs32/modules/usr/src/sys/modules/lmc/@/netatalk/aarp.c |
/*- * Copyright (c) 2004-2009 Robert N. M. Watson * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * Copyright (c) 1990,1991,1994 Regents of The University of Michigan. * All Rights Reserved. * * Permission to use, copy, modify, and distribute this software and * its documentation for any purpose and without fee is hereby granted, * provided that the above copyright notice appears in all copies and * that both that copyright notice and this permission notice appear * in supporting documentation, and that the name of The University * of Michigan not be used in advertising or publicity pertaining to * distribution of the software without specific, written prior * permission. This software is supplied as is without expressed or * implied warranties of any kind. * * This product includes software developed by the University of * California, Berkeley and its contributors. * * Research Systems Unix Group * The University of Michigan * c/o Wesley Craig * 535 W. William Street * Ann Arbor, Michigan * +1-313-764-2278 * netatalk@umich.edu * * $FreeBSD: release/9.1.0/sys/netatalk/aarp.c 233200 2012-03-19 20:49:16Z jhb $ */ #include "opt_atalk.h" #include <sys/param.h> #include <sys/systm.h> #include <sys/mbuf.h> #include <sys/kernel.h> #include <sys/socket.h> #include <sys/syslog.h> #include <net/if.h> #include <net/if_dl.h> #include <netinet/in.h> #undef s_net #include <netinet/if_ether.h> #include <netatalk/at.h> #include <netatalk/at_var.h> #include <netatalk/aarp.h> #include <netatalk/phase2.h> #include <netatalk/at_extern.h> #include <security/mac/mac_framework.h> static void aarptfree(struct aarptab *aat); static void at_aarpinput(struct ifnet *ifp, struct mbuf *m); #define AARPTAB_BSIZ 9 #define AARPTAB_NB 19 #define AARPTAB_SIZE (AARPTAB_BSIZ * AARPTAB_NB) static struct aarptab aarptab[AARPTAB_SIZE]; struct mtx aarptab_mtx; MTX_SYSINIT(aarptab_mtx, &aarptab_mtx, "aarptab_mtx", MTX_DEF); #define AARPTAB_HASH(a) ((((a).s_net << 8) + (a).s_node) % AARPTAB_NB) #define AARPTAB_LOOK(aat, addr) do { \ int n; \ \ AARPTAB_LOCK_ASSERT(); \ aat = &aarptab[ AARPTAB_HASH(addr) * AARPTAB_BSIZ ]; \ for (n = 0; n < AARPTAB_BSIZ; n++, aat++) { \ if (aat->aat_ataddr.s_net == (addr).s_net && \ aat->aat_ataddr.s_node == (addr).s_node) \ break; \ } \ if (n >= AARPTAB_BSIZ) \ aat = NULL; \ } while (0) #define AARPT_AGE (60 * 1) #define AARPT_KILLC 20 #define AARPT_KILLI 3 static const u_char atmulticastaddr[6] = { 0x09, 0x00, 0x07, 0xff, 0xff, 0xff, }; u_char at_org_code[3] = { 0x08, 0x00, 0x07, }; const u_char aarp_org_code[3] = { 0x00, 0x00, 0x00, }; static struct callout_handle aarptimer_ch = CALLOUT_HANDLE_INITIALIZER(&aarptimer_ch); static void aarptimer(void *ignored) { struct aarptab *aat; int i; aarptimer_ch = timeout(aarptimer, NULL, AARPT_AGE * hz); aat = aarptab; AARPTAB_LOCK(); for (i = 0; i < AARPTAB_SIZE; i++, aat++) { if (aat->aat_flags == 0 || (aat->aat_flags & ATF_PERM)) continue; if (++aat->aat_timer < ((aat->aat_flags & ATF_COM) ? AARPT_KILLC : AARPT_KILLI)) continue; aarptfree(aat); } AARPTAB_UNLOCK(); } /* * Search through the network addresses to find one that includes the given * network. Remember to take netranges into consideration. * * The _locked variant relies on the caller holding the at_ifaddr lock; the * unlocked variant returns a reference that the caller must dispose of. */ struct at_ifaddr * at_ifawithnet_locked(struct sockaddr_at *sat) { struct at_ifaddr *aa; struct sockaddr_at *sat2; AT_IFADDR_LOCK_ASSERT(); TAILQ_FOREACH(aa, &at_ifaddrhead, aa_link) { sat2 = &(aa->aa_addr); if (sat2->sat_addr.s_net == sat->sat_addr.s_net) break; if ((aa->aa_flags & AFA_PHASE2) && (ntohs(aa->aa_firstnet) <= ntohs(sat->sat_addr.s_net)) && (ntohs(aa->aa_lastnet) >= ntohs(sat->sat_addr.s_net))) break; } return (aa); } struct at_ifaddr * at_ifawithnet(struct sockaddr_at *sat) { struct at_ifaddr *aa; AT_IFADDR_RLOCK(); aa = at_ifawithnet_locked(sat); if (aa != NULL) ifa_ref(&aa->aa_ifa); AT_IFADDR_RUNLOCK(); return (aa); } static void aarpwhohas(struct ifnet *ifp, struct sockaddr_at *sat) { struct mbuf *m; struct ether_header *eh; struct ether_aarp *ea; struct at_ifaddr *aa; struct llc *llc; struct sockaddr sa; AARPTAB_UNLOCK_ASSERT(); m = m_gethdr(M_DONTWAIT, MT_DATA); if (m == NULL) return; #ifdef MAC mac_netatalk_aarp_send(ifp, m); #endif m->m_len = sizeof(*ea); m->m_pkthdr.len = sizeof(*ea); MH_ALIGN(m, sizeof(*ea)); ea = mtod(m, struct ether_aarp *); bzero((caddr_t)ea, sizeof(*ea)); ea->aarp_hrd = htons(AARPHRD_ETHER); ea->aarp_pro = htons(ETHERTYPE_AT); ea->aarp_hln = sizeof(ea->aarp_sha); ea->aarp_pln = sizeof(ea->aarp_spu); ea->aarp_op = htons(AARPOP_REQUEST); bcopy(IF_LLADDR(ifp), (caddr_t)ea->aarp_sha, sizeof(ea->aarp_sha)); /* * We need to check whether the output ethernet type should be phase * 1 or 2. We have the interface that we'll be sending the aarp out. * We need to find an AppleTalk network on that interface with the * same address as we're looking for. If the net is phase 2, * generate an 802.2 and SNAP header. */ aa = at_ifawithnet(sat); if (aa == NULL) { m_freem(m); return; } eh = (struct ether_header *)sa.sa_data; if (aa->aa_flags & AFA_PHASE2) { bcopy(atmulticastaddr, eh->ether_dhost, sizeof(eh->ether_dhost)); eh->ether_type = htons(sizeof(struct llc) + sizeof(struct ether_aarp)); M_PREPEND(m, sizeof(struct llc), M_DONTWAIT); if (m == NULL) { ifa_free(&aa->aa_ifa); return; } llc = mtod(m, struct llc *); llc->llc_dsap = llc->llc_ssap = LLC_SNAP_LSAP; llc->llc_control = LLC_UI; bcopy(aarp_org_code, llc->llc_org_code, sizeof(aarp_org_code)); llc->llc_ether_type = htons(ETHERTYPE_AARP); bcopy(&AA_SAT(aa)->sat_addr.s_net, ea->aarp_spnet, sizeof(ea->aarp_spnet)); bcopy(&sat->sat_addr.s_net, ea->aarp_tpnet, sizeof(ea->aarp_tpnet)); ea->aarp_spnode = AA_SAT(aa)->sat_addr.s_node; ea->aarp_tpnode = sat->sat_addr.s_node; } else { bcopy(ifp->if_broadcastaddr, (caddr_t)eh->ether_dhost, sizeof(eh->ether_dhost)); eh->ether_type = htons(ETHERTYPE_AARP); ea->aarp_spa = AA_SAT(aa)->sat_addr.s_node; ea->aarp_tpa = sat->sat_addr.s_node; } #ifdef NETATALKDEBUG printf("aarp: sending request for %u.%u\n", ntohs(AA_SAT(aa)->sat_addr.s_net), AA_SAT(aa)->sat_addr.s_node); #endif /* NETATALKDEBUG */ ifa_free(&aa->aa_ifa); sa.sa_len = sizeof(struct sockaddr); sa.sa_family = AF_UNSPEC; ifp->if_output(ifp, m, &sa, NULL); } int aarpresolve(struct ifnet *ifp, struct mbuf *m, struct sockaddr_at *destsat, u_char *desten) { struct at_ifaddr *aa; struct aarptab *aat; AT_IFADDR_RLOCK(); if (at_broadcast(destsat)) { m->m_flags |= M_BCAST; if ((aa = at_ifawithnet_locked(destsat)) == NULL) { AT_IFADDR_RUNLOCK(); m_freem(m); return (0); } if (aa->aa_flags & AFA_PHASE2) bcopy(atmulticastaddr, (caddr_t)desten, sizeof(atmulticastaddr)); else bcopy(ifp->if_broadcastaddr, (caddr_t)desten, sizeof(ifp->if_addrlen)); AT_IFADDR_RUNLOCK(); return (1); } AT_IFADDR_RUNLOCK(); AARPTAB_LOCK(); AARPTAB_LOOK(aat, destsat->sat_addr); if (aat == NULL) { /* No entry. */ aat = aarptnew(&destsat->sat_addr); /* We should fail more gracefully. */ if (aat == NULL) panic("aarpresolve: no free entry"); goto done; } /* Found an entry. */ aat->aat_timer = 0; if (aat->aat_flags & ATF_COM) { /* Entry is COMplete. */ bcopy((caddr_t)aat->aat_enaddr, (caddr_t)desten, sizeof(aat->aat_enaddr)); AARPTAB_UNLOCK(); return (1); } /* Entry has not completed. */ if (aat->aat_hold) m_freem(aat->aat_hold); done: aat->aat_hold = m; AARPTAB_UNLOCK(); aarpwhohas(ifp, destsat); return (0); } void aarpintr(struct mbuf *m) { struct arphdr *ar; struct ifnet *ifp; ifp = m->m_pkthdr.rcvif; if (ifp->if_flags & IFF_NOARP) goto out; if (m->m_len < sizeof(struct arphdr)) goto out; ar = mtod(m, struct arphdr *); if (ntohs(ar->ar_hrd) != AARPHRD_ETHER) goto out; if (m->m_len < sizeof(struct arphdr) + 2 * ar->ar_hln + 2 * ar->ar_pln) goto out; switch(ntohs(ar->ar_pro)) { case ETHERTYPE_AT: at_aarpinput(ifp, m); return; default: break; } out: m_freem(m); } static void at_aarpinput(struct ifnet *ifp, struct mbuf *m) { struct ether_aarp *ea; struct at_ifaddr *aa; struct aarptab *aat; struct ether_header *eh; struct llc *llc; struct sockaddr_at sat; struct sockaddr sa; struct at_addr spa, tpa, ma; int op; u_short net; ea = mtod(m, struct ether_aarp *); /* Check to see if from my hardware address. */ if (!bcmp((caddr_t)ea->aarp_sha, IF_LLADDR(ifp), ETHER_ADDR_LEN)) { m_freem(m); return; } /* Don't accept requests from broadcast address. */ if (!bcmp(ea->aarp_sha, ifp->if_broadcastaddr, ifp->if_addrlen)) { log(LOG_ERR, "aarp: source link address is broadcast\n"); m_freem(m); return; } op = ntohs(ea->aarp_op); bcopy(ea->aarp_tpnet, &net, sizeof(net)); if (net != 0) { /* Should be ATADDR_ANYNET? */ sat.sat_len = sizeof(struct sockaddr_at); sat.sat_family = AF_APPLETALK; sat.sat_addr.s_net = net; aa = at_ifawithnet(&sat); if (aa == NULL) { m_freem(m); return; } bcopy(ea->aarp_spnet, &spa.s_net, sizeof(spa.s_net)); bcopy(ea->aarp_tpnet, &tpa.s_net, sizeof(tpa.s_net)); } else { /* * Since we don't know the net, we just look for the first * phase 1 address on the interface. */ IF_ADDR_RLOCK(ifp); for (aa = (struct at_ifaddr *)TAILQ_FIRST(&ifp->if_addrhead); aa; aa = (struct at_ifaddr *)aa->aa_ifa.ifa_link.tqe_next) { if (AA_SAT(aa)->sat_family == AF_APPLETALK && (aa->aa_flags & AFA_PHASE2) == 0) { break; } } if (aa == NULL) { IF_ADDR_RUNLOCK(ifp); m_freem(m); return; } ifa_ref(&aa->aa_ifa); IF_ADDR_RUNLOCK(ifp); tpa.s_net = spa.s_net = AA_SAT(aa)->sat_addr.s_net; } spa.s_node = ea->aarp_spnode; tpa.s_node = ea->aarp_tpnode; ma.s_net = AA_SAT(aa)->sat_addr.s_net; ma.s_node = AA_SAT(aa)->sat_addr.s_node; /* * This looks like it's from us. */ if (spa.s_net == ma.s_net && spa.s_node == ma.s_node) { if (aa->aa_flags & AFA_PROBING) { /* * We're probing, someone either responded to our * probe, or probed for the same address we'd like to * use. Change the address we're probing for. */ callout_stop(&aa->aa_callout); wakeup(aa); ifa_free(&aa->aa_ifa); m_freem(m); return; } else if (op != AARPOP_PROBE) { /* * This is not a probe, and we're not probing. This * means that someone's saying they have the same * source address as the one we're using. Get upset. */ ifa_free(&aa->aa_ifa); log(LOG_ERR, "aarp: duplicate AT address!! %x:%x:%x:%x:%x:%x\n", ea->aarp_sha[0], ea->aarp_sha[1], ea->aarp_sha[2], ea->aarp_sha[3], ea->aarp_sha[4], ea->aarp_sha[5]); m_freem(m); return; } } AARPTAB_LOCK(); AARPTAB_LOOK(aat, spa); if (aat != NULL) { if (op == AARPOP_PROBE) { /* * Someone's probing for spa, dealocate the one we've * got, so that if the prober keeps the address, * we'll be able to arp for him. */ aarptfree(aat); AARPTAB_UNLOCK(); ifa_free(&aa->aa_ifa); m_freem(m); return; } bcopy((caddr_t)ea->aarp_sha, (caddr_t)aat->aat_enaddr, sizeof(ea->aarp_sha)); aat->aat_flags |= ATF_COM; if (aat->aat_hold) { struct mbuf *mhold = aat->aat_hold; aat->aat_hold = NULL; AARPTAB_UNLOCK(); sat.sat_len = sizeof(struct sockaddr_at); sat.sat_family = AF_APPLETALK; sat.sat_addr = spa; (*ifp->if_output)(ifp, mhold, (struct sockaddr *)&sat, NULL); /* XXX */ } else AARPTAB_UNLOCK(); } else if ((tpa.s_net == ma.s_net) && (tpa.s_node == ma.s_node) && (op != AARPOP_PROBE) && ((aat = aarptnew(&spa)) != NULL)) { bcopy((caddr_t)ea->aarp_sha, (caddr_t)aat->aat_enaddr, sizeof(ea->aarp_sha)); aat->aat_flags |= ATF_COM; AARPTAB_UNLOCK(); } else AARPTAB_UNLOCK(); /* * Don't respond to responses, and never respond if we're still * probing. */ if (tpa.s_net != ma.s_net || tpa.s_node != ma.s_node || op == AARPOP_RESPONSE || (aa->aa_flags & AFA_PROBING)) { ifa_free(&aa->aa_ifa); m_freem(m); return; } bcopy((caddr_t)ea->aarp_sha, (caddr_t)ea->aarp_tha, sizeof(ea->aarp_sha)); bcopy(IF_LLADDR(ifp), (caddr_t)ea->aarp_sha, sizeof(ea->aarp_sha)); /* XXX */ eh = (struct ether_header *)sa.sa_data; bcopy((caddr_t)ea->aarp_tha, (caddr_t)eh->ether_dhost, sizeof(eh->ether_dhost)); if (aa->aa_flags & AFA_PHASE2) { eh->ether_type = htons(sizeof(struct llc) + sizeof(struct ether_aarp)); M_PREPEND(m, sizeof(struct llc), M_DONTWAIT); if (m == NULL) { ifa_free(&aa->aa_ifa); return; } llc = mtod(m, struct llc *); llc->llc_dsap = llc->llc_ssap = LLC_SNAP_LSAP; llc->llc_control = LLC_UI; bcopy(aarp_org_code, llc->llc_org_code, sizeof(aarp_org_code)); llc->llc_ether_type = htons(ETHERTYPE_AARP); bcopy(ea->aarp_spnet, ea->aarp_tpnet, sizeof(ea->aarp_tpnet)); bcopy(&ma.s_net, ea->aarp_spnet, sizeof(ea->aarp_spnet)); } else eh->ether_type = htons(ETHERTYPE_AARP); ifa_free(&aa->aa_ifa); ea->aarp_tpnode = ea->aarp_spnode; ea->aarp_spnode = ma.s_node; ea->aarp_op = htons(AARPOP_RESPONSE); sa.sa_len = sizeof(struct sockaddr); sa.sa_family = AF_UNSPEC; (*ifp->if_output)(ifp, m, &sa, NULL); /* XXX */ return; } static void aarptfree(struct aarptab *aat) { AARPTAB_LOCK_ASSERT(); if (aat->aat_hold) m_freem(aat->aat_hold); aat->aat_hold = NULL; aat->aat_timer = aat->aat_flags = 0; aat->aat_ataddr.s_net = 0; aat->aat_ataddr.s_node = 0; } struct aarptab * aarptnew(struct at_addr *addr) { int n; int oldest = -1; struct aarptab *aat, *aato = NULL; static int first = 1; AARPTAB_LOCK_ASSERT(); if (first) { first = 0; aarptimer_ch = timeout(aarptimer, (caddr_t)0, hz); } aat = &aarptab[AARPTAB_HASH(*addr) * AARPTAB_BSIZ]; for (n = 0; n < AARPTAB_BSIZ; n++, aat++) { if (aat->aat_flags == 0) goto out; if (aat->aat_flags & ATF_PERM) continue; if ((int) aat->aat_timer > oldest) { oldest = aat->aat_timer; aato = aat; } } if (aato == NULL) return (NULL); aat = aato; aarptfree(aat); out: aat->aat_ataddr = *addr; aat->aat_flags = ATF_INUSE; return (aat); } void aarpprobe(void *arg) { struct ifnet *ifp = arg; struct mbuf *m; struct ether_header *eh; struct ether_aarp *ea; struct at_ifaddr *aa; struct llc *llc; struct sockaddr sa; /* * We need to check whether the output ethernet type should be phase * 1 or 2. We have the interface that we'll be sending the aarp out. * We need to find an AppleTalk network on that interface with the * same address as we're looking for. If the net is phase 2, * generate an 802.2 and SNAP header. */ AARPTAB_LOCK(); for (aa = (struct at_ifaddr *)TAILQ_FIRST(&ifp->if_addrhead); aa; aa = (struct at_ifaddr *)aa->aa_ifa.ifa_link.tqe_next) { if (AA_SAT(aa)->sat_family == AF_APPLETALK && (aa->aa_flags & AFA_PROBING)) break; } if (aa == NULL) { /* Serious error XXX. */ AARPTAB_UNLOCK(); printf("aarpprobe why did this happen?!\n"); return; } if (aa->aa_probcnt <= 0) { aa->aa_flags &= ~AFA_PROBING; wakeup(aa); AARPTAB_UNLOCK(); return; } else callout_reset(&aa->aa_callout, hz / 5, aarpprobe, ifp); ifa_ref(&aa->aa_ifa); AARPTAB_UNLOCK(); m = m_gethdr(M_DONTWAIT, MT_DATA); if (m == NULL) { ifa_free(&aa->aa_ifa); return; } #ifdef MAC mac_netatalk_aarp_send(ifp, m); #endif m->m_len = sizeof(*ea); m->m_pkthdr.len = sizeof(*ea); MH_ALIGN(m, sizeof(*ea)); ea = mtod(m, struct ether_aarp *); bzero((caddr_t)ea, sizeof(*ea)); ea->aarp_hrd = htons(AARPHRD_ETHER); ea->aarp_pro = htons(ETHERTYPE_AT); ea->aarp_hln = sizeof(ea->aarp_sha); ea->aarp_pln = sizeof(ea->aarp_spu); ea->aarp_op = htons(AARPOP_PROBE); bcopy(IF_LLADDR(ifp), (caddr_t)ea->aarp_sha, sizeof(ea->aarp_sha)); eh = (struct ether_header *)sa.sa_data; if (aa->aa_flags & AFA_PHASE2) { bcopy(atmulticastaddr, eh->ether_dhost, sizeof(eh->ether_dhost)); eh->ether_type = htons(sizeof(struct llc) + sizeof(struct ether_aarp)); M_PREPEND(m, sizeof(struct llc), M_WAIT); llc = mtod(m, struct llc *); llc->llc_dsap = llc->llc_ssap = LLC_SNAP_LSAP; llc->llc_control = LLC_UI; bcopy(aarp_org_code, llc->llc_org_code, sizeof(aarp_org_code)); llc->llc_ether_type = htons(ETHERTYPE_AARP); bcopy(&AA_SAT(aa)->sat_addr.s_net, ea->aarp_spnet, sizeof(ea->aarp_spnet)); bcopy(&AA_SAT(aa)->sat_addr.s_net, ea->aarp_tpnet, sizeof(ea->aarp_tpnet)); ea->aarp_spnode = ea->aarp_tpnode = AA_SAT(aa)->sat_addr.s_node; } else { bcopy(ifp->if_broadcastaddr, (caddr_t)eh->ether_dhost, sizeof(eh->ether_dhost)); eh->ether_type = htons(ETHERTYPE_AARP); ea->aarp_spa = ea->aarp_tpa = AA_SAT(aa)->sat_addr.s_node; } #ifdef NETATALKDEBUG printf("aarp: sending probe for %u.%u\n", ntohs(AA_SAT(aa)->sat_addr.s_net), AA_SAT(aa)->sat_addr.s_node); #endif /* NETATALKDEBUG */ ifa_free(&aa->aa_ifa); sa.sa_len = sizeof(struct sockaddr); sa.sa_family = AF_UNSPEC; (*ifp->if_output)(ifp, m, &sa, NULL); /* XXX */ aa->aa_probcnt--; } void aarp_clean(void) { struct aarptab *aat; int i; untimeout(aarptimer, 0, aarptimer_ch); AARPTAB_LOCK(); for (i = 0, aat = aarptab; i < AARPTAB_SIZE; i++, aat++) { if (aat->aat_hold) { m_freem(aat->aat_hold); aat->aat_hold = NULL; } } AARPTAB_UNLOCK(); }