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