aboutsummaryrefslogtreecommitdiffstats
path: root/c/dhcp.c
blob: e4186f41a722f52fc7ff17aef74d748b45873f01 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
#include "sock.h"
/* dhcp - a minimal DHCP client. DISCOVER -> OFFER -> REQUEST -> ACK over UDP
   (kernel owns UDP; this owns BOOTP/DHCP), then hands the leased address to the
   kernel with net_setip(). Run at boot before the shell. Times out gracefully
   if there's no server (the shell then comes up unconfigured). */

static unsigned char pkt[288];        /* BOOTP is ~240B + options (frames > 255) */
static unsigned char ip[4];
static unsigned char bcast[4];

/* fill the fixed BOOTP request header + magic cookie; options start at 240 */
static void bootp(void) {
    unsigned int i;
    for (i = 0; i < 240; i++) pkt[i] = 0;
    pkt[0] = 1; pkt[1] = 1; pkt[2] = 6;                 /* op=request htype=eth hlen=6 */
    pkt[4] = 0x12; pkt[5] = 0x34; pkt[6] = 0x56; pkt[7] = 0x78;   /* xid */
    pkt[10] = 0x80;                                     /* flags: broadcast reply */
    pkt[28] = 0x02; pkt[29] = 0x47; pkt[30] = 0x42; pkt[33] = 0x01;   /* client MAC */
    pkt[236] = 0x63; pkt[237] = 0x82; pkt[238] = 0x53; pkt[239] = 0x63;  /* magic */
}

/* DHCP message type from the received packet (option 53), or 0 */
static unsigned char mtype(void) {
    unsigned int p = 240;
    while (p < 280 && pkt[p] != 255) {
        if (pkt[p] == 0) { p++; continue; }            /* pad */
        if (pkt[p] == 53) return pkt[p + 2];
        p += 2 + pkt[p + 1];
    }
    return 0;
}

void main(void) {
    unsigned char s, i, n;
    unsigned int p;
    bcast[0] = 255; bcast[1] = 255; bcast[2] = 255; bcast[3] = 255;

    puts("dhcp: discovering"); nl();
    s = net_socket(SOCK_UDP);
    if (s == 0xFF) { puts("dhcp: no socket"); nl(); return; }
    net_bind(s, 68);
    net_connect(s, bcast, 67);

    /* DISCOVER -> OFFER (retry a few times for startup timing) */
    bootp();
    p = 240;
    pkt[p++] = 53; pkt[p++] = 1; pkt[p++] = 1;          /* DHCPDISCOVER */
    pkt[p++] = 255;
    n = 0xFF;
    for (i = 0; i < 3 && n == 0xFF; i++) {
        net_send(s, pkt, (unsigned char)p);
        n = net_recv(s, pkt, 255);
    }
    if (n == 0xFF || mtype() != 2) { puts("dhcp: no offer"); nl(); net_close(s); return; }
    ip[0] = pkt[16]; ip[1] = pkt[17]; ip[2] = pkt[18]; ip[3] = pkt[19];   /* yiaddr */

    /* REQUEST -> ACK */
    bootp();
    p = 240;
    pkt[p++] = 53; pkt[p++] = 1; pkt[p++] = 3;          /* DHCPREQUEST */
    pkt[p++] = 50; pkt[p++] = 4;                        /* requested IP */
    pkt[p++] = ip[0]; pkt[p++] = ip[1]; pkt[p++] = ip[2]; pkt[p++] = ip[3];
    pkt[p++] = 255;
    net_send(s, pkt, (unsigned char)p);
    n = net_recv(s, pkt, 255);
    net_close(s);
    if (n == 0xFF || mtype() != 5) { puts("dhcp: request failed"); nl(); return; }
    ip[0] = pkt[16]; ip[1] = pkt[17]; ip[2] = pkt[18]; ip[3] = pkt[19];

    net_setip(ip);
    puts("dhcp: leased "); for (i = 0; i < 4; i++) { putu(ip[i]); if (i < 3) putc('.'); } nl();
}