/*
 *  $Id: dhcp_discover.c,v 1.3 2004/01/03 20:31:01 mike Exp $
 *
 *  libnet 1.1
 *  Build a DHCP discover packet
 *  To view: /usr/sbin/tcpdump -vvvvven -s 4096 'port 67 or port 68'
 *
 *  Copyright (c) 1998 - 2004 Mike D. Schiffman <mike@infonexus.com>
 *  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 */

#include "./libnet_test.h"

void
usage(char *prog)
{
    fprintf(stderr, "Usage: %s interface\n", prog);
    exit(1);
}


int
main(int argc, char *argv[])
{
    char *intf;
    u_long src_ip, options_len, orig_len;
    int i;
    
    libnet_t *l;
    libnet_ptag_t t;
    libnet_ptag_t ip;
    libnet_ptag_t udp;
    libnet_ptag_t dhcp;
    struct libnet_ether_addr *ethaddr;
    struct libnet_stats ls;
    
    char errbuf[LIBNET_ERRBUF_SIZE];
    
    u_char options_req[] = { LIBNET_DHCP_SUBNETMASK,
                             LIBNET_DHCP_BROADCASTADDR, LIBNET_DHCP_TIMEOFFSET,
                             LIBNET_DHCP_ROUTER, LIBNET_DHCP_DOMAINNAME,
                             LIBNET_DHCP_DNS, LIBNET_DHCP_HOSTNAME };
    u_char *options;
    u_char enet_dst[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
    u_char *tmp;
    
    if (argc != 2)
    {
        usage(argv[0]);
    }
    intf = argv[1];
    
    l = libnet_init(LIBNET_LINK, intf, errbuf);
    if (!l)
    {
        fprintf(stderr, "libnet_init: %s", errbuf);
        exit(EXIT_FAILURE);
    }
    else
    {
        src_ip = libnet_get_ipaddr4(l);;
        
        if ((ethaddr = libnet_get_hwaddr(l)) == NULL)
        {
            fprintf(stderr, "libnet_get_hwaddr: %s\n", libnet_geterror(l));
            exit(EXIT_FAILURE);
        }
        
        printf("ip addr     : %s\n", libnet_addr2name4(src_ip,
                LIBNET_DONT_RESOLVE));
        printf("eth addr    : ");
        for (i = 0; i < 6; i++)
        {
            printf("%2.2x", ethaddr->ether_addr_octet[i]);
            if (i != 5)
            {
                printf(":");
            }
        }
        printf("\n");
        
        
        /* build options packet */
        i = 0;
	/* update total payload size */
        options_len = 3;
        
        /* we are a discover packet */
        options = malloc(3);
        options[i++] = LIBNET_DHCP_MESSAGETYPE;     /* type */
        options[i++] = 1;                           /* len */
        options[i++] = LIBNET_DHCP_MSGDISCOVER;     /* data */
        
        orig_len = options_len;
	/* update total payload size */
        options_len += sizeof(options_req) + 2;
        
        tmp = malloc(options_len);
        memcpy(tmp, options, orig_len);
        free(options);
        options = tmp;
        
        /* we are going to request some parameters */
        options[i++] = LIBNET_DHCP_PARAMREQUEST;                 /* type */
        options[i++] = sizeof(options_req);                      /* len */
        memcpy(options + i, options_req, sizeof(options_req));   /* data */
        i += sizeof(options_req);
        
        /* if we have an ip already, let's request it. */
        if (src_ip)
        {
            orig_len = options_len;
            options_len += 2 + sizeof(src_ip);
            
            tmp = malloc(options_len);
            memcpy(tmp, options, orig_len);
            free(options);
            options = tmp;
            
            options[i++] = LIBNET_DHCP_DISCOVERADDR;	          /* type */
            options[i++] = sizeof(src_ip);			  /* len */
            memcpy(options + i, (char *)&src_ip, sizeof(src_ip)); /* data */
            i += sizeof(src_ip);
        }
        
        /* end our options packet */
        orig_len = options_len;
        options_len += 1;
        tmp = malloc(options_len);
        memcpy(tmp, options, orig_len);
        free(options);
        options = tmp;
        options[i++] = LIBNET_DHCP_END;

        
        /* make sure we are at least the minimum length, if not fill */
        /* this could go in libnet, but we will leave it in the app for now */
        if (options_len + LIBNET_DHCPV4_H < LIBNET_BOOTP_MIN_LEN)
        {
            orig_len = options_len;
            options_len = LIBNET_BOOTP_MIN_LEN - LIBNET_DHCPV4_H;
            
            tmp = malloc(options_len);
            memcpy(tmp, options, orig_len);
            free(options);
            options = tmp;
            
            memset(options + i, 0, options_len - i);
        }
        
        dhcp = libnet_build_dhcpv4(
                LIBNET_DHCP_REQUEST,            /* opcode */
                1,                              /* hardware type */
                6,                              /* hardware address length */
                0,                              /* hop count */
                0xdeadbeef,                     /* transaction id */
                0,                              /* seconds since bootstrap */
                0x8000,                         /* flags */
                0,                              /* client ip */
                0,                              /* your ip */
                0,                              /* server ip */
                0,                              /* gateway ip */
                ethaddr->ether_addr_octet,      /* client hardware addr */
                NULL,                           /* server host name */
                NULL,                           /* boot file */
                options,                        /* dhcp options in payload */
                options_len,                    /* length of options */
                l,                              /* libnet context */
                0);                             /* libnet ptag */
        
        udp = libnet_build_udp(
                68,                             /* source port */
                67,                             /* destination port */
                LIBNET_UDP_H + LIBNET_DHCPV4_H + options_len,  /* packet size */
                0,                              /* checksum */
                NULL,                           /* payload */
                0,                              /* payload size */
                l,                              /* libnet context */
                0);                             /* libnet ptag */
        
        ip = libnet_build_ipv4(
                LIBNET_IPV4_H + LIBNET_UDP_H + LIBNET_DHCPV4_H
                + options_len,                  /* length */
                0x10,                           /* TOS */
                0,                              /* IP ID */
                0,                              /* IP Frag */
                16,                             /* TTL */
                IPPROTO_UDP,                    /* protocol */
                0,                              /* checksum */
                src_ip,                         /* src ip */
                inet_addr("255.255.255.255"),   /* destination ip */
                NULL,                           /* payload */
                0,                              /* payload size */
                l,                              /* libnet context */
                0);                             /* libnet ptag */
        
        t = libnet_autobuild_ethernet(
                enet_dst,                       /* ethernet destination */
                ETHERTYPE_IP,                   /* protocol type */
                l);                             /* libnet context */
        
        if (libnet_write(l) == -1)
        {
            fprintf(stderr, " %s: libnet_write: %s\n", argv[0],
                    strerror(errno));
            exit(EXIT_FAILURE);
        }
        
        libnet_stats(l, &ls);
        fprintf(stderr, "Packets sent:  %lld\n"
                    "Packet errors: %lld\n"
                    "Bytes written: %lld\n",
                    (long long)ls.packets_sent, (long long)ls.packet_errors,
		    (long long)ls.bytes_written);
        libnet_destroy(l);
        
        free(options);
        
        exit(0);
    }
    exit(0);
}
