DHCP 服务探测

1840阅读 0评论2020-11-05 khls27
分类:LINUX


在一些网络环境中,需要用到DHCP服务探测技术,动态调整本地dhcp服务的。本文提供一个探测的demo实现。
原理:
发送DHCP discover 报文,如果能够接受到 DHCP offer回应,则可以获取到服务信息。

代码如下:

点击(此处)折叠或打开

  1. #include <errno.h>
  2. #include <unistd.h>
  3. #include <stdio.h>
  4. #include <stdlib.h>
  5. #include <stddef.h>
  6. #include <time.h>
  7. #include <string.h>
  8. #include <signal.h>
  9. #include <sys/ioctl.h>
  10. #include <arpa/inet.h>
  11. #include <netpacket/packet.h>
  12. #include <netinet/if_ether.h>
  13. #include <netinet/ip.h>
  14. #include <netinet/udp.h>
  15. #include <linux/if.h>

  16. #define MAX_WAIT_TIME 15
  17. #define MAX_ERROR_LENGTH 1500

  18. /* Endianness check */
  19. #define IS_BIG_ENDIAN (*(uint16_t *)"\0\xff" < 0x100)


  20. /* DHCP protocol. RFC 2131, RFC 2132 */
  21. #define DHCP_MAGIC                 0x63825363
  22. #define DHCP_FIXED_LEN            240 /* with DHCP magic */
  23. #define DHCP_UDP_OVERHEAD        28 /* IP + UDP headers */
  24. #define DHCP_MTU_MAX            1500
  25. #define DHCP_MTU_MIN            576
  26. #define DHCP_OPTIONS_BUF_MIN    (DHCP_MTU_MIN - DHCP_FIXED_LEN - DHCP_UDP_OVERHEAD)
  27. #define DHCP_OPTIONS_BUF_MAX    (DHCP_MTU_MAX - DHCP_FIXED_LEN - DHCP_UDP_OVERHEAD)

  28. #define BOOTREQUEST                1
  29. #define BOOTREPLY                2

  30. /* DHCP Ports and Addresses */
  31. #define CLIENT_PORT        68
  32. #define SERVER_PORT        67

  33. /* DHCP packet */
  34. struct dhcp_packet
  35. {
  36.     uint8_t op;                /* BOOTREQUEST or BOOTREPLY */
  37.     uint8_t htype;            /* hardware address type. 1 = 10Mb ethernet */
  38.     uint8_t hlen;            /* hardware address length */
  39.     uint8_t hops;            /* used by relay agents only */
  40.     uint32_t xid;            /* unuque id */
  41.     uint16_t secs;             /* seconds since client started looking */
  42.     uint16_t flags;            /* only one flag */
  43.     uint32_t ciaddr;        /* clients IP address (if already in use) */
  44.     uint32_t yiaddr;        /* client IP address */
  45.     uint32_t siaddr_nip;    /* next server used in bootstrap */
  46.     uint32_t gateway_nip;    /* relay agent IP address */
  47.     uint8_t chaddr[16];        /* MAC address of client */
  48.     uint8_t sname[64];        /* server host name (ASCIZ) */
  49.     uint8_t file[128];        /* boot file name (ASCIIZ) */
  50.     uint32_t cookie;        /* fixed first four option bytes (99, 130, 83, 99 dec) */
  51.     uint8_t options[DHCP_OPTIONS_BUF_MAX];
  52. };

  53. /* IP packet with DHCP */
  54. struct ip_udp_dhcp_packet
  55. {
  56.     struct iphdr ip;            /* IP header */
  57.     struct udphdr udp;            /* UDP header */
  58.     struct dhcp_packet data;    /* UDP payload */
  59. };

  60. /* UDP packet with DHCP */
  61. struct udp_dhcp_packet
  62. {
  63.     struct udphdr udp;            /* UDP header */
  64.     struct dhcp_packet data;    /* UDP payload */
  65. };

  66. /* Packets size */
  67. enum
  68. {
  69.     IP_UDP_DHCP_SIZE = sizeof(struct ip_udp_dhcp_packet),
  70.     UDP_DHCP_SIZE = sizeof(struct udp_dhcp_packet),
  71.     DHCP_SIZE = sizeof(struct dhcp_packet),
  72. };

  73. /* DHCP options codes */
  74. #define DHCP_PADDING             0x00
  75. #define DHCP_SUBNET             0x01
  76. #define DHCP_ROUTER                0x03
  77. #define DHCP_DNS                0x06
  78. #define DHCP_BROADCAST            0x1c
  79. #define DHCP_STATIC                0x21
  80. #define DHCP_NTP                0x2a
  81. #define DHCP_VENDOR                0x2b
  82. #define DHCP_NETBIOS_NAME_SRV    0x2c
  83. #define DHCP_LEASE_TIME         0x33
  84. #define DHCP_OVERLOAD            0x34
  85. #define DHCP_MESSAGE_TYPE        0x35
  86. #define DHCP_SERVER_ID            0x36 /* DHCP server IP */
  87. #define DHCP_PARAM_REQ            0x37 /* list of options client wants */
  88. #define DHCP_MAX_SIZE            0x39
  89. #define DHCP_RENEWAL_TIME        0x3a
  90. #define DHCP_REBINDING_TIME        0x3b
  91. #define DHCP_VENDOR_CLASS_ID    0x3c
  92. #define DHCP_CLIENT_ID            0x3d /* client's MAC addr*/
  93. #define DHCP_SIP                0x78
  94. #define DHCP_CLASSLESS_STATIC    0x79
  95. #define DHCP_END                0xff

  96. /* DHCP_MESSAGE_TYPE values */
  97. #define DHCPDISCOVER    1 /* client -> server */
  98. #define DHCPOFFER        2 /* client <- server */

  99. /* Offsets in option byte sequence */
  100. #define OPT_CODE 0
  101. #define OPT_LEN 1
  102. #define OPT_DATA 2

  103. /* Bits in "overload" option */
  104. #define OPTION_FIELD            0
  105. #define FILE_FIELD                1
  106. #define SNAME_FIELD                2


  107. static volatile sig_atomic_t got_alarm = 0;

  108. /**
  109.  * Get MAC address of interface.
  110.  *
  111.  * @param ifname Interface name.
  112.  * @param addr Array for MAC address.
  113.  *
  114.  * @return -1 on failure, 0 on success.
  115.  */
  116. int get_if_mac(const char* ifname, uint8_t addr[ETH_ALEN])
  117. {
  118.     /* Copy interface name into ifreq */
  119.     struct ifreq ifr;
  120.     memset(&ifr, 0, sizeof(struct ifreq));

  121.     size_t if_name_len = strlen(ifname);

  122.     if (if_name_len < sizeof(ifr.ifr_name))
  123.     {
  124.         memcpy(ifr.ifr_name, ifname, if_name_len);
  125.         ifr.ifr_name[if_name_len] = 0;
  126.     }
  127.     else
  128.     {
  129.         return -1;
  130.     }

  131.     /* Create socket */
  132.     int fd = socket(AF_UNIX, SOCK_DGRAM, 0);

  133.     if (fd < 0)
  134.     {
  135.         return -1;
  136.     }

  137.     /* Get hw address */
  138.     if (ioctl(fd, SIOCGIFHWADDR, &ifr) < 0)
  139.     {
  140.         close (fd);
  141.         return -1;
  142.     }

  143.     close(fd);
  144.     memcpy (addr, ifr.ifr_hwaddr.sa_data, ETH_ALEN);
  145.     return 0;
  146. }


  147. /**
  148.  * Get Interface index.
  149.  *
  150.  * @param ifname Interface name.
  151.  *
  152.  * @return Interface index on success, zero on failure.
  153.  */
  154. unsigned int get_if_index(const char* ifname)
  155. {
  156.     /* Copy interface name into ifreq */
  157.     struct ifreq ifr;
  158.     memset(&ifr, 0, sizeof(struct ifreq));

  159.     size_t if_name_len = strlen(ifname);

  160.     if (if_name_len < sizeof(ifr.ifr_name))
  161.     {
  162.         memcpy(ifr.ifr_name, ifname, if_name_len);
  163.         ifr.ifr_name[if_name_len] = 0;
  164.     }
  165.     else
  166.     {
  167.         return -1;
  168.     }

  169.     /* Create socket */
  170.     int fd = socket(AF_UNIX, SOCK_DGRAM, 0);

  171.     if (fd < 0)
  172.     {
  173.         return 0;
  174.     }

  175.     /* Get interface index */
  176.     if (ioctl(fd, SIOCGIFINDEX, &ifr) < 0)
  177.     {
  178.         close (fd);
  179.         return 0;
  180.     }

  181.     close(fd);
  182.     return ifr.ifr_ifindex;
  183. }

  184. /**
  185.  * Calculate internet checksum.
  186.  *
  187.  * @param addr Start address.
  188.  * @param nleft Length in bytes.
  189.  *
  190.  * @return Checksum.
  191.  */
  192. uint16_t inet_chksum(uint16_t* addr, int nleft)
  193. {
  194.     /*
  195.      * Algorithm is simple, using a 32 bit accumulator,
  196.      * we add sequential 16 bit words to it, and at the end, fold
  197.      * back all the carry bits from the top 16 bits into the lower
  198.      * 16 bits.
  199.      */
  200.     unsigned sum = 0;
  201.     while (nleft > 1)
  202.     {
  203.         sum += *addr++;
  204.         nleft -= 2;
  205.     }
  206.     /* Mop up an odd byte, if necessary */
  207.     if (nleft == 1)
  208.     {
  209.         if (IS_BIG_ENDIAN)
  210.             sum += *(uint8_t*)addr << 8;
  211.         else
  212.             sum += *(uint8_t*)addr;
  213.     }

  214.     /* Add back carry outs from top 16 bits to low 16 bits */
  215.     sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */
  216.     sum += (sum >> 16); /* add carry */

  217.     return (uint16_t)~sum;
  218. }

  219. /**
  220.  * Calculate random id (unsigned 32-bit value).
  221.  *
  222.  * @param Void.
  223.  * @return id
  224.  */
  225. uint32_t random_id(void)
  226. {
  227.     srand(time(NULL));
  228.     return rand();
  229. }


  230. /**
  231.  * Fill DHCP Packet.
  232.  *
  233.  * @param packet DHCP packet.
  234.  * @param hwaddr MAC address of interface.
  235.  * @return Void.
  236.  */
  237. static void dhcp_init_packet(struct dhcp_packet* packet, uint8_t* hwaddr)
  238. {
  239.     unsigned index = 0;

  240.     /* DHCP header */
  241.     memset(packet, 0, sizeof(*packet));
  242.     packet->op = BOOTREQUEST;
  243.     packet->htype = 1; /* ethernet */
  244.     packet->hlen = ETH_ALEN;
  245.     packet->cookie = htonl(DHCP_MAGIC);
  246.     packet->secs = 0;
  247.     memcpy(packet->chaddr, hwaddr, ETH_ALEN);

  248.     /* DHCP options */
  249.     /* Message type */
  250.     packet->options[index++] = DHCP_MESSAGE_TYPE;
  251.     packet->options[index++] = 1; //length of option data
  252.     packet->options[index++] = DHCPDISCOVER;

  253.     /* Requested parameters */
  254.     packet->options[index++] = DHCP_PARAM_REQ;
  255.     packet->options[index++] = 9; // number of options
  256.     packet->options[index++] = DHCP_SUBNET;
  257.     packet->options[index++] = DHCP_ROUTER;
  258.     packet->options[index++] = DHCP_DNS;
  259.     packet->options[index++] = DHCP_BROADCAST;
  260.     packet->options[index++] = DHCP_STATIC;
  261.     packet->options[index++] = DHCP_NTP;
  262.     packet->options[index++] = DHCP_VENDOR;
  263.     packet->options[index++] = DHCP_SIP;
  264.     packet->options[index++] = DHCP_CLASSLESS_STATIC;

  265.     /* client id */
  266.     packet->options[index++] = DHCP_CLIENT_ID;
  267.     packet->options[index++] = ETH_ALEN + 1;
  268.     packet->options[index++] = 1; //ethernet
  269.     memcpy(&packet->options[index], hwaddr, ETH_ALEN);
  270.     index += ETH_ALEN;

  271.     /* end option */
  272.     packet->options[index] = DHCP_END;
  273. }

  274. /**
  275.  * Calculate position of the END option.
  276.  *
  277.  * @param optionptr Beginning of dhcp options.
  278.  * @return Position of 'end' option.
  279.  */
  280. static int dhcp_end_option(uint8_t* optionptr)
  281. {
  282.     int i = 0;
  283.     while (optionptr[i] != DHCP_END)
  284.     {
  285.         if (optionptr[i] != DHCP_PADDING)
  286.             i += optionptr[i + OPT_LEN] + OPT_DATA - 1;
  287.         i++;
  288.     }
  289.     return i;
  290. }

  291. /**
  292.  * Get an option with bounds checking
  293.  *
  294.  * @param packet DHCP packet.
  295.  * @param code Option code.
  296.  *
  297.  * @return NULL or pointer to beginning of the option data.
  298.  */
  299. static uint8_t* dhcp_get_option(struct dhcp_packet* packet, int code)
  300. {
  301.     uint8_t* optionptr;
  302.     int len;
  303.     int rem;
  304.     int overload = 0;
  305.     enum
  306.     {
  307.         FILE_FIELD101 = FILE_FIELD * 0x101,
  308.         SNAME_FIELD101 = SNAME_FIELD * 0x101,
  309.     };

  310.     /* option bytes: [code][len][data1][data2]...[dataLEN] */
  311.     optionptr = packet->options;
  312.     rem = sizeof(packet->options);
  313.     while (1)
  314.     {
  315.         if (rem <= 0)
  316.         {
  317.             return NULL;
  318.         }
  319.         if (optionptr[OPT_CODE] == DHCP_PADDING)
  320.         {
  321.             rem--;
  322.             optionptr++;
  323.             continue;
  324.         }
  325.         if (optionptr[OPT_CODE] == DHCP_END)
  326.         {
  327.             if ((overload & FILE_FIELD101) == FILE_FIELD)
  328.             {
  329.                 /* we can use packet->file */
  330.                 overload |= FILE_FIELD101;
  331.                 optionptr = packet->file;
  332.                 rem = sizeof(packet->file);
  333.                 continue;
  334.             }
  335.             if ((overload & SNAME_FIELD101) == SNAME_FIELD)
  336.             {
  337.                 /* we can use packet->sname */
  338.                 overload |= SNAME_FIELD101;
  339.                 optionptr = packet->sname;
  340.                 rem = sizeof(packet->sname);
  341.                 continue;
  342.             }
  343.             break;
  344.         }
  345.         len = OPT_DATA + optionptr[OPT_LEN];
  346.         rem -= len;
  347.         if (rem < 0)
  348.             continue;

  349.         if (optionptr[OPT_CODE] == code)
  350.             return optionptr + OPT_DATA;

  351.         if (optionptr[OPT_CODE] == DHCP_OVERLOAD)
  352.             overload |= optionptr[OPT_DATA];

  353.         optionptr += len;
  354.     }

  355.     return NULL;
  356. }

  357. /**
  358.  * Send DHCP DISCOVER
  359.  *
  360.  * @param fd Socket descriptor.
  361.  * @param ifindex Interface index.
  362.  * @param hwaddr MAC address of interface.
  363.  * @param id Unique ID.
  364.  *
  365.  * @return Number of bytes sent on success, -1 on error.
  366.  */
  367. static int dhcp_send_discover(int fd, int ifindex, uint8_t* hwaddr, uint32_t id)
  368. {
  369.     struct sockaddr_ll sa;
  370.     struct ip_udp_dhcp_packet packet;
  371.     unsigned padding;
  372.     int result = -1;

  373.     /* Bind */
  374.     memset(&sa, 0, sizeof(sa));
  375.     sa.sll_family = AF_PACKET;
  376.     sa.sll_protocol = htons(ETH_P_IP);
  377.     sa.sll_ifindex = ifindex;
  378.     sa.sll_halen = ETH_ALEN;
  379.     memset(&sa.sll_addr, 0xff, ETH_ALEN);

  380.     if (bind(fd, (struct sockaddr*)&sa, sizeof(sa)) < 0)
  381.     {
  382.         return result;
  383.     }

  384.     /* Craft packet */
  385.     memset(&packet, 0, sizeof(packet));
  386.     dhcp_init_packet(&packet.data, hwaddr);
  387.     packet.data.xid = id;

  388.     /* For badly configured servers (they drop DHCP packets > 576 octets (with ethernet header),
  389.      * but they may only drop packets > 576 octets without ethernet header (590 with ethernet header).
  390.      * RFC 1542: minimal BOOTP header 300 octets.
  391.      */
  392.     padding = DHCP_OPTIONS_BUF_MAX - 1 - dhcp_end_option(packet.data.options);
  393.     if (padding > DHCP_SIZE - 300)
  394.         padding = DHCP_SIZE - 300;

  395.     /* IP and UDP headers */
  396.     packet.ip.protocol = IPPROTO_UDP;
  397.     packet.ip.saddr = htonl(INADDR_ANY);
  398.     packet.ip.daddr = htonl(INADDR_BROADCAST);
  399.     packet.udp.source = htons(CLIENT_PORT);
  400.     packet.udp.dest = htons(SERVER_PORT);
  401.     /* size, excluding IP header */
  402.     packet.udp.len = htons(UDP_DHCP_SIZE - padding);
  403.     /* for UDP checksumming, ip.len is set to UDP packet len */
  404.     packet.ip.tot_len = packet.udp.len;

  405.     packet.udp.check = inet_chksum((uint16_t*)&packet,
  406.      IP_UDP_DHCP_SIZE - padding);
  407.     /* for sending, it is set to IP packet len */
  408.     packet.ip.tot_len = htons(IP_UDP_DHCP_SIZE - padding);
  409.     packet.ip.version = IPVERSION;
  410.     packet.ip.ihl = sizeof(packet.ip) >> 2;
  411.     packet.ip.ttl = IPDEFTTL;
  412.     packet.ip.check = inet_chksum((uint16_t*)&packet.ip, sizeof(packet.ip));

  413.     /* Send packet */
  414.     result = sendto(fd, &packet, IP_UDP_DHCP_SIZE - padding, 0,
  415.      (struct sockaddr*)&sa, sizeof(sa));

  416.     return result;
  417. }


  418. /**
  419.  * Receive OFFER
  420.  *
  421.  * @param fd Socket descriptor.
  422.  * @param id Unique ID.
  423.  * @param hwaddr MAC address of interface.
  424.  *
  425.  * @return 0 on success, -1 on read error,
  426.  * -2 packet is not correct, -3 on EINTR (time is out).
  427.  */
  428. static int dhcp_recv_offer(int fd, uint32_t id, uint8_t* hwaddr)
  429. {
  430.     int bytes;
  431.     struct ip_udp_dhcp_packet packet;
  432.     struct dhcp_packet data;

  433.     uint8_t* opt_data;
  434.     uint16_t check;
  435.     char ip_str[INET_ADDRSTRLEN];
  436.     uint32_t ip_addr;
  437.     int i;

  438.     memset(&packet, 0, sizeof(packet));
  439.     memset(&data, 0, sizeof(data));

  440.     /* Read packet */
  441.     bytes = read(fd, &packet, sizeof(packet));

  442.     if (bytes < 0)
  443.     {
  444.         if (errno == EINTR)
  445.         {
  446.             return -3;
  447.         }
  448.         else
  449.         {
  450.             return -1;
  451.         }
  452.     }
  453.     /* Packet is too short */
  454.     if (bytes < (int) (sizeof(packet.ip) + sizeof(packet.udp)))
  455.         return -2;
  456.     /* Oversized packet */
  457.     if (bytes < (int) ntohs(packet.ip.tot_len))
  458.         return -2;

  459.     /* Ignore any extra garbage bytes */
  460.     bytes = ntohs(packet.ip.tot_len);

  461.     /* Unrelated/bogus packet */
  462.     if (packet.ip.protocol != IPPROTO_UDP
  463.      || packet.ip.version != IPVERSION
  464.      || packet.ip.ihl != (sizeof(packet.ip) >> 2)
  465.      || packet.udp.dest != htons(CLIENT_PORT)
  466.      || ntohs(packet.udp.len) != (uint16_t)(bytes - sizeof(packet.ip)) )
  467.         return -2;

  468.     /* Verify IP checksum */
  469.     check = packet.ip.check;
  470.     packet.ip.check = 0;
  471.     if (check != inet_chksum((uint16_t*)&packet.ip, sizeof(packet.ip)))
  472.         return -2;

  473.     /* Verify UDP checksum, IP header has to be modified for this */
  474.     memset(&packet.ip, 0, offsetof(struct iphdr, protocol));
  475.     /* ip.xx fields which are not memset: protocol, check, saddr, daddr */
  476.     packet.ip.tot_len = packet.udp.len;
  477.     check = packet.udp.check;
  478.     packet.udp.check = 0;
  479.     if (check && check != inet_chksum((uint16_t*)&packet, bytes))
  480.         return -2;

  481.     memcpy(&data, &packet.data, sizeof(data));

  482.     /* Check DHCP magic */
  483.     if (bytes < (int)offsetof(struct dhcp_packet, options)
  484.      || data.cookie != htonl(DHCP_MAGIC))
  485.         return    -2;

  486.     /* Check xid */
  487.     if (data.xid != id)
  488.         return -2;

  489.     /* Ignore packets that aren't for us */
  490.     if (data.hlen != ETH_ALEN || memcmp(&data.chaddr, hwaddr, ETH_ALEN))
  491.         return -2;

  492.     /* Check message type */
  493.     opt_data = dhcp_get_option(&data, DHCP_MESSAGE_TYPE);
  494.     if (!opt_data || *opt_data != DHCPOFFER)
  495.         return -2;

  496.     /* print output */
  497.     /* Get DHCP server ID */
  498.     opt_data = dhcp_get_option(&data, DHCP_SERVER_ID);
  499.     if (opt_data)
  500.     {
  501.         ip_addr = *(uint32_t*)opt_data;
  502.         if (inet_ntop(AF_INET, &ip_addr, ip_str, sizeof(ip_str)))
  503.             printf("-->> server:%s\n", ip_str);
  504.     }

  505.     /* Get netmask */
  506.     opt_data = dhcp_get_option(&data, DHCP_SUBNET);
  507.     if (opt_data)
  508.     {
  509.         ip_addr = *(uint32_t*)opt_data;
  510.         if (inet_ntop(AF_INET, &ip_addr, ip_str, sizeof(ip_str)))
  511.             printf("-->> mask:%s\n", ip_str);
  512.     }

  513.     /* Get router */
  514.     opt_data = dhcp_get_option(&data, DHCP_ROUTER);
  515.     if (opt_data)
  516.     {
  517.         ip_addr = *(uint32_t*)opt_data;
  518.         if (inet_ntop(AF_INET, &ip_addr, ip_str, sizeof(ip_str)))
  519.             printf("-->> router:%s\n", ip_str);

  520.     }

  521.     /* Get DNS servers */
  522.     opt_data = dhcp_get_option(&data, DHCP_DNS);
  523.     if (opt_data)
  524.     {

  525.         int num = *(opt_data - 1) >> 2; /* get number of servers */
  526.         for (i = 0; i < num; i++)
  527.         {
  528.             ip_addr = (*((uint32_t*)opt_data + i));
  529.             if (inet_ntop(AF_INET, &ip_addr, ip_str, sizeof(ip_str)))
  530.                 printf("-->> dns%d:%s\n", i + 1, ip_str);
  531.             else
  532.                 break;
  533.         }
  534.     }

  535.     /* Get Broadcast address */
  536.     opt_data = dhcp_get_option(&data, DHCP_BROADCAST);
  537.     if (opt_data)
  538.     {
  539.         ip_addr = *(uint32_t*)opt_data;
  540.         if (inet_ntop(AF_INET, &ip_addr, ip_str, sizeof(ip_str)))
  541.             printf("-->> broadcast:%s\n", ip_str);

  542.     }
  543.     /* Get NETBIOS Name servers */
  544.     opt_data = dhcp_get_option(&data, DHCP_NETBIOS_NAME_SRV);
  545.     if (opt_data)
  546.     {
  547.         int num = *(opt_data - 1) >> 2; /* get number of servers */
  548.         for (i = 0; i < num; i++)
  549.         {
  550.             ip_addr = *((uint32_t*)opt_data + i);
  551.             if (inet_ntop(AF_INET, &ip_addr, ip_str, sizeof(ip_str)))
  552.                 printf("-->> netbios%d:%s\n", i + 1, ip_str);
  553.             else
  554.                 break;
  555.         }
  556.     }
  557.     return 0;
  558. }

  559. /**
  560.  * Signal handler
  561.  * @param Signal value.
  562.  *
  563.  * @return Void.
  564.  */
  565. static void handler(int sig)
  566. {
  567.     if (sig == SIGALRM)
  568.         got_alarm = 1;
  569. }


  570. /**
  571.  * Search DHCP server.
  572.  *
  573.  * @param ifname Network interface name.
  574.  * @param time Wait time.
  575.  *
  576.  * @return 1 on success, 0 on time is out, -2 on invalid json object for result,
  577.  * -1 on other errors.
  578.  */
  579. int scan_dhcp(const char* ifname, unsigned int time)
  580. {
  581.     int ifindex;
  582.     uint8_t hwaddr[ETH_ALEN];
  583.     struct sigaction sa;
  584.     uid_t euid;
  585.     uint32_t id;
  586.     int fd = -1;
  587.     int ret = -1;


  588.     /* Check superuser priveleges */
  589.     if ((euid = geteuid()) != 0)
  590.     {
  591.         printf( "You must be root\n");
  592.         goto err_exit;
  593.     }

  594.     /* Check input data */
  595.     /* Check wait time */
  596.     if (time == 0)
  597.         time = 1;

  598.     if (time > MAX_WAIT_TIME)
  599.     {
  600.         printf("Max wait time is %d\n", MAX_WAIT_TIME);
  601.         goto err_exit;
  602.     }

  603.     /* Check interface name */
  604.     if (!ifname || *ifname == '\0')
  605.     {
  606.         printf("Empty interface name\n");
  607.         goto err_exit;
  608.     }

  609.     /* Check interface name length */
  610.     if (strlen(ifname) > IFNAMSIZ)
  611.     {
  612.         printf("Interface name is too long\n");
  613.         goto err_exit;
  614.     }

  615.     /* Get interface index */
  616.     if ((ifindex = get_if_index(ifname)) == 0)
  617.     {
  618.         printf("No interface found\n");
  619.         goto err_exit;
  620.     }

  621.     /* Get MAC address */
  622.     if ((get_if_mac(ifname, hwaddr)) < 0)
  623.     {
  624.         printf( "Can't get MAC address\n");
  625.         goto err_exit;
  626.     }

  627.     /* Create socket */
  628.     fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP));

  629.     if (fd < 0)
  630.     {
  631.         printf("Can't create socket\n");
  632.         goto err_exit;
  633.     }

  634.     /* Establish handler for notification signal */
  635.     memset(&sa, 0, sizeof(struct sigaction));
  636.     sa.sa_flags = 0;
  637.     sigemptyset(&sa.sa_mask);
  638.     sa.sa_handler = handler;
  639.     if ((sigaction(SIGALRM, &sa, NULL)) == -1)
  640.     {
  641.         printf("Can't set handler for timer\n");
  642.         goto err_exit2;
  643.     }

  644.     /* Send Request */
  645.     id = random_id();
  646.     ret = dhcp_send_discover(fd, ifindex, hwaddr, id);
  647.     if (ret < 0)
  648.     {
  649.         printf("Can't send request\n");
  650.         goto err_exit2;
  651.     }

  652.     /* Start timer */
  653.     alarm(time);

  654.     /* Loop, recv packets && search answer for us */
  655.     while(1)
  656.     {
  657.         ret = dhcp_recv_offer(fd, id, hwaddr);

  658.         /* Answer received */
  659.         if (ret == 0)
  660.         {
  661.             ret = 1;
  662.             break;
  663.         }
  664.         /* Read error */
  665.         if (ret == -1)
  666.         {
  667.             printf("Read socket error\n");
  668.             break;
  669.         }

  670.         /* Check timer */
  671.         if (got_alarm == 1)
  672.         {
  673.             ret = 0;
  674.             break;
  675.         }
  676.     }
  677.     /* Cancel timer */
  678.     alarm(0);
  679. err_exit2:
  680.     close(fd);
  681. err_exit:
  682.     return ret;
  683. }


  684. int main(int argc, char** argv)
  685. {
  686.     if(argc < 2)
  687.     {
  688.         printf("must appoint a interface\n");
  689.         return 127;
  690.     }
  691.     return scan_dhcp(argv[1], 3);
  692. }

demo测试:


注意,需要root权限运行
上一篇:池化技术与线程池的简单实现
下一篇:一文读懂内核spin lock接口原理及使用