xref: /freebsd/contrib/wpa/src/common/gas_server.c (revision a90b9d01)
1 /*
2  * Generic advertisement service (GAS) server
3  * Copyright (c) 2017, Qualcomm Atheros, Inc.
4  * Copyright (c) 2020, The Linux Foundation
5  * Copyright (c) 2022, Qualcomm Innovation Center, Inc.
6  *
7  * This software may be distributed under the terms of the BSD license.
8  * See README for more details.
9  */
10 
11 #include "includes.h"
12 
13 #include "utils/common.h"
14 #include "utils/list.h"
15 #include "utils/eloop.h"
16 #include "ieee802_11_defs.h"
17 #include "gas.h"
18 #include "gas_server.h"
19 
20 
21 #define MAX_ADV_PROTO_ID_LEN 10
22 #define GAS_QUERY_TIMEOUT 60
23 
24 struct gas_server_handler {
25 	struct dl_list list;
26 	u8 adv_proto_id[MAX_ADV_PROTO_ID_LEN];
27 	u8 adv_proto_id_len;
28 	struct wpabuf * (*req_cb)(void *ctx, void *resp_ctx, const u8 *sa,
29 				  const u8 *query, size_t query_len,
30 				  int *comeback_delay);
31 	void (*status_cb)(void *ctx, struct wpabuf *resp, int ok);
32 	void *ctx;
33 	struct gas_server *gas;
34 };
35 
36 struct gas_server_response {
37 	struct dl_list list;
38 	size_t offset;
39 	u8 frag_id;
40 	struct wpabuf *resp;
41 	int freq;
42 	u8 dst[ETH_ALEN];
43 	u8 dialog_token;
44 	struct gas_server_handler *handler;
45 	u16 comeback_delay;
46 	bool initial_resp_sent;
47 };
48 
49 struct gas_server {
50 	struct dl_list handlers; /* struct gas_server_handler::list */
51 	struct dl_list responses; /* struct gas_server_response::list */
52 	void (*tx)(void *ctx, int freq, const u8 *da, struct wpabuf *resp,
53 		   unsigned int wait_time);
54 	void *ctx;
55 };
56 
57 static void gas_server_free_response(struct gas_server_response *response);
58 
59 
gas_server_response_timeout(void * eloop_ctx,void * user_ctx)60 static void gas_server_response_timeout(void *eloop_ctx, void *user_ctx)
61 {
62 	struct gas_server_response *response = eloop_ctx;
63 
64 	wpa_printf(MSG_DEBUG, "GAS: Response @%p timeout for " MACSTR
65 		   " (dialog_token=%u freq=%d frag_id=%u sent=%lu/%lu) - drop pending data",
66 		   response, MAC2STR(response->dst), response->dialog_token,
67 		   response->freq, response->frag_id,
68 		   (unsigned long) response->offset,
69 		   (unsigned long) (response->resp ?
70 				    wpabuf_len(response->resp) : 0));
71 	response->handler->status_cb(response->handler->ctx,
72 				     response->resp, 0);
73 	response->resp = NULL;
74 	dl_list_del(&response->list);
75 	gas_server_free_response(response);
76 }
77 
78 
gas_server_free_response(struct gas_server_response * response)79 static void gas_server_free_response(struct gas_server_response *response)
80 {
81 	if (!response)
82 		return;
83 	wpa_printf(MSG_DEBUG, "DPP: Free GAS response @%p", response);
84 	eloop_cancel_timeout(gas_server_response_timeout, response, NULL);
85 	wpabuf_free(response->resp);
86 	os_free(response);
87 }
88 
89 
90 static void
gas_server_send_resp(struct gas_server * gas,struct gas_server_response * response,struct wpabuf * query_resp,u16 comeback_delay)91 gas_server_send_resp(struct gas_server *gas,
92 		     struct gas_server_response *response,
93 		     struct wpabuf *query_resp, u16 comeback_delay)
94 {
95 	struct gas_server_handler *handler = response->handler;
96 	size_t max_len = (response->freq > 56160) ? 928 : 1400;
97 	size_t hdr_len = 24 + 2 + 5 + 3 + handler->adv_proto_id_len + 2;
98 	size_t resp_frag_len;
99 	struct wpabuf *resp;
100 
101 	if (comeback_delay == 0 && !query_resp) {
102 		dl_list_del(&response->list);
103 		gas_server_free_response(response);
104 		return;
105 	}
106 
107 	if (comeback_delay) {
108 		/* Need more time to prepare the response */
109 		resp_frag_len = 0;
110 		response->comeback_delay = comeback_delay;
111 	} else if (hdr_len + wpabuf_len(query_resp) > max_len) {
112 		/* Need to use comeback to initiate fragmentation */
113 		comeback_delay = 1;
114 		resp_frag_len = 0;
115 	} else {
116 		/* Full response fits into the initial response */
117 		comeback_delay = 0;
118 		resp_frag_len = wpabuf_len(query_resp);
119 	}
120 
121 	resp = gas_build_initial_resp(response->dialog_token,
122 				      WLAN_STATUS_SUCCESS,
123 				      comeback_delay,
124 				      handler->adv_proto_id_len +
125 				      resp_frag_len);
126 	if (!resp) {
127 		wpabuf_free(query_resp);
128 		dl_list_del(&response->list);
129 		gas_server_free_response(response);
130 		return;
131 	}
132 
133 	/* Advertisement Protocol element */
134 	wpabuf_put_u8(resp, WLAN_EID_ADV_PROTO);
135 	wpabuf_put_u8(resp, 1 + handler->adv_proto_id_len); /* Length */
136 	wpabuf_put_u8(resp, 0x7f);
137 	/* Advertisement Protocol ID */
138 	wpabuf_put_data(resp, handler->adv_proto_id, handler->adv_proto_id_len);
139 
140 	/* Query Response Length */
141 	wpabuf_put_le16(resp, resp_frag_len);
142 	if (!comeback_delay && query_resp)
143 		wpabuf_put_buf(resp, query_resp);
144 
145 	if (comeback_delay && !query_resp) {
146 		wpa_printf(MSG_DEBUG, "GAS: No response available yet");
147 	} else if (comeback_delay) {
148 		wpa_printf(MSG_DEBUG,
149 			   "GAS: Need to fragment query response");
150 	} else {
151 		wpa_printf(MSG_DEBUG,
152 			   "GAS: Full query response fits in the GAS Initial Response frame");
153 	}
154 	response->offset = resp_frag_len;
155 	response->resp = query_resp;
156 	response->initial_resp_sent = true;
157 	gas->tx(gas->ctx, response->freq, response->dst, resp,
158 		comeback_delay ? 2000 : 0);
159 	wpabuf_free(resp);
160 	eloop_register_timeout(GAS_QUERY_TIMEOUT, 0,
161 			       gas_server_response_timeout, response, NULL);
162 }
163 
164 
165 static int
gas_server_rx_initial_req(struct gas_server * gas,const u8 * da,const u8 * sa,const u8 * bssid,int freq,u8 dialog_token,const u8 * data,size_t len)166 gas_server_rx_initial_req(struct gas_server *gas, const u8 *da, const u8 *sa,
167 			  const u8 *bssid, int freq, u8 dialog_token,
168 			  const u8 *data, size_t len)
169 {
170 	const u8 *pos, *end, *adv_proto, *query_req;
171 	u8 adv_proto_len;
172 	u16 query_req_len;
173 	struct gas_server_handler *handler;
174 	struct wpabuf *resp;
175 	struct gas_server_response *response;
176 
177 	wpa_hexdump(MSG_MSGDUMP, "GAS: Received GAS Initial Request frame",
178 		    data, len);
179 	pos = data;
180 	end = data + len;
181 
182 	if (end - pos < 2 || pos[0] != WLAN_EID_ADV_PROTO) {
183 		wpa_printf(MSG_DEBUG,
184 			   "GAS: No Advertisement Protocol element found");
185 		return -1;
186 	}
187 	pos++;
188 	adv_proto_len = *pos++;
189 	if (end - pos < adv_proto_len || adv_proto_len < 2) {
190 		wpa_printf(MSG_DEBUG,
191 			   "GAS: Truncated Advertisement Protocol element");
192 		return -1;
193 	}
194 
195 	adv_proto = pos;
196 	pos += adv_proto_len;
197 	wpa_hexdump(MSG_MSGDUMP, "GAS: Advertisement Protocol element",
198 		    adv_proto, adv_proto_len);
199 
200 	if (end - pos < 2) {
201 		wpa_printf(MSG_DEBUG, "GAS: No Query Request Length field");
202 		return -1;
203 	}
204 	query_req_len = WPA_GET_LE16(pos);
205 	pos += 2;
206 	if (end - pos < query_req_len) {
207 		wpa_printf(MSG_DEBUG, "GAS: Truncated Query Request field");
208 		return -1;
209 	}
210 	query_req = pos;
211 	pos += query_req_len;
212 	wpa_hexdump(MSG_MSGDUMP, "GAS: Query Request",
213 		    query_req, query_req_len);
214 
215 	if (pos < end) {
216 		wpa_hexdump(MSG_MSGDUMP,
217 			    "GAS: Ignored extra data after Query Request field",
218 			    pos, end - pos);
219 	}
220 
221 	response = os_zalloc(sizeof(*response));
222 	if (!response)
223 		return -1;
224 
225 	wpa_printf(MSG_DEBUG, "DPP: Allocated GAS response @%p", response);
226 	dl_list_for_each(handler, &gas->handlers, struct gas_server_handler,
227 			 list) {
228 		int comeback_delay = 0;
229 
230 		if (adv_proto_len < 1 + handler->adv_proto_id_len ||
231 		    os_memcmp(adv_proto + 1, handler->adv_proto_id,
232 			      handler->adv_proto_id_len) != 0)
233 			continue;
234 
235 		response->freq = freq;
236 		response->handler = handler;
237 		os_memcpy(response->dst, sa, ETH_ALEN);
238 		response->dialog_token = dialog_token;
239 		dl_list_add(&gas->responses, &response->list);
240 
241 		wpa_printf(MSG_DEBUG,
242 			   "GAS: Calling handler for the requested Advertisement Protocol ID");
243 		resp = handler->req_cb(handler->ctx, response, sa, query_req,
244 				       query_req_len, &comeback_delay);
245 		wpa_hexdump_buf(MSG_MSGDUMP, "GAS: Response from the handler",
246 				resp);
247 		if (comeback_delay < 0) {
248 			wpa_printf(MSG_DEBUG,
249 				   "GAS: Handler requested short delay before sending out the initial response");
250 			return 0;
251 		}
252 		if (comeback_delay)
253 			wpa_printf(MSG_DEBUG,
254 				   "GAS: Handler requested comeback delay: %u TU",
255 				   comeback_delay);
256 		gas_server_send_resp(gas, response, resp, comeback_delay);
257 		return 0;
258 	}
259 
260 	wpa_printf(MSG_DEBUG,
261 		   "GAS: No registered handler for the requested Advertisement Protocol ID");
262 	gas_server_free_response(response);
263 	return -1;
264 }
265 
266 
267 static void
gas_server_handle_rx_comeback_req(struct gas_server_response * response)268 gas_server_handle_rx_comeback_req(struct gas_server_response *response)
269 {
270 	struct gas_server_handler *handler = response->handler;
271 	struct gas_server *gas = handler->gas;
272 	size_t max_len = (response->freq > 56160) ? 928 : 1400;
273 	size_t hdr_len = 24 + 2 + 6 + 3 + handler->adv_proto_id_len + 2;
274 	size_t remaining, resp_frag_len;
275 	struct wpabuf *resp;
276 	unsigned int wait_time = 0;
277 
278 	if (!response->resp) {
279 		resp = gas_build_comeback_resp(response->dialog_token,
280 					       WLAN_STATUS_SUCCESS, 0, 0,
281 					       response->comeback_delay,
282 					       handler->adv_proto_id_len);
283 		if (!resp) {
284 			dl_list_del(&response->list);
285 			gas_server_free_response(response);
286 			return;
287 		}
288 
289 		/* Advertisement Protocol element */
290 		wpabuf_put_u8(resp, WLAN_EID_ADV_PROTO);
291 		wpabuf_put_u8(resp, 1 + handler->adv_proto_id_len); /* Length */
292 		wpabuf_put_u8(resp, 0x7f);
293 		/* Advertisement Protocol ID */
294 		wpabuf_put_data(resp, handler->adv_proto_id,
295 				handler->adv_proto_id_len);
296 
297 		/* Query Response Length */
298 		wpabuf_put_le16(resp, 0);
299 		goto send_resp;
300 	}
301 
302 	remaining = wpabuf_len(response->resp) - response->offset;
303 	if (hdr_len + remaining > max_len)
304 		resp_frag_len = max_len - hdr_len;
305 	else
306 		resp_frag_len = remaining;
307 	wpa_printf(MSG_DEBUG,
308 		   "GAS: Sending out %u/%u remaining Query Response octets",
309 		   (unsigned int) resp_frag_len, (unsigned int) remaining);
310 
311 	resp = gas_build_comeback_resp(response->dialog_token,
312 				       WLAN_STATUS_SUCCESS,
313 				       response->frag_id++,
314 				       resp_frag_len < remaining, 0,
315 				       handler->adv_proto_id_len +
316 				       resp_frag_len);
317 	if (!resp) {
318 		dl_list_del(&response->list);
319 		gas_server_free_response(response);
320 		return;
321 	}
322 
323 	/* Advertisement Protocol element */
324 	wpabuf_put_u8(resp, WLAN_EID_ADV_PROTO);
325 	wpabuf_put_u8(resp, 1 + handler->adv_proto_id_len); /* Length */
326 	wpabuf_put_u8(resp, 0x7f);
327 	/* Advertisement Protocol ID */
328 	wpabuf_put_data(resp, handler->adv_proto_id, handler->adv_proto_id_len);
329 
330 	/* Query Response Length */
331 	wpabuf_put_le16(resp, resp_frag_len);
332 	wpabuf_put_data(resp, wpabuf_head_u8(response->resp) + response->offset,
333 			resp_frag_len);
334 
335 	response->offset += resp_frag_len;
336 
337 	if (remaining > resp_frag_len)
338 		wait_time = 2000;
339 
340 send_resp:
341 	gas->tx(gas->ctx, response->freq, response->dst, resp, wait_time);
342 	wpabuf_free(resp);
343 }
344 
345 
346 static int
gas_server_rx_comeback_req(struct gas_server * gas,const u8 * da,const u8 * sa,const u8 * bssid,int freq,u8 dialog_token)347 gas_server_rx_comeback_req(struct gas_server *gas, const u8 *da, const u8 *sa,
348 			   const u8 *bssid, int freq, u8 dialog_token)
349 {
350 	struct gas_server_response *response;
351 
352 	dl_list_for_each(response, &gas->responses, struct gas_server_response,
353 			 list) {
354 		if (response->dialog_token != dialog_token ||
355 		    !ether_addr_equal(sa, response->dst))
356 			continue;
357 		gas_server_handle_rx_comeback_req(response);
358 		return 0;
359 	}
360 
361 	wpa_printf(MSG_DEBUG, "GAS: No pending GAS response for " MACSTR
362 		   " (dialog token %u)", MAC2STR(sa), dialog_token);
363 	return -1;
364 }
365 
366 
367 /**
368  * gas_query_rx - Indicate reception of a Public Action or Protected Dual frame
369  * @gas: GAS query data from gas_server_init()
370  * @da: Destination MAC address of the Action frame
371  * @sa: Source MAC address of the Action frame
372  * @bssid: BSSID of the Action frame
373  * @categ: Category of the Action frame
374  * @data: Payload of the Action frame
375  * @len: Length of @data
376  * @freq: Frequency (in MHz) on which the frame was received
377  * Returns: 0 if the Public Action frame was a GAS request frame or -1 if not
378  */
gas_server_rx(struct gas_server * gas,const u8 * da,const u8 * sa,const u8 * bssid,u8 categ,const u8 * data,size_t len,int freq)379 int gas_server_rx(struct gas_server *gas, const u8 *da, const u8 *sa,
380 		  const u8 *bssid, u8 categ, const u8 *data, size_t len,
381 		  int freq)
382 {
383 	u8 action, dialog_token;
384 	const u8 *pos, *end;
385 
386 	if (!gas || len < 2)
387 		return -1;
388 
389 	if (categ == WLAN_ACTION_PROTECTED_DUAL)
390 		return -1; /* Not supported for now */
391 
392 	pos = data;
393 	end = data + len;
394 	action = *pos++;
395 	dialog_token = *pos++;
396 
397 	if (action != WLAN_PA_GAS_INITIAL_REQ &&
398 	    action != WLAN_PA_GAS_COMEBACK_REQ)
399 		return -1; /* Not a GAS request */
400 
401 	wpa_printf(MSG_DEBUG, "GAS: Received GAS %s Request frame DA=" MACSTR
402 		   " SA=" MACSTR " BSSID=" MACSTR
403 		   " freq=%d dialog_token=%u len=%u",
404 		   action == WLAN_PA_GAS_INITIAL_REQ ? "Initial" : "Comeback",
405 		   MAC2STR(da), MAC2STR(sa), MAC2STR(bssid), freq, dialog_token,
406 		   (unsigned int) len);
407 
408 	if (action == WLAN_PA_GAS_INITIAL_REQ)
409 		return gas_server_rx_initial_req(gas, da, sa, bssid,
410 						 freq, dialog_token,
411 						 pos, end - pos);
412 	return gas_server_rx_comeback_req(gas, da, sa, bssid,
413 					  freq, dialog_token);
414 }
415 
416 
gas_server_handle_tx_status(struct gas_server_response * response,int ack)417 static void gas_server_handle_tx_status(struct gas_server_response *response,
418 					int ack)
419 {
420 	if (ack && response->resp &&
421 	    response->offset < wpabuf_len(response->resp)) {
422 		wpa_printf(MSG_DEBUG,
423 			   "GAS: More fragments remaining - keep pending entry");
424 		return;
425 	}
426 
427 	if (ack && !response->resp && response->comeback_delay) {
428 		wpa_printf(MSG_DEBUG,
429 			   "GAS: Waiting for response - keep pending entry");
430 		return;
431 	}
432 
433 	if (!ack)
434 		wpa_printf(MSG_DEBUG,
435 			   "GAS: No ACK received - drop pending entry");
436 	else
437 		wpa_printf(MSG_DEBUG,
438 			   "GAS: Last fragment of the response sent out - drop pending entry");
439 
440 	response->handler->status_cb(response->handler->ctx,
441 				     response->resp, ack);
442 	response->resp = NULL;
443 	dl_list_del(&response->list);
444 	gas_server_free_response(response);
445 }
446 
447 
gas_server_tx_status(struct gas_server * gas,const u8 * dst,const u8 * data,size_t data_len,int ack)448 void gas_server_tx_status(struct gas_server *gas, const u8 *dst, const u8 *data,
449 			  size_t data_len, int ack)
450 {
451 	const u8 *pos;
452 	u8 action, code, dialog_token;
453 	struct gas_server_response *response;
454 
455 	if (data_len < 24 + 3)
456 		return;
457 	pos = data + 24;
458 	action = *pos++;
459 	code = *pos++;
460 	dialog_token = *pos++;
461 	if (action != WLAN_ACTION_PUBLIC ||
462 	    (code != WLAN_PA_GAS_INITIAL_RESP &&
463 	     code != WLAN_PA_GAS_COMEBACK_RESP))
464 		return;
465 	wpa_printf(MSG_DEBUG, "GAS: TX status dst=" MACSTR
466 		   " ack=%d %s dialog_token=%u",
467 		   MAC2STR(dst), ack,
468 		   code == WLAN_PA_GAS_INITIAL_RESP ? "initial" : "comeback",
469 		   dialog_token);
470 	dl_list_for_each(response, &gas->responses, struct gas_server_response,
471 			 list) {
472 		if (response->dialog_token != dialog_token ||
473 		    !ether_addr_equal(dst, response->dst))
474 			continue;
475 		gas_server_handle_tx_status(response, ack);
476 		return;
477 	}
478 
479 	wpa_printf(MSG_DEBUG, "GAS: No pending response matches TX status");
480 }
481 
482 
gas_server_set_resp(struct gas_server * gas,void * resp_ctx,struct wpabuf * resp)483 int gas_server_set_resp(struct gas_server *gas, void *resp_ctx,
484 			struct wpabuf *resp)
485 {
486 	struct gas_server_response *tmp, *response = NULL;
487 
488 	dl_list_for_each(tmp, &gas->responses, struct gas_server_response,
489 			 list) {
490 		if (tmp == resp_ctx) {
491 			response = tmp;
492 			break;
493 		}
494 	}
495 
496 	if (!response || response->resp)
497 		return -1;
498 
499 	if (!response->initial_resp_sent) {
500 		wpa_printf(MSG_DEBUG, "GAS: Send the delayed initial response");
501 		gas_server_send_resp(gas, response, resp, 0);
502 		return 0;
503 	}
504 
505 	response->resp = resp;
506 	return 0;
507 }
508 
509 
gas_server_set_comeback_delay(struct gas_server * gas,void * resp_ctx,u16 comeback_delay)510 int gas_server_set_comeback_delay(struct gas_server *gas, void *resp_ctx,
511 				  u16 comeback_delay)
512 {
513 	struct gas_server_response *tmp, *response = NULL;
514 
515 	dl_list_for_each(tmp, &gas->responses, struct gas_server_response,
516 			 list) {
517 		if (tmp == resp_ctx) {
518 			response = tmp;
519 			break;
520 		}
521 	}
522 
523 	if (!response || response->initial_resp_sent)
524 		return -1;
525 
526 	wpa_printf(MSG_DEBUG,
527 		   "GAS: Send the delayed initial response with comeback delay %u",
528 		   comeback_delay);
529 	gas_server_send_resp(gas, response, NULL, comeback_delay);
530 
531 	return 0;
532 }
533 
534 
gas_server_response_sent(struct gas_server * gas,void * resp_ctx)535 bool gas_server_response_sent(struct gas_server *gas, void *resp_ctx)
536 {
537 	struct gas_server_response *tmp;
538 
539 	dl_list_for_each(tmp, &gas->responses, struct gas_server_response,
540 			 list) {
541 		if (tmp == resp_ctx)
542 			return tmp->resp &&
543 				tmp->offset == wpabuf_len(tmp->resp);
544 	}
545 
546 	return false;
547 }
548 
549 
gas_server_init(void * ctx,void (* tx)(void * ctx,int freq,const u8 * da,struct wpabuf * buf,unsigned int wait_time))550 struct gas_server * gas_server_init(void *ctx,
551 				    void (*tx)(void *ctx, int freq,
552 					       const u8 *da,
553 					       struct wpabuf *buf,
554 					       unsigned int wait_time))
555 {
556 	struct gas_server *gas;
557 
558 	gas = os_zalloc(sizeof(*gas));
559 	if (!gas)
560 		return NULL;
561 	gas->ctx = ctx;
562 	gas->tx = tx;
563 	dl_list_init(&gas->handlers);
564 	dl_list_init(&gas->responses);
565 	return gas;
566 }
567 
568 
gas_server_deinit(struct gas_server * gas)569 void gas_server_deinit(struct gas_server *gas)
570 {
571 	struct gas_server_handler *handler, *tmp;
572 	struct gas_server_response *response, *tmp_r;
573 
574 	if (!gas)
575 		return;
576 
577 	dl_list_for_each_safe(handler, tmp, &gas->handlers,
578 			      struct gas_server_handler, list) {
579 		dl_list_del(&handler->list);
580 		os_free(handler);
581 	}
582 
583 	dl_list_for_each_safe(response, tmp_r, &gas->responses,
584 			      struct gas_server_response, list) {
585 		dl_list_del(&response->list);
586 		gas_server_free_response(response);
587 	}
588 
589 	os_free(gas);
590 }
591 
592 
gas_server_register(struct gas_server * gas,const u8 * adv_proto_id,u8 adv_proto_id_len,struct wpabuf * (* req_cb)(void * ctx,void * resp_ctx,const u8 * sa,const u8 * query,size_t query_len,int * comeback_delay),void (* status_cb)(void * ctx,struct wpabuf * resp,int ok),void * ctx)593 int gas_server_register(struct gas_server *gas,
594 			const u8 *adv_proto_id, u8 adv_proto_id_len,
595 			struct wpabuf *
596 			(*req_cb)(void *ctx, void *resp_ctx, const u8 *sa,
597 				  const u8 *query, size_t query_len,
598 				  int *comeback_delay),
599 			void (*status_cb)(void *ctx, struct wpabuf *resp,
600 					  int ok),
601 			void *ctx)
602 {
603 	struct gas_server_handler *handler;
604 
605 	if (!gas || adv_proto_id_len > MAX_ADV_PROTO_ID_LEN)
606 		return -1;
607 	handler = os_zalloc(sizeof(*handler));
608 	if (!handler)
609 		return -1;
610 
611 	os_memcpy(handler->adv_proto_id, adv_proto_id, adv_proto_id_len);
612 	handler->adv_proto_id_len = adv_proto_id_len;
613 	handler->req_cb = req_cb;
614 	handler->status_cb = status_cb;
615 	handler->ctx = ctx;
616 	handler->gas = gas;
617 	dl_list_add(&gas->handlers, &handler->list);
618 
619 	return 0;
620 }
621