Path: blob/main/sys/netgraph/bluetooth/l2cap/ng_l2cap_evnt.c
34876 views
/*1* ng_l2cap_evnt.c2*/34/*-5* SPDX-License-Identifier: BSD-2-Clause6*7* Copyright (c) Maksim Yevmenkin <[email protected]>8* All rights reserved.9*10* Redistribution and use in source and binary forms, with or without11* modification, are permitted provided that the following conditions12* are met:13* 1. Redistributions of source code must retain the above copyright14* notice, this list of conditions and the following disclaimer.15* 2. Redistributions in binary form must reproduce the above copyright16* notice, this list of conditions and the following disclaimer in the17* documentation and/or other materials provided with the distribution.18*19* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND20* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE21* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE22* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE23* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL24* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS25* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)26* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT27* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY28* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF29* SUCH DAMAGE.30*31* $Id: ng_l2cap_evnt.c,v 1.5 2003/09/08 19:11:45 max Exp $32*/3334#include <sys/param.h>35#include <sys/systm.h>36#include <sys/kernel.h>37#include <sys/endian.h>38#include <sys/malloc.h>39#include <sys/mbuf.h>40#include <sys/queue.h>41#include <netgraph/ng_message.h>42#include <netgraph/netgraph.h>43#include <netgraph/bluetooth/include/ng_bluetooth.h>44#include <netgraph/bluetooth/include/ng_hci.h>45#include <netgraph/bluetooth/include/ng_l2cap.h>46#include <netgraph/bluetooth/l2cap/ng_l2cap_var.h>47#include <netgraph/bluetooth/l2cap/ng_l2cap_cmds.h>48#include <netgraph/bluetooth/l2cap/ng_l2cap_evnt.h>49#include <netgraph/bluetooth/l2cap/ng_l2cap_llpi.h>50#include <netgraph/bluetooth/l2cap/ng_l2cap_ulpi.h>51#include <netgraph/bluetooth/l2cap/ng_l2cap_misc.h>5253/******************************************************************************54******************************************************************************55** L2CAP events processing module56******************************************************************************57******************************************************************************/5859static int ng_l2cap_process_signal_cmd (ng_l2cap_con_p);60static int ng_l2cap_process_lesignal_cmd (ng_l2cap_con_p);61static int ng_l2cap_process_cmd_rej (ng_l2cap_con_p, u_int8_t);62static int ng_l2cap_process_cmd_urq (ng_l2cap_con_p, u_int8_t);63static int ng_l2cap_process_cmd_urs (ng_l2cap_con_p, u_int8_t);64static int ng_l2cap_process_con_req (ng_l2cap_con_p, u_int8_t);65static int ng_l2cap_process_con_rsp (ng_l2cap_con_p, u_int8_t);66static int ng_l2cap_process_cfg_req (ng_l2cap_con_p, u_int8_t);67static int ng_l2cap_process_cfg_rsp (ng_l2cap_con_p, u_int8_t);68static int ng_l2cap_process_discon_req (ng_l2cap_con_p, u_int8_t);69static int ng_l2cap_process_discon_rsp (ng_l2cap_con_p, u_int8_t);70static int ng_l2cap_process_echo_req (ng_l2cap_con_p, u_int8_t);71static int ng_l2cap_process_echo_rsp (ng_l2cap_con_p, u_int8_t);72static int ng_l2cap_process_info_req (ng_l2cap_con_p, u_int8_t);73static int ng_l2cap_process_info_rsp (ng_l2cap_con_p, u_int8_t);74static int send_l2cap_reject75(ng_l2cap_con_p, u_int8_t, u_int16_t, u_int16_t, u_int16_t, u_int16_t);76static int send_l2cap_con_rej77(ng_l2cap_con_p, u_int8_t, u_int16_t, u_int16_t, u_int16_t);78static int send_l2cap_cfg_rsp79(ng_l2cap_con_p, u_int8_t, u_int16_t, u_int16_t, struct mbuf *);80static int send_l2cap_param_urs81(ng_l2cap_con_p , u_int8_t , u_int16_t);8283static int get_next_l2cap_opt84(struct mbuf *, int *, ng_l2cap_cfg_opt_p, ng_l2cap_cfg_opt_val_p);8586/*87* Receive L2CAP packet. First get L2CAP header and verify packet. Than88* get destination channel and process packet.89*/9091int92ng_l2cap_receive(ng_l2cap_con_p con)93{94ng_l2cap_p l2cap = con->l2cap;95ng_l2cap_hdr_t *hdr = NULL;96int error = 0;9798/* Check packet */99if (con->rx_pkt->m_pkthdr.len < sizeof(*hdr)) {100NG_L2CAP_ERR(101"%s: %s - invalid L2CAP packet. Packet too small, len=%d\n",102__func__, NG_NODE_NAME(l2cap->node),103con->rx_pkt->m_pkthdr.len);104error = EMSGSIZE;105goto drop;106}107108/* Get L2CAP header */109NG_L2CAP_M_PULLUP(con->rx_pkt, sizeof(*hdr));110if (con->rx_pkt == NULL)111return (ENOBUFS);112113hdr = mtod(con->rx_pkt, ng_l2cap_hdr_t *);114hdr->length = le16toh(hdr->length);115hdr->dcid = le16toh(hdr->dcid);116117/* Check payload size */118if (hdr->length != con->rx_pkt->m_pkthdr.len - sizeof(*hdr)) {119NG_L2CAP_ERR(120"%s: %s - invalid L2CAP packet. Payload length mismatch, length=%d, len=%zd\n",121__func__, NG_NODE_NAME(l2cap->node), hdr->length,122con->rx_pkt->m_pkthdr.len - sizeof(*hdr));123error = EMSGSIZE;124goto drop;125}126127/* Process packet */128switch (hdr->dcid) {129case NG_L2CAP_SIGNAL_CID: /* L2CAP command */130m_adj(con->rx_pkt, sizeof(*hdr));131error = ng_l2cap_process_signal_cmd(con);132break;133case NG_L2CAP_LESIGNAL_CID:134m_adj(con->rx_pkt, sizeof(*hdr));135error = ng_l2cap_process_lesignal_cmd(con);136break;137case NG_L2CAP_CLT_CID: /* Connectionless packet */138error = ng_l2cap_l2ca_clt_receive(con);139break;140141default: /* Data packet */142error = ng_l2cap_l2ca_receive(con);143break;144}145146return (error);147drop:148NG_FREE_M(con->rx_pkt);149150return (error);151} /* ng_l2cap_receive */152153/*154* Process L2CAP signaling command. We already know that destination channel ID155* is 0x1 that means we have received signaling command from peer's L2CAP layer.156* So get command header, decode and process it.157*158* XXX do we need to check signaling MTU here?159*/160161static int162ng_l2cap_process_signal_cmd(ng_l2cap_con_p con)163{164ng_l2cap_p l2cap = con->l2cap;165ng_l2cap_cmd_hdr_t *hdr = NULL;166struct mbuf *m = NULL;167168while (con->rx_pkt != NULL) {169/* Verify packet length */170if (con->rx_pkt->m_pkthdr.len < sizeof(*hdr)) {171NG_L2CAP_ERR(172"%s: %s - invalid L2CAP signaling command. Packet too small, len=%d\n",173__func__, NG_NODE_NAME(l2cap->node),174con->rx_pkt->m_pkthdr.len);175NG_FREE_M(con->rx_pkt);176177return (EMSGSIZE);178}179180/* Get signaling command */181NG_L2CAP_M_PULLUP(con->rx_pkt, sizeof(*hdr));182if (con->rx_pkt == NULL)183return (ENOBUFS);184185hdr = mtod(con->rx_pkt, ng_l2cap_cmd_hdr_t *);186hdr->length = le16toh(hdr->length);187m_adj(con->rx_pkt, sizeof(*hdr));188189/* Verify command length */190if (con->rx_pkt->m_pkthdr.len < hdr->length) {191NG_L2CAP_ERR(192"%s: %s - invalid L2CAP signaling command, code=%#x, ident=%d. " \193"Invalid command length=%d, m_pkthdr.len=%d\n",194__func__, NG_NODE_NAME(l2cap->node),195hdr->code, hdr->ident, hdr->length,196con->rx_pkt->m_pkthdr.len);197NG_FREE_M(con->rx_pkt);198199return (EMSGSIZE);200}201202/* Get the command, save the rest (if any) */203if (con->rx_pkt->m_pkthdr.len > hdr->length)204m = m_split(con->rx_pkt, hdr->length, M_NOWAIT);205else206m = NULL;207208/* Process command */209switch (hdr->code) {210case NG_L2CAP_CMD_REJ:211ng_l2cap_process_cmd_rej(con, hdr->ident);212break;213214case NG_L2CAP_CON_REQ:215ng_l2cap_process_con_req(con, hdr->ident);216break;217218case NG_L2CAP_CON_RSP:219ng_l2cap_process_con_rsp(con, hdr->ident);220break;221222case NG_L2CAP_CFG_REQ:223ng_l2cap_process_cfg_req(con, hdr->ident);224break;225226case NG_L2CAP_CFG_RSP:227ng_l2cap_process_cfg_rsp(con, hdr->ident);228break;229230case NG_L2CAP_DISCON_REQ:231ng_l2cap_process_discon_req(con, hdr->ident);232break;233234case NG_L2CAP_DISCON_RSP:235ng_l2cap_process_discon_rsp(con, hdr->ident);236break;237238case NG_L2CAP_ECHO_REQ:239ng_l2cap_process_echo_req(con, hdr->ident);240break;241242case NG_L2CAP_ECHO_RSP:243ng_l2cap_process_echo_rsp(con, hdr->ident);244break;245246case NG_L2CAP_INFO_REQ:247ng_l2cap_process_info_req(con, hdr->ident);248break;249250case NG_L2CAP_INFO_RSP:251ng_l2cap_process_info_rsp(con, hdr->ident);252break;253254default:255NG_L2CAP_ERR(256"%s: %s - unknown L2CAP signaling command, code=%#x, ident=%d, length=%d\n",257__func__, NG_NODE_NAME(l2cap->node),258hdr->code, hdr->ident, hdr->length);259260/*261* Send L2CAP_CommandRej. Do not really care262* about the result263*/264265send_l2cap_reject(con, hdr->ident,266NG_L2CAP_REJ_NOT_UNDERSTOOD, 0, 0, 0);267NG_FREE_M(con->rx_pkt);268break;269}270271con->rx_pkt = m;272}273274return (0);275} /* ng_l2cap_process_signal_cmd */276static int277ng_l2cap_process_lesignal_cmd(ng_l2cap_con_p con)278{279ng_l2cap_p l2cap = con->l2cap;280ng_l2cap_cmd_hdr_t *hdr = NULL;281struct mbuf *m = NULL;282283while (con->rx_pkt != NULL) {284/* Verify packet length */285if (con->rx_pkt->m_pkthdr.len < sizeof(*hdr)) {286NG_L2CAP_ERR(287"%s: %s - invalid L2CAP signaling command. Packet too small, len=%d\n",288__func__, NG_NODE_NAME(l2cap->node),289con->rx_pkt->m_pkthdr.len);290NG_FREE_M(con->rx_pkt);291292return (EMSGSIZE);293}294295/* Get signaling command */296NG_L2CAP_M_PULLUP(con->rx_pkt, sizeof(*hdr));297if (con->rx_pkt == NULL)298return (ENOBUFS);299300hdr = mtod(con->rx_pkt, ng_l2cap_cmd_hdr_t *);301hdr->length = le16toh(hdr->length);302m_adj(con->rx_pkt, sizeof(*hdr));303304/* Verify command length */305if (con->rx_pkt->m_pkthdr.len < hdr->length) {306NG_L2CAP_ERR(307"%s: %s - invalid L2CAP signaling command, code=%#x, ident=%d. " \308"Invalid command length=%d, m_pkthdr.len=%d\n",309__func__, NG_NODE_NAME(l2cap->node),310hdr->code, hdr->ident, hdr->length,311con->rx_pkt->m_pkthdr.len);312NG_FREE_M(con->rx_pkt);313314return (EMSGSIZE);315}316317/* Get the command, save the rest (if any) */318if (con->rx_pkt->m_pkthdr.len > hdr->length)319m = m_split(con->rx_pkt, hdr->length, M_NOWAIT);320else321m = NULL;322323/* Process command */324switch (hdr->code) {325case NG_L2CAP_CMD_REJ:326ng_l2cap_process_cmd_rej(con, hdr->ident);327break;328case NG_L2CAP_CMD_PARAM_UPDATE_REQUEST:329ng_l2cap_process_cmd_urq(con, hdr->ident);330break;331case NG_L2CAP_CMD_PARAM_UPDATE_RESPONSE:332ng_l2cap_process_cmd_urs(con, hdr->ident);333break;334335336default:337NG_L2CAP_ERR(338"%s: %s - unknown L2CAP signaling command, code=%#x, ident=%d, length=%d\n",339__func__, NG_NODE_NAME(l2cap->node),340hdr->code, hdr->ident, hdr->length);341342/*343* Send L2CAP_CommandRej. Do not really care344* about the result345*/346347send_l2cap_reject(con, hdr->ident,348NG_L2CAP_REJ_NOT_UNDERSTOOD, 0, 0, 0);349NG_FREE_M(con->rx_pkt);350break;351}352353con->rx_pkt = m;354}355356return (0);357} /* ng_l2cap_process_signal_cmd */358/*Update Paramater Request*/359static int ng_l2cap_process_cmd_urq(ng_l2cap_con_p con, uint8_t ident)360{361/* We do not implement parameter negotiation for now. */362send_l2cap_param_urs(con, ident, NG_L2CAP_UPDATE_PARAM_ACCEPT);363NG_FREE_M(con->rx_pkt);364return 0;365}366367static int ng_l2cap_process_cmd_urs(ng_l2cap_con_p con, uint8_t ident)368{369/* We only support master side yet .*/370//send_l2cap_reject(con,ident ... );371372NG_FREE_M(con->rx_pkt);373return 0;374}375376/*377* Process L2CAP_CommandRej command378*/379380static int381ng_l2cap_process_cmd_rej(ng_l2cap_con_p con, u_int8_t ident)382{383ng_l2cap_p l2cap = con->l2cap;384ng_l2cap_cmd_rej_cp *cp = NULL;385ng_l2cap_cmd_p cmd = NULL;386387/* Get command parameters */388NG_L2CAP_M_PULLUP(con->rx_pkt, sizeof(*cp));389if (con->rx_pkt == NULL)390return (ENOBUFS);391392cp = mtod(con->rx_pkt, ng_l2cap_cmd_rej_cp *);393cp->reason = le16toh(cp->reason);394395/* Check if we have pending command descriptor */396cmd = ng_l2cap_cmd_by_ident(con, ident);397if (cmd != NULL) {398/* If command timeout already happened then ignore reject */399if (ng_l2cap_command_untimeout(cmd) != 0) {400NG_FREE_M(con->rx_pkt);401return (ETIMEDOUT);402}403404ng_l2cap_unlink_cmd(cmd);405406switch (cmd->code) {407case NG_L2CAP_CON_REQ:408ng_l2cap_l2ca_con_rsp(cmd->ch,cmd->token,cp->reason,0);409ng_l2cap_free_chan(cmd->ch);410break;411412case NG_L2CAP_CFG_REQ:413ng_l2cap_l2ca_cfg_rsp(cmd->ch, cmd->token, cp->reason);414break;415416case NG_L2CAP_DISCON_REQ:417ng_l2cap_l2ca_discon_rsp(cmd->ch,cmd->token,cp->reason);418ng_l2cap_free_chan(cmd->ch); /* XXX free channel */419break;420421case NG_L2CAP_ECHO_REQ:422ng_l2cap_l2ca_ping_rsp(cmd->con, cmd->token,423cp->reason, NULL);424break;425426case NG_L2CAP_INFO_REQ:427ng_l2cap_l2ca_get_info_rsp(cmd->con, cmd->token,428cp->reason, NULL);429break;430431default:432NG_L2CAP_ALERT(433"%s: %s - unexpected L2CAP_CommandRej. Unexpected L2CAP command opcode=%d\n",434__func__, NG_NODE_NAME(l2cap->node), cmd->code);435break;436}437438ng_l2cap_free_cmd(cmd);439} else440NG_L2CAP_ERR(441"%s: %s - unexpected L2CAP_CommandRej command. " \442"Requested ident does not exist, ident=%d\n",443__func__, NG_NODE_NAME(l2cap->node), ident);444445NG_FREE_M(con->rx_pkt);446447return (0);448} /* ng_l2cap_process_cmd_rej */449450/*451* Process L2CAP_ConnectReq command452*/453454static int455ng_l2cap_process_con_req(ng_l2cap_con_p con, u_int8_t ident)456{457ng_l2cap_p l2cap = con->l2cap;458struct mbuf *m = con->rx_pkt;459ng_l2cap_con_req_cp *cp = NULL;460ng_l2cap_chan_p ch = NULL;461int error = 0;462u_int16_t dcid, psm;463int idtype;464465/* Get command parameters */466NG_L2CAP_M_PULLUP(m, sizeof(*cp));467if (m == NULL)468return (ENOBUFS);469470cp = mtod(m, ng_l2cap_con_req_cp *);471psm = le16toh(cp->psm);472dcid = le16toh(cp->scid);473474NG_FREE_M(m);475con->rx_pkt = NULL;476if(dcid == NG_L2CAP_ATT_CID)477idtype = NG_L2CAP_L2CA_IDTYPE_ATT;478else if(dcid == NG_L2CAP_SMP_CID)479idtype = NG_L2CAP_L2CA_IDTYPE_SMP;480else if( con->linktype != NG_HCI_LINK_ACL)481idtype = NG_L2CAP_L2CA_IDTYPE_LE;482else483idtype = NG_L2CAP_L2CA_IDTYPE_BREDR;484485/*486* Create new channel and send L2CA_ConnectInd notification487* to the upper layer protocol.488*/489490ch = ng_l2cap_new_chan(l2cap, con, psm, idtype);491492if (ch == NULL)493return (send_l2cap_con_rej(con, ident, 0, dcid,494NG_L2CAP_NO_RESOURCES));495496/* Update channel IDs */497ch->dcid = dcid;498499/* Sent L2CA_ConnectInd notification to the upper layer */500ch->ident = ident;501ch->state = NG_L2CAP_W4_L2CA_CON_RSP;502503error = ng_l2cap_l2ca_con_ind(ch);504if (error != 0) {505send_l2cap_con_rej(con, ident, ch->scid, dcid,506(error == ENOMEM)? NG_L2CAP_NO_RESOURCES :507NG_L2CAP_PSM_NOT_SUPPORTED);508ng_l2cap_free_chan(ch);509}510511return (error);512} /* ng_l2cap_process_con_req */513514/*515* Process L2CAP_ConnectRsp command516*/517518static int519ng_l2cap_process_con_rsp(ng_l2cap_con_p con, u_int8_t ident)520{521ng_l2cap_p l2cap = con->l2cap;522struct mbuf *m = con->rx_pkt;523ng_l2cap_con_rsp_cp *cp = NULL;524ng_l2cap_cmd_p cmd = NULL;525u_int16_t scid, dcid, result, status;526int error = 0;527528/* Get command parameters */529NG_L2CAP_M_PULLUP(m, sizeof(*cp));530if (m == NULL)531return (ENOBUFS);532533cp = mtod(m, ng_l2cap_con_rsp_cp *);534dcid = le16toh(cp->dcid);535scid = le16toh(cp->scid);536result = le16toh(cp->result);537status = le16toh(cp->status);538539NG_FREE_M(m);540con->rx_pkt = NULL;541542/* Check if we have pending command descriptor */543cmd = ng_l2cap_cmd_by_ident(con, ident);544if (cmd == NULL) {545NG_L2CAP_ERR(546"%s: %s - unexpected L2CAP_ConnectRsp command. ident=%d, con_handle=%d\n",547__func__, NG_NODE_NAME(l2cap->node), ident,548con->con_handle);549550return (ENOENT);551}552553/* Verify channel state, if invalid - do nothing */554if (cmd->ch->state != NG_L2CAP_W4_L2CAP_CON_RSP) {555NG_L2CAP_ERR(556"%s: %s - unexpected L2CAP_ConnectRsp. " \557"Invalid channel state, cid=%d, state=%d\n",558__func__, NG_NODE_NAME(l2cap->node), scid,559cmd->ch->state);560goto reject;561}562563/* Verify CIDs and send reject if does not match */564if (cmd->ch->scid != scid) {565NG_L2CAP_ERR(566"%s: %s - unexpected L2CAP_ConnectRsp. Channel IDs do not match, scid=%d(%d)\n",567__func__, NG_NODE_NAME(l2cap->node), cmd->ch->scid,568scid);569goto reject;570}571572/*573* Looks good. We got confirmation from our peer. Now process574* it. First disable RTX timer. Then check the result and send575* notification to the upper layer. If command timeout already576* happened then ignore response.577*/578579if ((error = ng_l2cap_command_untimeout(cmd)) != 0)580return (error);581582if (result == NG_L2CAP_PENDING) {583/*584* Our peer wants more time to complete connection. We shall585* start ERTX timer and wait. Keep command in the list.586*/587588cmd->ch->dcid = dcid;589ng_l2cap_command_timeout(cmd, bluetooth_l2cap_ertx_timeout());590591error = ng_l2cap_l2ca_con_rsp(cmd->ch, cmd->token,592result, status);593if (error != 0)594ng_l2cap_free_chan(cmd->ch);595} else {596ng_l2cap_unlink_cmd(cmd);597598if (result == NG_L2CAP_SUCCESS) {599/*600* Channel is open. Complete command and move to CONFIG601* state. Since we have sent positive confirmation we602* expect to receive L2CA_Config request from the upper603* layer protocol.604*/605606cmd->ch->dcid = dcid;607cmd->ch->state = ((cmd->ch->scid == NG_L2CAP_ATT_CID)||608(cmd->ch->scid == NG_L2CAP_SMP_CID))609?610NG_L2CAP_OPEN : NG_L2CAP_CONFIG;611} else612/* There was an error, so close the channel */613NG_L2CAP_INFO(614"%s: %s - failed to open L2CAP channel, result=%d, status=%d\n",615__func__, NG_NODE_NAME(l2cap->node), result,616status);617618error = ng_l2cap_l2ca_con_rsp(cmd->ch, cmd->token,619result, status);620621/* XXX do we have to remove the channel on error? */622if (error != 0 || result != NG_L2CAP_SUCCESS)623ng_l2cap_free_chan(cmd->ch);624625ng_l2cap_free_cmd(cmd);626}627628return (error);629630reject:631/* Send reject. Do not really care about the result */632send_l2cap_reject(con, ident, NG_L2CAP_REJ_INVALID_CID, 0, scid, dcid);633634return (0);635} /* ng_l2cap_process_con_rsp */636637/*638* Process L2CAP_ConfigReq command639*/640641static int642ng_l2cap_process_cfg_req(ng_l2cap_con_p con, u_int8_t ident)643{644ng_l2cap_p l2cap = con->l2cap;645struct mbuf *m = con->rx_pkt;646ng_l2cap_cfg_req_cp *cp = NULL;647ng_l2cap_chan_p ch = NULL;648u_int16_t dcid, respond, result;649ng_l2cap_cfg_opt_t hdr;650ng_l2cap_cfg_opt_val_t val;651int off, error = 0;652653/* Get command parameters */654con->rx_pkt = NULL;655NG_L2CAP_M_PULLUP(m, sizeof(*cp));656if (m == NULL)657return (ENOBUFS);658659cp = mtod(m, ng_l2cap_cfg_req_cp *);660dcid = le16toh(cp->dcid);661respond = NG_L2CAP_OPT_CFLAG(le16toh(cp->flags));662m_adj(m, sizeof(*cp));663664/* Check if we have this channel and it is in valid state */665ch = ng_l2cap_chan_by_scid(l2cap, dcid, NG_L2CAP_L2CA_IDTYPE_BREDR);666if (ch == NULL) {667NG_L2CAP_ERR(668"%s: %s - unexpected L2CAP_ConfigReq command. " \669"Channel does not exist, cid=%d\n",670__func__, NG_NODE_NAME(l2cap->node), dcid);671goto reject;672}673674/* Verify channel state */675if (ch->state != NG_L2CAP_CONFIG && ch->state != NG_L2CAP_OPEN) {676NG_L2CAP_ERR(677"%s: %s - unexpected L2CAP_ConfigReq. " \678"Invalid channel state, cid=%d, state=%d\n",679__func__, NG_NODE_NAME(l2cap->node), dcid, ch->state);680goto reject;681}682683if (ch->state == NG_L2CAP_OPEN) { /* Re-configuration */684ch->cfg_state = 0;685ch->state = NG_L2CAP_CONFIG;686}687688for (result = 0, off = 0; ; ) {689error = get_next_l2cap_opt(m, &off, &hdr, &val);690if (error == 0) { /* We done with this packet */691NG_FREE_M(m);692break;693} else if (error > 0) { /* Got option */694switch (hdr.type) {695case NG_L2CAP_OPT_MTU:696ch->omtu = val.mtu;697break;698699case NG_L2CAP_OPT_FLUSH_TIMO:700ch->flush_timo = val.flush_timo;701break;702703case NG_L2CAP_OPT_QOS:704bcopy(&val.flow, &ch->iflow, sizeof(ch->iflow));705break;706707default: /* Ignore unknown hint option */708break;709}710} else { /* Oops, something is wrong */711respond = 1;712713if (error == -3) {714/*715* Adjust mbuf so we can get to the start716* of the first option we did not like.717*/718719m_adj(m, off - sizeof(hdr));720m->m_pkthdr.len = sizeof(hdr) + hdr.length;721722result = NG_L2CAP_UNKNOWN_OPTION;723} else {724/* XXX FIXME Send other reject codes? */725NG_FREE_M(m);726result = NG_L2CAP_REJECT;727}728729break;730}731}732733/*734* Now check and see if we have to respond. If everything was OK then735* respond contain "C flag" and (if set) we will respond with empty736* packet and will wait for more options.737*738* Other case is that we did not like peer's options and will respond739* with L2CAP_Config response command with Reject error code.740*741* When "respond == 0" than we have received all options and we will742* sent L2CA_ConfigInd event to the upper layer protocol.743*/744745if (respond) {746error = send_l2cap_cfg_rsp(con, ident, ch->dcid, result, m);747if (error != 0) {748ng_l2cap_l2ca_discon_ind(ch);749ng_l2cap_free_chan(ch);750}751} else {752/* Send L2CA_ConfigInd event to the upper layer protocol */753ch->ident = ident;754error = ng_l2cap_l2ca_cfg_ind(ch);755if (error != 0)756ng_l2cap_free_chan(ch);757}758759return (error);760761reject:762/* Send reject. Do not really care about the result */763NG_FREE_M(m);764765send_l2cap_reject(con, ident, NG_L2CAP_REJ_INVALID_CID, 0, 0, dcid);766767return (0);768} /* ng_l2cap_process_cfg_req */769770/*771* Process L2CAP_ConfigRsp command772*/773774static int775ng_l2cap_process_cfg_rsp(ng_l2cap_con_p con, u_int8_t ident)776{777ng_l2cap_p l2cap = con->l2cap;778struct mbuf *m = con->rx_pkt;779ng_l2cap_cfg_rsp_cp *cp = NULL;780ng_l2cap_cmd_p cmd = NULL;781u_int16_t scid, cflag, result;782ng_l2cap_cfg_opt_t hdr;783ng_l2cap_cfg_opt_val_t val;784int off, error = 0;785786/* Get command parameters */787con->rx_pkt = NULL;788NG_L2CAP_M_PULLUP(m, sizeof(*cp));789if (m == NULL)790return (ENOBUFS);791792cp = mtod(m, ng_l2cap_cfg_rsp_cp *);793scid = le16toh(cp->scid);794cflag = NG_L2CAP_OPT_CFLAG(le16toh(cp->flags));795result = le16toh(cp->result);796m_adj(m, sizeof(*cp));797798/* Check if we have this command */799cmd = ng_l2cap_cmd_by_ident(con, ident);800if (cmd == NULL) {801NG_L2CAP_ERR(802"%s: %s - unexpected L2CAP_ConfigRsp command. ident=%d, con_handle=%d\n",803__func__, NG_NODE_NAME(l2cap->node), ident,804con->con_handle);805NG_FREE_M(m);806807return (ENOENT);808}809810/* Verify CIDs and send reject if does not match */811if (cmd->ch->scid != scid) {812NG_L2CAP_ERR(813"%s: %s - unexpected L2CAP_ConfigRsp. " \814"Channel ID does not match, scid=%d(%d)\n",815__func__, NG_NODE_NAME(l2cap->node), cmd->ch->scid,816scid);817goto reject;818}819820/* Verify channel state and reject if invalid */821if (cmd->ch->state != NG_L2CAP_CONFIG) {822NG_L2CAP_ERR(823"%s: %s - unexpected L2CAP_ConfigRsp. " \824"Invalid channel state, scid=%d, state=%d\n",825__func__, NG_NODE_NAME(l2cap->node), cmd->ch->scid,826cmd->ch->state);827goto reject;828}829830/*831* Looks like it is our response, so process it. First parse options,832* then verify C flag. If it is set then we shall expect more833* configuration options from the peer and we will wait. Otherwise we834* have received all options and we will send L2CA_ConfigRsp event to835* the upper layer protocol. If command timeout already happened then836* ignore response.837*/838839if ((error = ng_l2cap_command_untimeout(cmd)) != 0) {840NG_FREE_M(m);841return (error);842}843844for (off = 0; ; ) {845error = get_next_l2cap_opt(m, &off, &hdr, &val);846if (error == 0) /* We done with this packet */847break;848else if (error > 0) { /* Got option */849switch (hdr.type) {850case NG_L2CAP_OPT_MTU:851cmd->ch->imtu = val.mtu;852break;853854case NG_L2CAP_OPT_FLUSH_TIMO:855cmd->ch->flush_timo = val.flush_timo;856break;857858case NG_L2CAP_OPT_QOS:859bcopy(&val.flow, &cmd->ch->oflow,860sizeof(cmd->ch->oflow));861break;862863default: /* Ignore unknown hint option */864break;865}866} else {867/*868* XXX FIXME What to do here?869*870* This is really BAD :( options packet was broken, or871* peer sent us option that we did not understand. Let872* upper layer know and do not wait for more options.873*/874875NG_L2CAP_ALERT(876"%s: %s - failed to parse configuration options, error=%d\n",877__func__, NG_NODE_NAME(l2cap->node), error);878879result = NG_L2CAP_UNKNOWN;880cflag = 0;881882break;883}884}885886NG_FREE_M(m);887888if (cflag) /* Restart timer and wait for more options */889ng_l2cap_command_timeout(cmd, bluetooth_l2cap_rtx_timeout());890else {891ng_l2cap_unlink_cmd(cmd);892893/* Send L2CA_Config response to the upper layer protocol */894error = ng_l2cap_l2ca_cfg_rsp(cmd->ch, cmd->token, result);895if (error != 0) {896/*897* XXX FIXME what to do here? we were not able to send898* response to the upper layer protocol, so for now899* just close the channel. Send L2CAP_Disconnect to900* remote peer?901*/902903NG_L2CAP_ERR(904"%s: %s - failed to send L2CA_Config response, error=%d\n",905__func__, NG_NODE_NAME(l2cap->node), error);906907ng_l2cap_free_chan(cmd->ch);908}909910ng_l2cap_free_cmd(cmd);911}912913return (error);914915reject:916/* Send reject. Do not really care about the result */917NG_FREE_M(m);918919send_l2cap_reject(con, ident, NG_L2CAP_REJ_INVALID_CID, 0, scid, 0);920921return (0);922} /* ng_l2cap_process_cfg_rsp */923924/*925* Process L2CAP_DisconnectReq command926*/927928static int929ng_l2cap_process_discon_req(ng_l2cap_con_p con, u_int8_t ident)930{931ng_l2cap_p l2cap = con->l2cap;932ng_l2cap_discon_req_cp *cp = NULL;933ng_l2cap_chan_p ch = NULL;934ng_l2cap_cmd_p cmd = NULL;935u_int16_t scid, dcid;936937/* Get command parameters */938NG_L2CAP_M_PULLUP(con->rx_pkt, sizeof(*cp));939if (con->rx_pkt == NULL)940return (ENOBUFS);941942cp = mtod(con->rx_pkt, ng_l2cap_discon_req_cp *);943dcid = le16toh(cp->dcid);944scid = le16toh(cp->scid);945946NG_FREE_M(con->rx_pkt);947948/* Check if we have this channel and it is in valid state */949ch = ng_l2cap_chan_by_scid(l2cap, dcid, NG_L2CAP_L2CA_IDTYPE_BREDR);950if (ch == NULL) {951NG_L2CAP_ERR(952"%s: %s - unexpected L2CAP_DisconnectReq message. " \953"Channel does not exist, cid=%d\n",954__func__, NG_NODE_NAME(l2cap->node), dcid);955goto reject;956}957958/* XXX Verify channel state and reject if invalid -- is that true? */959if (ch->state != NG_L2CAP_OPEN && ch->state != NG_L2CAP_CONFIG &&960ch->state != NG_L2CAP_W4_L2CAP_DISCON_RSP) {961NG_L2CAP_ERR(962"%s: %s - unexpected L2CAP_DisconnectReq. " \963"Invalid channel state, cid=%d, state=%d\n",964__func__, NG_NODE_NAME(l2cap->node), dcid, ch->state);965goto reject;966}967968/* Match destination channel ID */969if (ch->dcid != scid || ch->scid != dcid) {970NG_L2CAP_ERR(971"%s: %s - unexpected L2CAP_DisconnectReq. " \972"Channel IDs does not match, channel: scid=%d, dcid=%d, " \973"request: scid=%d, dcid=%d\n",974__func__, NG_NODE_NAME(l2cap->node), ch->scid, ch->dcid,975scid, dcid);976goto reject;977}978979/*980* Looks good, so notify upper layer protocol that channel is about981* to be disconnected and send L2CA_DisconnectInd message. Then respond982* with L2CAP_DisconnectRsp.983*/984985if (ch->state != NG_L2CAP_W4_L2CAP_DISCON_RSP) {986ng_l2cap_l2ca_discon_ind(ch); /* do not care about result */987ng_l2cap_free_chan(ch);988}989990/* Send L2CAP_DisconnectRsp */991cmd = ng_l2cap_new_cmd(con, NULL, ident, NG_L2CAP_DISCON_RSP, 0);992if (cmd == NULL)993return (ENOMEM);994995_ng_l2cap_discon_rsp(cmd->aux, ident, dcid, scid);996if (cmd->aux == NULL) {997ng_l2cap_free_cmd(cmd);998999return (ENOBUFS);1000}10011002/* Link command to the queue */1003ng_l2cap_link_cmd(con, cmd);1004ng_l2cap_lp_deliver(con);10051006return (0);10071008reject:1009/* Send reject. Do not really care about the result */1010send_l2cap_reject(con, ident, NG_L2CAP_REJ_INVALID_CID, 0, scid, dcid);10111012return (0);1013} /* ng_l2cap_process_discon_req */10141015/*1016* Process L2CAP_DisconnectRsp command1017*/10181019static int1020ng_l2cap_process_discon_rsp(ng_l2cap_con_p con, u_int8_t ident)1021{1022ng_l2cap_p l2cap = con->l2cap;1023ng_l2cap_discon_rsp_cp *cp = NULL;1024ng_l2cap_cmd_p cmd = NULL;1025u_int16_t scid, dcid;1026int error = 0;10271028/* Get command parameters */1029NG_L2CAP_M_PULLUP(con->rx_pkt, sizeof(*cp));1030if (con->rx_pkt == NULL)1031return (ENOBUFS);10321033cp = mtod(con->rx_pkt, ng_l2cap_discon_rsp_cp *);1034dcid = le16toh(cp->dcid);1035scid = le16toh(cp->scid);10361037NG_FREE_M(con->rx_pkt);10381039/* Check if we have pending command descriptor */1040cmd = ng_l2cap_cmd_by_ident(con, ident);1041if (cmd == NULL) {1042NG_L2CAP_ERR(1043"%s: %s - unexpected L2CAP_DisconnectRsp command. ident=%d, con_handle=%d\n",1044__func__, NG_NODE_NAME(l2cap->node), ident,1045con->con_handle);1046goto out;1047}10481049/* Verify channel state, do nothing if invalid */1050if (cmd->ch->state != NG_L2CAP_W4_L2CAP_DISCON_RSP) {1051NG_L2CAP_ERR(1052"%s: %s - unexpected L2CAP_DisconnectRsp. " \1053"Invalid channel state, cid=%d, state=%d\n",1054__func__, NG_NODE_NAME(l2cap->node), scid,1055cmd->ch->state);1056goto out;1057}10581059/* Verify CIDs and send reject if does not match */1060if (cmd->ch->scid != scid || cmd->ch->dcid != dcid) {1061NG_L2CAP_ERR(1062"%s: %s - unexpected L2CAP_DisconnectRsp. " \1063"Channel IDs do not match, scid=%d(%d), dcid=%d(%d)\n",1064__func__, NG_NODE_NAME(l2cap->node), cmd->ch->scid,1065scid, cmd->ch->dcid, dcid);1066goto out;1067}10681069/*1070* Looks like we have successfully disconnected channel, so notify1071* upper layer. If command timeout already happened then ignore1072* response.1073*/10741075if ((error = ng_l2cap_command_untimeout(cmd)) != 0)1076goto out;10771078error = ng_l2cap_l2ca_discon_rsp(cmd->ch, cmd->token, NG_L2CAP_SUCCESS);1079ng_l2cap_free_chan(cmd->ch); /* this will free commands too */1080out:1081return (error);1082} /* ng_l2cap_process_discon_rsp */10831084/*1085* Process L2CAP_EchoReq command1086*/10871088static int1089ng_l2cap_process_echo_req(ng_l2cap_con_p con, u_int8_t ident)1090{1091ng_l2cap_p l2cap = con->l2cap;1092ng_l2cap_cmd_hdr_t *hdr = NULL;1093ng_l2cap_cmd_p cmd = NULL;10941095con->rx_pkt = ng_l2cap_prepend(con->rx_pkt, sizeof(*hdr));1096if (con->rx_pkt == NULL) {1097NG_L2CAP_ALERT(1098"%s: %s - ng_l2cap_prepend() failed, size=%zd\n",1099__func__, NG_NODE_NAME(l2cap->node), sizeof(*hdr));11001101return (ENOBUFS);1102}11031104hdr = mtod(con->rx_pkt, ng_l2cap_cmd_hdr_t *);1105hdr->code = NG_L2CAP_ECHO_RSP;1106hdr->ident = ident;1107hdr->length = htole16(con->rx_pkt->m_pkthdr.len - sizeof(*hdr));11081109cmd = ng_l2cap_new_cmd(con, NULL, ident, NG_L2CAP_ECHO_RSP, 0);1110if (cmd == NULL) {1111NG_FREE_M(con->rx_pkt);11121113return (ENOBUFS);1114}11151116/* Attach data and link command to the queue */1117cmd->aux = con->rx_pkt;1118con->rx_pkt = NULL;1119ng_l2cap_link_cmd(con, cmd);1120ng_l2cap_lp_deliver(con);11211122return (0);1123} /* ng_l2cap_process_echo_req */11241125/*1126* Process L2CAP_EchoRsp command1127*/11281129static int1130ng_l2cap_process_echo_rsp(ng_l2cap_con_p con, u_int8_t ident)1131{1132ng_l2cap_p l2cap = con->l2cap;1133ng_l2cap_cmd_p cmd = NULL;1134int error = 0;11351136/* Check if we have this command */1137cmd = ng_l2cap_cmd_by_ident(con, ident);1138if (cmd != NULL) {1139/* If command timeout already happened then ignore response */1140if ((error = ng_l2cap_command_untimeout(cmd)) != 0) {1141NG_FREE_M(con->rx_pkt);1142return (error);1143}11441145ng_l2cap_unlink_cmd(cmd);11461147error = ng_l2cap_l2ca_ping_rsp(cmd->con, cmd->token,1148NG_L2CAP_SUCCESS, con->rx_pkt);11491150ng_l2cap_free_cmd(cmd);1151con->rx_pkt = NULL;1152} else {1153NG_L2CAP_ERR(1154"%s: %s - unexpected L2CAP_EchoRsp command. " \1155"Requested ident does not exist, ident=%d\n",1156__func__, NG_NODE_NAME(l2cap->node), ident);1157NG_FREE_M(con->rx_pkt);1158}11591160return (error);1161} /* ng_l2cap_process_echo_rsp */11621163/*1164* Process L2CAP_InfoReq command1165*/11661167static int1168ng_l2cap_process_info_req(ng_l2cap_con_p con, u_int8_t ident)1169{1170ng_l2cap_p l2cap = con->l2cap;1171ng_l2cap_cmd_p cmd = NULL;1172u_int16_t type;11731174/* Get command parameters */1175NG_L2CAP_M_PULLUP(con->rx_pkt, sizeof(ng_l2cap_info_req_cp));1176if (con->rx_pkt == NULL)1177return (ENOBUFS);11781179type = le16toh(mtod(con->rx_pkt, ng_l2cap_info_req_cp *)->type);1180NG_FREE_M(con->rx_pkt);11811182cmd = ng_l2cap_new_cmd(con, NULL, ident, NG_L2CAP_INFO_RSP, 0);1183if (cmd == NULL)1184return (ENOMEM);11851186switch (type) {1187case NG_L2CAP_CONNLESS_MTU:1188_ng_l2cap_info_rsp(cmd->aux, ident, NG_L2CAP_CONNLESS_MTU,1189NG_L2CAP_SUCCESS, NG_L2CAP_MTU_DEFAULT);1190break;11911192default:1193_ng_l2cap_info_rsp(cmd->aux, ident, type,1194NG_L2CAP_NOT_SUPPORTED, 0);1195break;1196}11971198if (cmd->aux == NULL) {1199ng_l2cap_free_cmd(cmd);12001201return (ENOBUFS);1202}12031204/* Link command to the queue */1205ng_l2cap_link_cmd(con, cmd);1206ng_l2cap_lp_deliver(con);12071208return (0);1209} /* ng_l2cap_process_info_req */12101211/*1212* Process L2CAP_InfoRsp command1213*/12141215static int1216ng_l2cap_process_info_rsp(ng_l2cap_con_p con, u_int8_t ident)1217{1218ng_l2cap_p l2cap = con->l2cap;1219ng_l2cap_info_rsp_cp *cp = NULL;1220ng_l2cap_cmd_p cmd = NULL;1221int error = 0;12221223/* Get command parameters */1224NG_L2CAP_M_PULLUP(con->rx_pkt, sizeof(*cp));1225if (con->rx_pkt == NULL)1226return (ENOBUFS);12271228cp = mtod(con->rx_pkt, ng_l2cap_info_rsp_cp *);1229cp->type = le16toh(cp->type);1230cp->result = le16toh(cp->result);1231m_adj(con->rx_pkt, sizeof(*cp));12321233/* Check if we have pending command descriptor */1234cmd = ng_l2cap_cmd_by_ident(con, ident);1235if (cmd == NULL) {1236NG_L2CAP_ERR(1237"%s: %s - unexpected L2CAP_InfoRsp command. " \1238"Requested ident does not exist, ident=%d\n",1239__func__, NG_NODE_NAME(l2cap->node), ident);1240NG_FREE_M(con->rx_pkt);12411242return (ENOENT);1243}12441245/* If command timeout already happened then ignore response */1246if ((error = ng_l2cap_command_untimeout(cmd)) != 0) {1247NG_FREE_M(con->rx_pkt);1248return (error);1249}12501251ng_l2cap_unlink_cmd(cmd);12521253if (cp->result == NG_L2CAP_SUCCESS) {1254switch (cp->type) {1255case NG_L2CAP_CONNLESS_MTU:1256if (con->rx_pkt->m_pkthdr.len == sizeof(u_int16_t))1257*mtod(con->rx_pkt, u_int16_t *) =1258le16toh(*mtod(con->rx_pkt,u_int16_t *));1259else {1260cp->result = NG_L2CAP_UNKNOWN; /* XXX */12611262NG_L2CAP_ERR(1263"%s: %s - invalid L2CAP_InfoRsp command. " \1264"Bad connectionless MTU parameter, len=%d\n",1265__func__, NG_NODE_NAME(l2cap->node),1266con->rx_pkt->m_pkthdr.len);1267}1268break;12691270default:1271NG_L2CAP_WARN(1272"%s: %s - invalid L2CAP_InfoRsp command. Unknown info type=%d\n",1273__func__, NG_NODE_NAME(l2cap->node), cp->type);1274break;1275}1276}12771278error = ng_l2cap_l2ca_get_info_rsp(cmd->con, cmd->token,1279cp->result, con->rx_pkt);12801281ng_l2cap_free_cmd(cmd);1282con->rx_pkt = NULL;12831284return (error);1285} /* ng_l2cap_process_info_rsp */12861287/*1288* Send L2CAP reject1289*/12901291static int1292send_l2cap_reject(ng_l2cap_con_p con, u_int8_t ident, u_int16_t reason,1293u_int16_t mtu, u_int16_t scid, u_int16_t dcid)1294{1295ng_l2cap_cmd_p cmd = NULL;12961297cmd = ng_l2cap_new_cmd(con, NULL, ident, NG_L2CAP_CMD_REJ, 0);1298if (cmd == NULL)1299return (ENOMEM);13001301_ng_l2cap_cmd_rej(cmd->aux, cmd->ident, reason, mtu, scid, dcid);1302if (cmd->aux == NULL) {1303ng_l2cap_free_cmd(cmd);13041305return (ENOBUFS);1306}13071308/* Link command to the queue */1309ng_l2cap_link_cmd(con, cmd);1310ng_l2cap_lp_deliver(con);13111312return (0);1313} /* send_l2cap_reject */13141315/*1316* Send L2CAP connection reject1317*/13181319static int1320send_l2cap_con_rej(ng_l2cap_con_p con, u_int8_t ident, u_int16_t scid,1321u_int16_t dcid, u_int16_t result)1322{1323ng_l2cap_cmd_p cmd = NULL;13241325cmd = ng_l2cap_new_cmd(con, NULL, ident, NG_L2CAP_CON_RSP, 0);1326if (cmd == NULL)1327return (ENOMEM);13281329_ng_l2cap_con_rsp(cmd->aux, cmd->ident, scid, dcid, result, 0);1330if (cmd->aux == NULL) {1331ng_l2cap_free_cmd(cmd);13321333return (ENOBUFS);1334}13351336/* Link command to the queue */1337ng_l2cap_link_cmd(con, cmd);1338ng_l2cap_lp_deliver(con);13391340return (0);1341} /* send_l2cap_con_rej */13421343/*1344* Send L2CAP config response1345*/13461347static int1348send_l2cap_cfg_rsp(ng_l2cap_con_p con, u_int8_t ident, u_int16_t scid,1349u_int16_t result, struct mbuf *opt)1350{1351ng_l2cap_cmd_p cmd = NULL;13521353cmd = ng_l2cap_new_cmd(con, NULL, ident, NG_L2CAP_CFG_RSP, 0);1354if (cmd == NULL) {1355NG_FREE_M(opt);13561357return (ENOMEM);1358}13591360_ng_l2cap_cfg_rsp(cmd->aux, cmd->ident, scid, 0, result, opt);1361if (cmd->aux == NULL) {1362ng_l2cap_free_cmd(cmd);13631364return (ENOBUFS);1365}13661367/* Link command to the queue */1368ng_l2cap_link_cmd(con, cmd);1369ng_l2cap_lp_deliver(con);13701371return (0);1372} /* send_l2cap_cfg_rsp */13731374static int1375send_l2cap_param_urs(ng_l2cap_con_p con, u_int8_t ident,1376u_int16_t result)1377{1378ng_l2cap_cmd_p cmd = NULL;13791380cmd = ng_l2cap_new_cmd(con, NULL, ident,1381NG_L2CAP_CMD_PARAM_UPDATE_RESPONSE,13820);1383if (cmd == NULL) {1384return (ENOMEM);1385}13861387_ng_l2cap_cmd_urs(cmd->aux, cmd->ident, result);1388if (cmd->aux == NULL) {1389ng_l2cap_free_cmd(cmd);13901391return (ENOBUFS);1392}13931394/* Link command to the queue */1395ng_l2cap_link_cmd(con, cmd);1396ng_l2cap_lp_deliver(con);13971398return (0);1399} /* send_l2cap_cfg_rsp */14001401/*1402* Get next L2CAP configuration option1403*1404* Return codes:1405* 0 no option1406* 1 we have got option1407* -1 header too short1408* -2 bad option value or length1409* -3 unknown option1410*/14111412static int1413get_next_l2cap_opt(struct mbuf *m, int *off, ng_l2cap_cfg_opt_p hdr,1414ng_l2cap_cfg_opt_val_p val)1415{1416int hint, len = m->m_pkthdr.len - (*off);14171418if (len == 0)1419return (0);1420if (len < 0 || len < sizeof(*hdr))1421return (-1);14221423m_copydata(m, *off, sizeof(*hdr), (caddr_t) hdr);1424*off += sizeof(*hdr);1425len -= sizeof(*hdr);14261427hint = NG_L2CAP_OPT_HINT(hdr->type);1428hdr->type &= NG_L2CAP_OPT_HINT_MASK;14291430switch (hdr->type) {1431case NG_L2CAP_OPT_MTU:1432if (hdr->length != NG_L2CAP_OPT_MTU_SIZE || len < hdr->length)1433return (-2);14341435m_copydata(m, *off, NG_L2CAP_OPT_MTU_SIZE, (caddr_t) val);1436val->mtu = le16toh(val->mtu);1437*off += NG_L2CAP_OPT_MTU_SIZE;1438break;14391440case NG_L2CAP_OPT_FLUSH_TIMO:1441if (hdr->length != NG_L2CAP_OPT_FLUSH_TIMO_SIZE ||1442len < hdr->length)1443return (-2);14441445m_copydata(m, *off, NG_L2CAP_OPT_FLUSH_TIMO_SIZE, (caddr_t)val);1446val->flush_timo = le16toh(val->flush_timo);1447*off += NG_L2CAP_OPT_FLUSH_TIMO_SIZE;1448break;14491450case NG_L2CAP_OPT_QOS:1451if (hdr->length != NG_L2CAP_OPT_QOS_SIZE || len < hdr->length)1452return (-2);14531454m_copydata(m, *off, NG_L2CAP_OPT_QOS_SIZE, (caddr_t) val);1455val->flow.token_rate = le32toh(val->flow.token_rate);1456val->flow.token_bucket_size =1457le32toh(val->flow.token_bucket_size);1458val->flow.peak_bandwidth = le32toh(val->flow.peak_bandwidth);1459val->flow.latency = le32toh(val->flow.latency);1460val->flow.delay_variation = le32toh(val->flow.delay_variation);1461*off += NG_L2CAP_OPT_QOS_SIZE;1462break;14631464default:1465if (hint)1466*off += hdr->length;1467else1468return (-3);1469break;1470}14711472return (1);1473} /* get_next_l2cap_opt */147414751476