qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Qemu-devel] [7198] slirp: Handle DHCP requests for specific IP (Jan Kis


From: Anthony Liguori
Subject: [Qemu-devel] [7198] slirp: Handle DHCP requests for specific IP (Jan Kiszka)
Date: Tue, 21 Apr 2009 19:56:20 +0000

Revision: 7198
          http://svn.sv.gnu.org/viewvc/?view=rev&root=qemu&revision=7198
Author:   aliguori
Date:     2009-04-21 19:56:20 +0000 (Tue, 21 Apr 2009)
Log Message:
-----------
slirp: Handle DHCP requests for specific IP (Jan Kiszka)

This adds proper handling of the ciaddr field as well as the "Requested
IP Address" option to slirp's DHCP server. If the client requests an
invalid or used IP, a NAK reply is sent, if it requests a specific but
valid IP, this is now respected.

NAK'ing invalid IPs is specifically useful when changing the slirp IP
range via '-net user,ip=...' while the client saved its previously used
address and tries to reacquire it. Now this will be NAK'ed and the
client will start a new discovery round.

Signed-off-by: Jan Kiszka <address@hidden>
Signed-off-by: Anthony Liguori <address@hidden>

Modified Paths:
--------------
    trunk/slirp/bootp.c
    trunk/slirp/bootp.h

Modified: trunk/slirp/bootp.c
===================================================================
--- trunk/slirp/bootp.c 2009-04-21 19:56:15 UTC (rev 7197)
+++ trunk/slirp/bootp.c 2009-04-21 19:56:20 UTC (rev 7198)
@@ -66,6 +66,24 @@
     return bc;
 }
 
+static BOOTPClient *request_addr(const struct in_addr *paddr,
+                                 const uint8_t *macaddr)
+{
+    uint32_t req_addr = ntohl(paddr->s_addr);
+    uint32_t spec_addr = ntohl(special_addr.s_addr);
+    BOOTPClient *bc;
+
+    if (req_addr >= (spec_addr | START_ADDR) &&
+        req_addr < (spec_addr | (NB_ADDR + START_ADDR))) {
+        bc = &bootp_clients[(req_addr & 0xff) - START_ADDR];
+        if (!bc->allocated || !memcmp(macaddr, bc->macaddr, 6)) {
+            bc->allocated = 1;
+            return bc;
+        }
+    }
+    return NULL;
+}
+
 static BOOTPClient *find_addr(struct in_addr *paddr, const uint8_t *macaddr)
 {
     BOOTPClient *bc;
@@ -83,18 +101,17 @@
     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,
+                        const struct in_addr **preq_addr)
 {
     const uint8_t *p, *p_end;
     int len, tag;
 
     *pmsg_type = 0;
+    *preq_addr = NULL;
 
-    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;
@@ -109,34 +126,46 @@
             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)
+                    *preq_addr = (struct in_addr *)p;
+                break;
             default:
                 break;
             }
             p += len;
         }
     }
+    if (*pmsg_type == DHCPREQUEST && !*preq_addr && bp->bp_ciaddr.s_addr) {
+        *preq_addr = &bp->bp_ciaddr;
+    }
 }
 
-static void bootp_reply(struct bootp_t *bp)
+static void bootp_reply(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;
+    const struct in_addr *preq_addr;
     int dhcp_msg_type, val;
     uint8_t *q;
 
     /* 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)
+        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 */
@@ -155,13 +184,29 @@
     memset(rbp, 0, sizeof(struct bootp_t));
 
     if (dhcp_msg_type == DHCPDISCOVER) {
-    new_addr:
-        bc = get_new_addr(&daddr.sin_addr);
+        if (preq_addr) {
+            bc = request_addr(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(&daddr.sin_addr);
+            if (!bc) {
+                dprintf("no address left\n");
+                return;
+            }
         }
         memcpy(bc->macaddr, client_ethaddr, 6);
+    } else if (preq_addr) {
+        bc = request_addr(preq_addr, client_ethaddr);
+        if (bc) {
+            daddr.sin_addr = *preq_addr;
+            memcpy(bc->macaddr, client_ethaddr, 6);
+        } else {
+            daddr.sin_addr.s_addr = 0;
+        }
     } else {
         bc = find_addr(&daddr.sin_addr, bp->bp_hwaddr);
         if (!bc) {
@@ -171,12 +216,6 @@
         }
     }
 
-    if (bootp_filename)
-        snprintf((char *)rbp->bp_file, sizeof(rbp->bp_file), "%s",
-                 bootp_filename);
-
-    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_port = htons(BOOTP_SERVER);
 
@@ -191,24 +230,29 @@
     rbp->bp_yiaddr = daddr.sin_addr; /* Client IP address */
     rbp->bp_siaddr = saddr.sin_addr; /* Server IP address */
 
-    daddr.sin_addr.s_addr = 0xffffffffu;
-
     q = rbp->bp_vend;
     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 ||
-        dhcp_msg_type == DHCPREQUEST) {
+        if (dhcp_msg_type == DHCPDISCOVER) {
+            *q++ = RFC2132_MSG_TYPE;
+            *q++ = 1;
+            *q++ = DHCPOFFER;
+        } else /* DHCPREQUEST */ {
+            *q++ = RFC2132_MSG_TYPE;
+            *q++ = 1;
+            *q++ = DHCPACK;
+        }
+
+        if (bootp_filename)
+            snprintf((char *)rbp->bp_file, sizeof(rbp->bp_file), "%s",
+                     bootp_filename);
+
         *q++ = RFC2132_SRV_ID;
         *q++ = 4;
         memcpy(q, &saddr.sin_addr, 4);
@@ -247,9 +291,24 @@
             memcpy(q, slirp_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;
 
+    daddr.sin_addr.s_addr = 0xffffffffu;
+
     m->m_len = sizeof(struct bootp_t) -
         sizeof(struct ip) - sizeof(struct udphdr);
     udp_output2(NULL, m, &saddr, &daddr, IPTOS_LOWDELAY);

Modified: trunk/slirp/bootp.h
===================================================================
--- trunk/slirp/bootp.h 2009-04-21 19:56:15 UTC (rev 7197)
+++ trunk/slirp/bootp.h 2009-04-21 19:56:20 UTC (rev 7198)
@@ -63,6 +63,7 @@
 #define RFC2132_MSG_TYPE       53
 #define RFC2132_SRV_ID         54
 #define RFC2132_PARAM_LIST     55
+#define RFC2132_MESSAGE                56
 #define RFC2132_MAX_SIZE       57
 #define RFC2132_RENEWAL_TIME    58
 #define RFC2132_REBIND_TIME     59
@@ -71,6 +72,7 @@
 #define DHCPOFFER              2
 #define DHCPREQUEST            3
 #define DHCPACK                        5
+#define DHCPNAK                        6
 
 #define RFC1533_VENDOR_MAJOR   0
 #define RFC1533_VENDOR_MINOR   0





reply via email to

[Prev in Thread] Current Thread [Next in Thread]