Fix memory leak
[platform/core/connectivity/asp-manager.git] / src / tech / asp-tech-infra.c
1 /*
2  * Copyright (c) 2016 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 /*****************************************************************************
18  * Standard headers
19  *****************************************************************************/
20
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <time.h>
25
26 /*****************************************************************************
27  * System headers
28  *****************************************************************************/
29
30 #include <glib.h>
31 #include <dlog.h>
32
33 #include <vconf.h>
34 #include <vconf-keys.h>
35
36 #include <dns-sd.h>
37 #include <dns-sd-internal.h>
38
39 #include <wifi-manager.h>
40
41 /*****************************************************************************
42  * Application Service Platform Daemon headers
43  *****************************************************************************/
44 #include "asp-manager-util.h"
45 #include "asp-service.h"
46 #include "asp-session.h"
47 #include "asp-tech.h"
48 #include "asp-tech-infra.h"
49
50 /*****************************************************************************
51  * Macros and Typedefs
52  *****************************************************************************/
53
54 /* TODO : define service data structure */
55
56 /*****************************************************************************
57  * Global Variables
58  *****************************************************************************/
59 static __thread GHashTable *g_local_service_hash = NULL;
60 static __thread GHashTable *g_browser_hash = NULL;
61
62
63 /* TODO : define local memory for service data structure */
64
65 /*****************************************************************************
66  * Local Functions Definition
67  *****************************************************************************/
68 static void _advertise_status_cb(dnssd_error_e result,
69                 dnssd_service_h local_service, gpointer user_data)
70 {
71         guint32 adv_id = GPOINTER_TO_UINT(user_data);
72         asp_service_advertise_status_e status = 0;
73         asp_service_advertise_reason_e reason = 0;
74
75         switch (result) {
76         case DNSSD_ERROR_NONE:
77         case DNSSD_ERROR_ALREADY_REGISTERED:
78                 status = ASP_SERVICE_ADV_STATUS_ADVERTISED;
79                 reason = ASP_SERVICE_ADVERTISE_REASON_SUCCESS;
80                 break;
81         case DNSSD_ERROR_NAME_CONFLICT:
82                 status = ASP_SERVICE_ADV_STATUS_NOT_ADVERTISED;
83                 reason = ASP_SERVICE_ADVERTISE_REASON_DUP;
84                 break;
85         default:
86                 status = ASP_SERVICE_ADV_STATUS_NOT_ADVERTISED;
87                 reason = ASP_SERVICE_ADVERTISE_REASON_OTHER;
88         }
89
90         asp_service_notify_advertise_status(adv_id, status, reason);
91 }
92
93 static void __txt_record_to_g_hash(unsigned short txt_len, const gchar *txt_records,
94                 GHashTable *service_info_map)
95 {
96         const gchar *ptr = txt_records;
97         const gchar *max = txt_records + txt_len;
98
99         ASP_LOGD("Parsing TXT record. Length: %hu", txt_len);
100
101         while (ptr < max) {
102                 unsigned short len = ptr[0];
103                 gchar *buf = g_strndup(ptr + 1, len);
104                 ASP_LOGD("buf: %s", buf);
105                 gchar *key;
106                 gchar *value;
107                 gchar *save_str = NULL;
108                 key = strtok_r(buf, "=", &save_str);
109                 value = strtok_r(NULL, "=", &save_str);
110                 g_hash_table_replace(service_info_map, g_strdup(key), g_strdup(value));
111                 ASP_LOGD("Insert (%s, %s) to hash. len: %d)", key, value, len);
112                 g_free(buf);
113                 ptr = ptr + 1 + len;
114         }
115 }
116
117 static asp_service_status_e __get_service_status(dnssd_service_state_e service_state)
118 {
119         asp_service_status_e service_status;
120         switch (service_state) {
121         case DNSSD_SERVICE_STATE_AVAILABLE:
122                 service_status = ASP_SERVICE_STATUS_AVAILABLE;
123                 break;
124         case DNSSD_SERVICE_STATE_UNAVAILABLE:
125         case DNSSD_SERVICE_STATE_NAME_LOOKUP_FAILED:
126         case DNSSD_SERVICE_STATE_HOST_NAME_LOOKUP_FAILED:
127         case DNSSD_SERVICE_STATE_ADDRESS_LOOKUP_FAILED:
128                 service_status = ASP_SERVICE_STATUS_NOT_AVAILABLE;
129                 break;
130         default:
131                 service_status = ASP_SERVICE_STATUS_NOT_AVAILABLE;
132         }
133
134         return service_status;
135 }
136
137 static void _search_result_cb(dnssd_service_state_e service_state,
138                 dnssd_service_h remote_service, gpointer user_data)
139 {
140         asp_event_data event;
141         guint32 search_id = -1;
142         gchar *service_mac = NULL;
143         gchar *service_device_name = NULL;
144         guint32 adv_id = -1;
145         gchar *service_name = NULL;
146         GHashTable *service_info_map = NULL;
147         asp_service_status_e service_status;
148         unsigned short txt_len = 0;
149         gchar *txt = NULL;
150         const gchar *adv_id_str = NULL;
151
152         ASP_LOGD("A service is found %u", remote_service);
153
154         search_id = GPOINTER_TO_UINT(user_data);
155         dnssd_service_get_name(remote_service, &service_name);
156         ASP_LOGD("Service name: %s", service_name);
157
158         service_status = __get_service_status(service_state);
159         ASP_LOGD("Service status: %s",
160                         service_status == ASP_SERVICE_STATUS_AVAILABLE ? "Available" : "Unavailable");
161
162         dnssd_service_get_all_txt_record(remote_service, &txt_len, (gpointer)&txt);
163         if (txt_len > 1) {
164                 service_info_map = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
165                 __txt_record_to_g_hash(txt_len, txt, service_info_map);
166                 if (g_hash_table_contains(service_info_map, ASP_INFRA_ADV_KEY)) {
167                         adv_id_str = g_hash_table_lookup(service_info_map, ASP_INFRA_ADV_KEY);
168                         adv_id = g_ascii_strtoll(adv_id_str, NULL, 10);
169                         ASP_LOGD("Advertisement ID: %u", adv_id);
170                 } else {
171                         ASP_LOGE("TXT records don't contain advertisement ID");
172                         g_hash_table_unref(service_info_map);
173                         g_free(service_name);
174                         g_free(txt);
175                         return;
176                 }
177
178                 if (g_hash_table_contains(service_info_map, ASP_INFRA_MAC_ADDR_KEY)) {
179                         service_mac = g_strdup(g_hash_table_lookup(service_info_map, ASP_INFRA_MAC_ADDR_KEY));
180                         ASP_LOGD("Advertisement MAC Address: %s", service_mac);
181                 } else {
182                         ASP_LOGE("TXT records don't contain advertisement MAC");
183                         g_hash_table_unref(service_info_map);
184                         g_free(service_name);
185                         g_free(txt);
186                         return;
187                 }
188
189                 if (g_hash_table_contains(service_info_map, ASP_INFRA_DEV_NAME)) {
190                         service_device_name = g_strdup(g_hash_table_lookup(service_info_map, ASP_INFRA_DEV_NAME));
191                         ASP_LOGD("Service device name: %s", service_device_name);
192                 }
193         } else {
194                 ASP_LOGE("Found service should has at least one TXT record");
195                 g_free(service_name);
196                 g_free(txt);
197                 return;
198         }
199
200         memset(&event, 0x0, sizeof(asp_event_data));
201         event.search_result.tech = ASP_TECH_INFRA;
202         event.search_result.search_id = search_id;
203         event.search_result.advertisement_id = adv_id;
204         event.search_result.service_status = service_status;
205         g_strlcpy(event.search_result.service_mac, service_mac, MACSTR_LEN + 1);
206         event.search_result.device_name = NULL;
207         event.search_result.instance_name = service_name;
208         event.search_result.service_info = service_info_map;
209         asp_manager_event(NULL, ASP_EVENT_SEARCH_RESULT, &event);
210
211         g_hash_table_unref(service_info_map);
212         g_free(service_device_name);
213         g_free(service_mac);
214         g_free(txt);
215 }
216
217 static gint32 __asp_service_infra_convert_error_type(gint32 dnssd_error)
218 {
219         switch (dnssd_error) {
220         case DNSSD_ERROR_NONE:
221                 return ASP_ERROR_NONE;
222         case DNSSD_ERROR_OUT_OF_MEMORY:
223                 return ASP_ERROR_MEMORY;
224         case DNSSD_ERROR_INVALID_PARAMETER:
225                 return ASP_ERROR_INVALIED_PARAMS;
226         case DNSSD_ERROR_NOT_SUPPORTED:
227                 return ASP_ERROR_NOT_SUPPORTED_TECH;
228         case DNSSD_ERROR_SERVICE_NOT_FOUND:
229                 return ASP_ERROR_SERVICE_NOT_FOUND;
230         case DNSSD_ERROR_INVALID_OPERATION:
231         case DNSSD_ERROR_NOT_INITIALIZED:
232         case DNSSD_ERROR_ALREADY_REGISTERED:
233         case DNSSD_ERROR_NAME_CONFLICT:
234         case DNSSD_ERROR_SERVICE_NOT_RUNNING:
235         case DNSSD_ERROR_OPERATION_FAILED:
236                 return ASP_ERROR_OPERATION_FAILED;
237         default:
238                 return ASP_ERROR_UNKNOWN;
239         }
240         return dnssd_error;
241 }
242
243 static void __init_hash_tables()
244 {
245         g_local_service_hash = g_hash_table_new(g_direct_hash, g_direct_equal);
246         g_browser_hash = g_hash_table_new(g_direct_hash, g_direct_equal);
247 }
248
249 static void __g_hash_free_handle(gpointer key, gpointer value, gpointer user_data)
250 {
251 }
252
253 static void __deinit_hash_tables()
254 {
255         g_hash_table_foreach(g_local_service_hash, (GHFunc)__g_hash_free_handle, NULL);
256         g_hash_table_foreach(g_browser_hash, (GHFunc)__g_hash_free_handle, NULL);
257
258         g_local_service_hash = NULL;
259         g_browser_hash = NULL;
260 }
261
262 static gint32 __add_txt_record(dnssd_service_h handle, const gchar* key, const gchar* value, gint32 len)
263 {
264         gint32 result = 0;
265         unsigned short txt_length;
266         gpointer txt_data = NULL;
267
268         result = dnssd_service_add_txt_record(handle, key, (unsigned short)len, value);
269         if (result != DNSSD_ERROR_NONE) {
270                 result = __asp_service_infra_convert_error_type(result);
271                 return result;
272         }
273         ASP_LOGD("Add (%s=%s) TXT record. length: %d", key, value, len);
274
275         result = dnssd_service_get_all_txt_record(handle, &txt_length, &txt_data);
276         if (result != DNSSD_ERROR_NONE) {
277                 result = __asp_service_infra_convert_error_type(result);
278                 return result;
279         }
280         ASP_LOGD("Get all TXT records");
281
282         result = dnssd_service_set_record(handle,
283                         ASP_INFRA_DNSSD_TXT_RECORD_TYPE, txt_length, txt_data);
284         if (result != DNSSD_ERROR_NONE) {
285                 ASP_LOGE("Failed to add record");
286                 result = __asp_service_infra_convert_error_type(result);
287                 g_free(txt_data);
288                 return result;
289         }
290         ASP_LOGD("Add TXT records");
291         return result;
292 }
293
294 gint32 asp_tech_infra_init()
295 {
296         __ASP_LOG_FUNC_ENTER__;
297         gint32 result = 0;
298
299         result = dnssd_initialize();
300         if (result != DNSSD_ERROR_NONE) {
301                 result = __asp_service_infra_convert_error_type(result);
302                 return result;
303         }
304
305         __init_hash_tables();
306
307         __ASP_LOG_FUNC_EXIT__;
308         return result;
309 }
310
311 gint32 asp_tech_infra_deinit()
312 {
313         __ASP_LOG_FUNC_ENTER__;
314         gint32 result = 0;
315
316         result = dnssd_deinitialize();
317         if (result != DNSSD_ERROR_NONE) {
318                 result = __asp_service_infra_convert_error_type(result);
319                 return result;
320         }
321
322         __deinit_hash_tables();
323
324         __ASP_LOG_FUNC_EXIT__;
325         return result;
326 }
327
328 gint32 asp_tech_infra_advertise(asp_service_advertise_s *service, gint32 replace)
329 {
330         __ASP_LOG_FUNC_ENTER__;
331         gint32 result = 0;
332         dnssd_service_h service_handle = 0;
333         gchar adv_id[ASP_SERVICE_ADV_ID_LEN + 1];
334         gint32 adv_id_len = 0;
335         gchar *mac_addr;
336         gint32 mac_addr_len;
337         gint32 port = 1234; /* TODO */
338         gchar *if_name;
339         wifi_manager_h wifi_handle = NULL;
340
341         ASP_LOGD("Infrastructure advertise service. replace: %d", replace);
342
343         if (replace) {
344                 /*  TODO */
345                 ;
346         }
347
348         result = dnssd_create_local_service(service->service_type, &service_handle);
349         if (result != DNSSD_ERROR_NONE) {
350                 result = __asp_service_infra_convert_error_type(result);
351                 return result;
352         }
353         ASP_LOGD("Success to create local service handle: %u, service type: %s",
354                         service_handle, service->service_type);
355
356         result = dnssd_service_set_name(service_handle, service->instance_name);
357         if (result != DNSSD_ERROR_NONE) {
358                 result = __asp_service_infra_convert_error_type(result);
359                 return result;
360         }
361         ASP_LOGD("Success to set service name %s", service->instance_name);
362
363         /*  TODO set port */
364         result = dnssd_service_set_port(service_handle, port);
365         if (result != DNSSD_ERROR_NONE) {
366                 result = __asp_service_infra_convert_error_type(result);
367                 return result;
368         }
369         ASP_LOGD("Success to set port %d", port);
370
371         result = wifi_manager_initialize(&wifi_handle);
372         if (result != WIFI_MANAGER_ERROR_NONE) {
373                 ASP_LOGD("Failed to initialize wifi-manager");
374                 return ASP_ERROR_OPERATION_FAILED;
375         }
376         result = wifi_manager_get_network_interface_name(wifi_handle, &if_name);
377         if (result != WIFI_MANAGER_ERROR_NONE) {
378                 ASP_LOGD("Failed to get interface name");
379                 return ASP_ERROR_OPERATION_FAILED;
380         }
381         ASP_LOGD("Success to get wifi interface name %s", if_name);
382         result = wifi_manager_deinitialize(wifi_handle);
383         if (result != WIFI_MANAGER_ERROR_NONE) {
384                 ASP_LOGD("Failed to deinitialize wifi_manager");
385                 free(if_name);
386                 return ASP_ERROR_OPERATION_FAILED;
387         }
388
389         result = dnssd_service_set_interface(service_handle, if_name);
390         if (result != DNSSD_ERROR_NONE) {
391                 result = __asp_service_infra_convert_error_type(result);
392                 free(if_name);
393                 return result;
394         }
395         ASP_LOGD("Success to set interface %s", if_name);
396         free(if_name);
397
398         result = dnssd_register_local_service(service_handle,
399                         _advertise_status_cb, GUINT_TO_POINTER(service->adv_id));
400         if (result != DNSSD_ERROR_NONE) {
401                 result = __asp_service_infra_convert_error_type(result);
402                 return result;
403         }
404         ASP_LOGD("Success to register local service %u", service_handle);
405
406         g_hash_table_insert(g_local_service_hash,
407                         GUINT_TO_POINTER(service->adv_id), GUINT_TO_POINTER(service_handle));
408
409         ASP_LOGD("Insert (%u, %u) to g_local_service_hash (size: %d)",
410                         service->adv_id, service_handle,
411                         g_hash_table_size(g_local_service_hash));
412
413
414         /* Add TXT records */
415         /* First of all, add Advertisement ID */
416         snprintf(adv_id, ASP_SERVICE_ADV_ID_LEN + 1, "%u", service->adv_id);
417         adv_id_len = strlen(adv_id);
418         result = __add_txt_record(service_handle, ASP_INFRA_ADV_KEY, adv_id, adv_id_len);
419         if (result != DNSSD_ERROR_NONE) {
420                 result = __asp_service_infra_convert_error_type(result);
421                 return result;
422         }
423
424         /* Add MAC Address to TXT record */
425         mac_addr = vconf_get_str(VCONFKEY_WIFI_BSSID_ADDRESS);
426         if (!mac_addr) {
427                 ASP_LOGE("Failed to get vconf value for %s", VCONFKEY_WIFI_BSSID_ADDRESS);
428                 return ASP_ERROR_OPERATION_FAILED;
429         }
430
431         mac_addr_len = strlen(mac_addr);
432         result = __add_txt_record(service_handle, ASP_INFRA_MAC_ADDR_KEY, mac_addr, mac_addr_len);
433         if (result != DNSSD_ERROR_NONE) {
434                 result = __asp_service_infra_convert_error_type(result);
435                 free(mac_addr);
436                 return result;
437         }
438         free(mac_addr);
439
440         /* Other TXT records */
441         if (service->service_info_map != NULL) {
442                 GHashTableIter iter;
443                 gpointer key, value;
444
445                 g_hash_table_iter_init(&iter, service->service_info_map);
446                 while (g_hash_table_iter_next(&iter, &key, &value)) {
447                         unsigned short len = (unsigned short)strlen(value);
448                         result = __add_txt_record(service_handle, key, value, len);
449                 }
450         }
451
452         __ASP_LOG_FUNC_EXIT__;
453         return result;
454 }
455
456 gint32 asp_tech_infra_cancel_advertise(asp_service_advertise_s *service)
457 {
458         __ASP_LOG_FUNC_ENTER__;
459         gint32 result = 0;
460         dnssd_service_h service_handle;
461
462         service_handle = (dnssd_service_h)GPOINTER_TO_UINT(g_hash_table_lookup(g_local_service_hash,
463                                                                                                                 GUINT_TO_POINTER(service->adv_id)));
464
465         if (!service_handle) {
466                 /* TODO */
467                 ASP_LOGE("Failed to lookup advertisement id %u", service->adv_id);
468                 return result;
469         }
470
471         result = dnssd_deregister_local_service(service_handle);
472         if (result != DNSSD_ERROR_NONE) {
473                 result = __asp_service_infra_convert_error_type(result);
474                 ASP_LOGE("Failed to deregister local service %u", service_handle);
475                 return result;
476         }
477         ASP_LOGD("Success to deregister local service: %u", service_handle);
478
479         result = dnssd_destroy_local_service(service_handle);
480         if (result != DNSSD_ERROR_NONE) {
481                 result = __asp_service_infra_convert_error_type(result);
482                 return result;
483         }
484         ASP_LOGD("Success to destroy local service: %u", service_handle);
485
486         __ASP_LOG_FUNC_EXIT__;
487         return result;
488 }
489
490 gint32 asp_tech_infra_seek(asp_service_seek_s *service)
491 {
492         __ASP_LOG_FUNC_ENTER__;
493         gint32 result = 0;
494         dnssd_browser_h browser_handle;
495         gchar *if_name;
496         wifi_manager_h wifi_handle = NULL;
497
498         result = wifi_manager_initialize(&wifi_handle);
499         if (result != WIFI_MANAGER_ERROR_NONE) {
500                 ASP_LOGD("Failed to initialize wifi-manager");
501                 return ASP_ERROR_OPERATION_FAILED;
502         }
503         result = wifi_manager_get_network_interface_name(wifi_handle, &if_name);
504         if (result != WIFI_MANAGER_ERROR_NONE) {
505                 ASP_LOGD("Failed to get interface name");
506                 return ASP_ERROR_OPERATION_FAILED;
507         }
508         ASP_LOGD("Success to get wifi interface name %s", if_name);
509         result = wifi_manager_deinitialize(wifi_handle);
510         if (result != WIFI_MANAGER_ERROR_NONE) {
511                 ASP_LOGD("Failed to deinitialize wifi-manager");
512                 free(if_name);
513                 return ASP_ERROR_OPERATION_FAILED;
514         }
515
516         result = dnssd_start_browsing_service_on_interface(service->service_type,
517                                                         if_name, &browser_handle,
518                                                         _search_result_cb,
519                                                         GUINT_TO_POINTER(service->search_id));
520
521         if (result != DNSSD_ERROR_NONE) {
522                 result = __asp_service_infra_convert_error_type(result);
523                 free(if_name);
524                 return result;
525         }
526         ASP_LOGD("Success to start browsing service. service_type: %s, browser_handle: %u",
527                         service->service_type, browser_handle);
528
529         g_hash_table_insert(g_browser_hash,
530                         GUINT_TO_POINTER(service->search_id), GUINT_TO_POINTER(browser_handle));
531
532         ASP_LOGD("Insert (%" G_GUINT64_FORMAT ", %u) to g_browser_hash (size: %d)",
533                         service->search_id, browser_handle,
534                         g_hash_table_size(g_browser_hash));
535
536         free(if_name);
537         __ASP_LOG_FUNC_EXIT__;
538         return result;
539 }
540
541 gint32 asp_tech_infra_cancel_seek(asp_service_seek_s *service)
542 {
543         __ASP_LOG_FUNC_ENTER__;
544         gint32 result = 0;
545         dnssd_browser_h browser_handle;
546
547         browser_handle = (dnssd_browser_h)GPOINTER_TO_UINT(g_hash_table_lookup(g_browser_hash,
548                                                                                                                 GUINT_TO_POINTER(service->search_id)));
549
550         if (!browser_handle) {
551                 /* TODO */
552                 ASP_LOGE("Failed to lookup search id %" G_GUINT64_FORMAT, service->search_id);
553                 return result;
554         }
555
556         result = dnssd_stop_browsing_service(browser_handle);
557         if (result != DNSSD_ERROR_NONE) {
558                 result = __asp_service_infra_convert_error_type(result);
559                 return result;
560         }
561         ASP_LOGD("Success to stop browsing. browser_handle: %u", browser_handle);
562
563         __ASP_LOG_FUNC_EXIT__;
564         return result;
565 }
566
567 gint32 asp_tech_infra_connect_session(asp_tech_session_request_params_s *params)
568 {
569         __ASP_LOG_FUNC_ENTER__;
570         gint32 result = 0;
571
572         __ASP_LOG_FUNC_EXIT__;
573         return result;
574 }
575
576 gint32 asp_tech_infra_confirm_session(const guint8 *session_mac, gint32 session_id, gint32 confirm, guint32 pin)
577 {
578         __ASP_LOG_FUNC_ENTER__;
579         gint32 result = 0;
580
581         __ASP_LOG_FUNC_EXIT__;
582         return result;
583 }
584
585 gint32 asp_tech_infra_destroy_connection(const guint8 *peer_id, gint32 peer_id_length)
586 {
587         __ASP_LOG_FUNC_ENTER__;
588         gint32 result = 0;
589
590         __ASP_LOG_FUNC_EXIT__;
591         return result;
592 }
593
594 gint32 asp_tech_infra_is_peer_connected(const guint8 *peer_id, gint32 peer_id_length, gint32 *is_connected)
595 {
596         __ASP_LOG_FUNC_ENTER__;
597         gint32 result = 0;
598
599         __ASP_LOG_FUNC_EXIT__;
600         return result;
601 }
602
603 asp_tech_ops_s asp_tech_infra_ops = {
604                 .init = asp_tech_infra_init,
605                 .deinit = asp_tech_infra_deinit,
606                 .advertise = asp_tech_infra_advertise,
607                 .cancel_advertise = asp_tech_infra_cancel_advertise,
608                 .seek = asp_tech_infra_seek,
609                 .cancel_seek = asp_tech_infra_cancel_seek,
610                 .connect_session = asp_tech_infra_connect_session,
611                 .confirm_session = asp_tech_infra_confirm_session,
612                 .destroy_connection = asp_tech_infra_destroy_connection,
613                 .is_peer_connected = asp_tech_infra_is_peer_connected,
614                 .session_request_cb = NULL,
615                 .session_request_cb_user_data = NULL,
616                 .session_config_request_cb = NULL,
617                 .connect_status_cb = NULL,
618                 .connect_status_cb_user_data = NULL,
619                 .ip_assigned_cb = NULL,
620                 .ip_assigned_cb_user_data = NULL,
621 };