~pmdj/ubuntu/trusty/qemu/2.9+applesmc+fadtv3

« back to all changes in this revision

Viewing changes to roms/u-boot/net/link_local.c

  • Committer: Phil Dennis-Jordan
  • Date: 2017-07-21 08:03:43 UTC
  • mfrom: (1.1.1)
  • Revision ID: phil@philjordan.eu-20170721080343-2yr2vdj7713czahv
New upstream release 2.9.0.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * RFC3927 ZeroConf IPv4 Link-Local addressing
 
3
 * (see <http://www.zeroconf.org/>)
 
4
 *
 
5
 * Copied from BusyBox - networking/zcip.c
 
6
 *
 
7
 * Copyright (C) 2003 by Arthur van Hoff (avh@strangeberry.com)
 
8
 * Copyright (C) 2004 by David Brownell
 
9
 * Copyright (C) 2010 by Joe Hershberger
 
10
 *
 
11
 * Licensed under the GPL v2 or later
 
12
 */
 
13
 
 
14
#include <common.h>
 
15
#include <net.h>
 
16
#include "arp.h"
 
17
#include "net_rand.h"
 
18
 
 
19
/* We don't need more than 32 bits of the counter */
 
20
#define MONOTONIC_MS() ((unsigned)get_timer(0) * (1000 / CONFIG_SYS_HZ))
 
21
 
 
22
enum {
 
23
/* 169.254.0.0 */
 
24
        LINKLOCAL_ADDR = 0xa9fe0000,
 
25
 
 
26
        IN_CLASSB_NET = 0xffff0000,
 
27
        IN_CLASSB_HOST = 0x0000ffff,
 
28
 
 
29
/* protocol timeout parameters, specified in seconds */
 
30
        PROBE_WAIT = 1,
 
31
        PROBE_MIN = 1,
 
32
        PROBE_MAX = 2,
 
33
        PROBE_NUM = 3,
 
34
        MAX_CONFLICTS = 10,
 
35
        RATE_LIMIT_INTERVAL = 60,
 
36
        ANNOUNCE_WAIT = 2,
 
37
        ANNOUNCE_NUM = 2,
 
38
        ANNOUNCE_INTERVAL = 2,
 
39
        DEFEND_INTERVAL = 10
 
40
};
 
41
 
 
42
/* States during the configuration process. */
 
43
static enum ll_state_t {
 
44
        PROBE = 0,
 
45
        RATE_LIMIT_PROBE,
 
46
        ANNOUNCE,
 
47
        MONITOR,
 
48
        DEFEND,
 
49
        DISABLED
 
50
} state = DISABLED;
 
51
 
 
52
static IPaddr_t ip;
 
53
static int timeout_ms = -1;
 
54
static unsigned deadline_ms;
 
55
static unsigned conflicts;
 
56
static unsigned nprobes;
 
57
static unsigned nclaims;
 
58
static int ready;
 
59
static unsigned int seed;
 
60
 
 
61
static void link_local_timeout(void);
 
62
 
 
63
/**
 
64
 * Pick a random link local IP address on 169.254/16, except that
 
65
 * the first and last 256 addresses are reserved.
 
66
 */
 
67
static IPaddr_t pick(void)
 
68
{
 
69
        unsigned tmp;
 
70
 
 
71
        do {
 
72
                tmp = rand_r(&seed) & IN_CLASSB_HOST;
 
73
        } while (tmp > (IN_CLASSB_HOST - 0x0200));
 
74
        return (IPaddr_t) htonl((LINKLOCAL_ADDR + 0x0100) + tmp);
 
75
}
 
76
 
 
77
/**
 
78
 * Return milliseconds of random delay, up to "secs" seconds.
 
79
 */
 
80
static inline unsigned random_delay_ms(unsigned secs)
 
81
{
 
82
        return rand_r(&seed) % (secs * 1000);
 
83
}
 
84
 
 
85
static void configure_wait(void)
 
86
{
 
87
        if (timeout_ms == -1)
 
88
                return;
 
89
 
 
90
        /* poll, being ready to adjust current timeout */
 
91
        if (!timeout_ms)
 
92
                timeout_ms = random_delay_ms(PROBE_WAIT);
 
93
 
 
94
        /* set deadline_ms to the point in time when we timeout */
 
95
        deadline_ms = MONOTONIC_MS() + timeout_ms;
 
96
 
 
97
        debug_cond(DEBUG_DEV_PKT, "...wait %d %s nprobes=%u, nclaims=%u\n",
 
98
                        timeout_ms, eth_get_name(), nprobes, nclaims);
 
99
 
 
100
        NetSetTimeout(timeout_ms, link_local_timeout);
 
101
}
 
102
 
 
103
void link_local_start(void)
 
104
{
 
105
        ip = getenv_IPaddr("llipaddr");
 
106
        if (ip != 0 && (ntohl(ip) & IN_CLASSB_NET) != LINKLOCAL_ADDR) {
 
107
                puts("invalid link address");
 
108
                net_set_state(NETLOOP_FAIL);
 
109
                return;
 
110
        }
 
111
        NetOurSubnetMask = IN_CLASSB_NET;
 
112
 
 
113
        seed = seed_mac();
 
114
        if (ip == 0)
 
115
                ip = pick();
 
116
 
 
117
        state = PROBE;
 
118
        timeout_ms = 0;
 
119
        conflicts = 0;
 
120
        nprobes = 0;
 
121
        nclaims = 0;
 
122
        ready = 0;
 
123
 
 
124
        configure_wait();
 
125
}
 
126
 
 
127
static void link_local_timeout(void)
 
128
{
 
129
        switch (state) {
 
130
        case PROBE:
 
131
                /* timeouts in the PROBE state mean no conflicting ARP packets
 
132
                   have been received, so we can progress through the states */
 
133
                if (nprobes < PROBE_NUM) {
 
134
                        nprobes++;
 
135
                        debug_cond(DEBUG_LL_STATE, "probe/%u %s@%pI4\n",
 
136
                                        nprobes, eth_get_name(), &ip);
 
137
                        arp_raw_request(0, NetEtherNullAddr, ip);
 
138
                        timeout_ms = PROBE_MIN * 1000;
 
139
                        timeout_ms += random_delay_ms(PROBE_MAX - PROBE_MIN);
 
140
                } else {
 
141
                        /* Switch to announce state */
 
142
                        state = ANNOUNCE;
 
143
                        nclaims = 0;
 
144
                        debug_cond(DEBUG_LL_STATE, "announce/%u %s@%pI4\n",
 
145
                                        nclaims, eth_get_name(), &ip);
 
146
                        arp_raw_request(ip, NetOurEther, ip);
 
147
                        timeout_ms = ANNOUNCE_INTERVAL * 1000;
 
148
                }
 
149
                break;
 
150
        case RATE_LIMIT_PROBE:
 
151
                /* timeouts in the RATE_LIMIT_PROBE state mean no conflicting
 
152
                   ARP packets have been received, so we can move immediately
 
153
                   to the announce state */
 
154
                state = ANNOUNCE;
 
155
                nclaims = 0;
 
156
                debug_cond(DEBUG_LL_STATE, "announce/%u %s@%pI4\n",
 
157
                                nclaims, eth_get_name(), &ip);
 
158
                arp_raw_request(ip, NetOurEther, ip);
 
159
                timeout_ms = ANNOUNCE_INTERVAL * 1000;
 
160
                break;
 
161
        case ANNOUNCE:
 
162
                /* timeouts in the ANNOUNCE state mean no conflicting ARP
 
163
                   packets have been received, so we can progress through
 
164
                   the states */
 
165
                if (nclaims < ANNOUNCE_NUM) {
 
166
                        nclaims++;
 
167
                        debug_cond(DEBUG_LL_STATE, "announce/%u %s@%pI4\n",
 
168
                                        nclaims, eth_get_name(), &ip);
 
169
                        arp_raw_request(ip, NetOurEther, ip);
 
170
                        timeout_ms = ANNOUNCE_INTERVAL * 1000;
 
171
                } else {
 
172
                        /* Switch to monitor state */
 
173
                        state = MONITOR;
 
174
                        printf("Successfully assigned %pI4\n", &ip);
 
175
                        NetCopyIP(&NetOurIP, &ip);
 
176
                        ready = 1;
 
177
                        conflicts = 0;
 
178
                        timeout_ms = -1;
 
179
                        /* Never timeout in the monitor state */
 
180
                        NetSetTimeout(0, NULL);
 
181
 
 
182
                        /* NOTE: all other exit paths should deconfig ... */
 
183
                        net_set_state(NETLOOP_SUCCESS);
 
184
                        return;
 
185
                }
 
186
                break;
 
187
        case DEFEND:
 
188
                /* We won!  No ARP replies, so just go back to monitor */
 
189
                state = MONITOR;
 
190
                timeout_ms = -1;
 
191
                conflicts = 0;
 
192
                break;
 
193
        default:
 
194
                /* Invalid, should never happen.  Restart the whole protocol */
 
195
                state = PROBE;
 
196
                ip = pick();
 
197
                timeout_ms = 0;
 
198
                nprobes = 0;
 
199
                nclaims = 0;
 
200
                break;
 
201
        }
 
202
        configure_wait();
 
203
}
 
204
 
 
205
void link_local_receive_arp(struct arp_hdr *arp, int len)
 
206
{
 
207
        int source_ip_conflict;
 
208
        int target_ip_conflict;
 
209
        IPaddr_t null_ip = 0;
 
210
 
 
211
        if (state == DISABLED)
 
212
                return;
 
213
 
 
214
        /* We need to adjust the timeout in case we didn't receive a
 
215
           conflicting packet. */
 
216
        if (timeout_ms > 0) {
 
217
                unsigned diff = deadline_ms - MONOTONIC_MS();
 
218
                if ((int)(diff) < 0) {
 
219
                        /* Current time is greater than the expected timeout
 
220
                           time. This should never happen */
 
221
                        debug_cond(DEBUG_LL_STATE,
 
222
                                "missed an expected timeout\n");
 
223
                        timeout_ms = 0;
 
224
                } else {
 
225
                        debug_cond(DEBUG_INT_STATE, "adjusting timeout\n");
 
226
                        timeout_ms = diff | 1; /* never 0 */
 
227
                }
 
228
        }
 
229
#if 0
 
230
 /* XXX Don't bother with ethernet link just yet */
 
231
        if ((fds[0].revents & POLLIN) == 0) {
 
232
                if (fds[0].revents & POLLERR) {
 
233
                        /*
 
234
                         * FIXME: links routinely go down;
 
235
                         */
 
236
                        bb_error_msg("iface %s is down", eth_get_name());
 
237
                        if (ready) {
 
238
                                run(argv, "deconfig", &ip);
 
239
                        }
 
240
                        return EXIT_FAILURE;
 
241
                }
 
242
                continue;
 
243
        }
 
244
#endif
 
245
 
 
246
        debug_cond(DEBUG_INT_STATE, "%s recv arp type=%d, op=%d,\n",
 
247
                eth_get_name(), ntohs(arp->ar_pro),
 
248
                ntohs(arp->ar_op));
 
249
        debug_cond(DEBUG_INT_STATE, "\tsource=%pM %pI4\n",
 
250
                &arp->ar_sha,
 
251
                &arp->ar_spa);
 
252
        debug_cond(DEBUG_INT_STATE, "\ttarget=%pM %pI4\n",
 
253
                &arp->ar_tha,
 
254
                &arp->ar_tpa);
 
255
 
 
256
        if (arp->ar_op != htons(ARPOP_REQUEST)
 
257
         && arp->ar_op != htons(ARPOP_REPLY)
 
258
        ) {
 
259
                configure_wait();
 
260
                return;
 
261
        }
 
262
 
 
263
        source_ip_conflict = 0;
 
264
        target_ip_conflict = 0;
 
265
 
 
266
        if (memcmp(&arp->ar_spa, &ip, ARP_PLEN) == 0
 
267
         && memcmp(&arp->ar_sha, NetOurEther, ARP_HLEN) != 0
 
268
        ) {
 
269
                source_ip_conflict = 1;
 
270
        }
 
271
 
 
272
        /*
 
273
         * According to RFC 3927, section 2.2.1:
 
274
         * Check if packet is an ARP probe by checking for a null source IP
 
275
         * then check that target IP is equal to ours and source hw addr
 
276
         * is not equal to ours. This condition should cause a conflict only
 
277
         * during probe.
 
278
         */
 
279
        if (arp->ar_op == htons(ARPOP_REQUEST) &&
 
280
            memcmp(&arp->ar_spa, &null_ip, ARP_PLEN) == 0 &&
 
281
            memcmp(&arp->ar_tpa, &ip, ARP_PLEN) == 0 &&
 
282
            memcmp(&arp->ar_sha, NetOurEther, ARP_HLEN) != 0) {
 
283
                target_ip_conflict = 1;
 
284
        }
 
285
 
 
286
        debug_cond(DEBUG_NET_PKT,
 
287
                "state = %d, source ip conflict = %d, target ip conflict = "
 
288
                "%d\n", state, source_ip_conflict, target_ip_conflict);
 
289
        switch (state) {
 
290
        case PROBE:
 
291
        case ANNOUNCE:
 
292
                /* When probing or announcing, check for source IP conflicts
 
293
                   and other hosts doing ARP probes (target IP conflicts). */
 
294
                if (source_ip_conflict || target_ip_conflict) {
 
295
                        conflicts++;
 
296
                        state = PROBE;
 
297
                        if (conflicts >= MAX_CONFLICTS) {
 
298
                                debug("%s ratelimit\n", eth_get_name());
 
299
                                timeout_ms = RATE_LIMIT_INTERVAL * 1000;
 
300
                                state = RATE_LIMIT_PROBE;
 
301
                        }
 
302
 
 
303
                        /* restart the whole protocol */
 
304
                        ip = pick();
 
305
                        timeout_ms = 0;
 
306
                        nprobes = 0;
 
307
                        nclaims = 0;
 
308
                }
 
309
                break;
 
310
        case MONITOR:
 
311
                /* If a conflict, we try to defend with a single ARP probe */
 
312
                if (source_ip_conflict) {
 
313
                        debug("monitor conflict -- defending\n");
 
314
                        state = DEFEND;
 
315
                        timeout_ms = DEFEND_INTERVAL * 1000;
 
316
                        arp_raw_request(ip, NetOurEther, ip);
 
317
                }
 
318
                break;
 
319
        case DEFEND:
 
320
                /* Well, we tried.  Start over (on conflict) */
 
321
                if (source_ip_conflict) {
 
322
                        state = PROBE;
 
323
                        debug("defend conflict -- starting over\n");
 
324
                        ready = 0;
 
325
                        NetOurIP = 0;
 
326
 
 
327
                        /* restart the whole protocol */
 
328
                        ip = pick();
 
329
                        timeout_ms = 0;
 
330
                        nprobes = 0;
 
331
                        nclaims = 0;
 
332
                }
 
333
                break;
 
334
        default:
 
335
                /* Invalid, should never happen.  Restart the whole protocol */
 
336
                debug("invalid state -- starting over\n");
 
337
                state = PROBE;
 
338
                ip = pick();
 
339
                timeout_ms = 0;
 
340
                nprobes = 0;
 
341
                nclaims = 0;
 
342
                break;
 
343
        }
 
344
        configure_wait();
 
345
}