blob: 76dc526641b3ee968280b17833fbcfb288e61f11 [file] [log] [blame]
b.liu9fc00602023-09-24 13:16:50 +08001#include <stdio.h>
2#include <stdlib.h>
b.liu9fc00602023-09-24 13:16:50 +08003#include <unistd.h>
b.liueb040652023-09-25 18:50:56 +08004#include <errno.h>
5#include <sys/socket.h>
6#include <sys/un.h>
7#include <netinet/in.h>
8#include <pthread.h>
9#include <sys/epoll.h>
10#include <string.h>
11#include <fcntl.h>
12#include <signal.h>
13#include <netinet/tcp.h>
b.liu9fc00602023-09-24 13:16:50 +080014
15#include "mbtk_log.h"
16#include "mbtk_tcpip.h"
b.liueb040652023-09-25 18:50:56 +080017#include "mbtk_net_control.h"
18
19#define MBTK_TCPIP_CID_DEFAULT 0
20#define MBTK_TCPIP_READ_BUFF_SIZE 2048
21#define TCPIP_DEBUG 0
b.liu9fc00602023-09-24 13:16:50 +080022
23typedef struct {
b.liueb040652023-09-25 18:50:56 +080024 mbtk_sock_session sock_fd;
b.liu9fc00602023-09-24 13:16:50 +080025 char ser_addr[256];
26 int ser_port;
27 char local_addr[256];
28 int local_port;
29 bool ack_support;
30 bool ssl_support;
31 bool ignore_cert;
32 uint32 heartbeat_time;
33 uint32 delay_time;
34
35 mbtk_tcpip_read_callback_func read_cb;
36
37 uint32 data_traffic_send;
38 uint32 data_traffic_recv;
39
40} mbtk_tcpip_cli_info_t;
41
42typedef struct {
b.liueb040652023-09-25 18:50:56 +080043 mbtk_sock_session sock_fd;
b.liu9fc00602023-09-24 13:16:50 +080044 char ser_addr[256];
45 int ser_port;
46
47 int cli_num;
48 mbtk_tcpip_cli_info_t *cli_list;
49} mbtk_tcpip_ser_info_t;
50
51typedef struct {
52 int link_id;
53 int link_cid;
b.liueb040652023-09-25 18:50:56 +080054 bool link_connected;
55 mbtk_sock_type prot_type; // TCP/UDP
b.liu9fc00602023-09-24 13:16:50 +080056
57 mbtk_tcpip_type_enum type;
58 union
59 {
60 mbtk_tcpip_cli_info_t cli_info;
61 mbtk_tcpip_ser_info_t ser_info;
62 } tcpip_info;
63
64} mbtk_tcpip_link_t;
65
66static mbtk_tcpip_link_t tcpip_link[MBTK_TCPIP_LINK_MAX];
b.liueb040652023-09-25 18:50:56 +080067static bool tcpip_inited = FALSE;
68static mbtk_sock_handle tcpip_handle;
69static mbtk_tcpip_net_callback_func tcpip_net_cb = NULL;
70static mbtk_tcpip_sock_callback_func tcpip_sock_cb = NULL;
b.liu9fc00602023-09-24 13:16:50 +080071
b.liueb040652023-09-25 18:50:56 +080072/*
73struct tcp_info
b.liu9fc00602023-09-24 13:16:50 +080074{
b.liueb040652023-09-25 18:50:56 +080075 u_int8_t tcpi_state;
76 u_int8_t tcpi_ca_state;
77 u_int8_t tcpi_retransmits;
78 u_int8_t tcpi_probes;
79 u_int8_t tcpi_backoff;
80 u_int8_t tcpi_options;
81 u_int8_t tcpi_snd_wscale : 4, tcpi_rcv_wscale : 4;
82
83 u_int32_t tcpi_rto;
84 u_int32_t tcpi_ato;
85 u_int32_t tcpi_snd_mss;
86 u_int32_t tcpi_rcv_mss;
87
88 u_int32_t tcpi_unacked;
89 u_int32_t tcpi_sacked;
90 u_int32_t tcpi_lost;
91 u_int32_t tcpi_retrans;
92 u_int32_t tcpi_fackets;
93
94 u_int32_t tcpi_last_data_sent;
95 u_int32_t tcpi_last_ack_sent;
96 u_int32_t tcpi_last_data_recv;
97 u_int32_t tcpi_last_ack_recv;
98
99 u_int32_t tcpi_pmtu;
100 u_int32_t tcpi_rcv_ssthresh;
101 u_int32_t tcpi_rtt;
102 u_int32_t tcpi_rttvar;
103 u_int32_t tcpi_snd_ssthresh;
104 u_int32_t tcpi_snd_cwnd;
105 u_int32_t tcpi_advmss;
106 u_int32_t tcpi_reordering;
107
108 u_int32_t tcpi_rcv_rtt;
109 u_int32_t tcpi_rcv_space;
110
111 u_int32_t tcpi_total_retrans;
112};
113
114*/
115void tcp_info_print(struct tcp_info *tcp)
116{
117 if(tcp) {
118 LOGD("tcpi_state = %d", tcp->tcpi_state);
119 LOGD("tcpi_ca_state = %d", tcp->tcpi_ca_state);
120 LOGD("tcpi_retransmits = %d", tcp->tcpi_retransmits);
121 LOGD("tcpi_probes = %d", tcp->tcpi_probes);
122 LOGD("tcpi_backoff = %d", tcp->tcpi_backoff);
123 LOGD("tcpi_options = %d", tcp->tcpi_options);
124 LOGD("tcpi_snd_wscale = %d", 0x0F & tcp->tcpi_snd_wscale);
125 LOGD("tcpi_rcv_wscale = %d", 0xF0 & tcp->tcpi_rcv_wscale);
126 LOGD("tcpi_rto = %d", tcp->tcpi_rto);
127 LOGD("tcpi_ato = %d", tcp->tcpi_ato);
128 LOGD("tcpi_snd_mss = %d", tcp->tcpi_snd_mss);
129 LOGD("tcpi_rcv_mss = %d", tcp->tcpi_rcv_mss);
130 LOGD("tcpi_unacked = %d", tcp->tcpi_unacked);
131 LOGD("tcpi_sacked = %d", tcp->tcpi_sacked);
132 LOGD("tcpi_lost = %d", tcp->tcpi_lost);
133 LOGD("tcpi_retrans = %d", tcp->tcpi_retrans);
134 LOGD("tcpi_fackets = %d", tcp->tcpi_fackets);
135 LOGD("tcpi_last_data_sent = %d", tcp->tcpi_last_data_sent);
136 LOGD("tcpi_last_ack_sent = %d", tcp->tcpi_last_ack_sent);
137 LOGD("tcpi_last_data_recv = %d", tcp->tcpi_last_data_recv);
138 LOGD("tcpi_last_ack_recv = %d", tcp->tcpi_last_ack_recv);
139 LOGD("tcpi_pmtu = %d", tcp->tcpi_pmtu);
140 LOGD("tcpi_rcv_ssthresh = %d", tcp->tcpi_rcv_ssthresh);
141 LOGD("tcpi_rtt = %d", tcp->tcpi_rtt);
142 LOGD("tcpi_rttvar = %d", tcp->tcpi_rttvar);
143 LOGD("tcpi_snd_ssthresh = %d", tcp->tcpi_snd_ssthresh);
144 LOGD("tcpi_snd_cwnd = %d", tcp->tcpi_snd_cwnd);
145 LOGD("tcpi_advmss = %d", tcp->tcpi_advmss);
146 LOGD("tcpi_reordering = %d", tcp->tcpi_reordering);
147 LOGD("tcpi_rcv_rtt = %d", tcp->tcpi_rcv_rtt);
148 LOGD("tcpi_rcv_space = %d", tcp->tcpi_rcv_space);
149 LOGD("tcpi_total_retrans = %d", tcp->tcpi_total_retrans);
150 }
151}
152
153static int tcpip_fd_2_link(int fd)
154{
155 int link_id = 0;
156 mbtk_tcpip_link_t *link = NULL;
157 for(; link_id < MBTK_TCPIP_LINK_MAX; link_id++) {
158 link = tcpip_link + link_id;
159 if(link->link_connected) {
160 if(link->type == MBTK_TCPIP_TYPE_CLIENT) {
161 if(link->tcpip_info.cli_info.sock_fd > 0 && link->tcpip_info.cli_info.sock_fd == fd) {
162 break;
163 }
164 } else {
165 // Not support.
166
167 }
168 }
169 }
170
171 if(link_id == MBTK_TCPIP_LINK_MAX) {
172 return -1;
173 } else {
174 return link_id;
175 }
176}
177
178static void tcpip_sock_cb_func(int handle, int fd, int event)
179{
180 if(tcpip_inited && tcpip_handle == handle/* && http_fd == fd*/) {
181 if(event & (EPOLLIN | EPOLLOUT)) { // Cand read or write.
182 int sock_error = 0;
183 socklen_t socklen = sizeof(sock_error);
184 if(getsockopt(fd, SOL_SOCKET, SO_ERROR, &sock_error, (socklen_t*)&socklen) == 0) {
185 LOGD("Socket error:%d", sock_error);
186 }
187
188 if(sock_error) {
189 LOGW("errno = %d", errno);
190 return;
191 }
192
193 struct tcp_info info;
194 int len = sizeof(info);
195 if(getsockopt(fd, IPPROTO_TCP, TCP_INFO, &info, (socklen_t*)&len) == 0) {
196 LOGD("State : %d", info.tcpi_state);
197#if TCPIP_DEBUG
198 tcp_info_print(&info);
199#endif
200 }
201
202 if(TCP_ESTABLISHED != info.tcpi_state) {
203 LOGW("errno = %d", errno);
204 int link_id = tcpip_fd_2_link(fd);
205 if(link_id >= 0) {
206 // Socket disconnected?
207 mbtk_tcpip_sock_close(link_id);
208
209 if(tcpip_sock_cb) {
210 tcpip_sock_cb(link_id, 0);
211 }
212 }
213 return;
214 }
215 }
216
217 if(event & EPOLLIN) { // READ
218 LOGD("fd[%d] can read.", fd);
219 int link_id = tcpip_fd_2_link(fd);
220 if(link_id >= 0) {
221 if(tcpip_link[link_id].type == MBTK_TCPIP_TYPE_CLIENT) {
222 if(tcpip_link[link_id].tcpip_info.cli_info.read_cb) {
223 char buff[MBTK_TCPIP_READ_BUFF_SIZE];
224 memset(buff, 0x0, MBTK_TCPIP_READ_BUFF_SIZE);
225 int read_len = mbtk_sock_read_async(tcpip_handle, fd, buff, MBTK_TCPIP_READ_BUFF_SIZE);
226 if(read_len > 0) {
227 tcpip_link[link_id].tcpip_info.cli_info.data_traffic_recv += read_len;
228 tcpip_link[link_id].tcpip_info.cli_info.read_cb(link_id, (const char*)buff, read_len);
229 }
230#if 0
231 else { // Socket error(Such as server disconnected.).
232 LOGW("errno = %d", errno);
233 // Socket disconnected?
234 mbtk_tcpip_sock_close(link_id);
235
236 if(tcpip_sock_cb) {
237 tcpip_sock_cb(link_id, 0);
238 }
239 }
240#endif
241 while(read_len > 0) {
242 memset(buff, 0x0, MBTK_TCPIP_READ_BUFF_SIZE);
243 read_len = mbtk_sock_read_async(tcpip_handle, fd, buff, MBTK_TCPIP_READ_BUFF_SIZE);
244 // LOGD("read_len = %d", read_len);
245 if(read_len > 0) {
246 tcpip_link[link_id].tcpip_info.cli_info.data_traffic_recv += read_len;
247 tcpip_link[link_id].tcpip_info.cli_info.read_cb(link_id, (const char*)buff, read_len);
248 }
249 }
250 }
251 } else {
252 // Not support.
253
254 }
255 }
256 } else if(event & EPOLLRDHUP) { // Close
257 LOGD("fd[%d] Closed?", fd);
258 } else {
259 LOGW("Unknown event:%x",event);
260 }
261 }
262}
263
264static void tcpip_net_cb_func(mbtk_sock_handle handle, int state, const char* addr, const char* if_name)
265{
266 if(tcpip_inited && tcpip_handle == handle) {
267 LOGD("Net state : %d, %s, %s", state, if_name, addr);
268 if(state == 0) {
269 mbtk_tcpip_net_close();
270 }
271
272 if(tcpip_net_cb) {
273 tcpip_net_cb(state, addr);
274 }
275 }
276}
277
278static bool tcpip_link_check(int link_id)
279{
280 if(tcpip_inited && link_id >= 0 && link_id < MBTK_TCPIP_LINK_MAX) {
281 return TRUE;
282 }
283
284 return FALSE;
285}
286
287static bool tcpip_link_connected(int link_id)
288{
289 if(!tcpip_link_check(link_id)) {
290 return FALSE;
291 }
292
293 if(!tcpip_link[link_id].link_connected) {
294 return FALSE;
295 }
296
297 return TRUE;
298}
299
300static int tcpip_link_reset(mbtk_tcpip_link_t *link, int link_id)
301{
302 if(link) {
303 // Close socket if necessary.
304 if(link->link_connected) {
305 if(link->type == MBTK_TCPIP_TYPE_CLIENT) {
306 if(link->tcpip_info.cli_info.sock_fd > 0) {
307 int err;
308 if(mbtk_sock_close(tcpip_handle, link->tcpip_info.cli_info.sock_fd, 3000, &err) == 0
309 && err == MBTK_SOCK_SUCCESS) {
310 LOGD("Close socket[%d] success.", link->tcpip_info.cli_info.sock_fd);
311 } else {
312 LOGE("Close socket[%d] fail.", link->tcpip_info.cli_info.sock_fd);
313 return -1;
314 }
315 }
316 } else {
317 // Not support.
318
319 return -1;
320 }
321 }
322
323 memset(link, 0x0, sizeof(mbtk_tcpip_link_t));
324 link->link_id = link_id;
325 link->link_cid = MBTK_TCPIP_CID_DEFAULT;
326 return 0;
327 }
328
329 return -1;
330}
331
332mbtk_tcpip_err_enum mbtk_tcpip_net_open(mbtk_tcpip_net_callback_func net_cb, mbtk_tcpip_sock_callback_func sock_cb)
333{
334 if(tcpip_inited) {
335 LOGW("TCP/IP has inited.");
336 return MBTK_TCPIP_ERR_SUCCESS;
337 } else {
338 //mbtk_log_init("radio", "MBTK_TCPIP");
339 mbtk_net_state_t net_state = mbtk_net_state_get();
340 if(net_state == MBTK_NET_STATE_OFF) {
341 LOGE("Network unavailable.");
342 return MBTK_TCPIP_ERR_NET_UNAVAILABLE;
343 }
344
345 memset(&tcpip_link, 0x0, sizeof(mbtk_tcpip_link_t) * MBTK_TCPIP_LINK_MAX);
346 int i = 0;
347 for(; i < MBTK_TCPIP_LINK_MAX; i++) {
348 tcpip_link_reset(tcpip_link + i, i);
349 }
350
351 mbtk_init_info init_info;
352 init_info.net_type = MBTK_NET_LINUX;
353 init_info.net_cb = tcpip_net_cb_func;
354 init_info.sock_cb = tcpip_sock_cb_func;
355 sprintf(init_info.if_name, "ccinet%d", MBTK_TCPIP_CID_DEFAULT);
356 tcpip_handle = mbtk_sock_init(&init_info);
357 if(tcpip_handle < 0) {
358 LOGE("mbtk_sock_init() fail.");
359 return MBTK_TCPIP_ERR_NET_HANDLE;
360 }
361
362 tcpip_net_cb = net_cb;
363 tcpip_sock_cb = sock_cb;
364 tcpip_inited = TRUE;
365 return MBTK_TCPIP_ERR_SUCCESS;
366 }
b.liu9fc00602023-09-24 13:16:50 +0800367}
368
369mbtk_tcpip_err_enum mbtk_tcpip_net_close()
370{
b.liueb040652023-09-25 18:50:56 +0800371 if(tcpip_inited) {
372 int i = 0;
373 mbtk_tcpip_err_enum tcpip_err = MBTK_TCPIP_ERR_SUCCESS;
374
375 // Close all socket.
376 for(; i < MBTK_TCPIP_LINK_MAX; i++) {
377 if(tcpip_link_reset(tcpip_link + i, i)) {
378 tcpip_err = MBTK_TCPIP_ERR_UNKNOWN;
379 }
380 }
381
382 if(tcpip_err == MBTK_TCPIP_ERR_SUCCESS) {
383 tcpip_inited = FALSE;
384 }
385 return tcpip_err;
386 } else {
387 LOGW("TCP/IP not inited.");
388 return MBTK_TCPIP_ERR_SUCCESS;
389 }
b.liu9fc00602023-09-24 13:16:50 +0800390}
391
392mbtk_tcpip_err_enum mbtk_tcpip_sock_open(const mbtk_tcpip_info_t *tcpip_info)
393{
b.liueb040652023-09-25 18:50:56 +0800394 if(tcpip_info == NULL || strlen(tcpip_info->ser_addr) == 0 || tcpip_info->ser_port <= 0) {
395 LOGE("ARG error.");
396 return MBTK_TCPIP_ERR_ARG;
397 }
398
399 if(!tcpip_link_check(tcpip_info->link_id)) {
400 LOGE("Link[%d] error.", tcpip_info->link_id);
401 return MBTK_TCPIP_ERR_LINK_UNAVAILABLE;
402 }
403
404 if(tcpip_link_connected(tcpip_info->link_id)) {
405 LOGE("Link[%d] has connected.", tcpip_info->link_id);
406 return MBTK_TCPIP_ERR_LINK_UNAVAILABLE;
407 }
408
409 if(tcpip_info->tcpip_type == MBTK_TCPIP_TYPE_CLIENT) {
410 mbtk_tcpip_link_t *link = tcpip_link + tcpip_info->link_id;
411
412 int err;
413 mbtk_sock_info sock_info;
414 memset(&sock_info, 0x0, sizeof(mbtk_sock_info));
415 sock_info.type = tcpip_info->prot_type;
416 sock_info.is_support_ssl = tcpip_info->ssl_support;
417 sock_info.ingnore_cert = tcpip_info->ignore_cert;
418 memcpy(sock_info.address, tcpip_info->ser_addr, strlen(tcpip_info->ser_addr));
419 sock_info.port = tcpip_info->ser_port;
420 sock_info.local_port = tcpip_info->local_port;
421
422 link->tcpip_info.cli_info.sock_fd = mbtk_sock_open(tcpip_handle, &sock_info, 3000, &err);
423 if(link->tcpip_info.cli_info.sock_fd > 0) {
424 link->prot_type = tcpip_info->prot_type;
425 link->type = MBTK_TCPIP_TYPE_CLIENT;
426 memcpy(link->tcpip_info.cli_info.ser_addr, tcpip_info->ser_addr, strlen(tcpip_info->ser_addr));
427 link->tcpip_info.cli_info.ser_port = tcpip_info->ser_port;
428 link->tcpip_info.cli_info.local_port = tcpip_info->local_port;
429 link->tcpip_info.cli_info.ack_support = tcpip_info->ack_support;
430 link->tcpip_info.cli_info.ssl_support = tcpip_info->ssl_support;
431 link->tcpip_info.cli_info.ignore_cert = tcpip_info->ignore_cert;
432 link->tcpip_info.cli_info.heartbeat_time = tcpip_info->heartbeat_time;
433 link->tcpip_info.cli_info.delay_time = tcpip_info->delay_time;
434 link->tcpip_info.cli_info.read_cb = tcpip_info->read_cb;
435 link->link_connected = TRUE;
436 LOGD("Open socket[%d] success.");
437 return MBTK_TCPIP_ERR_SUCCESS;
438 } else {
439 LOGE("Open socket[%d] fail.");
440 return MBTK_TCPIP_ERR_UNKNOWN;
441 }
442 } else {
443 LOGE("Only support CLIENT now!");
444 return MBTK_TCPIP_ERR_UNKNOWN;
445 }
b.liu9fc00602023-09-24 13:16:50 +0800446}
447
448mbtk_tcpip_err_enum mbtk_tcpip_sock_close(int link_id)
449{
b.liueb040652023-09-25 18:50:56 +0800450 if(!tcpip_link_connected(link_id)) {
451 LOGE("Link[%d] not connected.", link_id);
452 return MBTK_TCPIP_ERR_LINK_NOT_CONNECT;
453 }
454
455 if(tcpip_link_reset(tcpip_link + link_id, link_id)) {
456 LOGE("Close link[%d] fail.", link_id);
457 return MBTK_TCPIP_ERR_UNKNOWN;
458 }
459
b.liu9fc00602023-09-24 13:16:50 +0800460 return MBTK_TCPIP_ERR_SUCCESS;
461}
462
463int mbtk_tcpip_send(int link_id, const char* data, int data_len, const char* ser_addr, int ser_port)
464{
b.liueb040652023-09-25 18:50:56 +0800465 if(!tcpip_link_connected(link_id)) {
466 LOGE("Link[%d] not connected.", link_id);
467 return -1;
468 }
469
470 if(tcpip_link[link_id].type == MBTK_TCPIP_TYPE_CLIENT) {
471 int err;
472 int len = mbtk_sock_write(tcpip_handle, tcpip_link[link_id].tcpip_info.cli_info.sock_fd,
473 data, data_len, 3000, &err);
474 if(len > 0) {
475 tcpip_link[link_id].tcpip_info.cli_info.data_traffic_send += len;
476 }
477 return len;
478 } else {
479 // Not support.
480 return -1;
481 }
b.liu9fc00602023-09-24 13:16:50 +0800482
483 return 0;
484}
485
486int mbtk_tcpip_read(int link_id, char* buff, int buff_size)
487{
b.liueb040652023-09-25 18:50:56 +0800488 if(!tcpip_link_connected(link_id)) {
489 LOGE("Link[%d] not connected.", link_id);
490 return -1;
491 }
492
493 if(tcpip_link[link_id].type == MBTK_TCPIP_TYPE_CLIENT) {
494 if(tcpip_handle, tcpip_link[link_id].tcpip_info.cli_info.read_cb) {
495 LOGE("Set read_cb function,can not manual read.");
496 return -1;
497 }
498 int err;
499 int len = mbtk_sock_read(tcpip_handle, tcpip_link[link_id].tcpip_info.cli_info.sock_fd,
500 buff, buff_size, 3000, &err);
501 if(len > 0) {
502 tcpip_link[link_id].tcpip_info.cli_info.data_traffic_recv += len;
503 }
504 return len;
505 } else {
506 // Not support.
507 return -1;
508 }
b.liu9fc00602023-09-24 13:16:50 +0800509}
510
511/*
b.liueb040652023-09-25 18:50:56 +0800512* Get the data traffic of the specified link. Return -1 if fail.
b.liu9fc00602023-09-24 13:16:50 +0800513*/
514int mbtk_tcpip_data_traffic_get(int link_id)
515{
b.liueb040652023-09-25 18:50:56 +0800516 if(!tcpip_link_connected(link_id)) {
517 LOGE("Link[%d] not connected.", link_id);
518 return -1;
519 }
520
521 if(tcpip_link[link_id].type == MBTK_TCPIP_TYPE_CLIENT) {
522 return tcpip_link[link_id].tcpip_info.cli_info.data_traffic_recv
523 + tcpip_link[link_id].tcpip_info.cli_info.data_traffic_send;
524 } else {
525 // Not support.
526 return -1;
527 }
b.liu9fc00602023-09-24 13:16:50 +0800528}
529
530/*
531* Reset the data traffic of the specified link.
532*/
533mbtk_tcpip_err_enum mbtk_tcpip_data_traffic_reset(int link_id)
534{
b.liueb040652023-09-25 18:50:56 +0800535 if(!tcpip_link_connected(link_id)) {
536 LOGE("Link[%d] not connected.", link_id);
537 return MBTK_TCPIP_ERR_LINK_NOT_CONNECT;
538 }
539
540 if(tcpip_link[link_id].type == MBTK_TCPIP_TYPE_CLIENT) {
541 tcpip_link[link_id].tcpip_info.cli_info.data_traffic_recv = 0;
542 tcpip_link[link_id].tcpip_info.cli_info.data_traffic_send = 0;
543 return MBTK_TCPIP_ERR_SUCCESS;
544 } else {
545 // Not support.
546
547 }
548
549 return MBTK_TCPIP_ERR_UNKNOWN;
b.liu9fc00602023-09-24 13:16:50 +0800550}
551
552/*
b.liueb040652023-09-25 18:50:56 +0800553* Return 0 if disconnected, 1 for connected, other for fail.
b.liu9fc00602023-09-24 13:16:50 +0800554*/
555int mbtk_tcpip_link_state_get(int link_id)
556{
b.liueb040652023-09-25 18:50:56 +0800557 if(!tcpip_link_check(link_id)) {
558 LOGE("Link error.");
559 return -1;
560 }
561
562 return tcpip_link[link_id].link_connected ? 1 : 0;
b.liu9fc00602023-09-24 13:16:50 +0800563}
564