00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013 #ifdef HAVE_CONFIG_H
00014 #include <config.h>
00015 #endif
00016
00017 #include <stdlib.h>
00018 #include <sys/socket.h>
00019 #include <netinet/in.h>
00020 #include <arpa/inet.h>
00021 #include <string.h>
00022 #include <assert.h>
00023 #include <errno.h>
00024 #include <stddef.h>
00025 #include <features.h>
00026 #include <unistd.h>
00027 #ifdef DYN_TARGET_LINUX
00028 #include <net/if_arp.h>
00029 #ifndef __GLIBC__
00030
00031 #include <linux/ip.h>
00032 #endif
00033 #endif
00034
00035 #include "hashtable.h"
00036 #include "debug.h"
00037 #include "fileio.h"
00038 #include "proxyarp.h"
00039 #ifdef WITH_WIRELESS
00040 #include "monitor.h"
00041 #endif
00042 #include "dyn_ip.h"
00043 #include "agentapi.h"
00044 #include "util.h"
00045 #include "mn_agentadv.h"
00046 #include "mn_handler.h"
00047 #include "mn.h"
00048
00049
00050
00051 static struct agentadv_data *max_adv;
00052 static int max_priority;
00053 static struct timeval max_adv_time;
00054
00055 extern struct mn_data mn;
00056 extern struct mn_config config;
00057 extern struct timeval timers[TIMER_COUNT];
00058 extern int real_tunnel_up;
00059
00060
00061 int adv_ok_fa(struct agentadv_data *adv)
00062 {
00063 int opts;
00064
00065 if (mn.force_fa_addr.s_addr != 0 &&
00066 mn.force_fa_addr.s_addr != adv->addr.s_addr)
00067 return 0;
00068
00069 if (adv->adv.ext == NULL)
00070 return 0;
00071
00072 opts = ntohs(adv->adv.ext->opts);
00073 if ((opts & AGENT_ADV_BUSY) || !(opts & AGENT_ADV_FOREIGN_AGENT))
00074 return 0;
00075
00076 if (config.tunneling_mode == TUNMODE_REVERSE &&
00077 !(opts & AGENT_ADV_BIDIR_TUNNELING))
00078 return 0;
00079
00080 if (config.tunneling_mode == TUNMODE_TRIANGLE &&
00081 adv->adv.own_ext != NULL &&
00082 !(adv->adv.own_ext->opts & AGENT_ADV_OWN_TRIANGLE_TUNNELING))
00083 return 0;
00084
00085 return 1;
00086 }
00087
00088
00089 static int addr_cmp(void *key, struct node *cmprd)
00090 {
00091 struct agentadv_data *entry = (struct agentadv_data *) cmprd;
00092 struct agentadv_key *akey = (struct agentadv_key *) key;
00093
00094 if (akey->addr.s_addr == entry->addr.s_addr &&
00095 (akey->ifindex == 0 || akey->ifindex == entry->ifindex))
00096 return TRUE;
00097 return FALSE;
00098 }
00099
00100
00101 static int addr_hash(void *key, const int tablesize)
00102 {
00103 unsigned char *addr;
00104 struct agentadv_key *akey = (struct agentadv_key *) key;
00105
00106 addr = (unsigned char *) &akey->addr;
00107 return (int) (addr[0] ^ addr[1] ^ addr[2] ^ addr[3]);
00108 }
00109
00110
00123 struct agentadv_data *
00124 adv_fetch(struct hashtable *adv_hash, struct in_addr *addr, int ifindex)
00125 {
00126 struct agentadv_data *adv;
00127 struct agentadv_key key;
00128 key.addr.s_addr = addr->s_addr;
00129 key.ifindex = ifindex;
00130
00131 adv = (struct agentadv_data *)
00132 hashtable_fetch(adv_hash, addr_hash, &key, addr_cmp);
00133 return (adv);
00134 }
00135
00136
00137 static int current_agent_expired;
00138
00139
00140
00141 int clean_agentadv(struct node *node, void *data)
00142 {
00143 struct agentadv_data *adv;
00144 struct timeval *tval;
00145 struct event_FA dat;
00146
00147 adv = (struct agentadv_data *) node;
00148 tval = (struct timeval *) data;
00149
00150 if (tval != NULL && cmp_timeval(tval, &adv->expire) < 0)
00151 return TRUE;
00152
00153 if (tval != NULL && adv->in_use) {
00154
00155 current_agent_expired = TRUE;
00156 return TRUE;
00157 }
00158
00159 DEBUG(DEBUG_AGENTADV, "clean_agentadv: removing %s\n",
00160 inet_ntoa(adv->addr));
00161
00162
00163 dat.adv = adv;
00164 dat.hash = mn.agentadv;
00165 handler_call_all(FA_ADV_EXPIRE, &dat);
00166
00167
00168 if (adv->arpentry == 1 &&
00169 arp_del_permanent_item(adv->addr, adv->ifname) < 0) {
00170 if (device_up(adv->ifindex)) {
00171 LOG2(LOG_WARNING,
00172 "arp_del_permanent_item(%s, %s) failed\n",
00173 inet_ntoa(adv->addr), adv->ifname);
00174 } else {
00175 DEBUG(DEBUG_INFO, "arp_del_permanent_item(%s, %s) "
00176 "failed (device down)\n",
00177 inet_ntoa(adv->addr), adv->ifname);
00178 }
00179 }
00180
00181
00182 if (adv->routeentry == 1) {
00183 DEBUG(DEBUG_INFO, "Removing FA route (%s,%s)\n",
00184 inet_ntoa(adv->addr), adv->ifname);
00185 if (dyn_ip_route_del(adv->addr, adv->ifname) != 0)
00186 DEBUG(DEBUG_INFO, "FA route removing failed\n");
00187 }
00188
00189 if (mn.current_adv == adv) {
00190 DEBUG(DEBUG_AGENTADV, "clean_agentadv: removed current_adv\n");
00191 mn.current_adv = NULL;
00192 }
00193
00194 hashtable_remove(node);
00195 free(adv);
00196
00197 return TRUE;
00198 }
00199
00200
00201 static void handle_home_adv(struct agentadv_data *adv)
00202 {
00203 struct in_addr addr;
00204
00205 DEBUG(DEBUG_AGENTADV, "Home advertisement\n");
00206 if (config.priv_ha_ip_addr.s_addr != 0)
00207 addr = config.priv_ha_ip_addr;
00208 else
00209 addr = config.ha_ip_addr;
00210 if (addr.s_addr != adv->addr.s_addr) {
00211 struct node *iter;
00212 int found = 0;
00213 for (iter = list_get_first(&config.alt_ha_ip_addrs);
00214 iter != NULL; iter = list_get_next(iter)) {
00215 struct alt_ha_entry *alt =
00216 (struct alt_ha_entry *) iter;
00217 if (alt->addr.s_addr == adv->addr.s_addr) {
00218 found = 1;
00219 break;
00220 }
00221 }
00222 if (!found) {
00223 DEBUG(DEBUG_AGENTADV, "\tnot our HA\n");
00224 return;
00225 }
00226 }
00227
00228 if (config.ha_nai_len > 0) {
00229 struct fa_nai_ext *fa_nai =
00230 (struct fa_nai_ext *) adv->adv.fa_nai;
00231 if (fa_nai == NULL) {
00232 DEBUG(DEBUG_AGENTADV, "\tno NAI extension in agent adv"
00233 " - ignoring packet\n");
00234 return;
00235 }
00236 if (config.ha_nai_len != GET_NAI_LEN(fa_nai)) {
00237 DEBUG(DEBUG_AGENTADV, "\tNAI length mismatch - "
00238 "ignoring packet\n");
00239 return;
00240 }
00241 if (memcmp(config.ha_nai, MSG_NAI_DATA(fa_nai),
00242 config.ha_nai_len) != 0) {
00243 DEBUG(DEBUG_AGENTADV, "\tNAI mismatch - ignoring "
00244 "packet\n");
00245 return;
00246 }
00247
00248 }
00249
00250 if (mn.current_adv != NULL && mn.current_adv->in_use)
00251 mn.current_adv->in_use = 0;
00252 mn.current_adv = adv;
00253 mn.current_adv->in_use = 1;
00254
00255 mn.cur_route_info.ifindex = mn.cur_route_info.ifindex_net =
00256 adv->ifindex;
00257 memcpy(mn.cur_route_info.ifname, adv->ifname, IFNAMSIZ);
00258 memcpy(mn.cur_route_info.ifname_net, adv->ifname, IFNAMSIZ);
00259 mn.cur_route_info.known = 1;
00260 mn.cur_route_info.via.s_addr = config.home_net_gateway.s_addr;
00261
00262 adv->adv_type = MN_ADV_TYPE_OWN_HA;
00263 DEBUG(DEBUG_AGENTADV, "\tfrom our own HA - MN is at home\n");
00264 if (mn.state == MN_FIND_AGENT || mn.state == MN_PASSIVE_FIND ||
00265 mn.state == MN_REQUEST_TUNNEL || mn.state == MN_CONNECTED) {
00266
00267
00268
00269
00270 DEBUG(DEBUG_INFO, "Deregistering due to the heard "
00271 "own HA agent advertisement\n");
00272 close_for_home(STATE_INIT);
00273 }
00274 }
00275
00276
00277
00278
00279 static int find_fa_with_priority(struct node *node, void *data)
00280 {
00281 struct agentadv_data *adv;
00282
00283 adv = (struct agentadv_data *) node;
00284
00285 if (monitor_check_policy(NEWEST_ADV_BIT)) {
00286
00287
00288 if (!timerisset(&max_adv_time) ||
00289 cmp_timeval(&max_adv_time, &adv->last) < 0) {
00290 max_adv_time = adv->last;
00291 max_adv = adv;
00292 }
00293 return TRUE;
00294 }
00295
00296 adv->priority_degraded = adv->priority;
00297 DEBUG(DEBUG_HANDLERS, "%s priority %i", inet_ntoa(adv->addr),
00298 adv->priority_degraded);
00299
00300
00301 if (adv->prio_degrade_percent > 0 && adv->priority_degraded > 0) {
00302 adv->priority_degraded = (int)
00303 (adv->priority_degraded *
00304 ((100.0 - adv->prio_degrade_percent) / 100.0));
00305
00306 if (adv->priority_degraded == 0)
00307 adv->priority_degraded = 1;
00308 DEBUG(DEBUG_HANDLERS, " => %i (-%i%%)", adv->priority_degraded,
00309 adv->prio_degrade_percent);
00310 }
00311 DEBUG(DEBUG_HANDLERS, "\n");
00312
00313 if (adv->priority_degraded > max_priority && !adv->reg_failed &&
00314 (adv_ok_fa(adv) || adv == mn.current_adv)) {
00315 max_priority = adv->priority_degraded;
00316 max_adv = adv;
00317 }
00318
00319 return TRUE;
00320 }
00321
00322
00323
00324 static int update_fa_decision(struct agentadv_data *adv)
00325 {
00326 int same_prio;
00327 struct timeval now;
00328
00329 gettimeofday(&now, NULL);
00330
00331
00332
00333
00334 if ((mn.state == MN_AT_HOME || mn.state == MN_CLOSE_FOR_HOME) &&
00335 mn.current_adv != NULL &&
00336 mn.current_adv->addr.s_addr == config.ha_ip_addr.s_addr &&
00337 mn.current_adv->adv.ext != NULL &&
00338 ntohs(mn.current_adv->adv.ext->opts) & AGENT_ADV_HOME_AGENT &&
00339 cmp_timeval(&mn.current_adv->expire, &now) >= 0 &&
00340 !monitor_check_policy(NEWEST_ADV_BIT)) {
00341 DEBUG(DEBUG_AGENTADV,
00342 "HA agentadv still valid - staying at home\n");
00343 return FA_GET_NO;
00344 }
00345
00346
00347
00348 if (mn.tunnel_mode == API_TUNNEL_FULL_HA) {
00349 DEBUG(DEBUG_AGENTADV, "Direct connection to the HA - ignoring "
00350 "FA agent advertisement\n");
00351 return FA_GET_NO;
00352 }
00353
00354
00355 max_adv = NULL;
00356 max_priority = 0;
00357 timerclear(&max_adv_time);
00358
00359 if (mn.agentadv == NULL) {
00360 DEBUG(DEBUG_AGENTADV, "update_fa_decision: mn.agentadv == "
00361 "NULL\n");
00362 return FA_GET_NO;
00363 }
00364 hashtable_iterator(mn.agentadv, find_fa_with_priority, NULL);
00365 if (max_adv == NULL) {
00366 DEBUG(DEBUG_AGENTADV, "update_fa_decision: Can't find FA!\n");
00367 return FA_GET_NO;
00368 }
00369
00370 if (mn.current_adv != NULL) {
00371 DEBUG(DEBUG_AGENTADV, "CURRENT: %s ",
00372 inet_ntoa(mn.current_adv->addr));
00373 DEBUG(DEBUG_AGENTADV, "MAX: %s\n",
00374 inet_ntoa(max_adv->addr));
00375 }
00376
00377
00378
00379
00380
00381 if (mn.current_adv != NULL)
00382 same_prio = mn.current_adv->priority_degraded == max_priority;
00383 else
00384 same_prio = FALSE;
00385
00386 if (monitor_check_policy(NEWEST_ADV_BIT))
00387 same_prio = FALSE;
00388 if (mn.current_adv != NULL &&
00389 (same_prio ||
00390 mn.current_adv->addr.s_addr == max_adv->addr.s_addr)) {
00391 return FA_GET_SAME;
00392 }
00393
00394
00395 mn.fa_addr.s_addr = max_adv->addr.s_addr;
00396 memcpy(&mn.co_addr, (max_adv->adv.ext) + 1, sizeof(struct in_addr));
00397 mn.req_lifetime = ntohs(max_adv->adv.ext->reg_lifetime);
00398 if (mn.current_adv != NULL)
00399 mn.current_adv->in_use = 0;
00400 mn.current_adv = max_adv;
00401 memcpy(&mn.fa_dynamics_ext, &max_adv->adv.own_ext,
00402 sizeof(struct agent_adv_dynamics));
00403 gettimeofday(&timers[TIMER_ADV], NULL);
00404 timers[TIMER_ADV].tv_sec += ntohs(max_adv->adv.radv->lifetime);
00405
00406 return FA_GET_CHANGED;
00407 }
00408
00409
00410
00411
00412
00413
00414
00415 int get_fa(struct agentadv_data *adv)
00416 {
00417 struct event_FA data;
00418
00419 data.adv = adv;
00420 data.hash = mn.agentadv;
00421 handler_call_all(FA_GET, &data);
00422 return update_fa_decision(adv);
00423 }
00424
00425
00426 static void handle_fa_adv(struct agentadv_data *adv)
00427 {
00428 struct in_addr _fa_addr, _highest_fa_addr;
00429 int chg = 0;
00430 struct event_FA data;
00431
00432 UNALIGNED_(&_fa_addr.s_addr, &adv->adv.ip->saddr);
00433 memcpy(&_highest_fa_addr, adv->adv.ext + 1, sizeof(struct in_addr));
00434
00435 DEBUG(DEBUG_AGENTADV, "FA advertisement - fa_addr=%s, ",
00436 inet_ntoa(_fa_addr));
00437 DEBUG(DEBUG_AGENTADV, "highest_fa_addr=%s\n",
00438 inet_ntoa(_highest_fa_addr));
00439
00440
00441
00442 data.adv = adv;
00443 data.hash = mn.agentadv;
00444 handler_call_all(FA_ADV_RECEIVE, &data);
00445
00446 if (mn.state == MN_CONNECTED && ntohs(adv->adv.ext->seq) < 256 &&
00447 adv->last_heard_seqn >= ntohs(adv->adv.ext->seq)) {
00448 adv->last_heard_seqn = ntohs(adv->adv.ext->seq);
00449 DEBUG(DEBUG_INFO, "FA rebooted - trying to reregister\n");
00450 request_tunnel(STATE_INIT, 0, 0);
00451 return;
00452 }
00453
00454
00455 adv->last_heard_seqn = ntohs(adv->adv.ext->seq);
00456
00457 switch (mn.state) {
00458 case MN_DISCONNECTED:
00459 break;
00460 case MN_AT_HOME:
00461 case MN_CLOSE_FOR_HOME:
00462
00463 if (mn.current_adv == NULL ||
00464 mn.current_adv->adv_type != MN_ADV_TYPE_OWN_HA ||
00465 adv->adv_type != MN_ADV_TYPE_FA)
00466 break;
00467 if (cmp_timeval(&adv->last, &mn.current_adv->expire) < 0 &&
00468 !monitor_check_policy(NEWEST_ADV_BIT))
00469 break;
00470
00471 DEBUG(DEBUG_INFO,
00472 "HA agentadv expired - trying to use FA (IP=%s)\n",
00473 inet_ntoa(adv->addr));
00474
00475 case MN_FIND_AGENT:
00476 case MN_PASSIVE_FIND:
00477 case MN_REQUEST_TUNNEL:
00478
00479
00480
00481
00482 chg = get_fa(adv);
00483 if (chg == FA_GET_CHANGED ||
00484 (chg == FA_GET_SAME && (real_tunnel_up == 0 ||
00485 mn.state != MN_REQUEST_TUNNEL)))
00486 request_tunnel(STATE_INIT, 0, 1);
00487 break;
00488 case MN_CONNECTED:
00489 chg = get_fa(adv);
00490
00491
00492
00493 if (chg == FA_GET_CHANGED) {
00494 request_tunnel(STATE_INIT, 0, 1);
00495 }
00496 break;
00497 default:
00498 break;
00499 }
00500 }
00501
00502
00503
00504
00505
00506
00507
00508 int check_expired_agent_advs(void)
00509 {
00510 static time_t prev_check = 0;
00511 struct timeval now;
00512 struct timeval tmp;
00513
00514 gettimeofday(&now, NULL);
00515
00516 if (prev_check <= now.tv_sec &&
00517 now.tv_sec < prev_check + ADV_CLEANUP_FREQ)
00518 return 0;
00519
00520 prev_check = now.tv_sec;
00521 current_agent_expired = FALSE;
00522
00523 tmp.tv_sec = now.tv_sec - ADV_EXTRA_TIME;
00524 tmp.tv_usec = now.tv_usec;
00525 DEBUG(DEBUG_AGENTADV,
00526 "\tchecking for expired agentadv data\n");
00527 hashtable_iterator(mn.agentadv, clean_agentadv, &tmp);
00528
00529 if (current_agent_expired) {
00530 DEBUG(DEBUG_AGENTADV, "Current agent's adv. expired - try to "
00531 "find new FA\n");
00532 return 1;
00533 }
00534
00535 return 0;
00536 }
00537
00538
00539
00540
00541
00542
00543 int handle_icmp(struct interface_data *iface)
00544 {
00545 char buf[MAX_ADV_MSG];
00546 struct adv_extensions ext;
00547 struct agentadv_key key;
00548 struct agentadv_data *adv;
00549 int res;
00550
00551
00552 res = handle_icmp_adv(iface->s_adv, buf, MAX_ADV_MSG, &ext);
00553 if (res == -1) {
00554
00555 check_interfaces(mn.iface, MAX_INTERFACES);
00556 }
00557
00558 if (res != 1)
00559 return 0;
00560
00561 UNALIGNED_(&key.addr.s_addr, &ext.ip->saddr);
00562
00563 #ifndef MN_FA_FORCE_AFTER_AGENTADV_UPDATE
00564 if (mn.force_fa_addr.s_addr != 0 &&
00565 mn.force_fa_addr.s_addr != key.addr.s_addr) {
00566 DEBUG(DEBUG_AGENTADV,
00567 "handle_icmp: force_fa set to %s - ignoring this FA(1)",
00568 inet_ntoa(mn.force_fa_addr));
00569 DEBUG(DEBUG_AGENTADV, " (%s)\n", inet_ntoa(key.addr));
00570 return 0;
00571 }
00572 #endif
00573
00574 key.ifindex = iface->index;
00575
00576 if (ntohs(ext.ext->reg_lifetime) < MIN_ALLOWED_LIFETIME) {
00577 DEBUG(DEBUG_AGENTADV,
00578 "handle_icmp: too small lifetime advertised (%d) "
00579 "- ignoring this FA (%s)\n",
00580 ntohs(ext.ext->reg_lifetime), inet_ntoa(key.addr));
00581 return 0;
00582 }
00583
00584
00585 adv = (struct agentadv_data *)
00586 hashtable_fetch(mn.agentadv, addr_hash, &key, addr_cmp);
00587 if (adv == NULL) {
00588
00589 adv = (struct agentadv_data *)
00590 malloc(sizeof(struct agentadv_data));
00591 if (adv == NULL) {
00592 DEBUG(DEBUG_AGENTADV,
00593 "handle_icmp: no memory for agentadv data\n");
00594 return 0;
00595 }
00596 DEBUG(DEBUG_AGENTADV, "Added new agentadv entry for %s\n",
00597 inet_ntoa(key.addr));
00598 memset(adv, 0, sizeof(struct agentadv_data));
00599 list_init_node(&adv->node);
00600 UNALIGNED_(&adv->addr.s_addr, &ext.ip->saddr);
00601 adv->counter = 1;
00602 adv->prio_degrade_percent = 0;
00603 if (!hashtable_add(mn.agentadv, addr_hash, &key.addr,
00604 &adv->node)) {
00605 DEBUG(DEBUG_AGENTADV,
00606 "handle_icmp: hashtable_add failed\n");
00607 free(adv);
00608 return 0;
00609 }
00610 } else {
00611
00612 DEBUG(DEBUG_AGENTADV, "Used old agentadv entry for %s\n",
00613 inet_ntoa(key.addr));
00614 adv->counter++;
00615
00616 if (adv->prio_degrade_percent > 0)
00617 adv->prio_degrade_percent = (int)
00618 (adv->prio_degrade_percent*
00619 DEFAULT_PRIO_DEGRADE_DEGRADE);
00620
00621 if (adv->reg_failed && ext.ext->opts != adv->adv.ext->opts) {
00622 DEBUG(DEBUG_AGENTADV,
00623 "Reseting reg_failed for this FA\n");
00624 adv->reg_failed = 0;
00625 }
00626 }
00627
00628 adv->ifindex = iface->index;
00629 dynamics_strlcpy(adv->ifname, iface->device, sizeof(adv->ifname));
00630 gettimeofday(&adv->last, NULL);
00631 adv->expire.tv_sec = adv->last.tv_sec + ntohs(ext.radv->lifetime) + 1;
00632 adv->expire.tv_usec = adv->last.tv_usec;
00633 DEBUG(DEBUG_AGENTADV, "\tnow=%li.%li, expire=%li.%li\n",
00634 adv->last.tv_sec, adv->last.tv_usec,
00635 adv->expire.tv_sec, adv->expire.tv_usec);
00636
00637 memcpy(adv->buf, buf, MAX_ADV_MSG);
00638 memcpy(&adv->adv, &ext, sizeof(struct adv_extensions));
00639 adv->adv.start = adv->buf;
00640 adv->adv.len = ext.len;
00641 adv->adv.coaddrs = ext.coaddrs;
00642 memcpy(&adv->adv.from, &ext.from, sizeof(ext.from));
00643 #define UPDATE_OFFSET(n) adv->adv.n = ext.n ? \
00644 (typeof(ext.n)) (((char *) ext.n) - ext.start + adv->adv.start) : NULL
00645 #ifndef DYN_TARGET_LINUX
00646 UPDATE_OFFSET(eth);
00647 #endif
00648 UPDATE_OFFSET(ip);
00649 UPDATE_OFFSET(radv);
00650 UPDATE_OFFSET(ext);
00651 UPDATE_OFFSET(prefix);
00652 UPDATE_OFFSET(own_ext);
00653 UPDATE_OFFSET(pubkey_hash);
00654 UPDATE_OFFSET(fa_nai);
00655 UPDATE_OFFSET(challenge);
00656
00657 #ifdef MN_FA_FORCE_AFTER_AGENTADV_UPDATE
00658 if (mn.force_fa_addr.s_addr != 0 &&
00659 mn.force_fa_addr.s_addr != key.addr.s_addr) {
00660 DEBUG(DEBUG_AGENTADV,
00661 "handle_icmp: force_fa set to %s - ignoring this FA(2)",
00662 inet_ntoa(mn.force_fa_addr));
00663 DEBUG(DEBUG_AGENTADV, " (%s)\n", inet_ntoa(key.addr));
00664 return 0;
00665 }
00666 #endif
00667
00668 if (ntohs(ext.ext->opts) & AGENT_ADV_HOME_AGENT) {
00669 adv->adv_type = MN_ADV_TYPE_HA;
00670 handle_home_adv(adv);
00671 }
00672 if (ntohs(ext.ext->opts) & AGENT_ADV_FOREIGN_AGENT) {
00673 if (adv->adv_type != MN_ADV_TYPE_OWN_HA)
00674 adv->adv_type = MN_ADV_TYPE_FA;
00675 if (ext.coaddrs <= 0)
00676 DEBUG(DEBUG_AGENTADV,
00677 "handle_icmp: FA adv with no COAs - ignoring\n");
00678 else
00679 handle_fa_adv(adv);
00680 }
00681
00682 return 0;
00683 }