Path: blob/main/usr.sbin/bsnmpd/modules/snmp_bridge/bridge_sys.c
105655 views
/*-1* SPDX-License-Identifier: BSD-2-Clause2*3* Copyright (c) 2006 Shteryana Shopova <[email protected]>4* All rights reserved.5*6* Redistribution and use in source and binary forms, with or without7* modification, are permitted provided that the following conditions8* are met:9* 1. Redistributions of source code must retain the above copyright10* notice, this list of conditions and the following disclaimer.11* 2. Redistributions in binary form must reproduce the above copyright12* notice, this list of conditions and the following disclaimer in the13* documentation and/or other materials provided with the distribution.14*15* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND16* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE17* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE18* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE19* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL20* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS21* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)22* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT23* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY24* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF25* SUCH DAMAGE.26*27* Bridge MIB implementation for SNMPd.28* Bridge OS specific ioctls.29*/3031#include <sys/ioctl.h>32#include <sys/param.h>33#include <sys/module.h>34#include <sys/linker.h>35#include <sys/socket.h>36#include <sys/sysctl.h>3738#include <net/bridgestp.h>39#include <net/ethernet.h>40#include <net/if.h>41#include <net/if_bridgevar.h>42#include <net/if_dl.h>43#include <net/if_mib.h>44#include <net/if_types.h>45#include <netinet/in.h>4647#include <errno.h>48#include <ifaddrs.h>49#include <stdarg.h>50#include <stdlib.h>51#include <stdio.h>52#include <string.h>53#include <syslog.h>5455#include <bsnmp/snmpmod.h>56#include <bsnmp/snmp_mibII.h>5758#define SNMPTREE_TYPES59#include "bridge_tree.h"60#include "bridge_snmp.h"6162int sock = -1;6364int65bridge_ioctl_init(void)66{67if ((sock = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {68syslog(LOG_ERR, "cannot open socket : %s", strerror(errno));69return (-1);70}7172return (0);73}7475/*76* Load the if_bridge.ko module in kernel if not already there.77*/78int79bridge_kmod_load(void)80{81int fileid, modid;82const char mod_name[] = "if_bridge";83struct module_stat mstat;8485/* Scan files in kernel. */86mstat.version = sizeof(struct module_stat);87for (fileid = kldnext(0); fileid > 0; fileid = kldnext(fileid)) {88/* Scan modules in file. */89for (modid = kldfirstmod(fileid); modid > 0;90modid = modfnext(modid)) {9192if (modstat(modid, &mstat) < 0)93continue;9495if (strcmp(mod_name, mstat.name) == 0)96return (0);97}98}99100/* Not present - load it. */101if (kldload(mod_name) < 0) {102syslog(LOG_ERR, "failed to load %s kernel module", mod_name);103return (-1);104}105106return (1);107}108109/************************************************************************110* Bridge interfaces.111*/112113/*114* Convert the kernel uint64_t value for a bridge id115*/116static void117snmp_uint64_to_bridgeid(uint64_t id, bridge_id b_id)118{119int i;120u_char *o;121122o = (u_char *) &id;123124for (i = 0; i < SNMP_BRIDGE_ID_LEN; i++, o++)125b_id[SNMP_BRIDGE_ID_LEN - i - 1] = *o;126}127128/*129* Fetch the bridge configuration parameters from the kernel excluding130* it's base MAC address.131*/132static int133bridge_get_conf_param(struct bridge_if *bif)134{135struct ifdrv ifd;136struct ifbrparam b_param;137138strlcpy(ifd.ifd_name, bif->bif_name, IFNAMSIZ);139ifd.ifd_len = sizeof(b_param);140ifd.ifd_data = &b_param;141142/* Bridge priority. */143ifd.ifd_cmd = BRDGGPRI;144if (ioctl(sock, SIOCGDRVSPEC, &ifd) < 0) {145syslog(LOG_ERR, "update bridge: ioctl(BRDGGPRI) failed: %s",146strerror(errno));147return (-1);148}149150bif->priority = b_param.ifbrp_prio;151152/* Configured max age. */153ifd.ifd_cmd = BRDGGMA;154if (ioctl(sock, SIOCGDRVSPEC, &ifd) < 0) {155syslog(LOG_ERR, "update bridge: ioctl(BRDGGMA) failed: %s",156strerror(errno));157return (-1);158}159160/* Centi-seconds. */161bif->bridge_max_age = 100 * b_param.ifbrp_maxage;162163/* Configured hello time. */164ifd.ifd_cmd = BRDGGHT;165if (ioctl(sock, SIOCGDRVSPEC, &ifd) < 0) {166syslog(LOG_ERR, "update bridge: ioctl(BRDGGHT) failed: %s",167strerror(errno));168return (-1);169}170bif->bridge_hello_time = 100 * b_param.ifbrp_hellotime;171172/* Forward delay. */173ifd.ifd_cmd = BRDGGFD;174if (ioctl(sock, SIOCGDRVSPEC, &ifd) < 0) {175syslog(LOG_ERR, "update bridge: ioctl(BRDGGFD) failed: %s",176strerror(errno));177return (-1);178}179bif->bridge_fwd_delay = 100 * b_param.ifbrp_fwddelay;180181/* Number of dropped addresses. */182ifd.ifd_cmd = BRDGGRTE;183if (ioctl(sock, SIOCGDRVSPEC, &ifd) < 0) {184syslog(LOG_ERR, "update bridge: ioctl(BRDGGRTE) failed: %s",185strerror(errno));186return (-1);187}188bif->lrnt_drops = b_param.ifbrp_cexceeded;189190/* Address table timeout. */191ifd.ifd_cmd = BRDGGTO;192if (ioctl(sock, SIOCGDRVSPEC, &ifd) < 0) {193syslog(LOG_ERR, "update bridge: ioctl(BRDGGTO) failed: %s",194strerror(errno));195return (-1);196}197bif->age_time = b_param.ifbrp_ctime;198199/* Address table size. */200ifd.ifd_cmd = BRDGGCACHE;201if (ioctl(sock, SIOCGDRVSPEC, &ifd) < 0) {202syslog(LOG_ERR, "update bridge: ioctl(BRDGGCACHE) "203"failed: %s", strerror(errno));204return (-1);205}206bif->max_addrs = b_param.ifbrp_csize;207208return (0);209}210211/*212* Fetch the current bridge STP operational parameters.213* Returns: -1 - on error;214* 0 - old TC time and Root Port values are same;215* 1 - topologyChange notification should be sent;216* 2 - newRoot notification should be sent.217*/218int219bridge_get_op_param(struct bridge_if *bif)220{221int new_root_send;222struct ifdrv ifd;223struct ifbropreq b_req;224225strlcpy(ifd.ifd_name, bif->bif_name, IFNAMSIZ);226ifd.ifd_len = sizeof(b_req);227ifd.ifd_data = &b_req;228ifd.ifd_cmd = BRDGPARAM;229230if (ioctl(sock, SIOCGDRVSPEC, &ifd) < 0) {231syslog(LOG_ERR, "update bridge: ioctl(BRDGPARAM) failed: %s",232strerror(errno));233return (-1);234}235236bif->max_age = 100 * b_req.ifbop_maxage;237bif->hello_time = 100 * b_req.ifbop_hellotime;238bif->fwd_delay = 100 * b_req.ifbop_fwddelay;239bif->stp_version = b_req.ifbop_protocol;240bif->tx_hold_count = b_req.ifbop_holdcount;241242if (b_req.ifbop_root_port == 0 &&243bif->root_port != b_req.ifbop_root_port)244new_root_send = 2;245else246new_root_send = 0;247248bif->root_port = b_req.ifbop_root_port;249bif->root_cost = b_req.ifbop_root_path_cost;250snmp_uint64_to_bridgeid(b_req.ifbop_designated_root,251bif->design_root);252253if (bif->last_tc_time.tv_sec != b_req.ifbop_last_tc_time.tv_sec) {254bif->top_changes++;255bif->last_tc_time.tv_sec = b_req.ifbop_last_tc_time.tv_sec;256bif->last_tc_time.tv_usec = b_req.ifbop_last_tc_time.tv_usec;257258/*259* "The trap is not sent if a (begemotBridge)NewRoot260* trap is sent for the same transition."261*/262if (new_root_send == 0)263return (1);264}265266return (new_root_send);267}268269int270bridge_getinfo_bif(struct bridge_if *bif)271{272if (bridge_get_conf_param(bif) < 0)273return (-1);274275return (bridge_get_op_param(bif));276}277278int279bridge_set_priority(struct bridge_if *bif, int32_t priority)280{281struct ifdrv ifd;282struct ifbrparam b_param;283284strlcpy(ifd.ifd_name, bif->bif_name, IFNAMSIZ);285ifd.ifd_len = sizeof(b_param);286ifd.ifd_data = &b_param;287b_param.ifbrp_prio = (uint32_t) priority;288ifd.ifd_cmd = BRDGSPRI;289290if (ioctl(sock, SIOCSDRVSPEC, &ifd) < 0) {291syslog(LOG_ERR, "set bridge param: ioctl(BRDGSPRI) "292"failed: %s", strerror(errno));293return (-1);294}295296/*297* Re-fetching the data from the driver after that might be a good298* idea, since changing our bridge's priority should invoke299* recalculation of the active spanning tree topology in the network.300*/301bif->priority = priority;302return (0);303}304305/*306* Convert 1/100 of seconds to 1/256 of seconds.307* Timeout ::= TEXTUAL-CONVENTION.308* To convert a Timeout value into a value in units of309* 1/256 seconds, the following algorithm should be used:310* b = floor( (n * 256) / 100)311* The conversion to 1/256 of a second happens in the kernel -312* just make sure we correctly convert the seconds to Timeout313* and vice versa.314*/315static uint32_t316snmp_timeout2_sec(int32_t secs)317{318return (secs / 100);319}320321int322bridge_set_maxage(struct bridge_if *bif, int32_t max_age)323{324struct ifdrv ifd;325struct ifbrparam b_param;326327strlcpy(ifd.ifd_name, bif->bif_name, IFNAMSIZ);328ifd.ifd_len = sizeof(b_param);329ifd.ifd_data = &b_param;330b_param.ifbrp_maxage = snmp_timeout2_sec(max_age);331ifd.ifd_cmd = BRDGSMA;332333if (ioctl(sock, SIOCSDRVSPEC, &ifd) < 0) {334syslog(LOG_ERR, "set bridge param: ioctl(BRDGSMA) "335"failed: %s", strerror(errno));336return (-1);337}338339bif->bridge_max_age = max_age;340return (0);341}342343int344bridge_set_hello_time(struct bridge_if *bif, int32_t hello_time)345{346struct ifdrv ifd;347struct ifbrparam b_param;348349strlcpy(ifd.ifd_name, bif->bif_name, IFNAMSIZ);350ifd.ifd_len = sizeof(b_param);351ifd.ifd_data = &b_param;352b_param.ifbrp_hellotime = snmp_timeout2_sec(hello_time);353ifd.ifd_cmd = BRDGSHT;354355if (ioctl(sock, SIOCSDRVSPEC, &ifd) < 0) {356syslog(LOG_ERR, "set bridge param: ioctl(BRDGSHT) "357"failed: %s", strerror(errno));358return (-1);359}360361bif->bridge_hello_time = b_param.ifbrp_hellotime;362return (0);363}364365int366bridge_set_forward_delay(struct bridge_if *bif, int32_t fwd_delay)367{368struct ifdrv ifd;369struct ifbrparam b_param;370371strlcpy(ifd.ifd_name, bif->bif_name, IFNAMSIZ);372ifd.ifd_len = sizeof(b_param);373ifd.ifd_data = &b_param;374b_param.ifbrp_fwddelay = snmp_timeout2_sec(fwd_delay);375ifd.ifd_cmd = BRDGSFD;376377if (ioctl(sock, SIOCSDRVSPEC, &ifd) < 0) {378syslog(LOG_ERR, "set bridge param: ioctl(BRDGSFD) "379"failed: %s", strerror(errno));380return (-1);381}382383bif->bridge_fwd_delay = b_param.ifbrp_fwddelay;384return (0);385}386387int388bridge_set_aging_time(struct bridge_if *bif, int32_t age_time)389{390struct ifdrv ifd;391struct ifbrparam b_param;392393strlcpy(ifd.ifd_name, bif->bif_name, IFNAMSIZ);394ifd.ifd_len = sizeof(b_param);395ifd.ifd_data = &b_param;396b_param.ifbrp_ctime = (uint32_t) age_time;397ifd.ifd_cmd = BRDGSTO;398399if (ioctl(sock, SIOCSDRVSPEC, &ifd) < 0) {400syslog(LOG_ERR, "set bridge param: ioctl(BRDGSTO) "401"failed: %s", strerror(errno));402return (-1);403}404405bif->age_time = age_time;406return (0);407}408409int410bridge_set_max_cache(struct bridge_if *bif, int32_t max_cache)411{412struct ifdrv ifd;413struct ifbrparam b_param;414415strlcpy(ifd.ifd_name, bif->bif_name, IFNAMSIZ);416ifd.ifd_len = sizeof(b_param);417ifd.ifd_data = &b_param;418b_param.ifbrp_csize = max_cache;419ifd.ifd_cmd = BRDGSCACHE;420421if (ioctl(sock, SIOCSDRVSPEC, &ifd) < 0) {422syslog(LOG_ERR, "set bridge param: ioctl(BRDGSCACHE) "423"failed: %s", strerror(errno));424return (-1);425}426427bif->max_addrs = b_param.ifbrp_csize;428return (0);429}430431int432bridge_set_tx_hold_count(struct bridge_if *bif, int32_t tx_hc)433{434struct ifdrv ifd;435struct ifbrparam b_param;436437if (tx_hc < SNMP_BRIDGE_MIN_TXHC || tx_hc > SNMP_BRIDGE_MAX_TXHC)438return (-1);439440strlcpy(ifd.ifd_name, bif->bif_name, IFNAMSIZ);441ifd.ifd_len = sizeof(b_param);442ifd.ifd_data = &b_param;443b_param.ifbrp_txhc = tx_hc;444ifd.ifd_cmd = BRDGSTXHC;445446if (ioctl(sock, SIOCSDRVSPEC, &ifd) < 0) {447syslog(LOG_ERR, "set bridge param: ioctl(BRDGSTXHC) "448"failed: %s", strerror(errno));449return (-1);450}451452bif->tx_hold_count = b_param.ifbrp_txhc;453return (0);454}455456int457bridge_set_stp_version(struct bridge_if *bif, int32_t stp_proto)458{459struct ifdrv ifd;460struct ifbrparam b_param;461462strlcpy(ifd.ifd_name, bif->bif_name, IFNAMSIZ);463ifd.ifd_len = sizeof(b_param);464ifd.ifd_data = &b_param;465b_param.ifbrp_proto = stp_proto;466ifd.ifd_cmd = BRDGSPROTO;467468if (ioctl(sock, SIOCSDRVSPEC, &ifd) < 0) {469syslog(LOG_ERR, "set bridge param: ioctl(BRDGSPROTO) "470"failed: %s", strerror(errno));471return (-1);472}473474bif->stp_version = b_param.ifbrp_proto;475return (0);476}477478/*479* Set the bridge interface status to up/down.480*/481int482bridge_set_if_up(const char* b_name, int8_t up)483{484int flags;485struct ifreq ifr;486487bzero(&ifr, sizeof(ifr));488strlcpy(ifr.ifr_name, b_name, sizeof(ifr.ifr_name));489if (ioctl(sock, SIOCGIFFLAGS, (caddr_t) &ifr) < 0) {490syslog(LOG_ERR, "set bridge up: ioctl(SIOCGIFFLAGS) "491"failed: %s", strerror(errno));492return (-1);493}494495flags = (ifr.ifr_flags & 0xffff) | (ifr.ifr_flagshigh << 16);496if (up == 1)497flags |= IFF_UP;498else499flags &= ~IFF_UP;500501ifr.ifr_flags = flags & 0xffff;502ifr.ifr_flagshigh = flags >> 16;503if (ioctl(sock, SIOCSIFFLAGS, (caddr_t) &ifr) < 0) {504syslog(LOG_ERR, "set bridge up: ioctl(SIOCSIFFLAGS) "505"failed: %s", strerror(errno));506return (-1);507}508509return (0);510}511512int513bridge_create(const char *b_name)514{515char *new_name;516struct ifreq ifr;517518bzero(&ifr, sizeof(ifr));519strlcpy(ifr.ifr_name, b_name, sizeof(ifr.ifr_name));520521if (ioctl(sock, SIOCIFCREATE, &ifr) < 0) {522syslog(LOG_ERR, "create bridge: ioctl(SIOCIFCREATE) "523"failed: %s", strerror(errno));524return (-1);525}526527if (strcmp(b_name, ifr.ifr_name) == 0)528return (0);529530if ((new_name = strdup(b_name)) == NULL) {531syslog(LOG_ERR, "create bridge: strdup() failed");532return (-1);533}534535ifr.ifr_data = new_name;536if (ioctl(sock, SIOCSIFNAME, (caddr_t) &ifr) < 0) {537syslog(LOG_ERR, "create bridge: ioctl(SIOCSIFNAME) "538"failed: %s", strerror(errno));539free(new_name);540return (-1);541}542543return (0);544}545546int547bridge_destroy(const char *b_name)548{549struct ifreq ifr;550551bzero(&ifr, sizeof(ifr));552strlcpy(ifr.ifr_name, b_name, sizeof(ifr.ifr_name));553554if (ioctl(sock, SIOCIFDESTROY, &ifr) < 0) {555syslog(LOG_ERR, "destroy bridge: ioctl(SIOCIFDESTROY) "556"failed: %s", strerror(errno));557return (-1);558}559560return (0);561}562563/*564* Fetch the bridge base MAC address. Return pointer to the565* buffer containing the MAC address, NULL on failure.566*/567u_char *568bridge_get_basemac(const char *bif_name, u_char *mac, size_t mlen)569{570int len;571char if_name[IFNAMSIZ];572struct ifaddrs *ifap, *ifa;573struct sockaddr_dl sdl;574575if (getifaddrs(&ifap) != 0) {576syslog(LOG_ERR, "bridge get mac: getifaddrs() failed - %s",577strerror(errno));578return (NULL);579}580581for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {582if (ifa->ifa_addr->sa_family != AF_LINK)583continue;584585/*586* Not just casting because of alignment constraints.587*/588bcopy(ifa->ifa_addr, &sdl, sizeof(struct sockaddr_dl));589590if (sdl.sdl_alen > mlen)591continue;592593if ((len = sdl.sdl_nlen) >= IFNAMSIZ)594len = IFNAMSIZ - 1;595596bcopy(sdl.sdl_data, if_name, len);597if_name[len] = '\0';598599if (strcmp(bif_name, if_name) == 0) {600bcopy(sdl.sdl_data + sdl.sdl_nlen, mac, sdl.sdl_alen);601freeifaddrs(ifap);602return (mac);603}604}605606freeifaddrs(ifap);607return (NULL);608}609610/************************************************************************611* Bridge ports.612*/613614/*615* Convert the kernel STP port state into616* the corresopnding enumerated type from SNMP Bridge MIB.617*/618static int619state2snmp_st(uint8_t ifbr_state)620{621switch (ifbr_state) {622case BSTP_IFSTATE_DISABLED:623return (StpPortState_disabled);624case BSTP_IFSTATE_LISTENING:625return (StpPortState_listening);626case BSTP_IFSTATE_LEARNING:627return (StpPortState_learning);628case BSTP_IFSTATE_FORWARDING:629return (StpPortState_forwarding);630case BSTP_IFSTATE_BLOCKING:631case BSTP_IFSTATE_DISCARDING:632return (StpPortState_blocking);633}634635return (StpPortState_broken);636}637638/*639* Fill in a bridge member information according to data polled from kernel.640*/641static void642bridge_port_getinfo_conf(struct ifbreq *k_info, struct bridge_port *bp)643{644bp->state = state2snmp_st(k_info->ifbr_state);645bp->priority = k_info->ifbr_priority;646647/*648* RFC 4188:649* "New implementations should support dot1dStpPortPathCost32.650* If the port path costs exceeds the maximum value of this651* object then this object should report the maximum value,652* namely 65535. Applications should try to read the653* dot1dStpPortPathCost32 object if this object reports654* the maximum value."655*/656657if (k_info->ifbr_ifsflags & IFBIF_BSTP_ADMCOST)658bp->admin_path_cost = k_info->ifbr_path_cost;659else660bp->admin_path_cost = 0;661662bp->path_cost = k_info->ifbr_path_cost;663664if (k_info->ifbr_ifsflags & IFBIF_STP)665bp->enable = dot1dStpPortEnable_enabled;666else667bp->enable = dot1dStpPortEnable_disabled;668669/* Begemot Bridge MIB only. */670if (k_info->ifbr_ifsflags & IFBIF_SPAN)671bp->span_enable = begemotBridgeBaseSpanEnabled_enabled;672else673bp->span_enable = begemotBridgeBaseSpanEnabled_disabled;674675if (k_info->ifbr_ifsflags & IFBIF_PRIVATE)676bp->priv_set = TruthValue_true;677else678bp->priv_set = TruthValue_false;679680if (k_info->ifbr_ifsflags & IFBIF_BSTP_ADMEDGE)681bp->admin_edge = TruthValue_true;682else683bp->admin_edge = TruthValue_false;684685if (k_info->ifbr_ifsflags & IFBIF_BSTP_EDGE)686bp->oper_edge = TruthValue_true;687else688bp->oper_edge = TruthValue_false;689690if (k_info->ifbr_ifsflags & IFBIF_BSTP_AUTOPTP) {691bp->admin_ptp = StpPortAdminPointToPointType_auto;692if (k_info->ifbr_ifsflags & IFBIF_BSTP_PTP)693bp->oper_ptp = TruthValue_true;694else695bp->oper_ptp = TruthValue_false;696} else if (k_info->ifbr_ifsflags & IFBIF_BSTP_PTP) {697bp->admin_ptp = StpPortAdminPointToPointType_forceTrue;698bp->oper_ptp = TruthValue_true;699} else {700bp->admin_ptp = StpPortAdminPointToPointType_forceFalse;701bp->oper_ptp = TruthValue_false;702}703}704705/*706* Fill in a bridge interface STP information according to707* data polled from kernel.708*/709static void710bridge_port_getinfo_opstp(struct ifbpstpreq *bp_stp, struct bridge_port *bp)711{712bp->enable = dot1dStpPortEnable_enabled;713bp->fwd_trans = bp_stp->ifbp_fwd_trans;714bp->design_cost = bp_stp->ifbp_design_cost;715snmp_uint64_to_bridgeid(bp_stp->ifbp_design_root, bp->design_root);716snmp_uint64_to_bridgeid(bp_stp->ifbp_design_bridge, bp->design_bridge);717bcopy(&(bp_stp->ifbp_design_port), &(bp->design_port),718sizeof(uint16_t));719}720721/*722* Clear a bridge interface STP information.723*/724static void725bridge_port_clearinfo_opstp(struct bridge_port *bp)726{727if (bp->enable == dot1dStpPortEnable_enabled) {728bp->design_cost = 0;729bzero(&(bp->design_root), sizeof(bridge_id));730bzero(&(bp->design_bridge), sizeof(bridge_id));731bzero(&(bp->design_port), sizeof(port_id));732bp->fwd_trans = 0;733}734735bp->enable = dot1dStpPortEnable_disabled;736}737738/*739* Set a bridge member priority.740*/741int742bridge_port_set_priority(const char *bif_name, struct bridge_port *bp,743int32_t priority)744{745struct ifdrv ifd;746struct ifbreq b_req;747748strlcpy(ifd.ifd_name, bif_name, sizeof(ifd.ifd_name));749ifd.ifd_len = sizeof(b_req);750ifd.ifd_data = &b_req;751strlcpy(b_req.ifbr_ifsname, bp->p_name, sizeof(b_req.ifbr_ifsname));752753b_req.ifbr_priority = (uint8_t) priority;754ifd.ifd_cmd = BRDGSIFPRIO;755756if (ioctl(sock, SIOCSDRVSPEC, &ifd) < 0) {757syslog(LOG_ERR, "set member %s param: ioctl(BRDGSIFPRIO) "758"failed: %s", bp->p_name, strerror(errno));759return (-1);760}761762bp->priority = priority;763return (0);764}765766/*767* Set a bridge member STP-enabled flag.768*/769int770bridge_port_set_stp_enable(const char *bif_name, struct bridge_port *bp,771uint32_t enable)772{773struct ifdrv ifd;774struct ifbreq b_req;775776if (bp->enable == enable)777return (0);778779bzero(&b_req, sizeof(b_req));780strlcpy(ifd.ifd_name, bif_name, sizeof(ifd.ifd_name));781ifd.ifd_len = sizeof(b_req);782ifd.ifd_data = &b_req;783strlcpy(b_req.ifbr_ifsname, bp->p_name, sizeof(b_req.ifbr_ifsname));784ifd.ifd_cmd = BRDGGIFFLGS;785786if (ioctl(sock, SIOCGDRVSPEC, &ifd) < 0) {787syslog(LOG_ERR, "get member %s param: ioctl(BRDGGIFFLGS) "788"failed: %s", bp->p_name, strerror(errno));789return (-1);790}791792if (enable == dot1dStpPortEnable_enabled)793b_req.ifbr_ifsflags |= IFBIF_STP;794else795b_req.ifbr_ifsflags &= ~IFBIF_STP;796797ifd.ifd_cmd = BRDGSIFFLGS;798if (ioctl(sock, SIOCSDRVSPEC, &ifd) < 0) {799syslog(LOG_ERR, "set member %s param: ioctl(BRDGSIFFLGS) "800"failed: %s", bp->p_name, strerror(errno));801return (-1);802}803804bp->enable = enable;805return (0);806}807808/*809* Set a bridge member STP path cost.810*/811int812bridge_port_set_path_cost(const char *bif_name, struct bridge_port *bp,813int32_t path_cost)814{815struct ifdrv ifd;816struct ifbreq b_req;817818if (path_cost < SNMP_PORT_MIN_PATHCOST ||819path_cost > SNMP_PORT_PATHCOST_OBSOLETE)820return (-2);821822strlcpy(ifd.ifd_name, bif_name, sizeof(ifd.ifd_name));823ifd.ifd_len = sizeof(b_req);824ifd.ifd_data = &b_req;825strlcpy(b_req.ifbr_ifsname, bp->p_name, sizeof(b_req.ifbr_ifsname));826827b_req.ifbr_path_cost = path_cost;828ifd.ifd_cmd = BRDGSIFCOST;829830if (ioctl(sock, SIOCSDRVSPEC, &ifd) < 0) {831syslog(LOG_ERR, "set member %s param: ioctl(BRDGSIFCOST) "832"failed: %s", bp->p_name, strerror(errno));833return (-1);834}835836bp->admin_path_cost = path_cost;837838return (0);839}840841/*842* Set the PonitToPoint status of the link administratively.843*/844int845bridge_port_set_admin_ptp(const char *bif_name, struct bridge_port *bp,846uint32_t admin_ptp)847{848struct ifdrv ifd;849struct ifbreq b_req;850851if (bp->admin_ptp == admin_ptp)852return (0);853854bzero(&b_req, sizeof(b_req));855strlcpy(ifd.ifd_name, bif_name, sizeof(ifd.ifd_name));856ifd.ifd_len = sizeof(b_req);857ifd.ifd_data = &b_req;858strlcpy(b_req.ifbr_ifsname, bp->p_name, sizeof(b_req.ifbr_ifsname));859ifd.ifd_cmd = BRDGGIFFLGS;860861if (ioctl(sock, SIOCGDRVSPEC, &ifd) < 0) {862syslog(LOG_ERR, "get member %s param: ioctl(BRDGGIFFLGS) "863"failed: %s", bp->p_name, strerror(errno));864return (-1);865}866867switch (admin_ptp) {868case StpPortAdminPointToPointType_forceTrue:869b_req.ifbr_ifsflags &= ~IFBIF_BSTP_AUTOPTP;870b_req.ifbr_ifsflags |= IFBIF_BSTP_PTP;871break;872case StpPortAdminPointToPointType_forceFalse:873b_req.ifbr_ifsflags &= ~IFBIF_BSTP_AUTOPTP;874b_req.ifbr_ifsflags &= ~IFBIF_BSTP_PTP;875break;876case StpPortAdminPointToPointType_auto:877b_req.ifbr_ifsflags |= IFBIF_BSTP_AUTOPTP;878break;879}880881ifd.ifd_cmd = BRDGSIFFLGS;882if (ioctl(sock, SIOCSDRVSPEC, &ifd) < 0) {883syslog(LOG_ERR, "set member %s param: ioctl(BRDGSIFFLGS) "884"failed: %s", bp->p_name, strerror(errno));885return (-1);886}887888bp->admin_ptp = admin_ptp;889return (0);890}891892/*893* Set admin edge.894*/895int896bridge_port_set_admin_edge(const char *bif_name, struct bridge_port *bp,897uint32_t enable)898{899struct ifdrv ifd;900struct ifbreq b_req;901902if (bp->admin_edge == enable)903return (0);904905bzero(&b_req, sizeof(b_req));906strlcpy(ifd.ifd_name, bif_name, sizeof(ifd.ifd_name));907ifd.ifd_len = sizeof(b_req);908ifd.ifd_data = &b_req;909strlcpy(b_req.ifbr_ifsname, bp->p_name, sizeof(b_req.ifbr_ifsname));910ifd.ifd_cmd = BRDGGIFFLGS;911912if (ioctl(sock, SIOCGDRVSPEC, &ifd) < 0) {913syslog(LOG_ERR, "get member %s param: ioctl(BRDGGIFFLGS) "914"failed: %s", bp->p_name, strerror(errno));915return (-1);916}917918if (enable == TruthValue_true) {919b_req.ifbr_ifsflags &= ~IFBIF_BSTP_AUTOEDGE;920b_req.ifbr_ifsflags |= IFBIF_BSTP_EDGE;921} else922b_req.ifbr_ifsflags &= ~IFBIF_BSTP_EDGE;923924ifd.ifd_cmd = BRDGSIFFLGS;925if (ioctl(sock, SIOCSDRVSPEC, &ifd) < 0) {926syslog(LOG_ERR, "set member %s param: ioctl(BRDGSIFFLGS) "927"failed: %s", bp->p_name, strerror(errno));928return (-1);929}930931bp->admin_edge = enable;932933return (0);934}935936/*937* Set 'private' flag.938*/939int940bridge_port_set_private(const char *bif_name, struct bridge_port *bp,941uint32_t priv_set)942{943struct ifdrv ifd;944struct ifbreq b_req;945946if (bp->priv_set == priv_set)947return (0);948949bzero(&b_req, sizeof(b_req));950strlcpy(ifd.ifd_name, bif_name, sizeof(ifd.ifd_name));951ifd.ifd_len = sizeof(b_req);952ifd.ifd_data = &b_req;953strlcpy(b_req.ifbr_ifsname, bp->p_name, sizeof(b_req.ifbr_ifsname));954ifd.ifd_cmd = BRDGGIFFLGS;955956if (ioctl(sock, SIOCGDRVSPEC, &ifd) < 0) {957syslog(LOG_ERR, "get member %s param: ioctl(BRDGGIFFLGS) "958"failed: %s", bp->p_name, strerror(errno));959return (-1);960}961962if (priv_set == TruthValue_true)963b_req.ifbr_ifsflags |= IFBIF_PRIVATE;964else if (priv_set == TruthValue_false)965b_req.ifbr_ifsflags &= ~IFBIF_PRIVATE;966else967return (SNMP_ERR_WRONG_VALUE);968969ifd.ifd_cmd = BRDGSIFFLGS;970if (ioctl(sock, SIOCSDRVSPEC, &ifd) < 0) {971syslog(LOG_ERR, "set member %s param: ioctl(BRDGSIFFLGS) "972"failed: %s", bp->p_name, strerror(errno));973return (-1);974}975976bp->priv_set = priv_set;977978return (0);979}980981982/*983* Add a bridge member port.984*/985int986bridge_port_addm(struct bridge_port *bp, const char *b_name)987{988struct ifdrv ifd;989struct ifbreq b_req;990991bzero(&ifd, sizeof(ifd));992bzero(&b_req, sizeof(b_req));993994strlcpy(ifd.ifd_name, b_name, sizeof(ifd.ifd_name));995ifd.ifd_len = sizeof(b_req);996ifd.ifd_data = &b_req;997strlcpy(b_req.ifbr_ifsname, bp->p_name, sizeof(b_req.ifbr_ifsname));998999if (bp->span_enable == begemotBridgeBaseSpanEnabled_enabled)1000ifd.ifd_cmd = BRDGADDS;1001else1002ifd.ifd_cmd = BRDGADD;10031004if (ioctl(sock, SIOCSDRVSPEC, &ifd) < 0) {1005syslog(LOG_ERR, "%s - add member : ioctl(%s) failed: %s",1006bp->p_name,1007(ifd.ifd_cmd == BRDGADDS ? "BRDGADDS" : "BRDGADD"),1008strerror(errno));1009return (-1);1010}10111012return (0);1013}10141015/*1016* Delete a bridge member port.1017*/1018int1019bridge_port_delm(struct bridge_port *bp, const char *b_name)1020{1021struct ifdrv ifd;1022struct ifbreq b_req;10231024bzero(&ifd, sizeof(ifd));1025bzero(&b_req, sizeof(b_req));10261027strlcpy(ifd.ifd_name, b_name, sizeof(ifd.ifd_name));1028ifd.ifd_len = sizeof(b_req);1029ifd.ifd_data = &b_req;1030strlcpy(b_req.ifbr_ifsname, bp->p_name, sizeof(b_req.ifbr_ifsname));10311032if (bp->span_enable == begemotBridgeBaseSpanEnabled_enabled)1033ifd.ifd_cmd = BRDGDELS;1034else1035ifd.ifd_cmd = BRDGDEL;10361037if (ioctl(sock, SIOCSDRVSPEC, &ifd) < 0) {1038syslog(LOG_ERR, "%s - add member : ioctl(%s) failed: %s",1039bp->p_name,1040(ifd.ifd_cmd == BRDGDELS ? "BRDGDELS" : "BRDGDEL"),1041strerror(errno));1042return (-1);1043}10441045return (0);1046}10471048/*1049* Fetch the bridge member list from kernel.1050* Return -1 on error, or buffer len if successful.1051*/1052static int32_t1053bridge_port_get_iflist(struct bridge_if *bif, struct ifbreq **buf)1054{1055int n = 128;1056uint32_t len;1057struct ifbreq *ninbuf;1058struct ifbifconf ifbc;1059struct ifdrv ifd;10601061*buf = NULL;1062strlcpy(ifd.ifd_name, bif->bif_name, IFNAMSIZ);1063ifd.ifd_cmd = BRDGGIFS;1064ifd.ifd_len = sizeof(ifbc);1065ifd.ifd_data = &ifbc;10661067for ( ; ; ) {1068len = n * sizeof(struct ifbreq);1069if ((ninbuf = (struct ifbreq *)realloc(*buf, len)) == NULL) {1070syslog(LOG_ERR, "get bridge member list: "1071"realloc failed: %s", strerror(errno));1072free(*buf);1073*buf = NULL;1074return (-1);1075}10761077ifbc.ifbic_len = len;1078ifbc.ifbic_req = *buf = ninbuf;10791080if (ioctl(sock, SIOCGDRVSPEC, &ifd) < 0) {1081syslog(LOG_ERR, "get bridge member list: ioctl "1082"(BRDGGIFS) failed: %s", strerror(errno));1083free(*buf);1084buf = NULL;1085return (-1);1086}10871088if ((ifbc.ifbic_len + sizeof(struct ifbreq)) < len)1089break;10901091n += 64;1092}10931094return (ifbc.ifbic_len);1095}10961097/*1098* Fetch the bridge STP member list from kernel.1099* Return -1 on error, or buffer len if successful.1100*/1101static int32_t1102bridge_port_get_ifstplist(struct bridge_if *bif, struct ifbpstpreq **buf)1103{1104int n = 128;1105uint32_t len;1106struct ifbpstpreq *ninbuf;1107struct ifbpstpconf ifbstp;1108struct ifdrv ifd;11091110*buf = NULL;1111strlcpy(ifd.ifd_name, bif->bif_name, IFNAMSIZ);1112ifd.ifd_cmd = BRDGGIFSSTP;1113ifd.ifd_len = sizeof(ifbstp);1114ifd.ifd_data = &ifbstp;11151116for ( ; ; ) {1117len = n * sizeof(struct ifbpstpreq);1118if ((ninbuf = (struct ifbpstpreq *)1119realloc(*buf, len)) == NULL) {1120syslog(LOG_ERR, "get bridge STP ports list: "1121"realloc failed: %s", strerror(errno));1122free(*buf);1123*buf = NULL;1124return (-1);1125}11261127ifbstp.ifbpstp_len = len;1128ifbstp.ifbpstp_req = *buf = ninbuf;11291130if (ioctl(sock, SIOCGDRVSPEC, &ifd) < 0) {1131syslog(LOG_ERR, "get bridge STP ports list: ioctl "1132"(BRDGGIFSSTP) failed: %s", strerror(errno));1133free(*buf);1134buf = NULL;1135return (-1);1136}11371138if ((ifbstp.ifbpstp_len + sizeof(struct ifbpstpreq)) < len)1139break;11401141n += 64;1142}11431144return (ifbstp.ifbpstp_len);1145}11461147/*1148* Locate a bridge if STP params structure in a buffer.1149*/1150static struct ifbpstpreq *1151bridge_port_find_ifstplist(uint8_t port_no, struct ifbpstpreq *buf,1152uint32_t buf_len)1153{1154uint32_t i;1155struct ifbpstpreq *bstp;11561157for (i = 0; i < buf_len / sizeof(struct ifbpstpreq); i++) {1158bstp = buf + i;1159if (bstp->ifbp_portno == port_no)1160return (bstp);1161}11621163return (NULL);1164}11651166/*1167* Read the initial info for all members of a bridge interface.1168* Returns the number of ports, 0 - if none, otherwise1169* -1 if some other error occurred.1170*/1171int1172bridge_getinfo_bif_ports(struct bridge_if *bif)1173{1174uint32_t i;1175int32_t buf_len;1176struct ifbreq *b_req_buf, *b_req;1177struct ifbpstpreq *bs_req_buf, *bs_req;1178struct bridge_port *bp;1179struct mibif *m_if;11801181if ((buf_len = bridge_port_get_iflist(bif, &b_req_buf)) < 0)1182return (-1);11831184for (i = 0; i < buf_len / sizeof(struct ifbreq); i++) {1185b_req = b_req_buf + i;11861187if ((m_if = mib_find_if_sys(b_req->ifbr_portno)) != NULL) {1188/* Hopefully we will not fail here. */1189if ((bp = bridge_new_port(m_if, bif)) != NULL) {1190bp->status = RowStatus_active;1191bridge_port_getinfo_conf(b_req, bp);1192bridge_port_getinfo_mibif(m_if, bp);1193}1194} else {1195syslog(LOG_ERR, "bridge member %s not present "1196"in mibII ifTable", b_req->ifbr_ifsname);1197}1198}1199free(b_req_buf);12001201if ((buf_len = bridge_port_get_ifstplist(bif, &bs_req_buf)) < 0)1202return (-1);12031204for (bp = bridge_port_bif_first(bif); bp != NULL;1205bp = bridge_port_bif_next(bp)) {1206if ((bs_req = bridge_port_find_ifstplist(bp->port_no,1207bs_req_buf, buf_len)) == NULL)1208bridge_port_clearinfo_opstp(bp);1209else1210bridge_port_getinfo_opstp(bs_req, bp);1211}1212free(bs_req_buf);12131214return (i);1215}12161217/*1218* Update the information for the bridge interface members.1219*/1220int1221bridge_update_memif(struct bridge_if *bif)1222{1223int updated;1224uint32_t i;1225int32_t buf_len;1226struct ifbreq *b_req_buf, *b_req;1227struct ifbpstpreq *bs_req_buf, *bs_req;1228struct bridge_port *bp, *bp_next;1229struct mibif *m_if;12301231if ((buf_len = bridge_port_get_iflist(bif, &b_req_buf)) < 0)1232return (-1);12331234updated = 0;12351236#define BP_FOUND 0x011237for (i = 0; i < buf_len / sizeof(struct ifbreq); i++) {1238b_req = b_req_buf + i;12391240if ((m_if = mib_find_if_sys(b_req->ifbr_portno)) == NULL) {1241syslog(LOG_ERR, "bridge member %s not present "1242"in mibII ifTable", b_req->ifbr_ifsname);1243continue;1244}12451246if ((bp = bridge_port_find(m_if->index, bif)) == NULL &&1247(bp = bridge_new_port(m_if, bif)) != NULL) {1248bp->status = RowStatus_active;1249}12501251if (bp != NULL) {1252updated++;1253bridge_port_getinfo_conf(b_req, bp);1254bridge_port_getinfo_mibif(m_if, bp);1255bp->flags |= BP_FOUND;1256}1257}1258free(b_req_buf);12591260/* Clean up list. */1261for (bp = bridge_port_bif_first(bif); bp != NULL; bp = bp_next) {1262bp_next = bridge_port_bif_next(bp);12631264if ((bp->flags & BP_FOUND) == 0 &&1265bp->status == RowStatus_active)1266bridge_port_remove(bp, bif);1267else1268bp->flags |= ~BP_FOUND;1269}1270#undef BP_FOUND12711272if ((buf_len = bridge_port_get_ifstplist(bif, &bs_req_buf)) < 0)1273return (-1);12741275for (bp = bridge_port_bif_first(bif); bp != NULL;1276bp = bridge_port_bif_next(bp)) {1277if ((bs_req = bridge_port_find_ifstplist(bp->port_no,1278bs_req_buf, buf_len)) == NULL)1279bridge_port_clearinfo_opstp(bp);1280else1281bridge_port_getinfo_opstp(bs_req, bp);1282}1283free(bs_req_buf);1284bif->ports_age = time(NULL);12851286return (updated);1287}12881289/************************************************************************1290* Bridge addresses.1291*/12921293/*1294* Update the bridge address info according to the polled data.1295*/1296static void1297bridge_addrs_info_ifaddrlist(struct ifbareq *ifba, struct tp_entry *tpe)1298{1299tpe->port_no = if_nametoindex(ifba->ifba_ifsname);13001301if ((ifba->ifba_flags & IFBAF_TYPEMASK) == IFBAF_STATIC)1302tpe->status = TpFdbStatus_mgmt;1303else1304tpe->status = TpFdbStatus_learned;1305}13061307/*1308* Read the bridge addresses from kernel.1309* Return -1 on error, or buffer len if successful.1310*/1311static int32_t1312bridge_addrs_getinfo_ifalist(struct bridge_if *bif, struct ifbareq **buf)1313{1314int n = 128;1315uint32_t len;1316struct ifbareq *ninbuf;1317struct ifbaconf bac;1318struct ifdrv ifd;13191320*buf = NULL;1321strlcpy(ifd.ifd_name, bif->bif_name, IFNAMSIZ);1322ifd.ifd_cmd = BRDGRTS;1323ifd.ifd_len = sizeof(bac);1324ifd.ifd_data = &bac;13251326for ( ; ; ) {1327len = n * sizeof(struct ifbareq);1328if ((ninbuf = (struct ifbareq *)realloc(*buf, len)) == NULL) {1329syslog(LOG_ERR, "get bridge address list: "1330" realloc failed: %s", strerror(errno));1331free(*buf);1332*buf = NULL;1333return (-1);1334}13351336bac.ifbac_len = len;1337bac.ifbac_req = *buf = ninbuf;13381339if (ioctl(sock, SIOCGDRVSPEC, &ifd) < 0) {1340syslog(LOG_ERR, "get bridge address list: "1341"ioctl(BRDGRTS) failed: %s", strerror(errno));1342free(*buf);1343buf = NULL;1344return (-1);1345}13461347if ((bac.ifbac_len + sizeof(struct ifbareq)) < len)1348break;13491350n += 64;1351}13521353return (bac.ifbac_len);1354}13551356/*1357* Read the initial info for all addresses on a bridge interface.1358* Returns the number of addresses, 0 - if none, otherwise1359* -1 if some other error occurred.1360*/1361int1362bridge_getinfo_bif_addrs(struct bridge_if *bif)1363{1364uint32_t i;1365int32_t buf_len;1366struct ifbareq *addr_req_buf, *addr_req;1367struct tp_entry *te;13681369if ((buf_len = bridge_addrs_getinfo_ifalist(bif, &addr_req_buf)) < 0)1370return (-1);13711372for (i = 0; i < buf_len / sizeof(struct ifbareq); i++) {1373addr_req = addr_req_buf + i;13741375if ((te = bridge_new_addrs(addr_req->ifba_dst, bif)) != NULL)1376bridge_addrs_info_ifaddrlist(addr_req, te);1377}13781379free(addr_req_buf);1380return (i);1381}13821383/*1384* Update the addresses for the bridge interface.1385*/1386int1387bridge_update_addrs(struct bridge_if *bif)1388{1389int added, updated;1390uint32_t i;1391int32_t buf_len;1392struct tp_entry *te, *te_next;1393struct ifbareq *addr_req_buf, *addr_req;13941395if ((buf_len = bridge_addrs_getinfo_ifalist(bif, &addr_req_buf)) < 0)1396return (-1);13971398added = updated = 0;13991400#define BA_FOUND 0x011401for (i = 0; i < buf_len / sizeof(struct ifbareq); i++) {1402addr_req = addr_req_buf + i;14031404if ((te = bridge_addrs_find(addr_req->ifba_dst, bif)) == NULL) {1405added++;14061407if ((te = bridge_new_addrs(addr_req->ifba_dst, bif))1408== NULL)1409continue;1410} else1411updated++;14121413bridge_addrs_info_ifaddrlist(addr_req, te);1414te-> flags |= BA_FOUND;1415}1416free(addr_req_buf);14171418for (te = bridge_addrs_bif_first(bif); te != NULL; te = te_next) {1419te_next = bridge_addrs_bif_next(te);14201421if ((te-> flags & BA_FOUND) == 0)1422bridge_addrs_remove(te, bif);1423else1424te-> flags &= ~BA_FOUND;1425}1426#undef BA_FOUND14271428bif->addrs_age = time(NULL);1429return (updated + added);1430}14311432/************************************************************************1433* Bridge packet filtering.1434*/1435const char bridge_sysctl[] = "net.link.bridge.";14361437static struct {1438int32_t val;1439const char *name;1440} bridge_pf_sysctl[] = {1441{ 1, "pfil_bridge" },1442{ 1, "pfil_member" },1443{ 1, "pfil_onlyip" },1444{ 0, "ipfw" },1445};14461447int32_t1448bridge_get_pfval(uint8_t which)1449{14501451if (which > nitems(bridge_pf_sysctl) || which < 1)1452return (-1);14531454return (bridge_pf_sysctl[which - 1].val);1455}14561457int32_t1458bridge_do_pfctl(int32_t bridge_ctl, enum snmp_op op, int32_t *val)1459{1460char *mib_oid;1461size_t len, s_len;1462int32_t i, s_i;14631464if (bridge_ctl >= LEAF_begemotBridgeLayer2PfStatus)1465return (-2);14661467if (op == SNMP_OP_SET) {1468s_i = *val;1469s_len = sizeof(s_i);1470} else1471s_len = 0;14721473len = sizeof(i);14741475asprintf(&mib_oid, "%s%s", bridge_sysctl,1476bridge_pf_sysctl[bridge_ctl].name);1477if (mib_oid == NULL)1478return (-1);14791480if (sysctlbyname(mib_oid, &i, &len, (op == SNMP_OP_SET ? &s_i : NULL),1481s_len) == -1) {1482syslog(LOG_ERR, "sysctl(%s) failed - %s", mib_oid,1483strerror(errno));1484free(mib_oid);1485return (-1);1486}14871488bridge_pf_sysctl[bridge_ctl].val = i;1489*val = i;14901491free(mib_oid);14921493return (i);1494}14951496void1497bridge_pf_dump(void)1498{1499uint8_t i;15001501for (i = 0; i < nitems(bridge_pf_sysctl); i++) {1502syslog(LOG_ERR, "%s%s = %d", bridge_sysctl,1503bridge_pf_sysctl[i].name, bridge_pf_sysctl[i].val);1504}1505}150615071508