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