001 // vim: set ft=c:
002 
003 #include "::/Adam/Net/Dhcp"
004 
005 #define CLIENT_START            0
006 #define CLIENT_DISCOVER         1
007 #define CLIENT_REQUEST          2
008 #define CLIENT_REQUEST_ACCEPTED 3
009 
010 #define DHCP_TIMEOUT  3000
011 #define MAX_RETRIES   3
012 
013 I64 DhcpConfigureInner(I64 sock, U32* yiaddr_out, U32* dns_ip_out, U32* router_ip_out, U32* subnet_mask_out) {
014   I64 state = CLIENT_START;
015   I64 retries = 0;
016 
017   I64 timeout = DHCP_TIMEOUT;
018 
019   if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO_MS, &timeout, sizeof(timeout)) < 0) {
020     "DhcpConfigure: setsockopt failed\n";
021   }
022 
023   sockaddr_in addr;
024   addr.sin_family = AF_INET;
025   addr.sin_port = htons(68);
026   addr.sin_addr.s_addr = INADDR_ANY;
027 
028   if (bind(sock, &addr, sizeof(addr)) < 0) {
029     "DhcpConfigure: failed to bind\n";
030     return -1;
031   }
032 
033   U32 xid = DhcpBeginTransaction();
034 
035   I64 error = 0;
036 
037   U32 dhcp_addr;
038   U8 buffer[2048];
039 
040   I64 count;
041   sockaddr_in addr_in;
042 
043   while (state != CLIENT_REQUEST_ACCEPTED) {
044     if (state == CLIENT_START) {
045       state = CLIENT_DISCOVER;
046       retries = 0;
047     }
048     else if (state == CLIENT_DISCOVER) {
049       error = DhcpSendDiscover(xid);
050       if (error < 0) return error;
051 
052       count = recvfrom(sock, buffer, sizeof(buffer), 0, &addr_in, sizeof(addr_in));
053 
054       if (count > 0) {
055         //"Try parse Offer\n";
056         error = DhcpParseOffer(xid, buffer, count, yiaddr_out, dns_ip_out, router_ip_out, subnet_mask_out);
057 
058         if (error < 0) {
059           "DhcpParseOffer: error %d\n", error;
060         }
061       }
062 
063       if (count > 0 && error >= 0) {
064         dhcp_addr = ntohl(addr_in.sin_addr.s_addr);
065         //"DHCP Offer from %08X: YIAddr %08X,\n\tDNS %08X, Router %08X, Subnet %08X\n",
066         //    dhcp_addr, *yiaddr_out, dns_ip, router_ip, subnet_mask;
067 
068         state = CLIENT_REQUEST;
069         retries = 0;
070       }
071       else if (++retries == MAX_RETRIES) {
072         "DhcpConfigure: max retries for DISCOVER\n";
073         return -1;
074       }
075     }
076     else if (state == CLIENT_REQUEST) {
077       error = DhcpSendRequest(xid, *yiaddr_out, dhcp_addr);
078       if (error < 0) return error;
079 
080       count = recvfrom(sock, buffer, sizeof(buffer), 0, &addr_in, sizeof(addr_in));
081 
082       if (count > 0) {
083         //"Try parse Ack\n";
084         error = DhcpParseAck(xid, buffer, count);
085 
086         if (error < 0) {
087           "DhcpParseOffer: error %d\n", error;
088         }
089       }
090 
091       if (count > 0 && error >= 0) {
092         dhcp_addr = ntohl(addr_in.sin_addr.s_addr);
093         //"DHCP Ack from %08X\n", dhcp_addr;
094 
095         state = CLIENT_REQUEST_ACCEPTED;
096       }
097       else if (++retries == MAX_RETRIES) {
098         "DhcpConfigure: max retries for REQUEST\n";
099         return -1;
100       }
101     }
102   }
103 
104   return state;
105 }
106 
107 I64 DhcpConfigure() {
108   I64 sock = socket(AF_INET, SOCK_DGRAM);
109 
110   if (sock < 0)
111     return -1;
112 
113   U32 yiaddr, dns_ip, router_ip, subnet_mask;
114   I64 state = DhcpConfigureInner(sock, &yiaddr, &dns_ip, &router_ip, &subnet_mask);
115 
116   close(sock);
117 
118   if (state == CLIENT_REQUEST_ACCEPTED) {
119     in_addr in;
120     in.s_addr = htonl(yiaddr);
121     "Obtained IP address %s\n", inet_ntoa(in);
122     IPv4SetAddress(yiaddr);
123     IPv4SetSubnet(router_ip, subnet_mask);
124     DnsSetResolverIPv4(dns_ip);
125     return 0;
126   }
127   else
128     return -1;
129 }
130 
131 U0 Netcfg() {
132   SocketInit();
133 
134   "Netcfg: Configuring network...\n";
135 
136   I64 error = DhcpConfigure();
137   if (error < 0)
138     "DhcpConfigure: error %d\n", error;
139 }