blob: cbc8bbfe6f1f9427019f420690480606be79ff45 [file] [log] [blame]
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001/* dnsmasq is Copyright (c) 2000-2003 Simon Kelley
2
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation; version 2 dated June, 1991.
6
7 This program is distributed in the hope that it will be useful,
8 but WITHOUT ANY WARRANTY; without even the implied warranty of
9 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 GNU General Public License for more details.
11*/
12
13/* Author's email: simon@thekelleys.org.uk */
14
15#include "dnsmasq.h"
16
17void dhcp_packet(struct dhcp_context *context, char *packet,
18 struct dhcp_opt *dhcp_opts, struct dhcp_config *dhcp_configs,
19 time_t now, char *namebuff, char *domain_suffix,
20 char *dhcp_file, char *dhcp_sname,
21 struct in_addr dhcp_next_server)
22{
23 struct udp_dhcp_packet *rawpacket = (struct udp_dhcp_packet *) packet;
24 struct dhcp_packet *mess = (struct dhcp_packet *)&rawpacket->data;
25 int sz, newlen;
26
27 sz = recvfrom(context->fd, &rawpacket->data,
28 PACKETSZ - (sizeof(struct ip) + sizeof(struct udphdr)),
29 0, NULL, 0);
30 if ((unsigned int)sz > (sizeof(*mess) - sizeof(mess->options)))
31 {
32 lease_prune(NULL, now); /* lose any expired leases */
33 newlen = dhcp_reply(context, mess, sz, now, namebuff, dhcp_opts,
34 dhcp_configs, domain_suffix, dhcp_file,
35 dhcp_sname, dhcp_next_server );
36 lease_update_dns(0);
37
38 if (newlen != 0)
39 {
40 int broadcast = ntohs(mess->flags) & 0x8000;
41
42 /* newlen -ve forces broadcast */
43 if (newlen < 0)
44 {
45 broadcast = 1;
46 newlen = -newlen;
47 }
48
49 if (mess->giaddr.s_addr || mess->ciaddr.s_addr)
50 {
51 /* To send to BOOTP relay or configured client, use
52 the IP packet */
53
54 struct sockaddr_in dest;
55 dest.sin_family = AF_INET;
56
57 if (mess->giaddr.s_addr)
58 {
59 dest.sin_port = htons(DHCP_SERVER_PORT);
60 dest.sin_addr = mess->giaddr;
61 }
62 else
63 {
64 dest.sin_port = htons(DHCP_CLIENT_PORT);
65 dest.sin_addr = mess->ciaddr;
66 }
67
68 sendto(context->fd, mess, newlen, 0, (struct sockaddr *)&dest, sizeof(dest));
69 }
70 else
71 {
72 /* Hairy stuff, packet either has to go to the
73 net broadcast or the destination can't reply to ARP yet,
74 but we do know the physical address.
75 Build the packet by steam, and send directly, bypassing
76 the kernel IP stack */
77
78 u32 i, sum;
79#ifdef HAVE_PF_PACKET
80 struct sockaddr_ll dest;
81
82 dest.sll_family = AF_PACKET;
83 dest.sll_halen = ETHER_ADDR_LEN;
84 dest.sll_ifindex = context->ifindex;
85 dest.sll_protocol = htons(ETHERTYPE_IP);
86
87 if (broadcast)
88 {
89 memset(dest.sll_addr, 255, ETHER_ADDR_LEN);
90 rawpacket->ip.ip_dst.s_addr = INADDR_BROADCAST;
91 }
92 else
93 {
94 memcpy(dest.sll_addr, mess->chaddr, ETHER_ADDR_LEN);
95 rawpacket->ip.ip_dst.s_addr = mess->yiaddr.s_addr;
96 }
97#endif
98
99#ifdef HAVE_BPF
100 struct ether_header header;
101 struct iovec iov [2];
102
103 header.ether_type = htons(ETHERTYPE_IP);
104 memcpy(header.ether_shost, context->hwaddr, ETHER_ADDR_LEN);
105
106 if (broadcast)
107 {
108 memset(header.ether_dhost, 255, ETHER_ADDR_LEN);
109 rawpacket->ip.ip_dst.s_addr = INADDR_BROADCAST;
110 }
111 else
112 {
113 memcpy(header.ether_dhost, mess->chaddr, ETHER_ADDR_LEN);
114 rawpacket->ip.ip_dst.s_addr = mess->yiaddr.s_addr;
115 }
116#endif
117
118 rawpacket->ip.ip_p = IPPROTO_UDP;
119 rawpacket->ip.ip_src.s_addr = context->serv_addr.s_addr;
120 rawpacket->ip.ip_len = htons(sizeof(struct ip) +
121 sizeof(struct udphdr) +
122 newlen) ;
123 rawpacket->ip.ip_hl = sizeof(struct ip) / 4;
124 rawpacket->ip.ip_v = IPVERSION;
125 rawpacket->ip.ip_tos = 0;
126 rawpacket->ip.ip_id = htons(0);
127 rawpacket->ip.ip_off = htons(0x4000); /* don't fragment */
128 rawpacket->ip.ip_ttl = IPDEFTTL;
129 rawpacket->ip.ip_sum = 0;
130 for (sum = 0, i = 0; i < sizeof(struct ip) / 2; i++)
131 sum += ((u16 *)&rawpacket->ip)[i];
132 while (sum>>16)
133 sum = (sum & 0xffff) + (sum >> 16);
134 rawpacket->ip.ip_sum = (sum == 0xffff) ? sum : ~sum;
135
136 rawpacket->udp.uh_sport = htons(DHCP_SERVER_PORT);
137 rawpacket->udp.uh_dport = htons(DHCP_CLIENT_PORT);
138 ((u8 *)&rawpacket->data)[newlen] = 0; /* for checksum, in case length is odd. */
139 rawpacket->udp.uh_sum = 0;
140 rawpacket->udp.uh_ulen = sum = htons(sizeof(struct udphdr) + newlen);
141 sum += htons(IPPROTO_UDP);
142 for (i = 0; i < 4; i++)
143 sum += ((u16 *)&rawpacket->ip.ip_src)[i];
144 for (i = 0; i < (sizeof(struct udphdr) + newlen + 1) / 2; i++)
145 sum += ((u16 *)&rawpacket->udp)[i];
146 while (sum>>16)
147 sum = (sum & 0xffff) + (sum >> 16);
148 rawpacket->udp.uh_sum = (sum == 0xffff) ? sum : ~sum;
149
150#ifdef HAVE_PF_PACKET
151 sendto(context->rawfd, rawpacket, ntohs(rawpacket->ip.ip_len),
152 0, (struct sockaddr *)&dest, sizeof(dest));
153#endif
154
155#ifdef HAVE_BPF
156 iov[0].iov_base = (char *)&header;
157 iov[0].iov_len = sizeof(struct ether_header);
158 iov[1].iov_base = (char *)rawpacket;
159 iov[1].iov_len = ntohs(rawpacket->ip.ip_len);
160 writev(context->rawfd, iov, 2);
161#endif
162 }
163 }
164 }
165}
166
167int address_available(struct dhcp_context *context, struct in_addr taddr)
168{
169 /* Check is an address is OK for this network, ie
170 within allowable range and not in an existing lease */
171
172 unsigned int addr, start, end;
173
174 addr = ntohl(taddr.s_addr);
175 start = ntohl(context->start.s_addr);
176 end = ntohl(context->end.s_addr);
177
178 if (addr < start)
179 return 0;
180
181 if (addr > end)
182 return 0;
183
184 if (lease_find_by_addr(taddr))
185 return 0;
186
187 return 1;
188}
189
190int address_allocate(struct dhcp_context *context, struct dhcp_config *configs,
191 struct in_addr *addrp)
192{
193 /* Find a free address: exlude anything in use and anything allocated to
194 a particular hwaddr/clientid/hostname in our configuration */
195
196 struct dhcp_config *config;
197 struct in_addr start = context->last;
198
199 do {
200 if (context->last.s_addr == context->end.s_addr)
201 context->last = context->start;
202 else
203 context->last.s_addr = htonl(ntohl(context->last.s_addr) + 1);
204
205
206 if (!lease_find_by_addr(context->last))
207 {
208 for (config = configs; config; config = config->next)
209 if (config->addr.s_addr == context->last.s_addr)
210 break;
211
212 if (!config)
213 {
214 *addrp = context->last;
215 return 1;
216 }
217 }
218 } while (context->last.s_addr != start.s_addr);
219
220 return 0;
221}
222
223static int is_addr_in_context(struct dhcp_context *context, struct dhcp_config *config)
224{
225 if (!context)
226 return 1;
227 if (config->addr.s_addr == 0)
228 return 1;
229 if ((config->addr.s_addr & context->netmask.s_addr) == (context->start.s_addr & context->netmask.s_addr))
230 return 1;
231
232 return 0;
233}
234
235struct dhcp_config *find_config(struct dhcp_config *configs,
236 struct dhcp_context *context,
237 unsigned char *clid, int clid_len,
238 unsigned char *hwaddr, char *hostname)
239{
240 struct dhcp_config *config;
241
242 if (clid_len)
243 for (config = configs; config; config = config->next)
244 {
245 if (config->clid_len == clid_len &&
246 memcmp(config->clid, clid, clid_len) == 0 &&
247 is_addr_in_context(context, config))
248 return config;
249
250 /* dhcpcd prefixes ASCII client IDs by zero which is wrong, but we try and
251 cope with that here */
252 if (*clid == 0 && config->clid_len == clid_len-1 &&
253 memcmp(config->clid, clid+1, clid_len-1) == 0 &&
254 is_addr_in_context(context, config))
255 return config;
256 }
257
258 for (config = configs; config; config = config->next)
259 if (memcmp(config->hwaddr, hwaddr, ETHER_ADDR_LEN) == 0 &&
260 is_addr_in_context(context, config))
261 return config;
262
263 if (hostname)
264 for (config = configs; config; config = config->next)
265 if (config->hostname && strcmp(config->hostname, hostname) == 0 &&
266 is_addr_in_context(context, config))
267 return config;
268
269 return NULL;
270}
271
272
273