/* XXX: only DHCP is supported */
-#define NB_ADDR 16
-
-#define START_ADDR 15
-
#define LEASE_TIME (24 * 3600)
-typedef struct {
- uint8_t allocated;
- uint8_t macaddr[6];
-} BOOTPClient;
-
-BOOTPClient bootp_clients[NB_ADDR];
-
-const char *bootp_filename;
-
static const uint8_t rfc1533_cookie[] = { RFC1533_COOKIE };
#ifdef DEBUG
-#define dprintf(fmt, args...) \
-if (slirp_debug & DBG_CALL) { fprintf(dfd, fmt, ## args); fflush(dfd); }
+#define DPRINTF(fmt, ...) \
+do if (slirp_debug & DBG_CALL) { fprintf(dfd, fmt, ## __VA_ARGS__); fflush(dfd); } while (0)
#else
-#define dprintf(fmt, args...)
+#define DPRINTF(fmt, ...) do{}while(0)
#endif
-static BOOTPClient *get_new_addr(struct in_addr *paddr)
+static BOOTPClient *get_new_addr(Slirp *slirp, struct in_addr *paddr,
+ const uint8_t *macaddr)
{
BOOTPClient *bc;
int i;
- for(i = 0; i < NB_ADDR; i++) {
- if (!bootp_clients[i].allocated)
+ for(i = 0; i < NB_BOOTP_CLIENTS; i++) {
+ bc = &slirp->bootp_clients[i];
+ if (!bc->allocated || !memcmp(macaddr, bc->macaddr, 6))
goto found;
}
return NULL;
found:
- bc = &bootp_clients[i];
+ bc = &slirp->bootp_clients[i];
bc->allocated = 1;
- paddr->s_addr = htonl(ntohl(special_addr.s_addr) | (i + START_ADDR));
+ paddr->s_addr = slirp->vdhcp_startaddr.s_addr + htonl(i);
return bc;
}
-static BOOTPClient *find_addr(struct in_addr *paddr, const uint8_t *macaddr)
+static BOOTPClient *request_addr(Slirp *slirp, const struct in_addr *paddr,
+ const uint8_t *macaddr)
+{
+ uint32_t req_addr = ntohl(paddr->s_addr);
+ uint32_t dhcp_addr = ntohl(slirp->vdhcp_startaddr.s_addr);
+ BOOTPClient *bc;
+
+ if (req_addr >= dhcp_addr &&
+ req_addr < (dhcp_addr + NB_BOOTP_CLIENTS)) {
+ bc = &slirp->bootp_clients[req_addr - dhcp_addr];
+ if (!bc->allocated || !memcmp(macaddr, bc->macaddr, 6)) {
+ bc->allocated = 1;
+ return bc;
+ }
+ }
+ return NULL;
+}
+
+static BOOTPClient *find_addr(Slirp *slirp, struct in_addr *paddr,
+ const uint8_t *macaddr)
{
BOOTPClient *bc;
int i;
- for(i = 0; i < NB_ADDR; i++) {
- if (!memcmp(macaddr, bootp_clients[i].macaddr, 6))
+ for(i = 0; i < NB_BOOTP_CLIENTS; i++) {
+ if (!memcmp(macaddr, slirp->bootp_clients[i].macaddr, 6))
goto found;
}
return NULL;
found:
- bc = &bootp_clients[i];
+ bc = &slirp->bootp_clients[i];
bc->allocated = 1;
- paddr->s_addr = htonl(ntohl(special_addr.s_addr) | (i + START_ADDR));
+ paddr->s_addr = slirp->vdhcp_startaddr.s_addr + htonl(i);
return bc;
}
-static void dhcp_decode(const uint8_t *buf, int size,
- int *pmsg_type)
+static void dhcp_decode(const struct bootp_t *bp, int *pmsg_type,
+ struct in_addr *preq_addr)
{
const uint8_t *p, *p_end;
int len, tag;
*pmsg_type = 0;
+ preq_addr->s_addr = htonl(0L);
- p = buf;
- p_end = buf + size;
- if (size < 5)
- return;
+ p = bp->bp_vend;
+ p_end = p + DHCP_OPT_LEN;
if (memcmp(p, rfc1533_cookie, 4) != 0)
return;
p += 4;
if (p >= p_end)
break;
len = *p++;
- dprintf("dhcp: tag=0x%02x len=%d\n", tag, len);
+ DPRINTF("dhcp: tag=%d len=%d\n", tag, len);
switch(tag) {
case RFC2132_MSG_TYPE:
if (len >= 1)
*pmsg_type = p[0];
break;
+ case RFC2132_REQ_ADDR:
+ if (len >= 4) {
+ memcpy(&(preq_addr->s_addr), p, 4);
+ }
+ break;
default:
break;
}
p += len;
}
}
+ if (*pmsg_type == DHCPREQUEST && preq_addr->s_addr == htonl(0L) &&
+ bp->bp_ciaddr.s_addr) {
+ memcpy(&(preq_addr->s_addr), &bp->bp_ciaddr, 4);
+ }
}
-static void bootp_reply(struct bootp_t *bp)
+static void bootp_reply(Slirp *slirp, const struct bootp_t *bp)
{
- BOOTPClient *bc;
+ BOOTPClient *bc = NULL;
struct mbuf *m;
struct bootp_t *rbp;
struct sockaddr_in saddr, daddr;
- struct in_addr dns_addr;
+ struct in_addr preq_addr;
int dhcp_msg_type, val;
uint8_t *q;
+ uint8_t client_ethaddr[ETH_ALEN];
/* extract exact DHCP msg type */
- dhcp_decode(bp->bp_vend, DHCP_OPT_LEN, &dhcp_msg_type);
- dprintf("bootp packet op=%d msgtype=%d\n", bp->bp_op, dhcp_msg_type);
+ dhcp_decode(bp, &dhcp_msg_type, &preq_addr);
+ DPRINTF("bootp packet op=%d msgtype=%d", bp->bp_op, dhcp_msg_type);
+ if (preq_addr.s_addr != htonl(0L))
+ DPRINTF(" req_addr=%08x\n", ntohl(preq_addr.s_addr));
+ else
+ DPRINTF("\n");
if (dhcp_msg_type == 0)
dhcp_msg_type = DHCPREQUEST; /* Force reply for old BOOTP clients */
if (dhcp_msg_type != DHCPDISCOVER &&
dhcp_msg_type != DHCPREQUEST)
return;
- /* XXX: this is a hack to get the client mac address */
- memcpy(client_ethaddr, bp->bp_hwaddr, 6);
- if ((m = m_get()) == NULL)
+ /* Get client's hardware address from bootp request */
+ memcpy(client_ethaddr, bp->bp_hwaddr, ETH_ALEN);
+
+ m = m_get(slirp);
+ if (!m) {
return;
+ }
m->m_data += IF_MAXLINKHDR;
rbp = (struct bootp_t *)m->m_data;
m->m_data += sizeof(struct udpiphdr);
memset(rbp, 0, sizeof(struct bootp_t));
if (dhcp_msg_type == DHCPDISCOVER) {
- new_addr:
- bc = get_new_addr(&daddr.sin_addr);
+ if (preq_addr.s_addr != htonl(0L)) {
+ bc = request_addr(slirp, &preq_addr, client_ethaddr);
+ if (bc) {
+ daddr.sin_addr = preq_addr;
+ }
+ }
if (!bc) {
- dprintf("no address left\n");
- return;
+ new_addr:
+ bc = get_new_addr(slirp, &daddr.sin_addr, client_ethaddr);
+ if (!bc) {
+ DPRINTF("no address left\n");
+ return;
+ }
+ }
+ memcpy(bc->macaddr, client_ethaddr, ETH_ALEN);
+ } else if (preq_addr.s_addr != htonl(0L)) {
+ bc = request_addr(slirp, &preq_addr, client_ethaddr);
+ if (bc) {
+ daddr.sin_addr = preq_addr;
+ memcpy(bc->macaddr, client_ethaddr, ETH_ALEN);
+ } else {
+ daddr.sin_addr.s_addr = 0;
}
- memcpy(bc->macaddr, client_ethaddr, 6);
} else {
- bc = find_addr(&daddr.sin_addr, bp->bp_hwaddr);
+ bc = find_addr(slirp, &daddr.sin_addr, bp->bp_hwaddr);
if (!bc) {
/* if never assigned, behaves as if it was already
assigned (windows fix because it remembers its address) */
}
}
- if (bootp_filename)
- snprintf((char *)rbp->bp_file, sizeof(rbp->bp_file), "%s",
- bootp_filename);
+ /* Update ARP table for this IP address */
+ arp_table_add(slirp, daddr.sin_addr.s_addr, client_ethaddr);
- dprintf("offered addr=%08x\n", ntohl(daddr.sin_addr.s_addr));
-
- saddr.sin_addr.s_addr = htonl(ntohl(special_addr.s_addr) | CTL_ALIAS);
+ saddr.sin_addr = slirp->vhost_addr;
saddr.sin_port = htons(BOOTP_SERVER);
daddr.sin_port = htons(BOOTP_CLIENT);
rbp->bp_xid = bp->bp_xid;
rbp->bp_htype = 1;
rbp->bp_hlen = 6;
- memcpy(rbp->bp_hwaddr, bp->bp_hwaddr, 6);
+ memcpy(rbp->bp_hwaddr, bp->bp_hwaddr, ETH_ALEN);
rbp->bp_yiaddr = daddr.sin_addr; /* Client IP address */
rbp->bp_siaddr = saddr.sin_addr; /* Server IP address */
memcpy(q, rfc1533_cookie, 4);
q += 4;
- if (dhcp_msg_type == DHCPDISCOVER) {
- *q++ = RFC2132_MSG_TYPE;
- *q++ = 1;
- *q++ = DHCPOFFER;
- } else if (dhcp_msg_type == DHCPREQUEST) {
- *q++ = RFC2132_MSG_TYPE;
- *q++ = 1;
- *q++ = DHCPACK;
- }
+ if (bc) {
+ DPRINTF("%s addr=%08x\n",
+ (dhcp_msg_type == DHCPDISCOVER) ? "offered" : "ack'ed",
+ ntohl(daddr.sin_addr.s_addr));
+
+ if (dhcp_msg_type == DHCPDISCOVER) {
+ *q++ = RFC2132_MSG_TYPE;
+ *q++ = 1;
+ *q++ = DHCPOFFER;
+ } else /* DHCPREQUEST */ {
+ *q++ = RFC2132_MSG_TYPE;
+ *q++ = 1;
+ *q++ = DHCPACK;
+ }
+
+ if (slirp->bootp_filename)
+ snprintf((char *)rbp->bp_file, sizeof(rbp->bp_file), "%s",
+ slirp->bootp_filename);
- if (dhcp_msg_type == DHCPDISCOVER ||
- dhcp_msg_type == DHCPREQUEST) {
*q++ = RFC2132_SRV_ID;
*q++ = 4;
memcpy(q, &saddr.sin_addr, 4);
*q++ = RFC1533_NETMASK;
*q++ = 4;
- *q++ = 0xff;
- *q++ = 0xff;
- *q++ = 0xff;
- *q++ = 0x00;
-
- *q++ = RFC1533_GATEWAY;
- *q++ = 4;
- memcpy(q, &saddr.sin_addr, 4);
+ memcpy(q, &slirp->vnetwork_mask, 4);
q += 4;
- *q++ = RFC1533_DNS;
- *q++ = 4;
- dns_addr.s_addr = htonl(ntohl(special_addr.s_addr) | CTL_DNS);
- memcpy(q, &dns_addr, 4);
- q += 4;
+ if (!slirp->restricted) {
+ *q++ = RFC1533_GATEWAY;
+ *q++ = 4;
+ memcpy(q, &saddr.sin_addr, 4);
+ q += 4;
+
+ *q++ = RFC1533_DNS;
+ *q++ = 4;
+ memcpy(q, &slirp->vnameserver_addr, 4);
+ q += 4;
+ }
*q++ = RFC2132_LEASE_TIME;
*q++ = 4;
memcpy(q, &val, 4);
q += 4;
- if (*slirp_hostname) {
- val = strlen(slirp_hostname);
+ if (*slirp->client_hostname) {
+ val = strlen(slirp->client_hostname);
*q++ = RFC1533_HOSTNAME;
*q++ = val;
- memcpy(q, slirp_hostname, val);
+ memcpy(q, slirp->client_hostname, val);
q += val;
}
+ } else {
+ static const char nak_msg[] = "requested address not available";
+
+ DPRINTF("nak'ed addr=%08x\n", ntohl(preq_addr.s_addr));
+
+ *q++ = RFC2132_MSG_TYPE;
+ *q++ = 1;
+ *q++ = DHCPNAK;
+
+ *q++ = RFC2132_MESSAGE;
+ *q++ = sizeof(nak_msg) - 1;
+ memcpy(q, nak_msg, sizeof(nak_msg) - 1);
+ q += sizeof(nak_msg) - 1;
}
- *q++ = RFC1533_END;
+ *q = RFC1533_END;
+
+ daddr.sin_addr.s_addr = 0xffffffffu;
m->m_len = sizeof(struct bootp_t) -
sizeof(struct ip) - sizeof(struct udphdr);
struct bootp_t *bp = mtod(m, struct bootp_t *);
if (bp->bp_op == BOOTP_REQUEST) {
- bootp_reply(bp);
+ bootp_reply(m->slirp, bp);
}
}