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 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 87 static BOOTPClient *find_addr(struct in_addr *paddr, const uint8_t *macaddr)
70 88 {
71 89 BOOTPClient *bc;
... ... @@ -83,18 +101,17 @@ static BOOTPClient *find_addr(struct in_addr *paddr, const uint8_t *macaddr)
83 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 107 const uint8_t *p, *p_end;
90 108 int len, tag;
91 109  
92 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 115 if (memcmp(p, rfc1533_cookie, 4) != 0)
99 116 return;
100 117 p += 4;
... ... @@ -109,34 +126,46 @@ static void dhcp_decode(const uint8_t *buf, int size,
109 126 if (p >= p_end)
110 127 break;
111 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 131 switch(tag) {
115 132 case RFC2132_MSG_TYPE:
116 133 if (len >= 1)
117 134 *pmsg_type = p[0];
118 135 break;
  136 + case RFC2132_REQ_ADDR:
  137 + if (len >= 4)
  138 + *preq_addr = (struct in_addr *)p;
  139 + break;
119 140 default:
120 141 break;
121 142 }
122 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 154 struct mbuf *m;
131 155 struct bootp_t *rbp;
132 156 struct sockaddr_in saddr, daddr;
133 157 struct in_addr dns_addr;
  158 + const struct in_addr *preq_addr;
134 159 int dhcp_msg_type, val;
135 160 uint8_t *q;
136 161  
137 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 170 if (dhcp_msg_type == 0)
142 171 dhcp_msg_type = DHCPREQUEST; /* Force reply for old BOOTP clients */
... ... @@ -155,13 +184,29 @@ static void bootp_reply(struct bootp_t *bp)
155 184 memset(rbp, 0, sizeof(struct bootp_t));
156 185  
157 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 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 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 210 } else {
166 211 bc = find_addr(&daddr.sin_addr, bp->bp_hwaddr);
167 212 if (!bc) {
... ... @@ -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 219 saddr.sin_addr.s_addr = htonl(ntohl(special_addr.s_addr) | CTL_ALIAS);
181 220 saddr.sin_port = htons(BOOTP_SERVER);
182 221  
... ... @@ -191,24 +230,29 @@ static void bootp_reply(struct bootp_t *bp)
191 230 rbp->bp_yiaddr = daddr.sin_addr; /* Client IP address */
192 231 rbp->bp_siaddr = saddr.sin_addr; /* Server IP address */
193 232  
194   - daddr.sin_addr.s_addr = 0xffffffffu;
195   -
196 233 q = rbp->bp_vend;
197 234 memcpy(q, rfc1533_cookie, 4);
198 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 256 *q++ = RFC2132_SRV_ID;
213 257 *q++ = 4;
214 258 memcpy(q, &saddr.sin_addr, 4);
... ... @@ -247,9 +291,24 @@ static void bootp_reply(struct bootp_t *bp)
247 291 memcpy(q, slirp_hostname, val);
248 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 308 *q++ = RFC1533_END;
252 309  
  310 + daddr.sin_addr.s_addr = 0xffffffffu;
  311 +
253 312 m->m_len = sizeof(struct bootp_t) -
254 313 sizeof(struct ip) - sizeof(struct udphdr);
255 314 udp_output2(NULL, m, &saddr, &daddr, IPTOS_LOWDELAY);
... ...
slirp/bootp.h
... ... @@ -63,6 +63,7 @@
63 63 #define RFC2132_MSG_TYPE 53
64 64 #define RFC2132_SRV_ID 54
65 65 #define RFC2132_PARAM_LIST 55
  66 +#define RFC2132_MESSAGE 56
66 67 #define RFC2132_MAX_SIZE 57
67 68 #define RFC2132_RENEWAL_TIME 58
68 69 #define RFC2132_REBIND_TIME 59
... ... @@ -71,6 +72,7 @@
71 72 #define DHCPOFFER 2
72 73 #define DHCPREQUEST 3
73 74 #define DHCPACK 5
  75 +#define DHCPNAK 6
74 76  
75 77 #define RFC1533_VENDOR_MAJOR 0
76 78 #define RFC1533_VENDOR_MINOR 0
... ...