Commit b63c7f6b77145c109d66a62bb3b6efe12b80d62b

Authored by aliguori
1 parent 8e4416af

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 <jan.kiszka@siemens.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>


git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@7198 c046a42c-6fe2-441c-8c8c-71466251a162
Showing 2 changed files with 95 additions and 34 deletions
slirp/bootp.c
@@ -66,6 +66,24 @@ static BOOTPClient *get_new_addr(struct in_addr *paddr) @@ -66,6 +66,24 @@ static BOOTPClient *get_new_addr(struct in_addr *paddr)
66 return bc; 66 return bc;
67 } 67 }
68 68
  69 +static BOOTPClient *request_addr(const struct in_addr *paddr,
  70 + const uint8_t *macaddr)
  71 +{
  72 + uint32_t req_addr = ntohl(paddr->s_addr);
  73 + uint32_t spec_addr = ntohl(special_addr.s_addr);
  74 + BOOTPClient *bc;
  75 +
  76 + if (req_addr >= (spec_addr | START_ADDR) &&
  77 + req_addr < (spec_addr | (NB_ADDR + START_ADDR))) {
  78 + bc = &bootp_clients[(req_addr & 0xff) - START_ADDR];
  79 + if (!bc->allocated || !memcmp(macaddr, bc->macaddr, 6)) {
  80 + bc->allocated = 1;
  81 + return bc;
  82 + }
  83 + }
  84 + return NULL;
  85 +}
  86 +
69 static BOOTPClient *find_addr(struct in_addr *paddr, const uint8_t *macaddr) 87 static BOOTPClient *find_addr(struct in_addr *paddr, const uint8_t *macaddr)
70 { 88 {
71 BOOTPClient *bc; 89 BOOTPClient *bc;
@@ -83,18 +101,17 @@ static BOOTPClient *find_addr(struct in_addr *paddr, const uint8_t *macaddr) @@ -83,18 +101,17 @@ static BOOTPClient *find_addr(struct in_addr *paddr, const uint8_t *macaddr)
83 return bc; 101 return bc;
84 } 102 }
85 103
86 -static void dhcp_decode(const uint8_t *buf, int size,  
87 - int *pmsg_type) 104 +static void dhcp_decode(const struct bootp_t *bp, int *pmsg_type,
  105 + const struct in_addr **preq_addr)
88 { 106 {
89 const uint8_t *p, *p_end; 107 const uint8_t *p, *p_end;
90 int len, tag; 108 int len, tag;
91 109
92 *pmsg_type = 0; 110 *pmsg_type = 0;
  111 + *preq_addr = NULL;
93 112
94 - p = buf;  
95 - p_end = buf + size;  
96 - if (size < 5)  
97 - return; 113 + p = bp->bp_vend;
  114 + p_end = p + DHCP_OPT_LEN;
98 if (memcmp(p, rfc1533_cookie, 4) != 0) 115 if (memcmp(p, rfc1533_cookie, 4) != 0)
99 return; 116 return;
100 p += 4; 117 p += 4;
@@ -109,34 +126,46 @@ static void dhcp_decode(const uint8_t *buf, int size, @@ -109,34 +126,46 @@ static void dhcp_decode(const uint8_t *buf, int size,
109 if (p >= p_end) 126 if (p >= p_end)
110 break; 127 break;
111 len = *p++; 128 len = *p++;
112 - dprintf("dhcp: tag=0x%02x len=%d\n", tag, len); 129 + dprintf("dhcp: tag=%d len=%d\n", tag, len);
113 130
114 switch(tag) { 131 switch(tag) {
115 case RFC2132_MSG_TYPE: 132 case RFC2132_MSG_TYPE:
116 if (len >= 1) 133 if (len >= 1)
117 *pmsg_type = p[0]; 134 *pmsg_type = p[0];
118 break; 135 break;
  136 + case RFC2132_REQ_ADDR:
  137 + if (len >= 4)
  138 + *preq_addr = (struct in_addr *)p;
  139 + break;
119 default: 140 default:
120 break; 141 break;
121 } 142 }
122 p += len; 143 p += len;
123 } 144 }
124 } 145 }
  146 + if (*pmsg_type == DHCPREQUEST && !*preq_addr && bp->bp_ciaddr.s_addr) {
  147 + *preq_addr = &bp->bp_ciaddr;
  148 + }
