Home | History | Annotate | Download | only in slirp-android
      1 /*
      2  * QEMU BOOTP/DHCP server
      3  *
      4  * Copyright (c) 2004 Fabrice Bellard
      5  *
      6  * Permission is hereby granted, free of charge, to any person obtaining a copy
      7  * of this software and associated documentation files (the "Software"), to deal
      8  * in the Software without restriction, including without limitation the rights
      9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     10  * copies of the Software, and to permit persons to whom the Software is
     11  * furnished to do so, subject to the following conditions:
     12  *
     13  * The above copyright notice and this permission notice shall be included in
     14  * all copies or substantial portions of the Software.
     15  *
     16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
     19  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     22  * THE SOFTWARE.
     23  */
     24 #include <slirp.h>
     25 #include "helper.h"
     26 
     27 /* XXX: only DHCP is supported */
     28 
     29 #define NB_ADDR 16
     30 
     31 #define START_ADDR 15
     32 
     33 #define LEASE_TIME (24 * 3600)
     34 
     35 typedef struct {
     36     uint8_t allocated;
     37     uint8_t macaddr[6];
     38 } BOOTPClient;
     39 
     40 static BOOTPClient bootp_clients[NB_ADDR];
     41 
     42 const char *bootp_filename;
     43 
     44 static const uint8_t rfc1533_cookie[] = { RFC1533_COOKIE };
     45 
     46 #ifdef DEBUG
     47 #define dprintf(fmt, ...) \
     48 if (slirp_debug & DBG_CALL) { fprintf(dfd, fmt, ##  __VA_ARGS__); fflush(dfd); }
     49 #else
     50 #define dprintf(fmt, ...)
     51 #endif
     52 
     53 static BOOTPClient *get_new_addr(SockAddress*  paddr,
     54                                  const uint8_t *macaddr)
     55 {
     56     BOOTPClient *bc;
     57     int i;
     58 
     59     for(i = 0; i < NB_ADDR; i++) {
     60         bc = &bootp_clients[i];
     61         if (!bc->allocated || !memcmp(macaddr, bc->macaddr, 6))
     62             goto found;
     63     }
     64     return NULL;
     65  found:
     66     bc = &bootp_clients[i];
     67     bc->allocated = 1;
     68     sock_address_init_inet( paddr,
     69                             special_addr_ip | (i+START_ADDR),
     70                             BOOTP_CLIENT );
     71     return bc;
     72 }
     73 
     74 static BOOTPClient *request_addr(const ipaddr_t *paddr,
     75                                  const uint8_t *macaddr)
     76 {
     77     uint32_t req_addr  = ip_geth(*paddr);
     78     uint32_t spec_addr = special_addr_ip;
     79     BOOTPClient *bc;
     80 
     81     if (req_addr >= (spec_addr | START_ADDR) &&
     82         req_addr < (spec_addr | (NB_ADDR + START_ADDR))) {
     83         bc = &bootp_clients[(req_addr & 0xff) - START_ADDR];
     84         if (!bc->allocated || !memcmp(macaddr, bc->macaddr, 6)) {
     85             bc->allocated = 1;
     86             return bc;
     87         }
     88     }
     89     return NULL;
     90 }
     91 
     92 static BOOTPClient *find_addr(SockAddress *paddr, const uint8_t *macaddr)
     93 {
     94     BOOTPClient *bc;
     95     int i;
     96 
     97     for(i = 0; i < NB_ADDR; i++) {
     98         if (!memcmp(macaddr, bootp_clients[i].macaddr, 6))
     99             goto found;
    100     }
    101     return NULL;
    102  found:
    103     bc = &bootp_clients[i];
    104     bc->allocated = 1;
    105     sock_address_init_inet( paddr,
    106                             special_addr_ip | (i + START_ADDR),
    107                             BOOTP_CLIENT );
    108     return bc;
    109 }
    110 
    111 static void dhcp_decode(const struct bootp_t *bp, int *pmsg_type,
    112                         const ipaddr_t **preq_addr)
    113 {
    114     const uint8_t *p, *p_end;
    115     int len, tag;
    116 
    117     *pmsg_type = 0;
    118     *preq_addr = NULL;
    119 
    120     p = bp->bp_vend;
    121     p_end = p + DHCP_OPT_LEN;
    122     if (memcmp(p, rfc1533_cookie, 4) != 0)
    123         return;
    124     p += 4;
    125     while (p < p_end) {
    126         tag = p[0];
    127         if (tag == RFC1533_PAD) {
    128             p++;
    129         } else if (tag == RFC1533_END) {
    130             break;
    131         } else {
    132             p++;
    133             if (p >= p_end)
    134                 break;
    135             len = *p++;
    136             dprintf("dhcp: tag=%d len=%d\n", tag, len);
    137 
    138             switch(tag) {
    139             case RFC2132_MSG_TYPE:
    140                 if (len >= 1)
    141                     *pmsg_type = p[0];
    142                 break;
    143             case RFC2132_REQ_ADDR:
    144                 if (len >= 4)
    145                     *preq_addr = (const ipaddr_t *)p;
    146                 break;
    147             default:
    148                 break;
    149             }
    150             p += len;
    151         }
    152     }
    153     if (*pmsg_type == DHCPREQUEST && !*preq_addr && bp->bp_ciaddr) {
    154         *preq_addr = (const ipaddr_t*)&bp->bp_ciaddr;
    155     }
    156 }
    157 
    158 static void bootp_reply(const struct bootp_t *bp)
    159 {
    160     BOOTPClient *bc = NULL;
    161     struct mbuf *m;
    162     struct bootp_t *rbp;
    163     SockAddress  saddr, daddr;
    164     uint32_t     dns_addr;
    165     const ipaddr_t *preq_addr;
    166     int dhcp_msg_type, val;
    167     uint8_t *q;
    168 
    169     /* extract exact DHCP msg type */
    170     dhcp_decode(bp, &dhcp_msg_type, &preq_addr);
    171     dprintf("bootp packet op=%d msgtype=%d", bp->bp_op, dhcp_msg_type);
    172     if (preq_addr) {
    173         dprintf(" req_addr=%08x\n", ntohl(*(uint32_t*)preq_addr));
    174     } else {
    175         dprintf("\n");
    176     }
    177     if (dhcp_msg_type == 0)
    178         dhcp_msg_type = DHCPREQUEST; /* Force reply for old BOOTP clients */
    179 
    180     if (dhcp_msg_type != DHCPDISCOVER &&
    181         dhcp_msg_type != DHCPREQUEST)
    182         return;
    183     /* XXX: this is a hack to get the client mac address */
    184     memcpy(client_ethaddr, bp->bp_hwaddr, 6);
    185 
    186     if ((m = m_get()) == NULL)
    187         return;
    188     m->m_data += IF_MAXLINKHDR;
    189     rbp = (struct bootp_t *)m->m_data;
    190     m->m_data += sizeof(struct udpiphdr);
    191     memset(rbp, 0, sizeof(struct bootp_t));
    192 
    193     if (dhcp_msg_type == DHCPDISCOVER) {
    194         if (preq_addr) {
    195             bc = request_addr(preq_addr, client_ethaddr);
    196             if (bc) {
    197 				sock_address_init_inet(&daddr, ip_geth(*preq_addr), BOOTP_CLIENT);
    198             }
    199         }
    200         if (!bc) {
    201          new_addr:
    202 	        bc = get_new_addr(&daddr, client_ethaddr);
    203             if (!bc) {
    204                 dprintf("no address left\n");
    205                 return;
    206             }
    207         }
    208         memcpy(bc->macaddr, client_ethaddr, 6);
    209     } else if (preq_addr) {
    210         bc = request_addr(preq_addr, client_ethaddr);
    211         if (bc) {
    212 			sock_address_init_inet(&daddr, ip_geth(*preq_addr), BOOTP_CLIENT);
    213             memcpy(bc->macaddr, client_ethaddr, 6);
    214         } else {
    215             sock_address_init_inet(&daddr, 0, BOOTP_CLIENT);
    216         }
    217     } else {
    218         bc = find_addr(&daddr, bp->bp_hwaddr);
    219         if (!bc) {
    220             /* if never assigned, behaves as if it was already
    221                assigned (windows fix because it remembers its address) */
    222             goto new_addr;
    223         }
    224     }
    225 
    226     sock_address_init_inet( &saddr, special_addr_ip | CTL_ALIAS,
    227                             BOOTP_SERVER );
    228 
    229     rbp->bp_op = BOOTP_REPLY;
    230     rbp->bp_xid = bp->bp_xid;
    231     rbp->bp_htype = 1;
    232     rbp->bp_hlen = 6;
    233     memcpy(rbp->bp_hwaddr, bp->bp_hwaddr, 6);
    234 
    235     rbp->bp_yiaddr = htonl(sock_address_get_ip(&daddr)); /* Client IP address */
    236     rbp->bp_siaddr = htonl(sock_address_get_ip(&saddr)); /* Server IP address */
    237 
    238     q = rbp->bp_vend;
    239     memcpy(q, rfc1533_cookie, 4);
    240     q += 4;
    241 
    242     if (bc) {
    243         uint32_t  saddr_ip = htonl(sock_address_get_ip(&saddr));
    244         dprintf("%s addr=%08x\n",
    245                 (dhcp_msg_type == DHCPDISCOVER) ? "offered" : "ack'ed",
    246                 sock_address_get_ip(&daddr));
    247 
    248         if (dhcp_msg_type == DHCPDISCOVER) {
    249             *q++ = RFC2132_MSG_TYPE;
    250             *q++ = 1;
    251             *q++ = DHCPOFFER;
    252         } else /* DHCPREQUEST */ {
    253             *q++ = RFC2132_MSG_TYPE;
    254             *q++ = 1;
    255             *q++ = DHCPACK;
    256         }
    257 
    258         if (bootp_filename)
    259             snprintf((char *)rbp->bp_file, sizeof(rbp->bp_file), "%s",
    260                      bootp_filename);
    261 
    262         *q++ = RFC2132_SRV_ID;
    263         *q++ = 4;
    264         memcpy(q, &saddr_ip, 4);
    265         q += 4;
    266 
    267         *q++ = RFC1533_NETMASK;
    268         *q++ = 4;
    269         *q++ = 0xff;
    270         *q++ = 0xff;
    271         *q++ = 0xff;
    272         *q++ = 0x00;
    273 
    274         if (!slirp_restrict) {
    275             *q++ = RFC1533_GATEWAY;
    276             *q++ = 4;
    277             memcpy(q, &saddr_ip, 4);
    278             q += 4;
    279 
    280             *q++ = RFC1533_DNS;
    281             *q++ = 4;
    282             dns_addr = htonl(special_addr_ip | CTL_DNS);
    283             memcpy(q, &dns_addr, 4);
    284             q += 4;
    285         }
    286 
    287         *q++ = RFC2132_LEASE_TIME;
    288         *q++ = 4;
    289         val = htonl(LEASE_TIME);
    290         memcpy(q, &val, 4);
    291         q += 4;
    292 
    293         if (*slirp_hostname) {
    294             val = strlen(slirp_hostname);
    295             *q++ = RFC1533_HOSTNAME;
    296             *q++ = val;
    297             memcpy(q, slirp_hostname, val);
    298             q += val;
    299         }
    300     } else {
    301         static const char nak_msg[] = "requested address not available";
    302 
    303         dprintf("nak'ed addr=%08x\n", ip_geth(*preq_addr));
    304 
    305         *q++ = RFC2132_MSG_TYPE;
    306         *q++ = 1;
    307         *q++ = DHCPNAK;
    308 
    309         *q++ = RFC2132_MESSAGE;
    310         *q++ = sizeof(nak_msg) - 1;
    311         memcpy(q, nak_msg, sizeof(nak_msg) - 1);
    312         q += sizeof(nak_msg) - 1;
    313     }
    314     *q++ = RFC1533_END;
    315 
    316 	sock_address_init_inet(&daddr, 0xffffffffu, BOOTP_CLIENT);
    317 
    318     m->m_len = sizeof(struct bootp_t) -
    319         sizeof(struct ip) - sizeof(struct udphdr);
    320     udp_output2_(NULL, m, &saddr, &daddr, IPTOS_LOWDELAY);
    321 }
    322 
    323 void bootp_input(struct mbuf *m)
    324 {
    325     struct bootp_t *bp = mtod(m, struct bootp_t *);
    326 
    327     if (bp->bp_op == BOOTP_REQUEST) {
    328         bootp_reply(bp);
    329     }
    330 }
    331