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 26/* XXX: only DHCP is supported */ 27 28#define NB_ADDR 16 29 30#define START_ADDR 15 31 32#define LEASE_TIME (24 * 3600) 33 34typedef struct { 35 uint8_t allocated; 36 uint8_t macaddr[6]; 37} BOOTPClient; 38 39static BOOTPClient bootp_clients[NB_ADDR]; 40 41const char *bootp_filename; 42 43static const uint8_t rfc1533_cookie[] = { RFC1533_COOKIE }; 44 45#ifdef DEBUG 46#define dprintf(fmt, ...) \ 47if (slirp_debug & DBG_CALL) { fprintf(dfd, fmt, ## __VA_ARGS__); fflush(dfd); } 48#else 49#define dprintf(fmt, ...) 50#endif 51 52static BOOTPClient *get_new_addr(struct in_addr *paddr, 53 const uint8_t *macaddr) 54{ 55 BOOTPClient *bc; 56 int i; 57 58 for(i = 0; i < NB_ADDR; i++) { 59 bc = &bootp_clients[i]; 60 if (!bc->allocated || !memcmp(macaddr, bc->macaddr, 6)) 61 goto found; 62 } 63 return NULL; 64 found: 65 bc = &bootp_clients[i]; 66 bc->allocated = 1; 67 paddr->s_addr = htonl(ntohl(special_addr.s_addr) | (i + START_ADDR)); 68 return bc; 69} 70 71static BOOTPClient *request_addr(const struct in_addr *paddr, 72 const uint8_t *macaddr) 73{ 74 uint32_t req_addr = ntohl(paddr->s_addr); 75 uint32_t spec_addr = ntohl(special_addr.s_addr); 76 BOOTPClient *bc; 77 78 if (req_addr >= (spec_addr | START_ADDR) && 79 req_addr < (spec_addr | (NB_ADDR + START_ADDR))) { 80 bc = &bootp_clients[(req_addr & 0xff) - START_ADDR]; 81 if (!bc->allocated || !memcmp(macaddr, bc->macaddr, 6)) { 82 bc->allocated = 1; 83 return bc; 84 } 85 } 86 return NULL; 87} 88 89static BOOTPClient *find_addr(struct in_addr *paddr, const uint8_t *macaddr) 90{ 91 BOOTPClient *bc; 92 int i; 93 94 for(i = 0; i < NB_ADDR; i++) { 95 if (!memcmp(macaddr, bootp_clients[i].macaddr, 6)) 96 goto found; 97 } 98 return NULL; 99 found: 100 bc = &bootp_clients[i]; 101 bc->allocated = 1; 102 paddr->s_addr = htonl(ntohl(special_addr.s_addr) | (i + START_ADDR)); 103 return bc; 104} 105 106static void dhcp_decode(const struct bootp_t *bp, int *pmsg_type, 107 const struct in_addr **preq_addr) 108{ 109 const uint8_t *p, *p_end; 110 int len, tag; 111 112 *pmsg_type = 0; 113 *preq_addr = NULL; 114 115 p = bp->bp_vend; 116 p_end = p + DHCP_OPT_LEN; 117 if (memcmp(p, rfc1533_cookie, 4) != 0) 118 return; 119 p += 4; 120 while (p < p_end) { 121 tag = p[0]; 122 if (tag == RFC1533_PAD) { 123 p++; 124 } else if (tag == RFC1533_END) { 125 break; 126 } else { 127 p++; 128 if (p >= p_end) 129 break; 130 len = *p++; 131 dprintf("dhcp: tag=%d len=%d\n", tag, len); 132 133 switch(tag) { 134 case RFC2132_MSG_TYPE: 135 if (len >= 1) 136 *pmsg_type = p[0]; 137 break; 138 case RFC2132_REQ_ADDR: 139 if (len >= 4) 140 *preq_addr = (struct in_addr *)p; 141 break; 142 default: 143 break; 144 } 145 p += len; 146 } 147 } 148 if (*pmsg_type == DHCPREQUEST && !*preq_addr && bp->bp_ciaddr.s_addr) { 149 *preq_addr = &bp->bp_ciaddr; 150 } 151} 152 153static void bootp_reply(const struct bootp_t *bp) 154{ 155 BOOTPClient *bc = NULL; 156 struct mbuf *m; 157 struct bootp_t *rbp; 158 struct sockaddr_in saddr, daddr; 159 struct in_addr dns_addr; 160 const struct in_addr *preq_addr; 161 int dhcp_msg_type, val; 162 uint8_t *q; 163 164 /* extract exact DHCP msg type */ 165 dhcp_decode(bp, &dhcp_msg_type, &preq_addr); 166 dprintf("bootp packet op=%d msgtype=%d", bp->bp_op, dhcp_msg_type); 167 if (preq_addr) 168 dprintf(" req_addr=%08x\n", ntohl(preq_addr->s_addr)); 169 else 170 dprintf("\n"); 171 172 if (dhcp_msg_type == 0) 173 dhcp_msg_type = DHCPREQUEST; /* Force reply for old BOOTP clients */ 174 175 if (dhcp_msg_type != DHCPDISCOVER && 176 dhcp_msg_type != DHCPREQUEST) 177 return; 178 /* XXX: this is a hack to get the client mac address */ 179 memcpy(client_ethaddr, bp->bp_hwaddr, 6); 180 181 if ((m = m_get()) == NULL) 182 return; 183 m->m_data += IF_MAXLINKHDR; 184 rbp = (struct bootp_t *)m->m_data; 185 m->m_data += sizeof(struct udpiphdr); 186 memset(rbp, 0, sizeof(struct bootp_t)); 187 188 if (dhcp_msg_type == DHCPDISCOVER) { 189 if (preq_addr) { 190 bc = request_addr(preq_addr, client_ethaddr); 191 if (bc) { 192 daddr.sin_addr = *preq_addr; 193 } 194 } 195 if (!bc) { 196 new_addr: 197 bc = get_new_addr(&daddr.sin_addr, client_ethaddr); 198 if (!bc) { 199 dprintf("no address left\n"); 200 return; 201 } 202 } 203 memcpy(bc->macaddr, client_ethaddr, 6); 204 } else if (preq_addr) { 205 bc = request_addr(preq_addr, client_ethaddr); 206 if (bc) { 207 daddr.sin_addr = *preq_addr; 208 memcpy(bc->macaddr, client_ethaddr, 6); 209 } else { 210 daddr.sin_addr.s_addr = 0; 211 } 212 } else { 213 bc = find_addr(&daddr.sin_addr, bp->bp_hwaddr); 214 if (!bc) { 215 /* if never assigned, behaves as if it was already 216 assigned (windows fix because it remembers its address) */ 217 goto new_addr; 218 } 219 } 220 221 saddr.sin_addr.s_addr = htonl(ntohl(special_addr.s_addr) | CTL_ALIAS); 222 saddr.sin_port = htons(BOOTP_SERVER); 223 224 daddr.sin_port = htons(BOOTP_CLIENT); 225 226 rbp->bp_op = BOOTP_REPLY; 227 rbp->bp_xid = bp->bp_xid; 228 rbp->bp_htype = 1; 229 rbp->bp_hlen = 6; 230 memcpy(rbp->bp_hwaddr, bp->bp_hwaddr, 6); 231 232 rbp->bp_yiaddr = daddr.sin_addr; /* Client IP address */ 233 rbp->bp_siaddr = saddr.sin_addr; /* Server IP address */ 234 235 q = rbp->bp_vend; 236 memcpy(q, rfc1533_cookie, 4); 237 q += 4; 238 239 if (bc) { 240 dprintf("%s addr=%08x\n", 241 (dhcp_msg_type == DHCPDISCOVER) ? "offered" : "ack'ed", 242 ntohl(daddr.sin_addr.s_addr)); 243 244 if (dhcp_msg_type == DHCPDISCOVER) { 245 *q++ = RFC2132_MSG_TYPE; 246 *q++ = 1; 247 *q++ = DHCPOFFER; 248 } else /* DHCPREQUEST */ { 249 *q++ = RFC2132_MSG_TYPE; 250 *q++ = 1; 251 *q++ = DHCPACK; 252 } 253 254 if (bootp_filename) 255 snprintf((char *)rbp->bp_file, sizeof(rbp->bp_file), "%s", 256 bootp_filename); 257 258 *q++ = RFC2132_SRV_ID; 259 *q++ = 4; 260 memcpy(q, &saddr.sin_addr, 4); 261 q += 4; 262 263 *q++ = RFC1533_NETMASK; 264 *q++ = 4; 265 *q++ = 0xff; 266 *q++ = 0xff; 267 *q++ = 0xff; 268 *q++ = 0x00; 269 270 if (!slirp_restrict) { 271 *q++ = RFC1533_GATEWAY; 272 *q++ = 4; 273 memcpy(q, &saddr.sin_addr, 4); 274 q += 4; 275 276 *q++ = RFC1533_DNS; 277 *q++ = 4; 278 dns_addr.s_addr = htonl(ntohl(special_addr.s_addr) | CTL_DNS); 279 memcpy(q, &dns_addr, 4); 280 q += 4; 281 } 282 283 *q++ = RFC2132_LEASE_TIME; 284 *q++ = 4; 285 val = htonl(LEASE_TIME); 286 memcpy(q, &val, 4); 287 q += 4; 288 289 if (*slirp_hostname) { 290 val = strlen(slirp_hostname); 291 *q++ = RFC1533_HOSTNAME; 292 *q++ = val; 293 memcpy(q, slirp_hostname, val); 294 q += val; 295 } 296 } else { 297 static const char nak_msg[] = "requested address not available"; 298 299 dprintf("nak'ed addr=%08x\n", ntohl(preq_addr->s_addr)); 300 301 *q++ = RFC2132_MSG_TYPE; 302 *q++ = 1; 303 *q++ = DHCPNAK; 304 305 *q++ = RFC2132_MESSAGE; 306 *q++ = sizeof(nak_msg) - 1; 307 memcpy(q, nak_msg, sizeof(nak_msg) - 1); 308 q += sizeof(nak_msg) - 1; 309 } 310 *q++ = RFC1533_END; 311 312 daddr.sin_addr.s_addr = 0xffffffffu; 313 314 m->m_len = sizeof(struct bootp_t) - 315 sizeof(struct ip) - sizeof(struct udphdr); 316 udp_output2(NULL, m, &saddr, &daddr, IPTOS_LOWDELAY); 317} 318 319void bootp_input(struct mbuf *m) 320{ 321 struct bootp_t *bp = mtod(m, struct bootp_t *); 322 323 if (bp->bp_op == BOOTP_REQUEST) { 324 bootp_reply(bp); 325 } 326} 327