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