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 35typedef struct { 36 uint8_t allocated; 37 uint8_t macaddr[6]; 38} BOOTPClient; 39 40static BOOTPClient bootp_clients[NB_ADDR]; 41 42const char *bootp_filename; 43 44static const uint8_t rfc1533_cookie[] = { RFC1533_COOKIE }; 45 46#ifdef DEBUG 47#define dprintf(fmt, ...) \ 48if (slirp_debug & DBG_CALL) { fprintf(dfd, fmt, ## __VA_ARGS__); fflush(dfd); } 49#else 50#define dprintf(fmt, ...) 51#endif 52 53static 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 74static 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 92static 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 111static 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 158static 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 323void 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