1 /* dnsmasq is Copyright (c) 2000-2012 Simon Kelley
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, or
6 (at your option) version 3 dated 29 June, 2007.
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
13 You should have received a copy of the GNU General Public License
14 along with this program. If not, see <http://www.gnu.org/licenses/>.
22 struct dhcp_context
*current
;
23 struct in6_addr fallback
;
29 struct listen_param
*next
;
32 static int join_multicast(struct in6_addr
*local
, int prefix
,
33 int scope
, int if_index
, int dad
, void *vparam
);
35 static int complete_context6(struct in6_addr
*local
, int prefix
,
36 int scope
, int if_index
, int dad
, void *vparam
);
38 static int make_duid1(unsigned int type
, char *mac
, size_t maclen
, void *parm
);
43 struct sockaddr_in6 saddr
;
44 struct listen_param
*listenp
, listen
;
45 #if defined(IP_TOS) && defined(IPTOS_CLASS_CS6)
46 int class = IPTOS_CLASS_CS6
;
49 if ((fd
= socket(PF_INET6
, SOCK_DGRAM
, IPPROTO_UDP
)) == -1 ||
50 #if defined(IP_TOS) && defined(IPTOS_CLASS_CS6)
51 setsockopt(fd
, IPPROTO_IPV6
, IPV6_TCLASS
, &class, sizeof(class)) == -1 ||
55 die (_("cannot create DHCPv6 socket: %s"), NULL
, EC_BADNET
);
57 memset(&saddr
, 0, sizeof(saddr
));
58 #ifdef HAVE_SOCKADDR_SA_LEN
59 saddr
.sin6_len
= sizeof(struct sockaddr_in6
);
61 saddr
.sin6_family
= AF_INET6
;
62 saddr
.sin6_addr
= in6addr_any
;
63 saddr
.sin6_port
= htons(DHCPV6_SERVER_PORT
);
65 if (bind(fd
, (struct sockaddr
*)&saddr
, sizeof(struct sockaddr_in6
)))
66 die(_("failed to bind DHCPv6 server socket: %s"), NULL
, EC_BADNET
);
68 /* join multicast groups on each interface we're interested in */
69 listen
.fd_or_iface
= fd
;
71 if (!iface_enumerate(AF_INET6
, &listen
, join_multicast
))
72 die(_("failed to join DHCPv6 multicast group: %s"), NULL
, EC_BADNET
);
73 for (listenp
= listen
.next
; listenp
; )
75 struct listen_param
*tmp
= listenp
->next
;
83 static int join_multicast(struct in6_addr
*local
, int prefix
,
84 int scope
, int if_index
, int dad
, void *vparam
)
86 char ifrn_name
[IFNAMSIZ
];
87 struct ipv6_mreq mreq
;
88 struct listen_param
*listenp
, *param
= vparam
;
89 int fd
= param
->fd_or_iface
;
90 struct dhcp_context
*context
;
97 /* record which interfaces we join on, so
98 that we do it at most one per interface, even when they
99 have multiple addresses */
100 for (listenp
= param
->next
; listenp
; listenp
= listenp
->next
)
101 if (if_index
== listenp
->fd_or_iface
)
104 if (!indextoname(fd
, if_index
, ifrn_name
))
107 /* Are we doing DHCP on this interface? */
108 if (!iface_check(AF_INET6
, (struct all_addr
*)local
, ifrn_name
))
111 for (tmp
= daemon
->dhcp_except
; tmp
; tmp
= tmp
->next
)
112 if (tmp
->name
&& (strcmp(tmp
->name
, ifrn_name
) == 0))
115 /* weird libvirt-inspired access control */
116 for (context
= daemon
->dhcp6
; context
; context
= context
->next
)
117 if (!context
->interface
|| strcmp(context
->interface
, ifrn_name
) == 0)
123 mreq
.ipv6mr_interface
= if_index
;
124 inet_pton(AF_INET6
, ALL_RELAY_AGENTS_AND_SERVERS
, &mreq
.ipv6mr_multiaddr
);
126 if (setsockopt(fd
, IPPROTO_IPV6
, IPV6_JOIN_GROUP
, &mreq
, sizeof(mreq
)) == -1)
129 inet_pton(AF_INET6
, ALL_SERVERS
, &mreq
.ipv6mr_multiaddr
);
131 if (setsockopt(fd
, IPPROTO_IPV6
, IPV6_JOIN_GROUP
, &mreq
, sizeof(mreq
)) == -1)
134 listenp
= whine_malloc(sizeof(struct listen_param
));
135 listenp
->fd_or_iface
= if_index
;
136 listenp
->next
= param
->next
;
137 param
->next
= listenp
;
143 void dhcp6_packet(time_t now
)
145 struct dhcp_context
*context
;
146 struct iface_param parm
;
147 struct cmsghdr
*cmptr
;
151 struct cmsghdr align
; /* this ensures alignment */
152 char control6
[CMSG_SPACE(sizeof(struct in6_pktinfo
))];
154 union mysockaddr from
;
155 struct all_addr dest
;
160 msg
.msg_control
= control_u
.control6
;
161 msg
.msg_controllen
= sizeof(control_u
);
163 msg
.msg_name
= &from
;
164 msg
.msg_namelen
= sizeof(from
);
165 msg
.msg_iov
= &daemon
->dhcp_packet
;
168 if ((sz
= recv_dhcp_packet(daemon
->dhcp6fd
, &msg
)) == -1 || sz
<= 4)
171 for (cmptr
= CMSG_FIRSTHDR(&msg
); cmptr
; cmptr
= CMSG_NXTHDR(&msg
, cmptr
))
172 if (cmptr
->cmsg_level
== IPPROTO_IPV6
&& cmptr
->cmsg_type
== daemon
->v6pktinfo
)
176 struct in6_pktinfo
*p
;
178 p
.c
= CMSG_DATA(cmptr
);
180 if_index
= p
.p
->ipi6_ifindex
;
181 dest
.addr
.addr6
= p
.p
->ipi6_addr
;
184 if (!indextoname(daemon
->dhcp6fd
, if_index
, ifr
.ifr_name
))
187 if (!iface_check(AF_INET6
, (struct all_addr
*)&dest
, ifr
.ifr_name
))
190 for (tmp
= daemon
->dhcp_except
; tmp
; tmp
= tmp
->next
)
191 if (tmp
->name
&& (strcmp(tmp
->name
, ifr
.ifr_name
) == 0))
194 /* weird libvirt-inspired access control */
195 for (context
= daemon
->dhcp6
; context
; context
= context
->next
)
196 if (!context
->interface
|| strcmp(context
->interface
, ifr
.ifr_name
) == 0)
202 /* unlinked contexts are marked by context->current == context */
203 for (context
= daemon
->dhcp6
; context
; context
= context
->next
)
205 context
->current
= context
;
206 memset(&context
->local6
, 0, IN6ADDRSZ
);
211 memset(&parm
.fallback
, 0, IN6ADDRSZ
);
213 if (!iface_enumerate(AF_INET6
, &parm
, complete_context6
))
216 lease_prune(NULL
, now
); /* lose any expired leases */
218 msg
.msg_iov
= &daemon
->dhcp_packet
;
219 sz
= dhcp6_reply(parm
.current
, if_index
, ifr
.ifr_name
, &parm
.fallback
, sz
, IN6_IS_ADDR_MULTICAST(&from
.in6
.sin6_addr
), now
);
221 lease_update_file(now
);
225 while (sendto(daemon
->dhcp6fd
, daemon
->outpacket
.iov_base
, sz
, 0, (struct sockaddr
*)&from
, sizeof(from
)) == -1 &&
229 static int complete_context6(struct in6_addr
*local
, int prefix
,
230 int scope
, int if_index
, int dad
, void *vparam
)
232 struct dhcp_context
*context
;
233 struct iface_param
*param
= vparam
;
235 (void)scope
; /* warning */
238 if (if_index
== param
->ind
&&
239 !IN6_IS_ADDR_LOOPBACK(local
) &&
240 !IN6_IS_ADDR_LINKLOCAL(local
) &&
241 !IN6_IS_ADDR_MULTICAST(local
))
243 /* Determine a globally address on the arrival interface, even
244 if we have no matching dhcp-context, because we're only
245 allocating on remote subnets via relays. This
246 is used as a default for the DNS server option. */
247 param
->fallback
= *local
;
249 for (context
= daemon
->dhcp6
; context
; context
= context
->next
)
251 if (prefix
== context
->prefix
&&
252 is_same_net6(local
, &context
->start6
, prefix
) &&
253 is_same_net6(local
, &context
->end6
, prefix
))
255 /* link it onto the current chain if we've not seen it before */
256 if (context
->current
== context
)
258 context
->current
= param
->current
;
259 param
->current
= context
;
260 context
->local6
= *local
;
268 struct dhcp_config
*config_find_by_address6(struct dhcp_config
*configs
, struct in6_addr
*net
, int prefix
, u64 addr
)
270 struct dhcp_config
*config
;
272 for (config
= configs
; config
; config
= config
->next
)
273 if ((config
->flags
& CONFIG_ADDR6
) &&
274 is_same_net6(&config
->addr6
, net
, prefix
) &&
275 (prefix
== 128 || addr6part(&config
->addr6
) == addr
))
281 int address6_allocate(struct dhcp_context
*context
, unsigned char *clid
, int clid_len
,
282 int serial
, struct dhcp_netid
*netids
, struct in6_addr
*ans
)
284 /* Find a free address: exclude anything in use and anything allocated to
285 a particular hwaddr/clientid/hostname in our configuration.
286 Try to return from contexts which match netids first.
288 Note that we assume the address prefix lengths are 64 or greater, so we can
289 get by with 64 bit arithmetic.
293 struct dhcp_context
*c
, *d
;
297 /* hash hwaddr: use the SDBM hashing algorithm. This works
298 for MAC addresses, let's see how it manages with client-ids! */
299 for (j
= 0, i
= 0; i
< clid_len
; i
++)
300 j
+= clid
[i
] + (j
<< 6) + (j
<< 16) - j
;
302 for (pass
= 0; pass
<= 1; pass
++)
303 for (c
= context
; c
; c
= c
->current
)
304 if (c
->flags
& (CONTEXT_STATIC
| CONTEXT_PROXY
))
306 else if (!match_netid(c
->filter
, netids
, pass
))
310 if (option_bool(OPT_CONSEC_ADDR
))
311 /* seed is largest extant lease addr in this context */
312 start
= lease_find_max_addr6(c
) + serial
;
314 start
= addr6part(&c
->start6
) + ((j
+ c
->addr_epoch
+ serial
) % (1 + addr6part(&c
->end6
) - addr6part(&c
->start6
)));
316 /* iterate until we find a free address. */
320 /* eliminate addresses in use by the server. */
321 for (d
= context
; d
; d
= d
->current
)
322 if (addr
== addr6part(&d
->local6
))
326 !lease6_find_by_addr(&c
->start6
, c
->prefix
, addr
) &&
327 !config_find_by_address6(daemon
->dhcp_conf
, &c
->start6
, c
->prefix
, addr
))
330 setaddr6part (ans
, addr
);
336 if (addr
== addr6part(&c
->end6
) + 1)
337 addr
= addr6part(&c
->start6
);
339 } while (addr
!= start
);
345 struct dhcp_context
*address6_available(struct dhcp_context
*context
,
346 struct in6_addr
*taddr
,
347 struct dhcp_netid
*netids
)
349 u64 start
, end
, addr
= addr6part(taddr
);
350 struct dhcp_context
*tmp
;
352 for (tmp
= context
; tmp
; tmp
= tmp
->current
)
354 start
= addr6part(&tmp
->start6
);
355 end
= addr6part(&tmp
->end6
);
357 if (!(tmp
->flags
& (CONTEXT_STATIC
| CONTEXT_PROXY
)) &&
358 is_same_net6(&context
->start6
, taddr
, context
->prefix
) &&
359 is_same_net6(&context
->end6
, taddr
, context
->prefix
) &&
362 match_netid(tmp
->filter
, netids
, 1))
369 struct dhcp_context
*narrow_context6(struct dhcp_context
*context
,
370 struct in6_addr
*taddr
,
371 struct dhcp_netid
*netids
)
373 /* We start of with a set of possible contexts, all on the current physical interface.
374 These are chained on ->current.
375 Here we have an address, and return the actual context correponding to that
376 address. Note that none may fit, if the address came a dhcp-host and is outside
377 any dhcp-range. In that case we return a static range if possible, or failing that,
378 any context on the correct subnet. (If there's more than one, this is a dodgy
379 configuration: maybe there should be a warning.) */
381 struct dhcp_context
*tmp
;
383 if (!(tmp
= address6_available(context
, taddr
, netids
)))
385 for (tmp
= context
; tmp
; tmp
= tmp
->current
)
386 if (match_netid(tmp
->filter
, netids
, 1) &&
387 is_same_net6(taddr
, &tmp
->start6
, tmp
->prefix
) &&
388 (tmp
->flags
& CONTEXT_STATIC
))
392 for (tmp
= context
; tmp
; tmp
= tmp
->current
)
393 if (match_netid(tmp
->filter
, netids
, 1) &&
394 is_same_net6(taddr
, &tmp
->start6
, tmp
->prefix
) &&
395 !(tmp
->flags
& CONTEXT_PROXY
))
399 /* Only one context allowed now */
406 static int is_addr_in_context6(struct dhcp_context
*context
, struct dhcp_config
*config
)
408 if (!context
) /* called via find_config() from lease_update_from_configs() */
410 if (!(config
->flags
& CONFIG_ADDR6
))
412 for (; context
; context
= context
->current
)
413 if (is_same_net6(&config
->addr6
, &context
->start6
, context
->prefix
))
420 struct dhcp_config
*find_config6(struct dhcp_config
*configs
,
421 struct dhcp_context
*context
,
422 unsigned char *duid
, int duid_len
,
425 struct dhcp_config
*config
;
428 for (config
= configs
; config
; config
= config
->next
)
429 if (config
->flags
& CONFIG_CLID
)
431 if (config
->clid_len
== duid_len
&&
432 memcmp(config
->clid
, duid
, duid_len
) == 0 &&
433 is_addr_in_context6(context
, config
))
437 if (hostname
&& context
)
438 for (config
= configs
; config
; config
= config
->next
)
439 if ((config
->flags
& CONFIG_NAME
) &&
440 hostname_isequal(config
->hostname
, hostname
) &&
441 is_addr_in_context6(context
, config
))
447 void make_duid(time_t now
)
449 /* rebase epoch to 1/1/2000 */
450 time_t newnow
= now
- 946684800;
452 iface_enumerate(AF_LOCAL
, &newnow
, make_duid1
);
455 die("Cannot create DHCPv6 server DUID: %s", NULL
, EC_MISC
);
458 static int make_duid1(unsigned int type
, char *mac
, size_t maclen
, void *parm
)
460 /* create DUID as specified in RFC3315. We use the MAC of the
461 first interface we find that isn't loopback or P-to-P */
465 daemon
->duid
= p
= safe_malloc(maclen
+ 8);
466 daemon
->duid_len
= maclen
+ 8;
468 #ifdef HAVE_BROKEN_RTC
469 PUTSHORT(3, p
); /* DUID_LL */
471 PUTSHORT(1, p
); /* DUID_LLT */
474 PUTSHORT(type
, p
); /* address type */
476 #ifndef HAVE_BROKEN_RTC
477 PUTLONG(*((time_t *)parm
), p
); /* time */
480 memcpy(p
, mac
, maclen
);