This commit was manufactured by cvs2svn to create branch 'rd235'.
[vde.git] / vde / slirpvde / bootp.c
blobb2df4966a6a6f6f9587e047f9c7ab7d9ea3ea623
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:
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
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.
24 #include <slirp.h>
26 /* XXX: only DHCP is supported */
28 #define NB_ADDR 16
30 #define START_ADDR 15
32 #define LEASE_TIME (120)
34 typedef struct {
35 uint8_t allocated;
36 uint8_t macaddr[6];
37 int time;
38 } BOOTPClient;
40 BOOTPClient bootp_clients[NB_ADDR];
42 static const uint8_t rfc1533_cookie[] = { RFC1533_COOKIE };
44 #ifdef DEBUG
45 #define dprintf(fmt, args...) \
46 if (slirp_debug & DBG_CALL) { fprintf(dfd, fmt, ## args); fflush(dfd); }
47 #else
48 #define dprintf(fmt, args...)
49 #endif
51 static BOOTPClient *get_new_addr(struct in_addr *paddr)
53 BOOTPClient *bc;
54 int i;
55 int now=time(NULL);
57 for(i = 0; i < NB_ADDR; i++) {
58 if (!bootp_clients[i].allocated)
59 goto found;
61 for(i = 0; i < NB_ADDR; i++) {
62 if (now-bootp_clients[i].time > 3*LEASE_TIME)
63 goto found;
65 return NULL;
66 found:
67 bc = &bootp_clients[i];
68 paddr->s_addr = htonl(ntohl(special_addr.s_addr) | (i + START_ADDR));
69 return bc;
72 static BOOTPClient *find_addr(struct in_addr *paddr, const uint8_t *macaddr)
74 BOOTPClient *bc;
75 int i;
77 for(i = 0; i < NB_ADDR; i++) {
78 if (!memcmp(macaddr, bootp_clients[i].macaddr, 6))
79 goto found;
81 return NULL;
82 found:
83 bc = &bootp_clients[i];
84 paddr->s_addr = htonl(ntohl(special_addr.s_addr) | (i + START_ADDR));
85 return bc;
88 static BOOTPClient *find_reqaddr(struct in_addr *paddr, struct in_addr *reqaddr, const uint8_t *macaddr)
90 BOOTPClient *bc=NULL;
91 int i;
92 /*check the net prefix*/
93 if ((ntohl(reqaddr->s_addr) & 0xffffff00) ==
94 (ntohl(special_addr.s_addr) & 0xffffff00)) {
95 i=(ntohl(reqaddr->s_addr) & 0xff) - START_ADDR;
96 if (i>=0 && i< NB_ADDR) {
97 bc = &bootp_clients[i];
98 if (bc->allocated &&
99 (memcmp(macaddr, bootp_clients[i].macaddr, 6)==0)) {
100 paddr->s_addr = reqaddr->s_addr;
101 return bc;
103 else
104 bc=NULL;
107 return bc;
110 static void dhcp_decode(const uint8_t *buf, int size,
111 int *pmsg_type, struct sockaddr_in *preqaddr)
113 const uint8_t *p, *p_end;
114 int len, tag;
116 *pmsg_type = 0;
117 preqaddr->sin_addr.s_addr=htonl(0L);
119 p = buf;
120 p_end = buf + size;
121 if (size < 5)
122 return;
123 if (memcmp(p, rfc1533_cookie, 4) != 0)
124 return;
125 p += 4;
126 while (p < p_end) {
127 tag = p[0];
128 if (tag == RFC1533_PAD) {
129 p++;
130 } else if (tag == RFC1533_END) {
131 break;
132 } else {
133 p++;
134 if (p >= p_end)
135 break;
136 len = *p++;
137 dprintf("dhcp: tag=0x%02x len=%d\n", tag, len);
139 switch(tag) {
140 case RFC2132_MSG_TYPE:
141 if (len >= 1)
142 *pmsg_type = p[0];
143 break;
144 case RFC2132_REQ_ADDR:
145 if (len == 4) {
146 memcpy(&(preqaddr->sin_addr),p,4);
148 default:
149 break;
151 p += len;
156 static void bootp_reply(struct bootp_t *bp)
158 BOOTPClient *bc;
159 struct mbuf *m;
160 struct bootp_t *rbp;
161 struct sockaddr_in saddr, daddr, reqaddr;
162 struct in_addr dns_addr;
163 int dhcp_msg_type, val;
164 uint8_t *q,replytype;
166 /* extract exact DHCP msg type */
167 dhcp_decode(bp->bp_vend, DHCP_OPT_LEN, &dhcp_msg_type,&reqaddr);
168 dprintf("bootp packet op=%d msgtype=%d reqaddr=%x\n", bp->bp_op, dhcp_msg_type,ntohl(reqaddr.sin_addr.s_addr));
170 if (dhcp_msg_type != DHCPDISCOVER &&
171 dhcp_msg_type != DHCPREQUEST)
172 return;
173 /* XXX: this is a hack to get the client mac address */
174 memcpy(client_ethaddr, bp->bp_hwaddr, 6);
176 if ((m = m_get()) == NULL)
177 return;
178 m->m_data += if_maxlinkhdr;
179 rbp = (struct bootp_t *)m->m_data;
180 m->m_data += sizeof(struct udpiphdr);
181 memset(rbp, 0, sizeof(struct bootp_t));
183 bc=NULL;
184 daddr.sin_addr.s_addr=htonl(0L);
185 if (dhcp_msg_type == DHCPREQUEST) {
186 if (reqaddr.sin_addr.s_addr != htonl(0L))
187 bc = find_reqaddr(&daddr.sin_addr, &reqaddr.sin_addr, bp->bp_hwaddr);
188 else
189 bc = find_addr(&daddr.sin_addr, bp->bp_hwaddr);
191 else if (dhcp_msg_type == DHCPDISCOVER) {
192 bc = find_addr(&daddr.sin_addr, bp->bp_hwaddr);
193 if (!bc)
194 bc = get_new_addr(&daddr.sin_addr);
197 dprintf("offered addr=%08x\n", ntohl(daddr.sin_addr.s_addr));
199 saddr.sin_addr.s_addr = htonl(ntohl(special_addr.s_addr) | CTL_ALIAS);
200 saddr.sin_port = htons(BOOTP_SERVER);
202 daddr.sin_port = htons(BOOTP_CLIENT);
204 rbp->bp_op = BOOTP_REPLY;
205 rbp->bp_xid = bp->bp_xid;
206 rbp->bp_htype = 1;
207 rbp->bp_hlen = 6;
208 memcpy(rbp->bp_hwaddr, bp->bp_hwaddr, 6);
210 rbp->bp_yiaddr = daddr.sin_addr; /* IP address */
212 q = rbp->bp_vend;
213 memcpy(q, rfc1533_cookie, 4);
214 q += 4;
216 if (bc != NULL) {
217 memcpy(bc->macaddr, client_ethaddr, 6);
218 bc->allocated = 1;
219 bc->time = time(NULL);
220 replytype=(dhcp_msg_type == DHCPDISCOVER)?DHCPOFFER:DHCPACK;
222 else
223 replytype=DHCPNACK;
225 *q++ = RFC2132_MSG_TYPE;
226 *q++ = 1;
227 *q++ = replytype;
229 if ((dhcp_msg_type == DHCPDISCOVER ||
230 dhcp_msg_type == DHCPREQUEST) && replytype!=DHCPNACK) {
231 *q++ = RFC2132_SRV_ID;
232 *q++ = 4;
233 memcpy(q, &saddr.sin_addr, 4);
234 q += 4;
236 *q++ = RFC1533_NETMASK;
237 *q++ = 4;
238 *q++ = 0xff;
239 *q++ = 0xff;
240 *q++ = 0xff;
241 *q++ = 0x00;
243 *q++ = RFC1533_GATEWAY;
244 *q++ = 4;
245 memcpy(q, &saddr.sin_addr, 4);
246 q += 4;
248 *q++ = RFC1533_DNS;
249 *q++ = 4;
250 dns_addr.s_addr = htonl(ntohl(special_addr.s_addr) | CTL_DNS);
251 memcpy(q, &dns_addr, 4);
252 q += 4;
254 *q++ = RFC2132_LEASE_TIME;
255 *q++ = 4;
256 val = htonl(LEASE_TIME);
257 memcpy(q, &val, 4);
258 q += 4;
260 *q++ = RFC1533_END;
262 m->m_len = sizeof(struct bootp_t);
263 udp_output2(NULL, m, &saddr, &daddr, IPTOS_LOWDELAY);
266 void bootp_input(struct mbuf *m)
268 struct bootp_t *bp = (struct bootp_t *)m->m_data;
270 if (bp->bp_op == BOOTP_REQUEST) {
271 bootp_reply(bp);