std1.0.0-beta-7 SafeC docs

Source net/dhcp.sc

View in the repository

// SafeC Standard Library — DHCP Client Implementation
#pragma once
#include <std/net/dhcp.h>
#include <std/net/ethernet.h>

// DHCP BOOTP message: 236 bytes fixed + 4-byte magic cookie + options
// op(1) htype(1) hlen(1) hops(1) xid(4) secs(2) flags(2)
// ciaddr(4) yiaddr(4) siaddr(4) giaddr(4)
// chaddr(16) sname(64) file(128)
// magic(4) options(...)

#define DHCP_FIXED_LEN   236
#define DHCP_HDR_LEN     (DHCP_FIXED_LEN + 4)  // + magic cookie

namespace std {

static unsigned char dhcp_bcast_mac_[NET_MAC_LEN] = {
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
};

// Write `len` bytes of `val` into buf.
static void dhcp_fill_(unsigned char* buf, unsigned char val, unsigned long len) {
    unsigned long i = (unsigned long)0;
    while (i < len) { unsafe { buf[i] = val; } i = i + (unsigned long)1; }
}

// Append a DHCP option: code, len, data.
static unsigned long dhcp_opt_(unsigned char* opts, unsigned long pos,
                                unsigned char code, unsigned char len,
                                const unsigned char* data) {
    unsafe {
        opts[pos] = code;
        opts[pos + (unsigned long)1] = len;
        unsigned long i = (unsigned long)0;
        while (i < (unsigned long)len) {
            opts[pos + (unsigned long)2 + i] = data[i];
            i = i + (unsigned long)1;
        }
    }
    return pos + (unsigned long)2 + (unsigned long)len;
}

static void dhcp_write_fixed_(unsigned char* d, unsigned char op,
                               const unsigned char mac[NET_MAC_LEN],
                               unsigned int xid) {
    unsafe {
        d[0] = op;            // op: 1=BOOTREQUEST
        d[1] = (unsigned char)1;    // htype: Ethernet
        d[2] = (unsigned char)6;    // hlen
        d[3] = (unsigned char)0;    // hops
        unsigned int xid_n = (xid);
        d[4] = (unsigned char)((xid_n >> 24) & (unsigned int)0xFF);
        d[5] = (unsigned char)((xid_n >> 16) & (unsigned int)0xFF);
        d[6] = (unsigned char)((xid_n >>  8) & (unsigned int)0xFF);
        d[7] = (unsigned char)(xid_n & (unsigned int)0xFF);
        d[8] = (unsigned char)0; d[9] = (unsigned char)0;    // secs
        d[10]= (unsigned char)0x80; d[11]= (unsigned char)0; // broadcast flag
        // ciaddr/yiaddr/siaddr/giaddr = 0
        dhcp_fill_(d + 12, (unsigned char)0, (unsigned long)16);
        // chaddr (MAC + pad)
        int i = 0;
        while (i < NET_MAC_LEN) { d[28 + i] = mac[i]; i = i + 1; }
        dhcp_fill_(d + 34, (unsigned char)0, (unsigned long)10); // chaddr pad
        dhcp_fill_(d + 44, (unsigned char)0, (unsigned long)192); // sname + file
        // Magic cookie
        d[236]= (unsigned char)0x63; d[237]= (unsigned char)0x82;
        d[238]= (unsigned char)0x53; d[239]= (unsigned char)0x63;
    }
}

void DhcpClient::discover(&PacketBuf pkt,
                           const unsigned char eth_dst[NET_MAC_LEN]) {
    unsigned short opts_len = (unsigned short)7; // type(3)+end(1) + pad
    unsigned short dhcp_payload = (unsigned short)((unsigned short)DHCP_HDR_LEN
                                                    + opts_len);
    unsigned long data_off = udp_frame(pkt,
                                       (const unsigned char*)self.mac,
                                       eth_dst,
                                       (unsigned int)0,           // src IP 0.0.0.0
                                       (unsigned int)0xFFFFFFFF,  // dst 255.255.255.255
                                       (unsigned short)DHCP_CLIENT_PORT,
                                       (unsigned short)DHCP_SERVER_PORT,
                                       dhcp_payload);
    unsafe {
        unsigned char* d = (unsigned char*)pkt.data + data_off;
        dhcp_write_fixed_(d, (unsigned char)1, (const unsigned char*)self.mac, self.xid);
        unsigned char* opts = d + (unsigned long)DHCP_HDR_LEN;
        unsigned long pos = (unsigned long)0;
        unsigned char msg_type = (unsigned char)DHCP_DISCOVER;
        pos = dhcp_opt_(opts, pos, (unsigned char)53, (unsigned char)1,
                        (const unsigned char*)&msg_type);
        opts[pos] = (unsigned char)255; // END
    }
    pkt.len = data_off + (unsigned long)dhcp_payload;
    self.state = DHCP_STATE_SELECTING;
}

void DhcpClient::request(&PacketBuf pkt,
                          const unsigned char eth_dst[NET_MAC_LEN],
                          unsigned int offered_ip, unsigned int server_ip) {
    unsigned short opts_len = (unsigned short)13; // type(3)+reqip(6)+serverid(6)+end(1)
    unsigned short dhcp_payload = (unsigned short)((unsigned short)DHCP_HDR_LEN
                                                    + opts_len);
    unsigned long data_off = udp_frame(pkt,
                                       (const unsigned char*)self.mac,
                                       eth_dst,
                                       (unsigned int)0,
                                       (unsigned int)0xFFFFFFFF,
                                       (unsigned short)DHCP_CLIENT_PORT,
                                       (unsigned short)DHCP_SERVER_PORT,
                                       dhcp_payload);
    unsafe {
        unsigned char* d = (unsigned char*)pkt.data + data_off;
        dhcp_write_fixed_(d, (unsigned char)1, (const unsigned char*)self.mac, self.xid);
        unsigned char* opts = d + (unsigned long)DHCP_HDR_LEN;
        unsigned long pos = (unsigned long)0;
        unsigned char msg_type = (unsigned char)DHCP_REQUEST;
        pos = dhcp_opt_(opts, pos, (unsigned char)53, (unsigned char)1,
                        (const unsigned char*)&msg_type);
        unsigned int ip_n = net_htonl(offered_ip);
        pos = dhcp_opt_(opts, pos, (unsigned char)50, (unsigned char)4,
                        (const unsigned char*)&ip_n);
        unsigned int srv_n = net_htonl(server_ip);
        pos = dhcp_opt_(opts, pos, (unsigned char)54, (unsigned char)4,
                        (const unsigned char*)&srv_n);
        opts[pos] = (unsigned char)255;
    }
    pkt.len = data_off + (unsigned long)dhcp_payload;
    self.state = DHCP_STATE_REQUESTING;
}

int DhcpClient::parse_reply(&PacketBuf pkt, unsigned long udp_payload_offset) {
    if (pkt.len < udp_payload_offset + (unsigned long)DHCP_HDR_LEN) { return 0; }
    int msg_type = 0;
    unsafe {
        unsigned char* d = (unsigned char*)pkt.data + udp_payload_offset;
        if (d[0] != (unsigned char)2) { return 0; }  // op must be BOOTREPLY
        // Check xid
        unsigned int xid = (((unsigned int)d[4] << 24) | ((unsigned int)d[5] << 16)
                                    | ((unsigned int)d[6] << 8)  |  (unsigned int)d[7]);
        if (xid != self.xid) { return 0; }
        // yiaddr at offset 16
        self.lease.your_ip = (((unsigned int)d[16] << 24)
                                      | ((unsigned int)d[17] << 16)
                                      | ((unsigned int)d[18] << 8)
                                      |  (unsigned int)d[19]);
        // siaddr at offset 20
        self.lease.server_ip = (((unsigned int)d[20] << 24)
                                        | ((unsigned int)d[21] << 16)
                                        | ((unsigned int)d[22] << 8)
                                        |  (unsigned int)d[23]);
        // Parse options
        unsigned long pos = udp_payload_offset + (unsigned long)DHCP_HDR_LEN;
        while (pos + (unsigned long)1 < pkt.len) {
            unsigned char code = ((unsigned char*)pkt.data)[pos];
            if (code == (unsigned char)255) { break; }
            if (code == (unsigned char)0)   { pos = pos + (unsigned long)1; }
            else {
                unsigned char opt_len = ((unsigned char*)pkt.data)[pos + (unsigned long)1];
                unsigned char* v = (unsigned char*)pkt.data + pos + (unsigned long)2;
                if (code == (unsigned char)53 && opt_len == (unsigned char)1) {
                    msg_type = (int)v[0];
                } else if (code == (unsigned char)1 && opt_len == (unsigned char)4) {
                    self.lease.subnet_mask = (((unsigned int)v[0] << 24)
                                                      | ((unsigned int)v[1] << 16)
                                                      | ((unsigned int)v[2] << 8)
                                                      |  (unsigned int)v[3]);
                } else if (code == (unsigned char)3 && opt_len >= (unsigned char)4) {
                    self.lease.gateway = (((unsigned int)v[0] << 24)
                                                  | ((unsigned int)v[1] << 16)
                                                  | ((unsigned int)v[2] << 8)
                                                  |  (unsigned int)v[3]);
                } else if (code == (unsigned char)6 && opt_len >= (unsigned char)4) {
                    self.lease.dns = (((unsigned int)v[0] << 24)
                                              | ((unsigned int)v[1] << 16)
                                              | ((unsigned int)v[2] << 8)
                                              |  (unsigned int)v[3]);
                } else if (code == (unsigned char)51 && opt_len == (unsigned char)4) {
                    self.lease.lease_secs = (((unsigned int)v[0] << 24)
                                                     | ((unsigned int)v[1] << 16)
                                                     | ((unsigned int)v[2] << 8)
                                                     |  (unsigned int)v[3]);
                }
                pos = pos + (unsigned long)2 + (unsigned long)opt_len;
            }
        }
    }
    if (msg_type == DHCP_ACK) { self.state = DHCP_STATE_BOUND; }
    return msg_type;
}

inline int DhcpClient::is_bound() const {
    if (self.state == DHCP_STATE_BOUND) { return 1; }
    return 0;
}

} // namespace std