Imported Upstream version 1.38
[platform/upstream/connman.git] / gdhcp / common.c
1 /*
2  *  DHCP library with GLib integration
3  *
4  *  Copyright (C) 2007-2013  Intel Corporation. All rights reserved.
5  *
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License version 2 as
8  *  published by the Free Software Foundation.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program; if not, write to the Free Software
17  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18  *
19  */
20
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <errno.h>
28 #include <unistd.h>
29 #include <sys/ioctl.h>
30 #include <stdint.h>
31 #include <string.h>
32 #include <endian.h>
33 #include <net/if_arp.h>
34 #include <linux/if.h>
35 #include <netpacket/packet.h>
36 #include <net/ethernet.h>
37 #include <arpa/inet.h>
38 #include <fcntl.h>
39
40 #include "gdhcp.h"
41 #include "common.h"
42 #include "../src/connman.h"
43
44 static const DHCPOption client_options[] = {
45         { OPTION_IP,                    0x01 }, /* subnet-mask */
46         { OPTION_IP | OPTION_LIST,      0x03 }, /* routers */
47         { OPTION_IP | OPTION_LIST,      0x06 }, /* domain-name-servers */
48         { OPTION_STRING,                0x0c }, /* hostname */
49         { OPTION_STRING,                0x0f }, /* domain-name */
50         { OPTION_U16,                   0x1a }, /* mtu */
51         { OPTION_IP | OPTION_LIST,      0x2a }, /* ntp-servers */
52         { OPTION_U32,                   0x33 }, /* dhcp-lease-time */
53         /* Options below will not be exposed to user */
54         { OPTION_IP,                    0x32 }, /* requested-ip */
55         { OPTION_U8,                    0x35 }, /* message-type */
56         { OPTION_U32,                   0x36 }, /* server-id */
57         { OPTION_U16,                   0x39 }, /* max-size */
58         { OPTION_STRING,                0x3c }, /* vendor */
59         { OPTION_STRING,                0x3d }, /* client-id */
60         { OPTION_STRING,                0xfc }, /* UNOFFICIAL proxy-pac */
61         { OPTION_UNKNOWN,               0x00 },
62 };
63
64 GDHCPOptionType dhcp_get_code_type(uint8_t code)
65 {
66         int i;
67
68         for (i = 0; client_options[i].code; i++) {
69                 if (client_options[i].code == code)
70                         return client_options[i].type;
71         }
72
73         return OPTION_UNKNOWN;
74 }
75
76 uint8_t *dhcp_get_option(struct dhcp_packet *packet, int code)
77 {
78         int len, rem;
79         uint8_t *optionptr;
80         uint8_t overload = 0;
81
82         /* option bytes: [code][len][data1][data2]..[dataLEN] */
83         optionptr = packet->options;
84         rem = sizeof(packet->options);
85
86         while (1) {
87                 if (rem <= 0)
88                         /* Bad packet, malformed option field */
89                         return NULL;
90
91                 if (optionptr[OPT_CODE] == DHCP_PADDING) {
92                         rem--;
93                         optionptr++;
94
95                         continue;
96                 }
97
98                 if (optionptr[OPT_CODE] == DHCP_END) {
99                         if (overload & FILE_FIELD) {
100                                 overload &= ~FILE_FIELD;
101
102                                 optionptr = packet->file;
103                                 rem = sizeof(packet->file);
104
105                                 continue;
106                         } else if (overload & SNAME_FIELD) {
107                                 overload &= ~SNAME_FIELD;
108
109                                 optionptr = packet->sname;
110                                 rem = sizeof(packet->sname);
111
112                                 continue;
113                         }
114
115                         break;
116                 }
117
118                 len = 2 + optionptr[OPT_LEN];
119
120                 rem -= len;
121                 if (rem < 0)
122                         continue; /* complain and return NULL */
123
124                 if (optionptr[OPT_CODE] == code)
125                         return optionptr + OPT_DATA;
126
127                 if (optionptr[OPT_CODE] == DHCP_OPTION_OVERLOAD)
128                         overload |= optionptr[OPT_DATA];
129
130                 optionptr += len;
131         }
132
133         return NULL;
134 }
135
136 int dhcp_end_option(uint8_t *optionptr)
137 {
138         int i = 0;
139
140         while (optionptr[i] != DHCP_END) {
141                 if (optionptr[i] != DHCP_PADDING)
142                         i += optionptr[i + OPT_LEN] + OPT_DATA - 1;
143
144                 i++;
145         }
146
147         return i;
148 }
149
150 uint8_t *dhcpv6_get_option(struct dhcpv6_packet *packet, uint16_t pkt_len,
151                         int code, uint16_t *option_len, int *option_count)
152 {
153         int rem, count = 0;
154         uint8_t *optionptr, *found = NULL;
155         uint16_t opt_code, opt_len, len;
156
157         optionptr = packet->options;
158         rem = pkt_len - 1 - 3;
159
160         if (rem <= 0)
161                 goto bad_packet;
162
163         while (1) {
164                 opt_code = optionptr[0] << 8 | optionptr[1];
165                 opt_len = len = optionptr[2] << 8 | optionptr[3];
166                 len += 2 + 2; /* skip code and len */
167
168                 if (len < 4)
169                         goto bad_packet;
170
171                 rem -= len;
172                 if (rem < 0)
173                         break;
174
175                 if (opt_code == code) {
176                         if (option_len)
177                                 *option_len = opt_len;
178                         found = optionptr + 2 + 2;
179                         count++;
180                 }
181
182                 if (rem == 0)
183                         break;
184
185                 optionptr += len;
186         }
187
188         if (option_count)
189                 *option_count = count;
190
191         return found;
192
193 bad_packet:
194         if (option_len)
195                 *option_len = 0;
196         if (option_count)
197                 *option_count = 0;
198         return NULL;
199 }
200
201 uint8_t *dhcpv6_get_sub_option(unsigned char *option, uint16_t max_len,
202                         uint16_t *option_code, uint16_t *option_len)
203 {
204         int rem;
205         uint16_t code, len;
206
207         rem = max_len - 2 - 2;
208
209         if (rem <= 0)
210                 /* Bad option */
211                 return NULL;
212
213         code = option[0] << 8 | option[1];
214         len = option[2] << 8 | option[3];
215
216         rem -= len;
217         if (rem < 0)
218                 return NULL;
219
220         *option_code = code;
221         *option_len = len;
222
223         return &option[4];
224 }
225
226 /*
227  * Add an option (supplied in binary form) to the options.
228  * Option format: [code][len][data1][data2]..[dataLEN]
229  */
230 void dhcp_add_binary_option(struct dhcp_packet *packet, uint8_t *addopt)
231 {
232         unsigned len;
233         uint8_t *optionptr = packet->options;
234         unsigned end = dhcp_end_option(optionptr);
235
236         len = OPT_DATA + addopt[OPT_LEN];
237
238         /* end position + (option code/length + addopt length) + end option */
239         if (end + len + 1 >= DHCP_OPTIONS_BUFSIZE)
240                 /* option did not fit into the packet */
241                 return;
242
243         memcpy(optionptr + end, addopt, len);
244
245         optionptr[end + len] = DHCP_END;
246 }
247
248 /*
249  * Add an option (supplied in binary form) to the options.
250  * Option format: [code][len][data1][data2]..[dataLEN]
251  */
252 void dhcpv6_add_binary_option(struct dhcpv6_packet *packet, uint16_t max_len,
253                                 uint16_t *pkt_len, uint8_t *addopt)
254 {
255         unsigned len;
256         uint8_t *optionptr = packet->options;
257
258         len = 2 + 2 + (addopt[2] << 8 | addopt[3]);
259
260         /* end position + (option code/length + addopt length) */
261         if (*pkt_len + len >= max_len)
262                 /* option did not fit into the packet */
263                 return;
264
265         memcpy(optionptr + *pkt_len, addopt, len);
266         *pkt_len += len;
267 }
268
269 static GDHCPOptionType check_option(uint8_t code, uint8_t data_len)
270 {
271         GDHCPOptionType type = dhcp_get_code_type(code);
272         uint8_t len;
273
274         if (type == OPTION_UNKNOWN)
275                 return type;
276
277         len = dhcp_option_lengths[type & OPTION_TYPE_MASK];
278         if (len != data_len) {
279                 printf("Invalid option len %d (expecting %d) for code 0x%x\n",
280                         data_len, len, code);
281                 return OPTION_UNKNOWN;
282         }
283
284         return type;
285 }
286
287 void dhcp_add_option_uint32(struct dhcp_packet *packet, uint8_t code,
288                                                         uint32_t data)
289 {
290         uint8_t option[6];
291
292         if (check_option(code, sizeof(data)) == OPTION_UNKNOWN)
293                 return;
294
295         option[OPT_CODE] = code;
296         option[OPT_LEN] = sizeof(data);
297         put_be32(data, option + OPT_DATA);
298
299         dhcp_add_binary_option(packet, option);
300 }
301
302 void dhcp_add_option_uint16(struct dhcp_packet *packet, uint8_t code,
303                                                         uint16_t data)
304 {
305         uint8_t option[6];
306
307         if (check_option(code, sizeof(data)) == OPTION_UNKNOWN)
308                 return;
309
310         option[OPT_CODE] = code;
311         option[OPT_LEN] = sizeof(data);
312         put_be16(data, option + OPT_DATA);
313
314         dhcp_add_binary_option(packet, option);
315 }
316
317 void dhcp_add_option_uint8(struct dhcp_packet *packet, uint8_t code,
318                                                         uint8_t data)
319 {
320         uint8_t option[6];
321
322         if (check_option(code, sizeof(data)) == OPTION_UNKNOWN)
323                 return;
324
325         option[OPT_CODE] = code;
326         option[OPT_LEN] = sizeof(data);
327         option[OPT_DATA] = data;
328
329         dhcp_add_binary_option(packet, option);
330 }
331
332 void dhcp_init_header(struct dhcp_packet *packet, char type)
333 {
334         memset(packet, 0, sizeof(*packet));
335
336         packet->op = BOOTREQUEST;
337
338         switch (type) {
339         case DHCPOFFER:
340         case DHCPACK:
341         case DHCPNAK:
342                 packet->op = BOOTREPLY;
343         }
344
345         packet->htype = 1;
346         packet->hlen = 6;
347         packet->cookie = htonl(DHCP_MAGIC);
348         packet->options[0] = DHCP_END;
349
350         dhcp_add_option_uint8(packet, DHCP_MESSAGE_TYPE, type);
351 }
352
353 void dhcpv6_init_header(struct dhcpv6_packet *packet, uint8_t type)
354 {
355         int id;
356         uint64_t rand;
357
358         memset(packet, 0, sizeof(*packet));
359
360         packet->message = type;
361
362         __connman_util_get_random(&rand);
363         id = rand;
364
365         packet->transaction_id[0] = (id >> 16) & 0xff;
366         packet->transaction_id[1] = (id >> 8) & 0xff;
367         packet->transaction_id[2] = id & 0xff;
368 }
369
370 int dhcp_recv_l3_packet(struct dhcp_packet *packet, int fd)
371 {
372         int n;
373
374         memset(packet, 0, sizeof(*packet));
375
376         n = read(fd, packet, sizeof(*packet));
377         if (n < 0)
378                 return -errno;
379
380         if (packet->cookie != htonl(DHCP_MAGIC))
381                 return -EPROTO;
382
383         return n;
384 }
385
386 int dhcpv6_recv_l3_packet(struct dhcpv6_packet **packet, unsigned char *buf,
387                         int buf_len, int fd)
388 {
389         int n;
390
391         n = read(fd, buf, buf_len);
392         if (n < 0)
393                 return -errno;
394
395         *packet = (struct dhcpv6_packet *)buf;
396
397         return n;
398 }
399
400 /* TODO: Use glib checksum */
401 uint16_t dhcp_checksum(void *addr, int count)
402 {
403         /*
404          * Compute Internet Checksum for "count" bytes
405          * beginning at location "addr".
406          */
407         int32_t sum = 0;
408         uint16_t *source = (uint16_t *) addr;
409
410         while (count > 1)  {
411                 /*  This is the inner loop */
412                 sum += *source++;
413                 count -= 2;
414         }
415
416         /*  Add left-over byte, if any */
417         if (count > 0) {
418                 /* Make sure that the left-over byte is added correctly both
419                  * with little and big endian hosts */
420                 uint16_t tmp = 0;
421                 *(uint8_t *) &tmp = *(uint8_t *) source;
422                 sum += tmp;
423         }
424         /*  Fold 32-bit sum to 16 bits */
425         while (sum >> 16)
426                 sum = (sum & 0xffff) + (sum >> 16);
427
428         return ~sum;
429 }
430
431 #define IN6ADDR_ALL_DHCP_RELAY_AGENTS_AND_SERVERS_MC_INIT \
432         { { { 0xff,0x02,0,0,0,0,0,0,0,0,0,0,0,0x1,0,0x2 } } } /* ff02::1:2 */
433 static const struct in6_addr in6addr_all_dhcp_relay_agents_and_servers_mc =
434         IN6ADDR_ALL_DHCP_RELAY_AGENTS_AND_SERVERS_MC_INIT;
435
436 int dhcpv6_send_packet(int index, struct dhcpv6_packet *dhcp_pkt, int len)
437 {
438         struct msghdr m;
439         struct iovec v;
440         struct in6_pktinfo *pktinfo;
441         struct cmsghdr *cmsg;
442         int fd, ret, opt = 1;
443         struct sockaddr_in6 src;
444         struct sockaddr_in6 dst;
445         void *control_buf;
446         size_t control_buf_len;
447
448         fd = socket(PF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, IPPROTO_UDP);
449         if (fd < 0)
450                 return -errno;
451
452         if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) {
453                 int err = errno;
454                 close(fd);
455                 return -err;
456         }
457
458         memset(&src, 0, sizeof(src));
459         src.sin6_family = AF_INET6;
460         src.sin6_port = htons(DHCPV6_CLIENT_PORT);
461
462         if (bind(fd, (struct sockaddr *) &src, sizeof(src)) <0) {
463                 int err = errno;
464                 close(fd);
465                 return -err;
466         }
467
468         memset(&dst, 0, sizeof(dst));
469         dst.sin6_family = AF_INET6;
470         dst.sin6_port = htons(DHCPV6_SERVER_PORT);
471
472         dst.sin6_addr = in6addr_all_dhcp_relay_agents_and_servers_mc;
473
474         control_buf_len = CMSG_SPACE(sizeof(struct in6_pktinfo));
475         control_buf = g_try_malloc0(control_buf_len);
476         if (!control_buf) {
477                 close(fd);
478                 return -ENOMEM;
479         }
480
481         memset(&m, 0, sizeof(m));
482         memset(&v, 0, sizeof(v));
483
484         m.msg_name = &dst;
485         m.msg_namelen = sizeof(dst);
486
487         v.iov_base = (char *)dhcp_pkt;
488         v.iov_len = len;
489         m.msg_iov = &v;
490         m.msg_iovlen = 1;
491
492         m.msg_control = control_buf;
493         m.msg_controllen = control_buf_len;
494         cmsg = CMSG_FIRSTHDR(&m);
495         cmsg->cmsg_level = IPPROTO_IPV6;
496         cmsg->cmsg_type = IPV6_PKTINFO;
497         cmsg->cmsg_len = CMSG_LEN(sizeof(*pktinfo));
498
499         pktinfo = (struct in6_pktinfo *)CMSG_DATA(cmsg);
500         memset(pktinfo, 0, sizeof(*pktinfo));
501         pktinfo->ipi6_ifindex = index;
502         m.msg_controllen = cmsg->cmsg_len;
503
504         ret = sendmsg(fd, &m, 0);
505         if (ret < 0) {
506                 char *msg = "DHCPv6 msg send failed";
507
508                 if (errno == EADDRNOTAVAIL) {
509                         char *str = g_strdup_printf("%s (index %d)",
510                                         msg, index);
511                         perror(str);
512                         g_free(str);
513                 } else
514                         perror(msg);
515         }
516
517         g_free(control_buf);
518         close(fd);
519
520         return ret;
521 }
522
523 int dhcp_send_raw_packet(struct dhcp_packet *dhcp_pkt,
524                         uint32_t source_ip, int source_port,
525                         uint32_t dest_ip, int dest_port,
526                         const uint8_t *dest_arp, int ifindex, bool bcast)
527 {
528         struct sockaddr_ll dest;
529         struct ip_udp_dhcp_packet packet;
530         int fd, n;
531
532         enum {
533                 IP_UPD_DHCP_SIZE = sizeof(struct ip_udp_dhcp_packet) -
534                                                 EXTEND_FOR_BUGGY_SERVERS,
535                 UPD_DHCP_SIZE = IP_UPD_DHCP_SIZE -
536                                 offsetof(struct ip_udp_dhcp_packet, udp),
537         };
538
539         fd = socket(PF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
540         if (fd < 0)
541                 return -errno;
542
543         if (bcast)
544                 dhcp_pkt->flags |= htons(BROADCAST_FLAG);
545
546         memset(&dest, 0, sizeof(dest));
547         memset(&packet, 0, sizeof(packet));
548         packet.data = *dhcp_pkt;
549
550         dest.sll_family = AF_PACKET;
551         dest.sll_protocol = htons(ETH_P_IP);
552         dest.sll_ifindex = ifindex;
553         dest.sll_halen = 6;
554         memcpy(dest.sll_addr, dest_arp, 6);
555         if (bind(fd, (struct sockaddr *)&dest, sizeof(dest)) < 0) {
556                 int err = errno;
557                 close(fd);
558                 return -err;
559         }
560
561         packet.ip.protocol = IPPROTO_UDP;
562         packet.ip.saddr = source_ip;
563         packet.ip.daddr = dest_ip;
564         packet.udp.source = htons(source_port);
565         packet.udp.dest = htons(dest_port);
566         /* size, excluding IP header: */
567         packet.udp.len = htons(UPD_DHCP_SIZE);
568         /* for UDP checksumming, ip.len is set to UDP packet len */
569         packet.ip.tot_len = packet.udp.len;
570         packet.udp.check = dhcp_checksum(&packet, IP_UPD_DHCP_SIZE);
571         /* but for sending, it is set to IP packet len */
572         packet.ip.tot_len = htons(IP_UPD_DHCP_SIZE);
573         packet.ip.ihl = sizeof(packet.ip) >> 2;
574         packet.ip.version = IPVERSION;
575         packet.ip.ttl = IPDEFTTL;
576         packet.ip.check = dhcp_checksum(&packet.ip, sizeof(packet.ip));
577
578         /*
579          * Currently we send full-sized DHCP packets (zero padded).
580          * If you need to change this: last byte of the packet is
581          * packet.data.options[dhcp_end_option(packet.data.options)]
582          */
583         n = sendto(fd, &packet, IP_UPD_DHCP_SIZE, 0,
584                         (struct sockaddr *) &dest, sizeof(dest));
585         if (n < 0) {
586                 int err = errno;
587                 close(fd);
588                 return -err;
589         }
590
591         close(fd);
592
593         return n;
594 }
595
596 int dhcp_send_kernel_packet(struct dhcp_packet *dhcp_pkt,
597                                 uint32_t source_ip, int source_port,
598                                 uint32_t dest_ip, int dest_port,
599                                 const char *interface)
600 {
601         struct sockaddr_in client;
602         int fd, n, opt = 1;
603
604         enum {
605                 DHCP_SIZE = sizeof(struct dhcp_packet) -
606                                         EXTEND_FOR_BUGGY_SERVERS,
607         };
608
609         fd = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, IPPROTO_UDP);
610         if (fd < 0)
611                 return -errno;
612
613         if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE,
614                                 interface, strlen(interface) + 1) < 0) {
615                 int err = errno;
616                 close(fd);
617                 return -err;
618         }
619
620         if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) {
621                 int err = errno;
622                 close(fd);
623                 return -err;
624         }
625
626         memset(&client, 0, sizeof(client));
627         client.sin_family = AF_INET;
628         client.sin_port = htons(source_port);
629         client.sin_addr.s_addr = htonl(source_ip);
630         if (bind(fd, (struct sockaddr *) &client, sizeof(client)) < 0) {
631                 int err = errno;
632                 close(fd);
633                 return -err;
634         }
635
636         memset(&client, 0, sizeof(client));
637         client.sin_family = AF_INET;
638         client.sin_port = htons(dest_port);
639         client.sin_addr.s_addr = htonl(dest_ip);
640         if (connect(fd, (struct sockaddr *) &client, sizeof(client)) < 0) {
641                 int err = errno;
642                 close(fd);
643                 return -err;
644         }
645
646         n = write(fd, dhcp_pkt, DHCP_SIZE);
647         if (n < 0) {
648                 int err = errno;
649                 close(fd);
650                 return -err;
651         }
652
653         close(fd);
654
655         return n;
656 }
657
658 int dhcp_l3_socket(int port, const char *interface, int family)
659 {
660         int fd, opt = 1, len;
661         struct sockaddr_in addr4;
662         struct sockaddr_in6 addr6;
663         struct sockaddr *addr;
664
665         fd = socket(family, SOCK_DGRAM | SOCK_CLOEXEC, IPPROTO_UDP);
666         if (fd < 0)
667                 return -errno;
668
669         if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) {
670                 int err = errno;
671                 close(fd);
672                 return -err;
673         }
674
675         if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE,
676                                 interface, strlen(interface) + 1) < 0) {
677                 int err = errno;
678                 close(fd);
679                 return -err;
680         }
681
682         if (family == AF_INET) {
683                 memset(&addr4, 0, sizeof(addr4));
684                 addr4.sin_family = family;
685                 addr4.sin_port = htons(port);
686                 addr = (struct sockaddr *)&addr4;
687                 len = sizeof(addr4);
688         } else if (family == AF_INET6) {
689                 memset(&addr6, 0, sizeof(addr6));
690                 addr6.sin6_family = family;
691                 addr6.sin6_port = htons(port);
692                 addr = (struct sockaddr *)&addr6;
693                 len = sizeof(addr6);
694         } else {
695                 close(fd);
696                 return -EINVAL;
697         }
698
699         if (bind(fd, addr, len) != 0) {
700                 close(fd);
701                 return -1;
702         }
703
704         return fd;
705 }
706
707 char *get_interface_name(int index)
708 {
709         struct ifreq ifr;
710         int sk, err;
711
712         if (index < 0)
713                 return NULL;
714
715         sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
716         if (sk < 0) {
717                 perror("Open socket error");
718                 return NULL;
719         }
720
721         memset(&ifr, 0, sizeof(ifr));
722         ifr.ifr_ifindex = index;
723
724         err = ioctl(sk, SIOCGIFNAME, &ifr);
725         if (err < 0) {
726                 perror("Get interface name error");
727                 close(sk);
728                 return NULL;
729         }
730
731         close(sk);
732
733         return g_strdup(ifr.ifr_name);
734 }
735
736 bool interface_is_up(int index)
737 {
738         int sk, err;
739         struct ifreq ifr;
740         bool ret = false;
741
742         sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
743         if (sk < 0) {
744                 perror("Open socket error");
745                 return false;
746         }
747
748         memset(&ifr, 0, sizeof(ifr));
749         ifr.ifr_ifindex = index;
750
751         err = ioctl(sk, SIOCGIFNAME, &ifr);
752         if (err < 0) {
753                 perror("Get interface name error");
754                 goto done;
755         }
756
757         err = ioctl(sk, SIOCGIFFLAGS, &ifr);
758         if (err < 0) {
759                 perror("Get interface flags error");
760                 goto done;
761         }
762
763         if (ifr.ifr_flags & IFF_UP)
764                 ret = true;
765
766 done:
767         close(sk);
768
769         return ret;
770 }