125 } 149 }
126 150
127 -static void bootp_reply(struct bootp_t *bp) 151 +static void bootp_reply(const struct bootp_t *bp)
128 { 152 {
129 - BOOTPClient *bc; 153 + BOOTPClient *bc = NULL;
130 struct mbuf *m; 154 struct mbuf *m;
131 struct bootp_t *rbp; 155 struct bootp_t *rbp;
132 struct sockaddr_in saddr, daddr; 156 struct sockaddr_in saddr, daddr;
133 struct in_addr dns_addr; 157 struct in_addr dns_addr;
  158 + const struct in_addr *preq_addr;
134 int dhcp_msg_type, val; 159 int dhcp_msg_type, val;
135 uint8_t *q; 160 uint8_t *q;
136 161
137 /* extract exact DHCP msg type */ 162 /* extract exact DHCP msg type */
138 - dhcp_decode(bp->bp_vend, DHCP_OPT_LEN, &dhcp_msg_type);  
139 - dprintf("bootp packet op=%d msgtype=%d\n", bp->bp_op, dhcp_msg_type); 163 + dhcp_decode(bp, &dhcp_msg_type, &preq_addr);
  164 + dprintf("bootp packet op=%d msgtype=%d", bp->bp_op, dhcp_msg_type);
  165 + if (preq_addr)
  166 + dprintf(" req_addr=%08x\n", ntohl(preq_addr->s_addr));
  167 + else
  168 + dprintf("\n");
140 169
141 if (dhcp_msg_type == 0) 170 if (dhcp_msg_type == 0)
142 dhcp_msg_type = DHCPREQUEST; /* Force reply for old BOOTP clients */ 171 dhcp_msg_type = DHCPREQUEST; /* Force reply for old BOOTP clients */
@@ -155,13 +184,29 @@ static void bootp_reply(struct bootp_t *bp) @@ -155,13 +184,29 @@ static void bootp_reply(struct bootp_t *bp)
155 memset(rbp, 0, sizeof(struct bootp_t)); 184 memset(rbp, 0, sizeof(struct bootp_t));
156 185
157 if (dhcp_msg_type == DHCPDISCOVER) { 186 if (dhcp_msg_type == DHCPDISCOVER) {
158 - new_addr:  
159 - bc = get_new_addr(&daddr.sin_addr); 187 + if (preq_addr) {
  188 + bc = request_addr(preq_addr, client_ethaddr);
  189 + if (bc) {
  190 + daddr.sin_addr = *preq_addr;
  191 + }
  192 + }
160 if (!bc) { 193 if (!bc) {
161 - dprintf("no address left\n");  
162 - return; 194 + new_addr:
  195 + bc = get_new_addr(&daddr.sin_addr);
  196 + if (!bc) {
  197 + dprintf("no address left\n");
  198 + return;
  199 + }
163 } 200 }
164 memcpy(bc->macaddr, client_ethaddr, 6); 201 memcpy(bc->macaddr, client_ethaddr, 6);
  202 + } else if (preq_addr) {
  203 + bc = request_addr(preq_addr, client_ethaddr);
  204 + if (bc) {
  205 + daddr.sin_addr = *preq_addr;
  206 + memcpy(bc->macaddr, client_ethaddr, 6);
  207 + } else {
  208 + daddr.sin_addr.s_addr = 0;
  209 + }
165 } else { 210 } else {
166 bc = find_addr(&daddr.sin_addr, bp->bp_hwaddr); 211 bc = find_addr(&daddr.sin_addr, bp->bp_hwaddr);
167 if (!bc) { 212 if (!bc) {
@@ -171,12 +216,6 @@ static void bootp_reply(struct bootp_t *bp) @@ -171,12 +216,6 @@ static void bootp_reply(struct bootp_t *bp)
171 } 216 }
172 } 217 }
173 218
174 - if (bootp_filename)  
175 - snprintf((char *)rbp->bp_file, sizeof(rbp->bp_file), "%s",  
176 - bootp_filename);  
177 -  
178 - dprintf("offered addr=%08x\n", ntohl(daddr.sin_addr.s_addr));  
179 -  
180 saddr.sin_addr.s_addr = htonl(ntohl(special_addr.s_addr) | CTL_ALIAS); 219 saddr.sin_addr.s_addr = htonl(ntohl(special_addr.s_addr) | CTL_ALIAS);
181 saddr.sin_port = htons(BOOTP_SERVER); 220 saddr.sin_port = htons(BOOTP_SERVER);
182 221
@@ -191,24 +230,29 @@ static void bootp_reply(struct bootp_t *bp) @@ -191,24 +230,29 @@ static void bootp_reply(struct bootp_t *bp)
191 rbp->bp_yiaddr = daddr.sin_addr; /* Client IP address */ 230 rbp->bp_yiaddr = daddr.sin_addr; /* Client IP address */
192 rbp->bp_siaddr = saddr.sin_addr; /* Server IP address */ 231 rbp->bp_siaddr = saddr.sin_addr; /* Server IP address */
193 232
194 - daddr.sin_addr.s_addr = 0xffffffffu;  
195 -  
196 q = rbp->bp_vend; 233 q = rbp->bp_vend;
197 memcpy(q, rfc1533_cookie, 4); 234 memcpy(q, rfc1533_cookie, 4);
198 q += 4; 235 q += 4;
199 236
200 - if (dhcp_msg_type == DHCPDISCOVER) {  
201 - *q++ = RFC2132_MSG_TYPE;  
202 - *q++ = 1;  
203 - *q++ = DHCPOFFER;  
204 - } else if (dhcp_msg_type == DHCPREQUEST) {  
205 - *q++ = RFC2132_MSG_TYPE;  
206 - *q++ = 1;  
207 - *q++ = DHCPACK;  
208 - } 237 + if (bc) {
  238 + dprintf("%s addr=%08x\n",
  239 + (dhcp_msg_type == DHCPDISCOVER) ? "offered" : "ack'ed",
  240 + ntohl(daddr.sin_addr.s_addr));
  241 +
  242 + if (dhcp_msg_type == DHCPDISCOVER) {
  243 + *q++ = RFC2132_MSG_TYPE;
  244 + *q++ = 1;
  245 + *q++ = DHCPOFFER;
  246 + } else /* DHCPREQUEST */ {
  247 + *q++ = RFC2132_MSG_TYPE;
  248 + *q++ = 1;
  249 + *q++ = DHCPACK;
  250 + }
  251 +
  252 + if (bootp_filename)
  253 + snprintf((char *)rbp->bp_file, sizeof(rbp->bp_file), "%s",
  254 + bootp_filename);
209 255
210 - if (dhcp_msg_type == DHCPDISCOVER ||  
211 - dhcp_msg_type == DHCPREQUEST) {  
212 *q++ = RFC2132_SRV_ID; 256 *q++ = RFC2132_SRV_ID;
213 *q++ = 4; 257 *q++ = 4;
214 memcpy(q, &saddr.sin_addr, 4); 258 memcpy(q, &saddr.sin_addr, 4);
@@ -247,9 +291,24 @@ static void bootp_reply(struct bootp_t *bp) @@ -247,9 +291,24 @@ static void bootp_reply(struct bootp_t *bp)
247 memcpy(q, slirp_hostname, val); 291 memcpy(q, slirp_hostname, val);
248 q += val; 292 q += val;
249 } 293 }
  294 + } else {
  295 + static const char nak_msg[] = "requested address not available";
  296 +
  297 + dprintf("nak'ed addr=%08x\n", ntohl(preq_addr->s_addr));
  298 +
  299 + *q++ = RFC2132_MSG_TYPE;
  300 + *q++ = 1;
  301 + *q++ = DHCPNAK;
  302 +
  303 + *q++ = RFC2132_MESSAGE;
  304 + *q++ = sizeof(nak_msg) - 1;
  305 + memcpy(q, nak_msg, sizeof(nak_msg) - 1);
  306 + q += sizeof(nak_msg) - 1;
250 } 307 }
251 *q++ = RFC1533_END; 308 *q++ = RFC1533_END;
252 309
  310 + daddr.sin_addr.s_addr = 0xffffffffu;
  311 +
