1228060Sbapt/* 2228060Sbapt * ng_l2cap_evnt.c 3228060Sbapt */ 4228060Sbapt 5228060Sbapt/*- 6228060Sbapt * Copyright (c) Maksim Yevmenkin <m_evmenkin@yahoo.com> 7228060Sbapt * All rights reserved. 8228060Sbapt * 9228060Sbapt * Redistribution and use in source and binary forms, with or without 10228060Sbapt * modification, are permitted provided that the following conditions 11228060Sbapt * are met: 12228060Sbapt * 1. Redistributions of source code must retain the above copyright 13228060Sbapt * notice, this list of conditions and the following disclaimer. 14228060Sbapt * 2. Redistributions in binary form must reproduce the above copyright 15228060Sbapt * notice, this list of conditions and the following disclaimer in the 16228060Sbapt * documentation and/or other materials provided with the distribution. 17228060Sbapt * 18228060Sbapt * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19228060Sbapt * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20228060Sbapt * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21228060Sbapt * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22228060Sbapt * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23228060Sbapt * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24228060Sbapt * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25228060Sbapt * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26228060Sbapt * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27228060Sbapt * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28228060Sbapt * SUCH DAMAGE. 29228060Sbapt * 30228060Sbapt * $Id: ng_l2cap_evnt.c,v 1.5 2003/09/08 19:11:45 max Exp $ 31228060Sbapt * $FreeBSD$ 32228060Sbapt */ 33228060Sbapt 34228060Sbapt#include <sys/param.h> 35228060Sbapt#include <sys/systm.h> 36228060Sbapt#include <sys/kernel.h> 37228060Sbapt#include <sys/endian.h> 38228060Sbapt#include <sys/malloc.h> 39228060Sbapt#include <sys/mbuf.h> 40228060Sbapt#include <sys/queue.h> 41228060Sbapt#include <netgraph/ng_message.h> 42228060Sbapt#include <netgraph/netgraph.h> 43228060Sbapt#include <netgraph/bluetooth/include/ng_bluetooth.h> 44228060Sbapt#include <netgraph/bluetooth/include/ng_hci.h> 45228060Sbapt#include <netgraph/bluetooth/include/ng_l2cap.h> 46228060Sbapt#include <netgraph/bluetooth/l2cap/ng_l2cap_var.h> 47228060Sbapt#include <netgraph/bluetooth/l2cap/ng_l2cap_cmds.h> 48228060Sbapt#include <netgraph/bluetooth/l2cap/ng_l2cap_evnt.h> 49228060Sbapt#include <netgraph/bluetooth/l2cap/ng_l2cap_llpi.h> 50228060Sbapt#include <netgraph/bluetooth/l2cap/ng_l2cap_ulpi.h> 51228060Sbapt#include <netgraph/bluetooth/l2cap/ng_l2cap_misc.h> 52228060Sbapt 53228060Sbapt/****************************************************************************** 54228060Sbapt ****************************************************************************** 55228060Sbapt ** L2CAP events processing module 56228060Sbapt ****************************************************************************** 57228060Sbapt ******************************************************************************/ 58228060Sbapt 59228060Sbaptstatic int ng_l2cap_process_signal_cmd (ng_l2cap_con_p); 60259976Sdimstatic int ng_l2cap_process_cmd_rej (ng_l2cap_con_p, u_int8_t); 61228060Sbaptstatic int ng_l2cap_process_con_req (ng_l2cap_con_p, u_int8_t); 62228060Sbaptstatic int ng_l2cap_process_con_rsp (ng_l2cap_con_p, u_int8_t); 63228060Sbaptstatic int ng_l2cap_process_cfg_req (ng_l2cap_con_p, u_int8_t); 64228060Sbaptstatic int ng_l2cap_process_cfg_rsp (ng_l2cap_con_p, u_int8_t); 65228060Sbaptstatic int ng_l2cap_process_discon_req (ng_l2cap_con_p, u_int8_t); 66228060Sbaptstatic int ng_l2cap_process_discon_rsp (ng_l2cap_con_p, u_int8_t); 67228060Sbaptstatic int ng_l2cap_process_echo_req (ng_l2cap_con_p, u_int8_t); 68228060Sbaptstatic int ng_l2cap_process_echo_rsp (ng_l2cap_con_p, u_int8_t); 69228060Sbaptstatic int ng_l2cap_process_info_req (ng_l2cap_con_p, u_int8_t); 70228060Sbaptstatic int ng_l2cap_process_info_rsp (ng_l2cap_con_p, u_int8_t); 71228060Sbaptstatic int send_l2cap_reject 72228060Sbapt (ng_l2cap_con_p, u_int8_t, u_int16_t, u_int16_t, u_int16_t, u_int16_t); 73228060Sbaptstatic int send_l2cap_con_rej 74228060Sbapt (ng_l2cap_con_p, u_int8_t, u_int16_t, u_int16_t, u_int16_t); 75228060Sbaptstatic int send_l2cap_cfg_rsp 76228060Sbapt (ng_l2cap_con_p, u_int8_t, u_int16_t, u_int16_t, struct mbuf *); 77228060Sbaptstatic int get_next_l2cap_opt 78228060Sbapt (struct mbuf *, int *, ng_l2cap_cfg_opt_p, ng_l2cap_cfg_opt_val_p); 79228060Sbapt 80228060Sbapt/* 81228060Sbapt * Receive L2CAP packet. First get L2CAP header and verify packet. Than 82228060Sbapt * get destination channel and process packet. 83228060Sbapt */ 84228060Sbapt 85228060Sbaptint 86228060Sbaptng_l2cap_receive(ng_l2cap_con_p con) 87228060Sbapt{ 88228060Sbapt ng_l2cap_p l2cap = con->l2cap; 89228060Sbapt ng_l2cap_hdr_t *hdr = NULL; 90228060Sbapt int error = 0; 91228060Sbapt 92228060Sbapt /* Check packet */ 93228060Sbapt if (con->rx_pkt->m_pkthdr.len < sizeof(*hdr)) { 94228060Sbapt NG_L2CAP_ERR( 95228060Sbapt"%s: %s - invalid L2CAP packet. Packet too small, len=%d\n", 96228060Sbapt __func__, NG_NODE_NAME(l2cap->node), 97228060Sbapt con->rx_pkt->m_pkthdr.len); 98228060Sbapt error = EMSGSIZE; 99228060Sbapt goto drop; 100228060Sbapt } 101228060Sbapt 102228060Sbapt /* Get L2CAP header */ 103228060Sbapt NG_L2CAP_M_PULLUP(con->rx_pkt, sizeof(*hdr)); 104228060Sbapt if (con->rx_pkt == NULL) 105228060Sbapt return (ENOBUFS); 106228060Sbapt 107228060Sbapt hdr = mtod(con->rx_pkt, ng_l2cap_hdr_t *); 108228060Sbapt hdr->length = le16toh(hdr->length); 109228060Sbapt hdr->dcid = le16toh(hdr->dcid); 110228060Sbapt 111228060Sbapt /* Check payload size */ 112228060Sbapt if (hdr->length != con->rx_pkt->m_pkthdr.len - sizeof(*hdr)) { 113228060Sbapt NG_L2CAP_ERR( 114228060Sbapt"%s: %s - invalid L2CAP packet. Payload length mismatch, length=%d, len=%zd\n", 115228060Sbapt __func__, NG_NODE_NAME(l2cap->node), hdr->length, 116228060Sbapt con->rx_pkt->m_pkthdr.len - sizeof(*hdr)); 117228060Sbapt error = EMSGSIZE; 118228060Sbapt goto drop; 119228060Sbapt } 120 121 /* Process packet */ 122 switch (hdr->dcid) { 123 case NG_L2CAP_SIGNAL_CID: /* L2CAP command */ 124 m_adj(con->rx_pkt, sizeof(*hdr)); 125 error = ng_l2cap_process_signal_cmd(con); 126 break; 127 128 case NG_L2CAP_CLT_CID: /* Connectionless packet */ 129 error = ng_l2cap_l2ca_clt_receive(con); 130 break; 131 132 default: /* Data packet */ 133 error = ng_l2cap_l2ca_receive(con); 134 break; 135 } 136 137 return (error); 138drop: 139 NG_FREE_M(con->rx_pkt); 140 141 return (error); 142} /* ng_l2cap_receive */ 143 144/* 145 * Process L2CAP signaling command. We already know that destination channel ID 146 * is 0x1 that means we have received signaling command from peer's L2CAP layer. 147 * So get command header, decode and process it. 148 * 149 * XXX do we need to check signaling MTU here? 150 */ 151 152static int 153ng_l2cap_process_signal_cmd(ng_l2cap_con_p con) 154{ 155 ng_l2cap_p l2cap = con->l2cap; 156 ng_l2cap_cmd_hdr_t *hdr = NULL; 157 struct mbuf *m = NULL; 158 159 while (con->rx_pkt != NULL) { 160 /* Verify packet length */ 161 if (con->rx_pkt->m_pkthdr.len < sizeof(*hdr)) { 162 NG_L2CAP_ERR( 163"%s: %s - invalid L2CAP signaling command. Packet too small, len=%d\n", 164 __func__, NG_NODE_NAME(l2cap->node), 165 con->rx_pkt->m_pkthdr.len); 166 NG_FREE_M(con->rx_pkt); 167 168 return (EMSGSIZE); 169 } 170 171 /* Get signaling command */ 172 NG_L2CAP_M_PULLUP(con->rx_pkt, sizeof(*hdr)); 173 if (con->rx_pkt == NULL) 174 return (ENOBUFS); 175 176 hdr = mtod(con->rx_pkt, ng_l2cap_cmd_hdr_t *); 177 hdr->length = le16toh(hdr->length); 178 m_adj(con->rx_pkt, sizeof(*hdr)); 179 180 /* Verify command length */ 181 if (con->rx_pkt->m_pkthdr.len < hdr->length) { 182 NG_L2CAP_ERR( 183"%s: %s - invalid L2CAP signaling command, code=%#x, ident=%d. " \ 184"Invalid command length=%d, m_pkthdr.len=%d\n", 185 __func__, NG_NODE_NAME(l2cap->node), 186 hdr->code, hdr->ident, hdr->length, 187 con->rx_pkt->m_pkthdr.len); 188 NG_FREE_M(con->rx_pkt); 189 190 return (EMSGSIZE); 191 } 192 193 /* Get the command, save the rest (if any) */ 194 if (con->rx_pkt->m_pkthdr.len > hdr->length) 195 m = m_split(con->rx_pkt, hdr->length, M_NOWAIT); 196 else 197 m = NULL; 198 199 /* Process command */ 200 switch (hdr->code) { 201 case NG_L2CAP_CMD_REJ: 202 ng_l2cap_process_cmd_rej(con, hdr->ident); 203 break; 204 205 case NG_L2CAP_CON_REQ: 206 ng_l2cap_process_con_req(con, hdr->ident); 207 break; 208 209 case NG_L2CAP_CON_RSP: 210 ng_l2cap_process_con_rsp(con, hdr->ident); 211 break; 212 213 case NG_L2CAP_CFG_REQ: 214 ng_l2cap_process_cfg_req(con, hdr->ident); 215 break; 216 217 case NG_L2CAP_CFG_RSP: 218 ng_l2cap_process_cfg_rsp(con, hdr->ident); 219 break; 220 221 case NG_L2CAP_DISCON_REQ: 222 ng_l2cap_process_discon_req(con, hdr->ident); 223 break; 224 225 case NG_L2CAP_DISCON_RSP: 226 ng_l2cap_process_discon_rsp(con, hdr->ident); 227 break; 228 229 case NG_L2CAP_ECHO_REQ: 230 ng_l2cap_process_echo_req(con, hdr->ident); 231 break; 232 233 case NG_L2CAP_ECHO_RSP: 234 ng_l2cap_process_echo_rsp(con, hdr->ident); 235 break; 236 237 case NG_L2CAP_INFO_REQ: 238 ng_l2cap_process_info_req(con, hdr->ident); 239 break; 240 241 case NG_L2CAP_INFO_RSP: 242 ng_l2cap_process_info_rsp(con, hdr->ident); 243 break; 244 245 default: 246 NG_L2CAP_ERR( 247"%s: %s - unknown L2CAP signaling command, code=%#x, ident=%d, length=%d\n", 248 __func__, NG_NODE_NAME(l2cap->node), 249 hdr->code, hdr->ident, hdr->length); 250 251 /* 252 * Send L2CAP_CommandRej. Do not really care 253 * about the result 254 */ 255 256 send_l2cap_reject(con, hdr->ident, 257 NG_L2CAP_REJ_NOT_UNDERSTOOD, 0, 0, 0); 258 NG_FREE_M(con->rx_pkt); 259 break; 260 } 261 262 con->rx_pkt = m; 263 } 264 265 return (0); 266} /* ng_l2cap_process_signal_cmd */ 267 268/* 269 * Process L2CAP_CommandRej command 270 */ 271 272static int 273ng_l2cap_process_cmd_rej(ng_l2cap_con_p con, u_int8_t ident) 274{ 275 ng_l2cap_p l2cap = con->l2cap; 276 ng_l2cap_cmd_rej_cp *cp = NULL; 277 ng_l2cap_cmd_p cmd = NULL; 278 279 /* Get command parameters */ 280 NG_L2CAP_M_PULLUP(con->rx_pkt, sizeof(*cp)); 281 if (con->rx_pkt == NULL) 282 return (ENOBUFS); 283 284 cp = mtod(con->rx_pkt, ng_l2cap_cmd_rej_cp *); 285 cp->reason = le16toh(cp->reason); 286 287 /* Check if we have pending command descriptor */ 288 cmd = ng_l2cap_cmd_by_ident(con, ident); 289 if (cmd != NULL) { 290 /* If command timeout already happened then ignore reject */ 291 if (ng_l2cap_command_untimeout(cmd) != 0) { 292 NG_FREE_M(con->rx_pkt); 293 return (ETIMEDOUT); 294 } 295 296 ng_l2cap_unlink_cmd(cmd); 297 298 switch (cmd->code) { 299 case NG_L2CAP_CON_REQ: 300 ng_l2cap_l2ca_con_rsp(cmd->ch,cmd->token,cp->reason,0); 301 ng_l2cap_free_chan(cmd->ch); 302 break; 303 304 case NG_L2CAP_CFG_REQ: 305 ng_l2cap_l2ca_cfg_rsp(cmd->ch, cmd->token, cp->reason); 306 break; 307 308 case NG_L2CAP_DISCON_REQ: 309 ng_l2cap_l2ca_discon_rsp(cmd->ch,cmd->token,cp->reason); 310 ng_l2cap_free_chan(cmd->ch); /* XXX free channel */ 311 break; 312 313 case NG_L2CAP_ECHO_REQ: 314 ng_l2cap_l2ca_ping_rsp(cmd->con, cmd->token, 315 cp->reason, NULL); 316 break; 317 318 case NG_L2CAP_INFO_REQ: 319 ng_l2cap_l2ca_get_info_rsp(cmd->con, cmd->token, 320 cp->reason, NULL); 321 break; 322 323 default: 324 NG_L2CAP_ALERT( 325"%s: %s - unexpected L2CAP_CommandRej. Unexpected L2CAP command opcode=%d\n", 326 __func__, NG_NODE_NAME(l2cap->node), cmd->code); 327 break; 328 } 329 330 ng_l2cap_free_cmd(cmd); 331 } else 332 NG_L2CAP_ERR( 333"%s: %s - unexpected L2CAP_CommandRej command. " \ 334"Requested ident does not exist, ident=%d\n", 335 __func__, NG_NODE_NAME(l2cap->node), ident); 336 337 NG_FREE_M(con->rx_pkt); 338 339 return (0); 340} /* ng_l2cap_process_cmd_rej */ 341 342/* 343 * Process L2CAP_ConnectReq command 344 */ 345 346static int 347ng_l2cap_process_con_req(ng_l2cap_con_p con, u_int8_t ident) 348{ 349 ng_l2cap_p l2cap = con->l2cap; 350 struct mbuf *m = con->rx_pkt; 351 ng_l2cap_con_req_cp *cp = NULL; 352 ng_l2cap_chan_p ch = NULL; 353 int error = 0; 354 u_int16_t dcid, psm; 355 356 /* Get command parameters */ 357 NG_L2CAP_M_PULLUP(m, sizeof(*cp)); 358 if (m == NULL) 359 return (ENOBUFS); 360 361 cp = mtod(m, ng_l2cap_con_req_cp *); 362 psm = le16toh(cp->psm); 363 dcid = le16toh(cp->scid); 364 365 NG_FREE_M(m); 366 con->rx_pkt = NULL; 367 368 /* 369 * Create new channel and send L2CA_ConnectInd notification 370 * to the upper layer protocol. 371 */ 372 373 ch = ng_l2cap_new_chan(l2cap, con, psm); 374 if (ch == NULL) 375 return (send_l2cap_con_rej(con, ident, 0, dcid, 376 NG_L2CAP_NO_RESOURCES)); 377 378 /* Update channel IDs */ 379 ch->dcid = dcid; 380 381 /* Sent L2CA_ConnectInd notification to the upper layer */ 382 ch->ident = ident; 383 ch->state = NG_L2CAP_W4_L2CA_CON_RSP; 384 385 error = ng_l2cap_l2ca_con_ind(ch); 386 if (error != 0) { 387 send_l2cap_con_rej(con, ident, ch->scid, dcid, 388 (error == ENOMEM)? NG_L2CAP_NO_RESOURCES : 389 NG_L2CAP_PSM_NOT_SUPPORTED); 390 ng_l2cap_free_chan(ch); 391 } 392 393 return (error); 394} /* ng_l2cap_process_con_req */ 395 396/* 397 * Process L2CAP_ConnectRsp command 398 */ 399 400static int 401ng_l2cap_process_con_rsp(ng_l2cap_con_p con, u_int8_t ident) 402{ 403 ng_l2cap_p l2cap = con->l2cap; 404 struct mbuf *m = con->rx_pkt; 405 ng_l2cap_con_rsp_cp *cp = NULL; 406 ng_l2cap_cmd_p cmd = NULL; 407 u_int16_t scid, dcid, result, status; 408 int error = 0; 409 410 /* Get command parameters */ 411 NG_L2CAP_M_PULLUP(m, sizeof(*cp)); 412 if (m == NULL) 413 return (ENOBUFS); 414 415 cp = mtod(m, ng_l2cap_con_rsp_cp *); 416 dcid = le16toh(cp->dcid); 417 scid = le16toh(cp->scid); 418 result = le16toh(cp->result); 419 status = le16toh(cp->status); 420 421 NG_FREE_M(m); 422 con->rx_pkt = NULL; 423 424 /* Check if we have pending command descriptor */ 425 cmd = ng_l2cap_cmd_by_ident(con, ident); 426 if (cmd == NULL) { 427 NG_L2CAP_ERR( 428"%s: %s - unexpected L2CAP_ConnectRsp command. ident=%d, con_handle=%d\n", 429 __func__, NG_NODE_NAME(l2cap->node), ident, 430 con->con_handle); 431 432 return (ENOENT); 433 } 434 435 /* Verify channel state, if invalid - do nothing */ 436 if (cmd->ch->state != NG_L2CAP_W4_L2CAP_CON_RSP) { 437 NG_L2CAP_ERR( 438"%s: %s - unexpected L2CAP_ConnectRsp. " \ 439"Invalid channel state, cid=%d, state=%d\n", 440 __func__, NG_NODE_NAME(l2cap->node), scid, 441 cmd->ch->state); 442 goto reject; 443 } 444 445 /* Verify CIDs and send reject if does not match */ 446 if (cmd->ch->scid != scid) { 447 NG_L2CAP_ERR( 448"%s: %s - unexpected L2CAP_ConnectRsp. Channel IDs do not match, scid=%d(%d)\n", 449 __func__, NG_NODE_NAME(l2cap->node), cmd->ch->scid, 450 scid); 451 goto reject; 452 } 453 454 /* 455 * Looks good. We got confirmation from our peer. Now process 456 * it. First disable RTX timer. Then check the result and send 457 * notification to the upper layer. If command timeout already 458 * happened then ignore response. 459 */ 460 461 if ((error = ng_l2cap_command_untimeout(cmd)) != 0) 462 return (error); 463 464 if (result == NG_L2CAP_PENDING) { 465 /* 466 * Our peer wants more time to complete connection. We shall 467 * start ERTX timer and wait. Keep command in the list. 468 */ 469 470 cmd->ch->dcid = dcid; 471 ng_l2cap_command_timeout(cmd, bluetooth_l2cap_ertx_timeout()); 472 473 error = ng_l2cap_l2ca_con_rsp(cmd->ch, cmd->token, 474 result, status); 475 if (error != 0) 476 ng_l2cap_free_chan(cmd->ch); 477 } else { 478 ng_l2cap_unlink_cmd(cmd); 479 480 if (result == NG_L2CAP_SUCCESS) { 481 /* 482 * Channel is open. Complete command and move to CONFIG 483 * state. Since we have sent positive confirmation we 484 * expect to receive L2CA_Config request from the upper 485 * layer protocol. 486 */ 487 488 cmd->ch->dcid = dcid; 489 cmd->ch->state = NG_L2CAP_CONFIG; 490 } else 491 /* There was an error, so close the channel */ 492 NG_L2CAP_INFO( 493"%s: %s - failed to open L2CAP channel, result=%d, status=%d\n", 494 __func__, NG_NODE_NAME(l2cap->node), result, 495 status); 496 497 error = ng_l2cap_l2ca_con_rsp(cmd->ch, cmd->token, 498 result, status); 499 500 /* XXX do we have to remove the channel on error? */ 501 if (error != 0 || result != NG_L2CAP_SUCCESS) 502 ng_l2cap_free_chan(cmd->ch); 503 504 ng_l2cap_free_cmd(cmd); 505 } 506 507 return (error); 508 509reject: 510 /* Send reject. Do not really care about the result */ 511 send_l2cap_reject(con, ident, NG_L2CAP_REJ_INVALID_CID, 0, scid, dcid); 512 513 return (0); 514} /* ng_l2cap_process_con_rsp */ 515 516/* 517 * Process L2CAP_ConfigReq command 518 */ 519 520static int 521ng_l2cap_process_cfg_req(ng_l2cap_con_p con, u_int8_t ident) 522{ 523 ng_l2cap_p l2cap = con->l2cap; 524 struct mbuf *m = con->rx_pkt; 525 ng_l2cap_cfg_req_cp *cp = NULL; 526 ng_l2cap_chan_p ch = NULL; 527 u_int16_t dcid, respond, result; 528 ng_l2cap_cfg_opt_t hdr; 529 ng_l2cap_cfg_opt_val_t val; 530 int off, error = 0; 531 532 /* Get command parameters */ 533 con->rx_pkt = NULL; 534 NG_L2CAP_M_PULLUP(m, sizeof(*cp)); 535 if (m == NULL) 536 return (ENOBUFS); 537 538 cp = mtod(m, ng_l2cap_cfg_req_cp *); 539 dcid = le16toh(cp->dcid); 540 respond = NG_L2CAP_OPT_CFLAG(le16toh(cp->flags)); 541 m_adj(m, sizeof(*cp)); 542 543 /* Check if we have this channel and it is in valid state */ 544 ch = ng_l2cap_chan_by_scid(l2cap, dcid); 545 if (ch == NULL) { 546 NG_L2CAP_ERR( 547"%s: %s - unexpected L2CAP_ConfigReq command. " \ 548"Channel does not exist, cid=%d\n", 549 __func__, NG_NODE_NAME(l2cap->node), dcid); 550 goto reject; 551 } 552 553 /* Verify channel state */ 554 if (ch->state != NG_L2CAP_CONFIG && ch->state != NG_L2CAP_OPEN) { 555 NG_L2CAP_ERR( 556"%s: %s - unexpected L2CAP_ConfigReq. " \ 557"Invalid channel state, cid=%d, state=%d\n", 558 __func__, NG_NODE_NAME(l2cap->node), dcid, ch->state); 559 goto reject; 560 } 561 562 if (ch->state == NG_L2CAP_OPEN) { /* Re-configuration */ 563 ch->cfg_state = 0; 564 ch->state = NG_L2CAP_CONFIG; 565 } 566 567 for (result = 0, off = 0; ; ) { 568 error = get_next_l2cap_opt(m, &off, &hdr, &val); 569 if (error == 0) { /* We done with this packet */ 570 NG_FREE_M(m); 571 break; 572 } else if (error > 0) { /* Got option */ 573 switch (hdr.type) { 574 case NG_L2CAP_OPT_MTU: 575 ch->omtu = val.mtu; 576 break; 577 578 case NG_L2CAP_OPT_FLUSH_TIMO: 579 ch->flush_timo = val.flush_timo; 580 break; 581 582 case NG_L2CAP_OPT_QOS: 583 bcopy(&val.flow, &ch->iflow, sizeof(ch->iflow)); 584 break; 585 586 default: /* Ignore unknown hint option */ 587 break; 588 } 589 } else { /* Oops, something is wrong */ 590 respond = 1; 591 592 if (error == -3) { 593 594 /* 595 * Adjust mbuf so we can get to the start 596 * of the first option we did not like. 597 */ 598 599 m_adj(m, off - sizeof(hdr)); 600 m->m_pkthdr.len = sizeof(hdr) + hdr.length; 601 602 result = NG_L2CAP_UNKNOWN_OPTION; 603 } else { 604 /* XXX FIXME Send other reject codes? */ 605 NG_FREE_M(m); 606 result = NG_L2CAP_REJECT; 607 } 608 609 break; 610 } 611 } 612 613 /* 614 * Now check and see if we have to respond. If everything was OK then 615 * respond contain "C flag" and (if set) we will respond with empty 616 * packet and will wait for more options. 617 * 618 * Other case is that we did not like peer's options and will respond 619 * with L2CAP_Config response command with Reject error code. 620 * 621 * When "respond == 0" than we have received all options and we will 622 * sent L2CA_ConfigInd event to the upper layer protocol. 623 */ 624 625 if (respond) { 626 error = send_l2cap_cfg_rsp(con, ident, ch->dcid, result, m); 627 if (error != 0) { 628 ng_l2cap_l2ca_discon_ind(ch); 629 ng_l2cap_free_chan(ch); 630 } 631 } else { 632 /* Send L2CA_ConfigInd event to the upper layer protocol */ 633 ch->ident = ident; 634 error = ng_l2cap_l2ca_cfg_ind(ch); 635 if (error != 0) 636 ng_l2cap_free_chan(ch); 637 } 638 639 return (error); 640 641reject: 642 /* Send reject. Do not really care about the result */ 643 NG_FREE_M(m); 644 645 send_l2cap_reject(con, ident, NG_L2CAP_REJ_INVALID_CID, 0, 0, dcid); 646 647 return (0); 648} /* ng_l2cap_process_cfg_req */ 649 650/* 651 * Process L2CAP_ConfigRsp command 652 */ 653 654static int 655ng_l2cap_process_cfg_rsp(ng_l2cap_con_p con, u_int8_t ident) 656{ 657 ng_l2cap_p l2cap = con->l2cap; 658 struct mbuf *m = con->rx_pkt; 659 ng_l2cap_cfg_rsp_cp *cp = NULL; 660 ng_l2cap_cmd_p cmd = NULL; 661 u_int16_t scid, cflag, result; 662 ng_l2cap_cfg_opt_t hdr; 663 ng_l2cap_cfg_opt_val_t val; 664 int off, error = 0; 665 666 /* Get command parameters */ 667 con->rx_pkt = NULL; 668 NG_L2CAP_M_PULLUP(m, sizeof(*cp)); 669 if (m == NULL) 670 return (ENOBUFS); 671 672 cp = mtod(m, ng_l2cap_cfg_rsp_cp *); 673 scid = le16toh(cp->scid); 674 cflag = NG_L2CAP_OPT_CFLAG(le16toh(cp->flags)); 675 result = le16toh(cp->result); 676 m_adj(m, sizeof(*cp)); 677 678 /* Check if we have this command */ 679 cmd = ng_l2cap_cmd_by_ident(con, ident); 680 if (cmd == NULL) { 681 NG_L2CAP_ERR( 682"%s: %s - unexpected L2CAP_ConfigRsp command. ident=%d, con_handle=%d\n", 683 __func__, NG_NODE_NAME(l2cap->node), ident, 684 con->con_handle); 685 NG_FREE_M(m); 686 687 return (ENOENT); 688 } 689 690 /* Verify CIDs and send reject if does not match */ 691 if (cmd->ch->scid != scid) { 692 NG_L2CAP_ERR( 693"%s: %s - unexpected L2CAP_ConfigRsp. " \ 694"Channel ID does not match, scid=%d(%d)\n", 695 __func__, NG_NODE_NAME(l2cap->node), cmd->ch->scid, 696 scid); 697 goto reject; 698 } 699 700 /* Verify channel state and reject if invalid */ 701 if (cmd->ch->state != NG_L2CAP_CONFIG) { 702 NG_L2CAP_ERR( 703"%s: %s - unexpected L2CAP_ConfigRsp. " \ 704"Invalid channel state, scid=%d, state=%d\n", 705 __func__, NG_NODE_NAME(l2cap->node), cmd->ch->scid, 706 cmd->ch->state); 707 goto reject; 708 } 709 710 /* 711 * Looks like it is our response, so process it. First parse options, 712 * then verify C flag. If it is set then we shall expect more 713 * configuration options from the peer and we will wait. Otherwise we 714 * have received all options and we will send L2CA_ConfigRsp event to 715 * the upper layer protocol. If command timeout already happened then 716 * ignore response. 717 */ 718 719 if ((error = ng_l2cap_command_untimeout(cmd)) != 0) { 720 NG_FREE_M(m); 721 return (error); 722 } 723 724 for (off = 0; ; ) { 725 error = get_next_l2cap_opt(m, &off, &hdr, &val); 726 if (error == 0) /* We done with this packet */ 727 break; 728 else if (error > 0) { /* Got option */ 729 switch (hdr.type) { 730 case NG_L2CAP_OPT_MTU: 731 cmd->ch->imtu = val.mtu; 732 break; 733 734 case NG_L2CAP_OPT_FLUSH_TIMO: 735 cmd->ch->flush_timo = val.flush_timo; 736 break; 737 738 case NG_L2CAP_OPT_QOS: 739 bcopy(&val.flow, &cmd->ch->oflow, 740 sizeof(cmd->ch->oflow)); 741 break; 742 743 default: /* Ignore unknown hint option */ 744 break; 745 } 746 } else { 747 /* 748 * XXX FIXME What to do here? 749 * 750 * This is really BAD :( options packet was broken, or 751 * peer sent us option that we did not understand. Let 752 * upper layer know and do not wait for more options. 753 */ 754 755 NG_L2CAP_ALERT( 756"%s: %s - failed to parse configuration options, error=%d\n", 757 __func__, NG_NODE_NAME(l2cap->node), error); 758 759 result = NG_L2CAP_UNKNOWN; 760 cflag = 0; 761 762 break; 763 } 764 } 765 766 NG_FREE_M(m); 767 768 if (cflag) /* Restart timer and wait for more options */ 769 ng_l2cap_command_timeout(cmd, bluetooth_l2cap_rtx_timeout()); 770 else { 771 ng_l2cap_unlink_cmd(cmd); 772 773 /* Send L2CA_Config response to the upper layer protocol */ 774 error = ng_l2cap_l2ca_cfg_rsp(cmd->ch, cmd->token, result); 775 if (error != 0) { 776 /* 777 * XXX FIXME what to do here? we were not able to send 778 * response to the upper layer protocol, so for now 779 * just close the channel. Send L2CAP_Disconnect to 780 * remote peer? 781 */ 782 783 NG_L2CAP_ERR( 784"%s: %s - failed to send L2CA_Config response, error=%d\n", 785 __func__, NG_NODE_NAME(l2cap->node), error); 786 787 ng_l2cap_free_chan(cmd->ch); 788 } 789 790 ng_l2cap_free_cmd(cmd); 791 } 792 793 return (error); 794 795reject: 796 /* Send reject. Do not really care about the result */ 797 NG_FREE_M(m); 798 799 send_l2cap_reject(con, ident, NG_L2CAP_REJ_INVALID_CID, 0, scid, 0); 800 801 return (0); 802} /* ng_l2cap_process_cfg_rsp */ 803 804/* 805 * Process L2CAP_DisconnectReq command 806 */ 807 808static int 809ng_l2cap_process_discon_req(ng_l2cap_con_p con, u_int8_t ident) 810{ 811 ng_l2cap_p l2cap = con->l2cap; 812 ng_l2cap_discon_req_cp *cp = NULL; 813 ng_l2cap_chan_p ch = NULL; 814 ng_l2cap_cmd_p cmd = NULL; 815 u_int16_t scid, dcid; 816 817 /* Get command parameters */ 818 NG_L2CAP_M_PULLUP(con->rx_pkt, sizeof(*cp)); 819 if (con->rx_pkt == NULL) 820 return (ENOBUFS); 821 822 cp = mtod(con->rx_pkt, ng_l2cap_discon_req_cp *); 823 dcid = le16toh(cp->dcid); 824 scid = le16toh(cp->scid); 825 826 NG_FREE_M(con->rx_pkt); 827 828 /* Check if we have this channel and it is in valid state */ 829 ch = ng_l2cap_chan_by_scid(l2cap, dcid); 830 if (ch == NULL) { 831 NG_L2CAP_ERR( 832"%s: %s - unexpected L2CAP_DisconnectReq message. " \ 833"Channel does not exist, cid=%d\n", 834 __func__, NG_NODE_NAME(l2cap->node), dcid); 835 goto reject; 836 } 837 838 /* XXX Verify channel state and reject if invalid -- is that true? */ 839 if (ch->state != NG_L2CAP_OPEN && ch->state != NG_L2CAP_CONFIG && 840 ch->state != NG_L2CAP_W4_L2CAP_DISCON_RSP) { 841 NG_L2CAP_ERR( 842"%s: %s - unexpected L2CAP_DisconnectReq. " \ 843"Invalid channel state, cid=%d, state=%d\n", 844 __func__, NG_NODE_NAME(l2cap->node), dcid, ch->state); 845 goto reject; 846 } 847 848 /* Match destination channel ID */ 849 if (ch->dcid != scid || ch->scid != dcid) { 850 NG_L2CAP_ERR( 851"%s: %s - unexpected L2CAP_DisconnectReq. " \ 852"Channel IDs does not match, channel: scid=%d, dcid=%d, " \ 853"request: scid=%d, dcid=%d\n", 854 __func__, NG_NODE_NAME(l2cap->node), ch->scid, ch->dcid, 855 scid, dcid); 856 goto reject; 857 } 858 859 /* 860 * Looks good, so notify upper layer protocol that channel is about 861 * to be disconnected and send L2CA_DisconnectInd message. Then respond 862 * with L2CAP_DisconnectRsp. 863 */ 864 865 if (ch->state != NG_L2CAP_W4_L2CAP_DISCON_RSP) { 866 ng_l2cap_l2ca_discon_ind(ch); /* do not care about result */ 867 ng_l2cap_free_chan(ch); 868 } 869 870 /* Send L2CAP_DisconnectRsp */ 871 cmd = ng_l2cap_new_cmd(con, NULL, ident, NG_L2CAP_DISCON_RSP, 0); 872 if (cmd == NULL) 873 return (ENOMEM); 874 875 _ng_l2cap_discon_rsp(cmd->aux, ident, dcid, scid); 876 if (cmd->aux == NULL) { 877 ng_l2cap_free_cmd(cmd); 878 879 return (ENOBUFS); 880 } 881 882 /* Link command to the queue */ 883 ng_l2cap_link_cmd(con, cmd); 884 ng_l2cap_lp_deliver(con); 885 886 return (0); 887 888reject: 889 /* Send reject. Do not really care about the result */ 890 send_l2cap_reject(con, ident, NG_L2CAP_REJ_INVALID_CID, 0, scid, dcid); 891 892 return (0); 893} /* ng_l2cap_process_discon_req */ 894 895/* 896 * Process L2CAP_DisconnectRsp command 897 */ 898 899static int 900ng_l2cap_process_discon_rsp(ng_l2cap_con_p con, u_int8_t ident) 901{ 902 ng_l2cap_p l2cap = con->l2cap; 903 ng_l2cap_discon_rsp_cp *cp = NULL; 904 ng_l2cap_cmd_p cmd = NULL; 905 u_int16_t scid, dcid; 906 int error = 0; 907 908 /* Get command parameters */ 909 NG_L2CAP_M_PULLUP(con->rx_pkt, sizeof(*cp)); 910 if (con->rx_pkt == NULL) 911 return (ENOBUFS); 912 913 cp = mtod(con->rx_pkt, ng_l2cap_discon_rsp_cp *); 914 dcid = le16toh(cp->dcid); 915 scid = le16toh(cp->scid); 916 917 NG_FREE_M(con->rx_pkt); 918 919 /* Check if we have pending command descriptor */ 920 cmd = ng_l2cap_cmd_by_ident(con, ident); 921 if (cmd == NULL) { 922 NG_L2CAP_ERR( 923"%s: %s - unexpected L2CAP_DisconnectRsp command. ident=%d, con_handle=%d\n", 924 __func__, NG_NODE_NAME(l2cap->node), ident, 925 con->con_handle); 926 goto out; 927 } 928 929 /* Verify channel state, do nothing if invalid */ 930 if (cmd->ch->state != NG_L2CAP_W4_L2CAP_DISCON_RSP) { 931 NG_L2CAP_ERR( 932"%s: %s - unexpected L2CAP_DisconnectRsp. " \ 933"Invalid channel state, cid=%d, state=%d\n", 934 __func__, NG_NODE_NAME(l2cap->node), scid, 935 cmd->ch->state); 936 goto out; 937 } 938 939 /* Verify CIDs and send reject if does not match */ 940 if (cmd->ch->scid != scid || cmd->ch->dcid != dcid) { 941 NG_L2CAP_ERR( 942"%s: %s - unexpected L2CAP_DisconnectRsp. " \ 943"Channel IDs do not match, scid=%d(%d), dcid=%d(%d)\n", 944 __func__, NG_NODE_NAME(l2cap->node), cmd->ch->scid, 945 scid, cmd->ch->dcid, dcid); 946 goto out; 947 } 948 949 /* 950 * Looks like we have successfuly disconnected channel, so notify 951 * upper layer. If command timeout already happened then ignore 952 * response. 953 */ 954 955 if ((error = ng_l2cap_command_untimeout(cmd)) != 0) 956 goto out; 957 958 error = ng_l2cap_l2ca_discon_rsp(cmd->ch, cmd->token, NG_L2CAP_SUCCESS); 959 ng_l2cap_free_chan(cmd->ch); /* this will free commands too */ 960out: 961 return (error); 962} /* ng_l2cap_process_discon_rsp */ 963 964/* 965 * Process L2CAP_EchoReq command 966 */ 967 968static int 969ng_l2cap_process_echo_req(ng_l2cap_con_p con, u_int8_t ident) 970{ 971 ng_l2cap_p l2cap = con->l2cap; 972 ng_l2cap_cmd_hdr_t *hdr = NULL; 973 ng_l2cap_cmd_p cmd = NULL; 974 975 con->rx_pkt = ng_l2cap_prepend(con->rx_pkt, sizeof(*hdr)); 976 if (con->rx_pkt == NULL) { 977 NG_L2CAP_ALERT( 978"%s: %s - ng_l2cap_prepend() failed, size=%zd\n", 979 __func__, NG_NODE_NAME(l2cap->node), sizeof(*hdr)); 980 981 return (ENOBUFS); 982 } 983 984 hdr = mtod(con->rx_pkt, ng_l2cap_cmd_hdr_t *); 985 hdr->code = NG_L2CAP_ECHO_RSP; 986 hdr->ident = ident; 987 hdr->length = htole16(con->rx_pkt->m_pkthdr.len - sizeof(*hdr)); 988 989 cmd = ng_l2cap_new_cmd(con, NULL, ident, NG_L2CAP_ECHO_RSP, 0); 990 if (cmd == NULL) { 991 NG_FREE_M(con->rx_pkt); 992 993 return (ENOBUFS); 994 } 995 996 /* Attach data and link command to the queue */ 997 cmd->aux = con->rx_pkt; 998 con->rx_pkt = NULL; 999 ng_l2cap_link_cmd(con, cmd); 1000 ng_l2cap_lp_deliver(con); 1001 1002 return (0); 1003} /* ng_l2cap_process_echo_req */ 1004 1005/* 1006 * Process L2CAP_EchoRsp command 1007 */ 1008 1009static int 1010ng_l2cap_process_echo_rsp(ng_l2cap_con_p con, u_int8_t ident) 1011{ 1012 ng_l2cap_p l2cap = con->l2cap; 1013 ng_l2cap_cmd_p cmd = NULL; 1014 int error = 0; 1015 1016 /* Check if we have this command */ 1017 cmd = ng_l2cap_cmd_by_ident(con, ident); 1018 if (cmd != NULL) { 1019 /* If command timeout already happened then ignore response */ 1020 if ((error = ng_l2cap_command_untimeout(cmd)) != 0) { 1021 NG_FREE_M(con->rx_pkt); 1022 return (error); 1023 } 1024 1025 ng_l2cap_unlink_cmd(cmd); 1026 1027 error = ng_l2cap_l2ca_ping_rsp(cmd->con, cmd->token, 1028 NG_L2CAP_SUCCESS, con->rx_pkt); 1029 1030 ng_l2cap_free_cmd(cmd); 1031 con->rx_pkt = NULL; 1032 } else { 1033 NG_L2CAP_ERR( 1034"%s: %s - unexpected L2CAP_EchoRsp command. " \ 1035"Requested ident does not exist, ident=%d\n", 1036 __func__, NG_NODE_NAME(l2cap->node), ident); 1037 NG_FREE_M(con->rx_pkt); 1038 } 1039 1040 return (error); 1041} /* ng_l2cap_process_echo_rsp */ 1042 1043/* 1044 * Process L2CAP_InfoReq command 1045 */ 1046 1047static int 1048ng_l2cap_process_info_req(ng_l2cap_con_p con, u_int8_t ident) 1049{ 1050 ng_l2cap_p l2cap = con->l2cap; 1051 ng_l2cap_cmd_p cmd = NULL; 1052 u_int16_t type; 1053 1054 /* Get command parameters */ 1055 NG_L2CAP_M_PULLUP(con->rx_pkt, sizeof(ng_l2cap_info_req_cp)); 1056 if (con->rx_pkt == NULL) 1057 return (ENOBUFS); 1058 1059 type = le16toh(mtod(con->rx_pkt, ng_l2cap_info_req_cp *)->type); 1060 NG_FREE_M(con->rx_pkt); 1061 1062 cmd = ng_l2cap_new_cmd(con, NULL, ident, NG_L2CAP_INFO_RSP, 0); 1063 if (cmd == NULL) 1064 return (ENOMEM); 1065 1066 switch (type) { 1067 case NG_L2CAP_CONNLESS_MTU: 1068 _ng_l2cap_info_rsp(cmd->aux, ident, NG_L2CAP_CONNLESS_MTU, 1069 NG_L2CAP_SUCCESS, NG_L2CAP_MTU_DEFAULT); 1070 break; 1071 1072 default: 1073 _ng_l2cap_info_rsp(cmd->aux, ident, type, 1074 NG_L2CAP_NOT_SUPPORTED, 0); 1075 break; 1076 } 1077 1078 if (cmd->aux == NULL) { 1079 ng_l2cap_free_cmd(cmd); 1080 1081 return (ENOBUFS); 1082 } 1083 1084 /* Link command to the queue */ 1085 ng_l2cap_link_cmd(con, cmd); 1086 ng_l2cap_lp_deliver(con); 1087 1088 return (0); 1089} /* ng_l2cap_process_info_req */ 1090 1091/* 1092 * Process L2CAP_InfoRsp command 1093 */ 1094 1095static int 1096ng_l2cap_process_info_rsp(ng_l2cap_con_p con, u_int8_t ident) 1097{ 1098 ng_l2cap_p l2cap = con->l2cap; 1099 ng_l2cap_info_rsp_cp *cp = NULL; 1100 ng_l2cap_cmd_p cmd = NULL; 1101 int error = 0; 1102 1103 /* Get command parameters */ 1104 NG_L2CAP_M_PULLUP(con->rx_pkt, sizeof(*cp)); 1105 if (con->rx_pkt == NULL) 1106 return (ENOBUFS); 1107 1108 cp = mtod(con->rx_pkt, ng_l2cap_info_rsp_cp *); 1109 cp->type = le16toh(cp->type); 1110 cp->result = le16toh(cp->result); 1111 m_adj(con->rx_pkt, sizeof(*cp)); 1112 1113 /* Check if we have pending command descriptor */ 1114 cmd = ng_l2cap_cmd_by_ident(con, ident); 1115 if (cmd == NULL) { 1116 NG_L2CAP_ERR( 1117"%s: %s - unexpected L2CAP_InfoRsp command. " \ 1118"Requested ident does not exist, ident=%d\n", 1119 __func__, NG_NODE_NAME(l2cap->node), ident); 1120 NG_FREE_M(con->rx_pkt); 1121 1122 return (ENOENT); 1123 } 1124 1125 /* If command timeout already happened then ignore response */ 1126 if ((error = ng_l2cap_command_untimeout(cmd)) != 0) { 1127 NG_FREE_M(con->rx_pkt); 1128 return (error); 1129 } 1130 1131 ng_l2cap_unlink_cmd(cmd); 1132 1133 if (cp->result == NG_L2CAP_SUCCESS) { 1134 switch (cp->type) { 1135 case NG_L2CAP_CONNLESS_MTU: 1136 if (con->rx_pkt->m_pkthdr.len == sizeof(u_int16_t)) 1137 *mtod(con->rx_pkt, u_int16_t *) = 1138 le16toh(*mtod(con->rx_pkt,u_int16_t *)); 1139 else { 1140 cp->result = NG_L2CAP_UNKNOWN; /* XXX */ 1141 1142 NG_L2CAP_ERR( 1143"%s: %s - invalid L2CAP_InfoRsp command. " \ 1144"Bad connectionless MTU parameter, len=%d\n", 1145 __func__, NG_NODE_NAME(l2cap->node), 1146 con->rx_pkt->m_pkthdr.len); 1147 } 1148 break; 1149 1150 default: 1151 NG_L2CAP_WARN( 1152"%s: %s - invalid L2CAP_InfoRsp command. Unknown info type=%d\n", 1153 __func__, NG_NODE_NAME(l2cap->node), cp->type); 1154 break; 1155 } 1156 } 1157 1158 error = ng_l2cap_l2ca_get_info_rsp(cmd->con, cmd->token, 1159 cp->result, con->rx_pkt); 1160 1161 ng_l2cap_free_cmd(cmd); 1162 con->rx_pkt = NULL; 1163 1164 return (error); 1165} /* ng_l2cap_process_info_rsp */ 1166 1167/* 1168 * Send L2CAP reject 1169 */ 1170 1171static int 1172send_l2cap_reject(ng_l2cap_con_p con, u_int8_t ident, u_int16_t reason, 1173 u_int16_t mtu, u_int16_t scid, u_int16_t dcid) 1174{ 1175 ng_l2cap_cmd_p cmd = NULL; 1176 1177 cmd = ng_l2cap_new_cmd(con, NULL, ident, NG_L2CAP_CMD_REJ, 0); 1178 if (cmd == NULL) 1179 return (ENOMEM); 1180 1181 _ng_l2cap_cmd_rej(cmd->aux, cmd->ident, reason, mtu, scid, dcid); 1182 if (cmd->aux == NULL) { 1183 ng_l2cap_free_cmd(cmd); 1184 1185 return (ENOBUFS); 1186 } 1187 1188 /* Link command to the queue */ 1189 ng_l2cap_link_cmd(con, cmd); 1190 ng_l2cap_lp_deliver(con); 1191 1192 return (0); 1193} /* send_l2cap_reject */ 1194 1195/* 1196 * Send L2CAP connection reject 1197 */ 1198 1199static int 1200send_l2cap_con_rej(ng_l2cap_con_p con, u_int8_t ident, u_int16_t scid, 1201 u_int16_t dcid, u_int16_t result) 1202{ 1203 ng_l2cap_cmd_p cmd = NULL; 1204 1205 cmd = ng_l2cap_new_cmd(con, NULL, ident, NG_L2CAP_CON_RSP, 0); 1206 if (cmd == NULL) 1207 return (ENOMEM); 1208 1209 _ng_l2cap_con_rsp(cmd->aux, cmd->ident, scid, dcid, result, 0); 1210 if (cmd->aux == NULL) { 1211 ng_l2cap_free_cmd(cmd); 1212 1213 return (ENOBUFS); 1214 } 1215 1216 /* Link command to the queue */ 1217 ng_l2cap_link_cmd(con, cmd); 1218 ng_l2cap_lp_deliver(con); 1219 1220 return (0); 1221} /* send_l2cap_con_rej */ 1222 1223/* 1224 * Send L2CAP config response 1225 */ 1226 1227static int 1228send_l2cap_cfg_rsp(ng_l2cap_con_p con, u_int8_t ident, u_int16_t scid, 1229 u_int16_t result, struct mbuf *opt) 1230{ 1231 ng_l2cap_cmd_p cmd = NULL; 1232 1233 cmd = ng_l2cap_new_cmd(con, NULL, ident, NG_L2CAP_CFG_RSP, 0); 1234 if (cmd == NULL) { 1235 NG_FREE_M(opt); 1236 1237 return (ENOMEM); 1238 } 1239 1240 _ng_l2cap_cfg_rsp(cmd->aux, cmd->ident, scid, 0, result, opt); 1241 if (cmd->aux == NULL) { 1242 ng_l2cap_free_cmd(cmd); 1243 1244 return (ENOBUFS); 1245 } 1246 1247 /* Link command to the queue */ 1248 ng_l2cap_link_cmd(con, cmd); 1249 ng_l2cap_lp_deliver(con); 1250 1251 return (0); 1252} /* send_l2cap_cfg_rsp */ 1253 1254/* 1255 * Get next L2CAP configuration option 1256 * 1257 * Return codes: 1258 * 0 no option 1259 * 1 we have got option 1260 * -1 header too short 1261 * -2 bad option value or length 1262 * -3 unknown option 1263 */ 1264 1265static int 1266get_next_l2cap_opt(struct mbuf *m, int *off, ng_l2cap_cfg_opt_p hdr, 1267 ng_l2cap_cfg_opt_val_p val) 1268{ 1269 int hint, len = m->m_pkthdr.len - (*off); 1270 1271 if (len == 0) 1272 return (0); 1273 if (len < 0 || len < sizeof(*hdr)) 1274 return (-1); 1275 1276 m_copydata(m, *off, sizeof(*hdr), (caddr_t) hdr); 1277 *off += sizeof(*hdr); 1278 len -= sizeof(*hdr); 1279 1280 hint = NG_L2CAP_OPT_HINT(hdr->type); 1281 hdr->type &= NG_L2CAP_OPT_HINT_MASK; 1282 1283 switch (hdr->type) { 1284 case NG_L2CAP_OPT_MTU: 1285 if (hdr->length != NG_L2CAP_OPT_MTU_SIZE || len < hdr->length) 1286 return (-2); 1287 1288 m_copydata(m, *off, NG_L2CAP_OPT_MTU_SIZE, (caddr_t) val); 1289 val->mtu = le16toh(val->mtu); 1290 *off += NG_L2CAP_OPT_MTU_SIZE; 1291 break; 1292 1293 case NG_L2CAP_OPT_FLUSH_TIMO: 1294 if (hdr->length != NG_L2CAP_OPT_FLUSH_TIMO_SIZE || 1295 len < hdr->length) 1296 return (-2); 1297 1298 m_copydata(m, *off, NG_L2CAP_OPT_FLUSH_TIMO_SIZE, (caddr_t)val); 1299 val->flush_timo = le16toh(val->flush_timo); 1300 *off += NG_L2CAP_OPT_FLUSH_TIMO_SIZE; 1301 break; 1302 1303 case NG_L2CAP_OPT_QOS: 1304 if (hdr->length != NG_L2CAP_OPT_QOS_SIZE || len < hdr->length) 1305 return (-2); 1306 1307 m_copydata(m, *off, NG_L2CAP_OPT_QOS_SIZE, (caddr_t) val); 1308 val->flow.token_rate = le32toh(val->flow.token_rate); 1309 val->flow.token_bucket_size = 1310 le32toh(val->flow.token_bucket_size); 1311 val->flow.peak_bandwidth = le32toh(val->flow.peak_bandwidth); 1312 val->flow.latency = le32toh(val->flow.latency); 1313 val->flow.delay_variation = le32toh(val->flow.delay_variation); 1314 *off += NG_L2CAP_OPT_QOS_SIZE; 1315 break; 1316 1317 default: 1318 if (hint) 1319 *off += hdr->length; 1320 else 1321 return (-3); 1322 break; 1323 } 1324 1325 return (1); 1326} /* get_next_l2cap_opt */ 1327 1328