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