1 /**
2 * @file
3 *
4 * ACD IPv4 Address Conflict Detection
5 *
6 * This is an IPv4 address conflict detection implementation for the lwIP TCP/IP
7 * stack. It aims to be conform to RFC5227.
8 *
9 * @defgroup acd ACD
10 * @ingroup ip4
11 * ACD related functions
12 * USAGE:
13 *
14 * define @ref LWIP_ACD 1 in your lwipopts.h
15 * Options:
16 * ACD_TMR_INTERVAL msecs,
17 * I recommend a value of 100. The value must divide 1000 with a remainder almost 0.
18 * Possible values are 1000, 500, 333, 250, 200, 166, 142, 125, 111, 100 ....
19 *
20 * For fixed IP:
21 * - call acd_start after selecting an IP address. The caller will be informed
22 * on conflict status via the callback function.
23 *
24 * With AUTOIP:
25 * - will be called from the autoip module. No extra's needed.
26 *
27 * With DHCP:
28 * - enable LWIP_DHCP_DOES_ACD_CHECK. Then it will be called from the dhcp module.
29 * No extra's needed.
30 */
31
32 /*
33 *
34 * Copyright (c) 2007 Dominik Spies <kontakt@dspies.de>
35 * Copyright (c) 2018 Jasper Verschueren <jasper.verschueren@apart-audio.com>
36 * All rights reserved.
37 *
38 * Redistribution and use in source and binary forms, with or without modification,
39 * are permitted provided that the following conditions are met:
40 *
41 * 1. Redistributions of source code must retain the above copyright notice,
42 * this list of conditions and the following disclaimer.
43 * 2. Redistributions in binary form must reproduce the above copyright notice,
44 * this list of conditions and the following disclaimer in the documentation
45 * and/or other materials provided with the distribution.
46 * 3. The name of the author may not be used to endorse or promote products
47 * derived from this software without specific prior written permission.
48 *
49 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
50 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
51 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
52 * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
53 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
54 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
55 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
56 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
57 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
58 * OF SUCH DAMAGE.
59 *
60 * Author: Jasper Verschueren <jasper.verschueren@apart-audio.com>
61 * Author: Dominik Spies <kontakt@dspies.de>
62 */
63
64 #include "lwip/opt.h"
65
66 /* don't build if not configured for use in lwipopts.h */
67 #if LWIP_IPV4 && LWIP_ACD
68
69 #include <string.h>
70
71 #include "lwip/acd.h"
72 #include "lwip/prot/acd.h"
73
74 #define ACD_FOREACH(acd, acd_list) for ((acd) = acd_list; (acd) != NULL; (acd) = (acd)->next)
75
76 #define ACD_TICKS_PER_SECOND (1000 / ACD_TMR_INTERVAL)
77
78 /* Define good random function (LWIP_RAND) in lwipopts.h */
79 #ifdef LWIP_RAND
80 #define LWIP_ACD_RAND(netif, acd) LWIP_RAND()
81 #else /* LWIP_RAND */
82 #ifdef LWIP_AUTOIP_RAND
83 #include "lwip/autoip.h"
84 #define LWIP_ACD_RAND(netif, acd) LWIP_AUTOIP_RAND(netif) /* for backwards compatibility */
85 #else
86 #define LWIP_ACD_RAND(netif, acd) ((((u32_t)((netif->hwaddr[5]) & 0xff) << 24) | \
87 ((u32_t)((netif->hwaddr[3]) & 0xff) << 16) | \
88 ((u32_t)((netif->hwaddr[2]) & 0xff) << 8) | \
89 ((u32_t)((netif->hwaddr[4]) & 0xff))) + \
90 (acd->sent_num))
91 #endif /* LWIP_AUTOIP_RAND */
92 #endif /* LWIP_RAND */
93
94
95 #define ACD_RANDOM_PROBE_WAIT(netif, acd) (LWIP_ACD_RAND(netif, acd) % \
96 (PROBE_WAIT * ACD_TICKS_PER_SECOND))
97
98 #define ACD_RANDOM_PROBE_INTERVAL(netif, acd) ((LWIP_ACD_RAND(netif, acd) % \
99 ((PROBE_MAX - PROBE_MIN) * ACD_TICKS_PER_SECOND)) + \
100 (PROBE_MIN * ACD_TICKS_PER_SECOND ))
101
102 /* Function definitions */
103 static void acd_restart(struct netif *netif, struct acd *acd);
104 static void acd_handle_arp_conflict(struct netif *netif, struct acd *acd);
105 static void acd_put_in_passive_mode(struct netif *netif, struct acd *acd);
106
107 /**
108 * @ingroup acd
109 * Add ACD client to the client list and initialize callback function
110 *
111 * @param netif network interface on which to start the acd
112 * client
113 * @param acd acd module to be added to the list
114 * @param acd_conflict_callback callback to be called when conflict information
115 * is available
116 */
117 err_t
acd_add(struct netif * netif,struct acd * acd,acd_conflict_callback_t acd_conflict_callback)118 acd_add(struct netif *netif, struct acd *acd,
119 acd_conflict_callback_t acd_conflict_callback)
120 {
121 struct acd *acd2;
122
123 /* Set callback */
124 LWIP_ASSERT_CORE_LOCKED();
125 LWIP_ASSERT("acd_conflict_callback != NULL", acd_conflict_callback != NULL);
126 acd->acd_conflict_callback = acd_conflict_callback;
127
128 /* Check if the acd struct is already added */
129 for (acd2 = netif->acd_list; acd2 != NULL; acd2 = acd2->next) {
130 if (acd2 == acd) {
131 LWIP_DEBUGF(ACD_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
132 ("acd_add(): acd already added to list\n"));
133 return ERR_OK;
134 }
135 }
136
137 /* add acd struct to the list */
138 acd->next = netif->acd_list;
139 netif->acd_list = acd;
140
141 return ERR_OK;
142 }
143
144 /**
145 * @ingroup acd
146 * Remvoe ACD client from the client list
147 *
148 * @param netif network interface from which to remove the acd client
149 * @param acd acd module to be removed from the list
150 */
151 void
acd_remove(struct netif * netif,struct acd * acd)152 acd_remove(struct netif *netif, struct acd *acd)
153 {
154 struct acd *acd2, *prev = NULL;
155
156 LWIP_ASSERT_CORE_LOCKED();
157
158 for (acd2 = netif->acd_list; acd2 != NULL; acd2 = acd2->next) {
159 if (acd2 == acd) {
160 if (prev) {
161 prev->next = acd->next;
162 } else {
163 netif->acd_list = acd->next;
164 }
165 return;
166 }
167 prev = acd2;
168 }
169 LWIP_ASSERT(("acd_remove(): acd not on list\n"), 0);
170 }
171
172
173 /**
174 * @ingroup acd
175 * Start ACD client
176 *
177 * @param netif network interface on which to start the acd client
178 * @param acd acd module to start
179 * @param ipaddr ip address to perform acd on
180 */
181 err_t
acd_start(struct netif * netif,struct acd * acd,ip4_addr_t ipaddr)182 acd_start(struct netif *netif, struct acd *acd, ip4_addr_t ipaddr)
183 {
184 err_t result = ERR_OK;
185
186 LWIP_UNUSED_ARG(netif);
187 LWIP_DEBUGF(ACD_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
188 ("acd_start(netif=%p) %c%c%"U16_F"\n",
189 (void *)netif, netif->name[0],
190 netif->name[1], (u16_t)netif->num));
191
192 /* init probing state */
193 acd->sent_num = 0;
194 acd->lastconflict = 0;
195 ip4_addr_copy(acd->ipaddr, ipaddr);
196 acd->state = ACD_STATE_PROBE_WAIT;
197
198 acd->ttw = (u16_t)(ACD_RANDOM_PROBE_WAIT(netif, acd));
199
200 return result;
201 }
202
203 /**
204 * @ingroup acd
205 * Stop ACD client
206 *
207 * @param acd acd module to stop
208 */
209 err_t
acd_stop(struct acd * acd)210 acd_stop(struct acd *acd)
211 {
212 LWIP_DEBUGF(ACD_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("acd_stop\n"));
213
214 if (acd != NULL) {
215 acd->state = ACD_STATE_OFF;
216 }
217 return ERR_OK;
218 }
219
220 /**
221 * @ingroup acd
222 * Inform the ACD modules when the link goes down
223 *
224 * @param netif network interface on which to inform the ACD clients
225 */
226 void
acd_network_changed_link_down(struct netif * netif)227 acd_network_changed_link_down(struct netif *netif)
228 {
229 struct acd *acd;
230 /* loop over the acd's*/
231 ACD_FOREACH(acd, netif->acd_list) {
232 acd_stop(acd);
233 }
234 }
235
236 /**
237 * Has to be called in loop every ACD_TMR_INTERVAL milliseconds
238 */
239 void
acd_tmr(void)240 acd_tmr(void)
241 {
242 struct netif *netif;
243 struct acd *acd;
244 /* loop through netif's */
245 NETIF_FOREACH(netif) {
246 ACD_FOREACH(acd, netif->acd_list) {
247 if (acd->lastconflict > 0) {
248 acd->lastconflict--;
249 }
250
251 LWIP_DEBUGF(ACD_DEBUG | LWIP_DBG_TRACE,
252 ("acd_tmr() ACD-State: %"U16_F", ttw=%"U16_F"\n",
253 (u16_t)(acd->state), acd->ttw));
254
255 if (acd->ttw > 0) {
256 acd->ttw--;
257 }
258
259 switch (acd->state) {
260 case ACD_STATE_PROBE_WAIT:
261 case ACD_STATE_PROBING:
262 if (acd->ttw == 0) {
263 acd->state = ACD_STATE_PROBING;
264 etharp_acd_probe(netif, &acd->ipaddr);
265 LWIP_DEBUGF(ACD_DEBUG | LWIP_DBG_TRACE,
266 ("acd_tmr() PROBING Sent Probe\n"));
267 acd->sent_num++;
268 if (acd->sent_num >= PROBE_NUM) {
269 /* Switch to ANNOUNCE_WAIT: last probe is sent*/
270 acd->state = ACD_STATE_ANNOUNCE_WAIT;
271
272 acd->sent_num = 0;
273
274 /* calculate time to wait before announcing */
275 acd->ttw = (u16_t)(ANNOUNCE_WAIT * ACD_TICKS_PER_SECOND);
276 } else {
277 /* calculate time to wait to next probe */
278 acd->ttw = (u16_t)(ACD_RANDOM_PROBE_INTERVAL(netif, acd));
279 }
280 }
281 break;
282
283 case ACD_STATE_ANNOUNCE_WAIT:
284 case ACD_STATE_ANNOUNCING:
285 if (acd->ttw == 0) {
286 if (acd->sent_num == 0) {
287 acd->state = ACD_STATE_ANNOUNCING;
288
289 /* reset conflict count to ensure fast re-probing after announcing */
290 acd->num_conflicts = 0;
291
292 LWIP_DEBUGF(ACD_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
293 ("acd_tmr(): changing state to ANNOUNCING: %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
294 ip4_addr1_16(&acd->ipaddr), ip4_addr2_16(&acd->ipaddr),
295 ip4_addr3_16(&acd->ipaddr), ip4_addr4_16(&acd->ipaddr)));
296 }
297
298 etharp_acd_announce(netif, &acd->ipaddr);
299 LWIP_DEBUGF(ACD_DEBUG | LWIP_DBG_TRACE,
300 ("acd_tmr() ANNOUNCING Sent Announce\n"));
301 acd->ttw = ANNOUNCE_INTERVAL * ACD_TICKS_PER_SECOND;
302 acd->sent_num++;
303
304 if (acd->sent_num >= ANNOUNCE_NUM) {
305 acd->state = ACD_STATE_ONGOING;
306 acd->sent_num = 0;
307 acd->ttw = 0;
308 LWIP_DEBUGF(ACD_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
309 ("acd_tmr(): changing state to ONGOING: %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
310 ip4_addr1_16(&acd->ipaddr), ip4_addr2_16(&acd->ipaddr),
311 ip4_addr3_16(&acd->ipaddr), ip4_addr4_16(&acd->ipaddr)));
312
313 /* finally, let acd user know that the address is good and can be used */
314 acd->acd_conflict_callback(netif, ACD_IP_OK);
315 }
316 }
317 break;
318
319 case ACD_STATE_RATE_LIMIT:
320 if (acd->ttw == 0) {
321 /* acd should be stopped because ipaddr isn't valid any more */
322 acd_stop(acd);
323 /* let the acd user (after rate limit interval) know that their is
324 * a conflict detected. So it can restart the address acquiring
325 * process.*/
326 acd->acd_conflict_callback(netif, ACD_RESTART_CLIENT);
327 }
328 break;
329
330 default:
331 /* nothing to do in other states */
332 break;
333 }
334 }
335 }
336 }
337
338 /**
339 * Restarts the acd module
340 *
341 * The number of conflicts is increased and the upper layer is informed.
342 */
343 static void
acd_restart(struct netif * netif,struct acd * acd)344 acd_restart(struct netif *netif, struct acd *acd)
345 {
346 /* increase conflict counter. */
347 acd->num_conflicts++;
348
349 /* Decline the address */
350 acd->acd_conflict_callback(netif, ACD_DECLINE);
351
352 /* if we tried more then MAX_CONFLICTS we must limit our rate for
353 * acquiring and probing addresses. compliant to RFC 5227 Section 2.1.1 */
354 if (acd->num_conflicts >= MAX_CONFLICTS) {
355 acd->state = ACD_STATE_RATE_LIMIT;
356 acd->ttw = (u16_t)(RATE_LIMIT_INTERVAL * ACD_TICKS_PER_SECOND);
357 LWIP_DEBUGF(ACD_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE | LWIP_DBG_LEVEL_WARNING,
358 ("acd_restart(): rate limiting initiated. too many conflicts\n"));
359 }
360 else {
361 /* acd should be stopped because ipaddr isn't valid any more */
362 acd_stop(acd);
363 /* let the acd user know right away that their is a conflict detected.
364 * So it can restart the address acquiring process. */
365 acd->acd_conflict_callback(netif, ACD_RESTART_CLIENT);
366 }
367 }
368
369 /**
370 * Handles every incoming ARP Packet, called by etharp_input().
371 *
372 * @param netif network interface to use for acd processing
373 * @param hdr Incoming ARP packet
374 */
375 void
acd_arp_reply(struct netif * netif,struct etharp_hdr * hdr)376 acd_arp_reply(struct netif *netif, struct etharp_hdr *hdr)
377 {
378 struct acd *acd;
379 ip4_addr_t sipaddr, dipaddr;
380 struct eth_addr netifaddr;
381 SMEMCPY(netifaddr.addr, netif->hwaddr, ETH_HWADDR_LEN);
382
383 /* Copy struct ip4_addr_wordaligned to aligned ip4_addr, to support
384 * compilers without structure packing (not using structure copy which
385 * breaks strict-aliasing rules).
386 */
387 IPADDR_WORDALIGNED_COPY_TO_IP4_ADDR_T(&sipaddr, &hdr->sipaddr);
388 IPADDR_WORDALIGNED_COPY_TO_IP4_ADDR_T(&dipaddr, &hdr->dipaddr);
389
390 LWIP_DEBUGF(ACD_DEBUG | LWIP_DBG_TRACE, ("acd_arp_reply()\n"));
391
392 /* loop over the acd's*/
393 ACD_FOREACH(acd, netif->acd_list) {
394 switch(acd->state) {
395 case ACD_STATE_OFF:
396 case ACD_STATE_RATE_LIMIT:
397 default:
398 /* do nothing */
399 break;
400
401 case ACD_STATE_PROBE_WAIT:
402 case ACD_STATE_PROBING:
403 case ACD_STATE_ANNOUNCE_WAIT:
404 /* RFC 5227 Section 2.1.1:
405 * from beginning to after ANNOUNCE_WAIT seconds we have a conflict if
406 * ip.src == ipaddr (someone is already using the address)
407 * OR
408 * ip.dst == ipaddr && hw.src != own hwaddr (someone else is probing it)
409 */
410 if ((ip4_addr_eq(&sipaddr, &acd->ipaddr)) ||
411 (ip4_addr_isany_val(sipaddr) &&
412 ip4_addr_eq(&dipaddr, &acd->ipaddr) &&
413 !eth_addr_eq(&netifaddr, &hdr->shwaddr))) {
414 LWIP_DEBUGF(ACD_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE | LWIP_DBG_LEVEL_WARNING,
415 ("acd_arp_reply(): Probe Conflict detected\n"));
416 acd_restart(netif, acd);
417 }
418 break;
419
420 case ACD_STATE_ANNOUNCING:
421 case ACD_STATE_ONGOING:
422 case ACD_STATE_PASSIVE_ONGOING:
423 /* RFC 5227 Section 2.4:
424 * in any state we have a conflict if
425 * ip.src == ipaddr && hw.src != own hwaddr (someone is using our address)
426 */
427 if (ip4_addr_eq(&sipaddr, &acd->ipaddr) &&
428 !eth_addr_eq(&netifaddr, &hdr->shwaddr)) {
429 LWIP_DEBUGF(ACD_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE | LWIP_DBG_LEVEL_WARNING,
430 ("acd_arp_reply(): Conflicting ARP-Packet detected\n"));
431 acd_handle_arp_conflict(netif, acd);
432 }
433 break;
434 }
435 }
436 }
437
438 /**
439 * Handle a IP address conflict after an ARP conflict detection
440 */
441 static void
acd_handle_arp_conflict(struct netif * netif,struct acd * acd)442 acd_handle_arp_conflict(struct netif *netif, struct acd *acd)
443 {
444 /* RFC5227, 2.4 "Ongoing Address Conflict Detection and Address Defense"
445 allows three options where:
446 a) means retreat on the first conflict,
447 b) allows to keep an already configured address when having only one
448 conflict in DEFEND_INTERVAL seconds and
449 c) the host will not give up it's address and defend it indefinitely
450
451 We use option b) when the acd module represents the netif address, since it
452 helps to improve the chance that one of the two conflicting hosts may be
453 able to retain its address. while we are flexible enough to help network
454 performance
455
456 We use option a) when the acd module does not represent the netif address,
457 since we cannot have the acd module announcing or restarting. This
458 situation occurs for the LL acd module when a routable address is used on
459 the netif but the LL address is still open in the background. */
460
461 if (acd->state == ACD_STATE_PASSIVE_ONGOING) {
462 /* Immediately back off on a conflict. */
463 LWIP_DEBUGF(ACD_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
464 ("acd_handle_arp_conflict(): conflict when we are in passive mode -> back off\n"));
465 acd_stop(acd);
466 acd->acd_conflict_callback(netif, ACD_DECLINE);
467 }
468 else {
469 if (acd->lastconflict > 0) {
470 /* retreat, there was a conflicting ARP in the last DEFEND_INTERVAL seconds */
471 LWIP_DEBUGF(ACD_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
472 ("acd_handle_arp_conflict(): conflict within DEFEND_INTERVAL -> retreating\n"));
473
474 /* Active TCP sessions are aborted when removing the ip address but a bad
475 * connection was inevitable anyway with conflicting hosts */
476 acd_restart(netif, acd);
477 } else {
478 LWIP_DEBUGF(ACD_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
479 ("acd_handle_arp_conflict(): we are defending, send ARP Announce\n"));
480 etharp_acd_announce(netif, &acd->ipaddr);
481 acd->lastconflict = DEFEND_INTERVAL * ACD_TICKS_PER_SECOND;
482 }
483 }
484 }
485
486 /**
487 * Put the acd module in passive ongoing conflict detection.
488 */
489 static void
acd_put_in_passive_mode(struct netif * netif,struct acd * acd)490 acd_put_in_passive_mode(struct netif *netif, struct acd *acd)
491 {
492 switch(acd->state) {
493 case ACD_STATE_OFF:
494 case ACD_STATE_PASSIVE_ONGOING:
495 default:
496 /* do nothing */
497 break;
498
499 case ACD_STATE_PROBE_WAIT:
500 case ACD_STATE_PROBING:
501 case ACD_STATE_ANNOUNCE_WAIT:
502 case ACD_STATE_RATE_LIMIT:
503 acd_stop(acd);
504 acd->acd_conflict_callback(netif, ACD_DECLINE);
505 break;
506
507 case ACD_STATE_ANNOUNCING:
508 case ACD_STATE_ONGOING:
509 acd->state = ACD_STATE_PASSIVE_ONGOING;
510 LWIP_DEBUGF(ACD_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
511 ("acd_put_in_passive_mode()\n"));
512 break;
513 }
514 }
515
516 /**
517 * @ingroup acd
518 * Inform the ACD modules of address changes
519 *
520 * @param netif network interface on which the address is changing
521 * @param old_addr old ip address
522 * @param new_addr new ip address
523 */
524 void
acd_netif_ip_addr_changed(struct netif * netif,const ip_addr_t * old_addr,const ip_addr_t * new_addr)525 acd_netif_ip_addr_changed(struct netif *netif, const ip_addr_t *old_addr,
526 const ip_addr_t *new_addr)
527 {
528 struct acd *acd;
529
530 LWIP_DEBUGF(ACD_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
531 ("acd_netif_ip_addr_changed(): Address changed\n"));
532
533 LWIP_DEBUGF(ACD_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
534 ("acd_netif_ip_addr_changed(): old address = %s\n", ipaddr_ntoa(old_addr)));
535 LWIP_DEBUGF(ACD_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
536 ("acd_netif_ip_addr_changed(): new address = %s\n", ipaddr_ntoa(new_addr)));
537
538 /* If we change from ANY to an IP or from an IP to ANY we do nothing */
539 if (ip_addr_isany(old_addr) || ip_addr_isany(new_addr)) {
540 return;
541 }
542
543 ACD_FOREACH(acd, netif->acd_list) {
544 /* Find ACD module of old address */
545 if(ip4_addr_eq(&acd->ipaddr, ip_2_ip4(old_addr))) {
546 /* Did we change from a LL address to a routable address? */
547 if (ip_addr_islinklocal(old_addr) && !ip_addr_islinklocal(new_addr)) {
548 LWIP_DEBUGF(ACD_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
549 ("acd_netif_ip_addr_changed(): changed from LL to routable address\n"));
550 /* Put the module in passive conflict detection mode */
551 acd_put_in_passive_mode(netif, acd);
552 }
553 }
554 }
555 }
556
557 #endif /* LWIP_IPV4 && LWIP_ACD */
558