LinuxQuestions.org

LinuxQuestions.org (/questions/)
-   Programming (http://www.linuxquestions.org/questions/programming-9/)
-   -   Writing a simple DHCP server (for Linux) (http://www.linuxquestions.org/questions/programming-9/writing-a-simple-dhcp-server-for-linux-4175439035/)

prushik 11-27-2012 09:07 PM

Writing a simple DHCP server (for Linux)
 
Hi guys, I'm working on writing a DHCP server following RFC2131. So far, RFC2131 is the most difficult RFC I have read. If you just want a basic idea of how the exchange works and not read all the crap in RFC2131, you can read RFC951 (BOOTP) since its basically the same thing.
Anyways, the idea is that the client broadcasts a message asking for an IP address. My server successfully receives the packet and understands it. Then the server has to send a packet back to the client.
Now, according to both RFC documents, the server can simply send the response packet back out to the broadcast address on port 68. This is where I run into trouble. The client sends a the request packet out from 67 to 68, so I figured I could just send one right back over the same socket, but this didn't work. So I manually set up a broadcast socket, and guess what, that doesn't work either.
According to wireshark (running on the server) the broadcast from the server never goes out, however, if I send the broadcast out on port 67 instead, then my server successfully picks up the packet it just sent out. No error is reported in any case, its as if the packet gets sent, but the client just never gets it.
The client I am using is an IPtime wired router (WAN port). I tried my girlfriend's laptop as a client, but the lights on the ethernet port didn't light up, and her machine is Windows and all in Chinese (which I can't read) so I gave up on that. I guess what I need is a crossover cable to make that work, although I swear I have shared internet to her computer before... That's another issue.
What I want to know is why doesn't the packet get to the client??
Here is what my code looks like:
Code:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

struct dhcp_packet
{
        unsigned char OP,HTYPE,HLEN,HOPS;
        uint32_t XID;
        uint16_t SECS, FLAGS;
        unsigned char CIADDR[4];
        unsigned char YIADDR[4];
        unsigned char SIADDR[4];
        unsigned char GIADDR[4];
        unsigned char CHADDR[16];
        unsigned char SNAME[64];
        unsigned char FILE[128];
        unsigned char OPTIONS[312];
};

struct bootp_packet
{
        unsigned char OP,HTYPE,HLEN,HOPS;
        uint32_t XID;
        uint16_t SECS, VOID;
        unsigned char CIADDR[4];
        unsigned char YIADDR[4];
        unsigned char SIADDR[4];
        unsigned char GIADDR[4];
        unsigned char CHADDR[16];
        unsigned char SNAME[64];
        unsigned char FILE[128];
        unsigned char VEND[64];
};

void error(unsigned char *msg)
{
        perror(msg);
        exit(1);
}

struct dhcp_packet * parse_dhcp_packet(unsigned char * data, int n)
{
//DHCP PACKET FORMAT
//        Octet        Description
//        1        OP
//        2        HTYPE
//        3        HLEN
//        4        HOPS
//        5-8        XID
//        9,10        SECS
//        11,12        FLAGS
//        13-16        CIADDR
//        17-20        YIADDR
//        21-24        SIADDR
//        25-28        GIADDR
//        29-44        CHADDR
//        45-108        SNAME
//        109-236        FILE
//        237-576        OPTIONS
        struct dhcp_packet *packet;
        packet = malloc(sizeof(struct dhcp_packet));

        return packet;
}


int main(int argc, unsigned char *argv[])
{
        int sockfd, clilen;
        unsigned char buffer[576], *data;
        struct sockaddr_in serv_addr, cli_addr;
        int n;

        printf("struct size: %d\n",sizeof(struct dhcp_packet));

        sockfd = socket(AF_INET, SOCK_DGRAM, 0);
        if (sockfd < 0)
                error("Error opening socket");

        int broadcastEnable=1;
        int ret=setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &broadcastEnable, sizeof(broadcastEnable));

        memset(&serv_addr, 0, sizeof(serv_addr));

        serv_addr.sin_family = AF_INET;
        serv_addr.sin_addr.s_addr = INADDR_ANY;
        serv_addr.sin_port = htons(67);        //DHCP uses port 67 for server and 68 for client
        if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0)
                error("Error binding");
        clilen = sizeof(cli_addr);
        while (1)
        {
                struct dhcp_packet packet;
                memset(&packet, 0, sizeof(struct dhcp_packet));
                n = recvfrom(sockfd,&packet,sizeof(struct dhcp_packet),0,(struct sockaddr *) &cli_addr,&clilen);
                if (n <= 0)
                        error("Error receiving");

                printf("|%4d|%3d|%3d|%4d|\n",packet.OP,packet.HTYPE,packet.HLEN,packet.HOPS);
                printf("|%17X|\n|%8hd|%8hd|\n",packet.XID,packet.SECS,packet.FLAGS);
                printf("|%5d.%3d.%3d.%3d|\n",packet.CIADDR[0],packet.CIADDR[1],packet.CIADDR[2],packet.CIADDR[3]);
                printf("|%5d.%3d.%3d.%3d|\n",packet.YIADDR[0],packet.YIADDR[1],packet.YIADDR[2],packet.YIADDR[3]);
                printf("|%5d.%3d.%3d.%3d|\n",packet.SIADDR[0],packet.SIADDR[1],packet.SIADDR[2],packet.SIADDR[3]);
                printf("|%5d.%3d.%3d.%3d|\n",packet.GIADDR[0],packet.GIADDR[1],packet.GIADDR[2],packet.GIADDR[3]);
                printf("|%2X:%2X:%2X:%2X:%2X:%2X|\n",packet.CHADDR[0],packet.CHADDR[1],packet.CHADDR[2],packet.CHADDR[3],packet.CHADDR[4],packet.CHADDR[5]);

                packet.OP=2;
                packet.HOPS++;
                packet.FLAGS=1;
                packet.YIADDR[0]=192; packet.YIADDR[1]=168; packet.YIADDR[2]=14; packet.YIADDR[3]=99;
                packet.SIADDR[0]=192; packet.SIADDR[1]=168; packet.SIADDR[2]=14; packet.SIADDR[3]=14;
//                packet.GIADDR[0]=192; packet.GIADDR[1]=168; packet.GIADDR[2]=14; packet.GIADDR[3]=14;

                memset(&cli_addr, 0, sizeof(cli_addr));
                cli_addr.sin_family = AF_INET;
                cli_addr.sin_port = (in_port_t)htons(68);
                cli_addr.sin_addr.s_addr = htonl(INADDR_BROADCAST);
               
                n = sendto(sockfd,&packet,sizeof(struct dhcp_packet),0,(struct sockaddr *) &cli_addr,clilen);

                if (n <= 0)
                        error("Error sending");
                }

        return 0;
}

Note that some of it is never used.



EDIT: UPDATE - I tried capturing on wiresharks "any" device (capture on all interfaces) and I see the packets which get sent out if I remove the broadcast code. For some reason they get sent from 127.0.0.1 to 127.0.0.1, I have no idea why that happens, the inbound packets clearly come from 0.0.0.0 to 255.255.255.255
Also, I built a new kernel and now broadcasts from my software fail with the error: network unreachable However, don't worry about this too much unless this info helps solve my issue, I had some trouble with some patches patches I applied to the kernel as well as some 3rd party drivers I built, as a result I screwed up my kernel .config so this new kernel has some issues.


All times are GMT -5. The time now is 06:28 AM.