]>
Commit | Line | Data |
---|---|---|
9e4abcb5 SK |
1 | /* 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 | ||
17 | void 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 | ||
167 | int 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 | ||
190 | int 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 | ||
223 | static 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 | ||
235 | struct 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 | ||
1ab84e2f SK |
272 | void set_configs_from_cache(struct dhcp_config *configs) |
273 | /* Some people like to keep all static IP addresses in /etc/hosts. | |
274 | This goes through /etc/hosts and sets static addresses for any DHCP config | |
275 | records which don't have an address and whose name matches. */ | |
276 | { | |
277 | struct dhcp_config *config; | |
278 | struct crec *crec; | |
279 | ||
280 | for (config = configs; config; config = config->next) | |
281 | if (config->addr.s_addr == 0 && config->hostname && | |
282 | (crec = cache_find_by_name(NULL, config->hostname, 0, F_IPV4)) && | |
283 | (crec->flags & F_HOSTS)) | |
284 | config->addr = crec->addr.addr.addr4; | |
285 | } | |
9e4abcb5 | 286 |