1/*
2 * dhcpcd - DHCP client daemon
3 * Copyright (c) 2006-2011 Roy Marples <roy@marples.name>
4 * All rights reserved
5
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 */
27
28#include <errno.h>
29#include <signal.h>
30#include <stdlib.h>
31#include <string.h>
32#include <syslog.h>
33#include <unistd.h>
34
35#include "arp.h"
36#include "common.h"
37#include "dhcpcd.h"
38#include "eloop.h"
39#include "if-options.h"
40#include "ipv4ll.h"
41#include "net.h"
42
43static struct dhcp_message *
44make_ipv4ll_lease(uint32_t addr)
45{
46	uint32_t u32;
47	struct dhcp_message *dhcp;
48	uint8_t *p;
49
50	dhcp = xzalloc(sizeof(*dhcp));
51	/* Put some LL options in */
52	dhcp->yiaddr = addr;
53	p = dhcp->options;
54	*p++ = DHO_SUBNETMASK;
55	*p++ = sizeof(u32);
56	u32 = htonl(LINKLOCAL_MASK);
57	memcpy(p, &u32, sizeof(u32));
58	p += sizeof(u32);
59	*p++ = DHO_BROADCAST;
60	*p++ = sizeof(u32);
61	u32 = htonl(LINKLOCAL_BRDC);
62	memcpy(p, &u32, sizeof(u32));
63	p += sizeof(u32);
64	*p++ = DHO_END;
65
66	return dhcp;
67}
68
69static struct dhcp_message *
70find_ipv4ll_lease(uint32_t old_addr)
71{
72	uint32_t addr;
73
74	for (;;) {
75		addr = htonl(LINKLOCAL_ADDR |
76		    (((uint32_t)abs((int)arc4random())
77			% 0xFD00) + 0x0100));
78		if (addr != old_addr &&
79		    IN_LINKLOCAL(ntohl(addr)))
80			break;
81	}
82	return make_ipv4ll_lease(addr);
83}
84
85void
86start_ipv4ll(void *arg)
87{
88	struct interface *iface = arg;
89	uint32_t addr;
90
91	delete_timeout(NULL, iface);
92	iface->state->probes = 0;
93	iface->state->claims = 0;
94	if (iface->addr.s_addr) {
95		iface->state->conflicts = 0;
96		if (IN_LINKLOCAL(htonl(iface->addr.s_addr))) {
97			send_arp_announce(iface);
98			return;
99		}
100	}
101
102	if (iface->state->offer == NULL)
103		addr = 0;
104	else {
105		addr = iface->state->offer->yiaddr;
106		free(iface->state->offer);
107	}
108	/* We maybe rebooting an IPv4LL address. */
109	if (!IN_LINKLOCAL(htonl(addr))) {
110		syslog(LOG_INFO, "%s: probing for an IPv4LL address",
111		    iface->name);
112		addr = 0;
113	}
114	if (addr == 0)
115		iface->state->offer = find_ipv4ll_lease(addr);
116	else
117		iface->state->offer = make_ipv4ll_lease(addr);
118	iface->state->lease.frominfo = 0;
119	send_arp_probe(iface);
120}
121
122void
123handle_ipv4ll_failure(void *arg)
124{
125	struct interface *iface = arg;
126	time_t up;
127
128	if (iface->state->fail.s_addr == iface->addr.s_addr) {
129		up = uptime();
130		if (iface->state->defend + DEFEND_INTERVAL > up) {
131			syslog(LOG_DEBUG,
132			    "%s: IPv4LL %d second defence failed",
133			    iface->name, DEFEND_INTERVAL);
134			drop_dhcp(iface, "EXPIRE");
135			iface->state->conflicts = -1;
136		} else {
137			syslog(LOG_DEBUG, "%s: defended IPv4LL address",
138			    iface->name);
139			iface->state->defend = up;
140			return;
141		}
142	}
143
144	close_sockets(iface);
145	free(iface->state->offer);
146	iface->state->offer = NULL;
147	delete_timeout(NULL, iface);
148	if (++iface->state->conflicts > MAX_CONFLICTS) {
149		syslog(LOG_ERR, "%s: failed to acquire an IPv4LL address",
150		    iface->name);
151		iface->state->interval = RATE_LIMIT_INTERVAL / 2;
152		start_discover(iface);
153	} else {
154		add_timeout_sec(PROBE_WAIT, start_ipv4ll, iface);
155	}
156}
157