253 m->m_len = sizeof(struct bootp_t) - 312 m->m_len = sizeof(struct bootp_t) -
254 sizeof(struct ip) - sizeof(struct udphdr); 313 sizeof(struct ip) - sizeof(struct udphdr);
255 udp_output2(NULL, m, &saddr, &daddr, IPTOS_LOWDELAY); 314 udp_output2(NULL, m, &saddr, &daddr, IPTOS_LOWDELAY);
slirp/bootp.h
@@ -63,6 +63,7 @@ @@ -63,6 +63,7 @@
63 #define RFC2132_MSG_TYPE 53 63 #define RFC2132_MSG_TYPE 53
64 #define RFC2132_SRV_ID 54 64 #define RFC2132_SRV_ID 54
65 #define RFC2132_PARAM_LIST 55 65 #define RFC2132_PARAM_LIST 55
  66 +#define RFC2132_MESSAGE 56
66 #define RFC2132_MAX_SIZE 57 67 #define RFC2132_MAX_SIZE 57
67 #define RFC2132_RENEWAL_TIME 58 68 #define RFC2132_RENEWAL_TIME 58
68 #define RFC2132_REBIND_TIME 59 69 #define RFC2132_REBIND_TIME 59
@@ -71,6 +72,7 @@ @@ -71,6 +72,7 @@
71 #define DHCPOFFER 2 72 #define DHCPOFFER 2
72 #define DHCPREQUEST 3 73 #define DHCPREQUEST 3
73 #define DHCPACK 5 74 #define DHCPACK 5
  75 +#define DHCPNAK 6
74 76
75 #define RFC1533_VENDOR_MAJOR 0 77 #define RFC1533_VENDOR_MAJOR 0
76 #define RFC1533_VENDOR_MINOR 0 78 #define RFC1533_VENDOR_MINOR 0