diff -Nur mipv6-daemon-umip-0.4-nepl/include/netinet/ip6mh.h mipv6-daemon-umip-0.4-nepl-mcoa/include/netinet/ip6mh.h --- mipv6-daemon-umip-0.4-nepl/include/netinet/ip6mh.h 2007-10-22 15:15:10.000000000 +0200 +++ mipv6-daemon-umip-0.4-nepl-mcoa/include/netinet/ip6mh.h 2007-10-23 10:30:03.000000000 +0200 @@ -146,6 +146,25 @@ } __attribute__ ((packed)); /* + * MCoA registration + * Binding Unique Identifier sub-option + */ +struct ip6_mh_opt_bid { + uint8_t ip6mobid_type; + uint8_t ip6mobid_len; + uint16_t ip6mobid_bid; + uint8_t ip6mobid_priority; + uint8_t ip6mobid_reserved; +} __attribute__((packed)); + +/* Binding Unique Identifier sub-option flags */ +#if BYTE_ORDER == BIG_ENDIAN +#define IP6_OPT_BID_BULK 0x80 /* Bulk Registration */ +#else /* BYTE_ORDER == LITTLE_ENDIAN */ +#define IP6_OPT_BID_BULK 0x80 /* Bulk Registration */ +#endif + +/* * Mobility Header Message Types */ #define IP6_MH_TYPE_BRR 0 /* Binding Refresh Request */ @@ -167,6 +186,7 @@ #define IP6_MHOPT_NONCEID 0x04 /* Nonce Index */ #define IP6_MHOPT_BAUTH 0x05 /* Binding Auth Data */ #define IP6_MHOPT_MOB_NET_PRFX 0x06 /* Mobile Network Prefix */ +#define IP6_MHOPT_BID 0x07 /* Binding Unique Identifier */ /* * Status values accompanied with Mobility Binding Acknowledgement @@ -191,6 +211,9 @@ #define IP6_MH_BAS_INVAL_PRFX 141 /* Invalid Prefix */ #define IP6_MH_BAS_NOT_AUTH_FOR_PRFX 142 /* Not Authorized for Prefix */ #define IP6_MH_BAS_FWDING_FAILED 143 /* Forwarding Setup failed */ +#define IP6_MH_BAS_CONFLICT_BINDING 144 /* (TBD) MCoA Conflict between bindings */ +#define IP6_MH_BAS_MCOA_BULK_FAILED 145 /* (TBD) MCoA Bulk registration failed */ + /* * Status values for the Binding Error mobility messages */ diff -Nur mipv6-daemon-umip-0.4-nepl/man/mip6d.conf.tmpl mipv6-daemon-umip-0.4-nepl-mcoa/man/mip6d.conf.tmpl --- mipv6-daemon-umip-0.4-nepl/man/mip6d.conf.tmpl 2007-10-22 15:15:10.000000000 +0200 +++ mipv6-daemon-umip-0.4-nepl-mcoa/man/mip6d.conf.tmpl 2007-10-23 10:30:03.000000000 +0200 @@ -57,6 +57,9 @@ .BR "Interface " "name" " {" .BR " MnIfPreference " "number" ";" .BR " IfType " "CN | HA | MN" ";" +.BR " Bid " "number" ";" +.BR " BidPriority " "number" ";" +.BR " Reliable " "boolean" ";" .B } .fi @@ -74,9 +77,10 @@ sets the interface preference value for an interface in a multi-homed Mobile Node. The most preferred intefaces have preference 1, the second most preferred have 2, etc. A preference of zero means the -interface will not be used. +interface will not be used. Do not use this option if you use the Multiple +Care-of Address registration on your Mobile Node with this interface. -Default: 5 +Default: 0 .B IfType overrides the default node behavior for this interface. If a MN doesn't @@ -86,6 +90,43 @@ Default: same as .B NodeConfig +.B Bid +Sets the BID for this interface. The BID must be unique within all +interface definitions. This BID will be used as an Interface Identifier +that the user can use for policy routing. If this option is defined, +also set the +.B BidPriority +option, and do not set the +.B MnIfPreference +option. +.B Bid +must be set between 1 and 251 (this is one of the current limitation +with this MCoA implementation). + +Default: 0 (no BID) + +.B BidPriority +Sets the priority of the binding (the higher the better). When no +policies are define for some flows, the binding with the higher +preference is used. If this option is defined, do not set the +.B MnIfPreference +option for this interface. +.B BidPriority +must be set between 1 and 255. + +Default: 0 + +.B Reliable +allows you to specify if the interface can be considered as reliable +when using MCoA. If set to false, the traffic will be redirected to +another interface during the BU-BAck exchange. You can thus use the +BU as a heartbeat (by specifying a small +.B MnMaxHaBindingLife +), which can be pretty useful when one of your MR-HA path is not +reliable: traffic is redirected to the interface with the best +priority as long as no BAck is received. + +Default: true .TP .BR "UseMnHaIPsec " "boolean" ";" @@ -191,6 +232,13 @@ Default: disabled; .TP +.BR "HaAcceptMCoAReg enabled | disabled" + +Indicates if the HA accepts Multiple Care-of Addresses registration. + +Default: disabled; + +.TP .BR "HaServedPrefix " "prefix/length" ";" Prefix is an IPv6 prefix and length is the prefix length. Defines the whole @@ -199,7 +247,7 @@ Mobile Network Prefixes, instead of one of the home link prefixes. .TP -.BR "BindingAclPolicy " "address MNP list " "allow | deny" +.BR "BindingAclPolicy " "address MNP list " "MCoAReg | NoMCoAReg " "allow | deny" Defines if a MN is allowed to register with the HA or not. The home address of the MN is given in the address field. The mobile network prefixes @@ -207,6 +255,12 @@ be an empty string or a comma separated list of network prefixes enclosed in braces, for example: .B "(3ffe:2620:6:3::/64, 3ffe:2620:6:4::/64)" +The HA can forbid or allow the MN to register Multiple Care-of Addresses by +setting the +.B NoMCoAReg +or +.B MCoAReg +flag. .TP .BR "DefaultBindingAclPolicy allow | deny" @@ -368,6 +422,31 @@ Default: disabled .TP +.BR "RegMultipleCoA enabled | disabled" + +Defines if the Mobile Node should try to register Multiple Care-of +Addresses to its Home Agent. If enabled, use also the +.B IfMultipleCoA +option. If disabled, the node will only register one CoA at a time. + +Default: disabled; + +.TP +.BR "IfMultipleCoA " "name1, name2, ..." ";" + +Defines the list of interfaces that will be used for Multiple Care-of +Addresses registration. This list must be composed with the names +(enclosed in double quotes) of the interfaces (eg. "eth1"), separated +with a coma. Each interface listed must have an +.B Interface +definition properly filled with the Multiple Care-of Address options +( +.B Bid +and +.B BidPriority +). + +.TP The route optimization policies are of the form: .TP @@ -389,7 +468,7 @@ .SH EXAMPLES .TP -.BR "A NEMO Home Agent example:" +.BR "A NEMO Home Agent example with MCoA support:" .nf NodeConfig HA; @@ -397,18 +476,19 @@ Interface "eth0"; HaAcceptMobRtr enabled; +HaAcceptMCoAReg enabled; HaServedPrefix 3ffe:2620:6::/48; DefaultBindingAclPolicy deny; -BindingAclPolicy 3ffe:2620:6:1::1234 (3ffe:2620:6:2::/64, 3ffe:2620:6:3::/64) allow; +BindingAclPolicy 3ffe:2620:6:1::1234 (3ffe:2620:6:2::/64, 3ffe:2620:6:3::/64) MCoAReg allow; BindingAclPolicy 3ffe:2620:6:1::1235 allow; UseMnHaIPsec disabled; .fi .TP -.BR "A NEMO Mobile Router example:" +.BR "A NEMO Mobile Router example with MCoA support:" .nf NodeConfig MN; @@ -416,7 +496,15 @@ DoRouteOptimizationCN disabled; DoRouteOptimizationMN disabled; -Interface "eth0"; +Interface "eth0" { + Bid 111; + BidPriority 10; +}; + +Interface "eth1" { + Bid 222; + BidPriority 5; +}; MnRouterProbes 1; @@ -426,6 +514,8 @@ IsMobRtr enabled; HomeAgentAddress 3ffe:2620:6:1::1; HomeAddress 3ffe:2620:6:1::1234/64 (3ffe:2620:6:2::/64, 3ffe:2620:6:3::/64); + RegMultipleCoA enabled; + IfMultipleCoA "eth0", "eth1"; } UseMnHaIPsec disabled; diff -Nur mipv6-daemon-umip-0.4-nepl/NEWS.MCOA mipv6-daemon-umip-0.4-nepl-mcoa/NEWS.MCOA --- mipv6-daemon-umip-0.4-nepl/NEWS.MCOA 1970-01-01 01:00:00.000000000 +0100 +++ mipv6-daemon-umip-0.4-nepl-mcoa/NEWS.MCOA 2007-10-23 10:39:22.000000000 +0200 @@ -0,0 +1,96 @@ +mipv6-daemon-umip-0.4-nepl-mcoa-20071022.patch +News and Bugfixes list since mipv6-daemon-umip-0.3-nepl-mcoa-20070613.patch: + +* Ported the code to UMIP 0.4. MCoA now runs on a 2.6.23 kernel, + with an UMIP 0.4-based code for the userland. +* Merged a fix to the proxy ND code for the HA (provided by + Tobias HOF): the HA can now be used as legacy node on the + Home Link (whereas it only worked as a gateway before). + +----------------------------------------------------------------------- +mipv6-daemon-umip-0.3-nepl-mcoa-20070613.patch +News and Bugfixes list since nemo-0.2-mcoa-beta2-20060630: + +* Ported the code to UMIP. MCoA now runs on a 2.6.21.3 kernel, + with an UMIP-based code for the userland. +* MCoA is now provided as a patch for the UMIP userland. +* The MCoA kernel patch is not needed anymore as it has been + integrated in the mainline kernel. +* No new functionnalities were added. + +----------------------------------------------------------------------- +nemo-0.2-mcoa-beta3-20070118 +News and Bugfixes list since nemo-0.2-mcoa-beta2-20060630: + +* Base code: +- Userland runs on a 2.6.16 kernel +- Upgraded fwmark patch for 2.6.16 kernel +- MCoA userland code updated to nemo-20060725 + +* Features: +- New "Reliable" option that allows to use the BU as a + heartbeat, and to automatically divert the traffic to + another interface when one MR-HA path fails. + See the manpage for details. +- MPS/MPA can now be used with MCoA. +- When handover occurs on an interface, forwarded traffic is now + blocked only for the interface performing the handover (whereas + it was blocked globally before). The local traffic is also + blocked for the interface that performs the handover, whereas + it was blocked for the HoA interface before. +- Merged patch to support HoA from MNP (for Mobile Routers) +- Merged patch to support SIT tunnels +- Merged patch to fix the MR behaviour in the Home Network + (the MR was not correctly forwarding packets from the MNNs) + +* Bugfixes: +- The source address used to send the BU was sometimes outdated + because the CoA was not correctly updated in the XFRM kernel + state. +- When packets originated from the MR were sent to the HA, the + HA did not behave correctly because the source address of the + packet may not match the one in the kernel XFRM state (that + can only store one CoA). Thus the MR behaviour has been + slightly changed to NOT use HAO dest. option when sending + packet to the HA (This behaviour seems to respect the MIP6 + spec). +- When packets originated from the MR were sent to the HA, and + the MR handoffs, the HA still sent replied packets to the + old CoA (using Routing Header Type 2). The HA now updates + correctly the CoA stored in the kernel, but as the kernel + can only store 1 CoA at a time, when the HA replies to the + MR the path may be asymetric. +- Added more checks in the configuration file grammar to + avoid configuration errors for MCoA. +- Solved infinite loop when interface was declared as Interface + but not in IfMultipleCoA. +- Fixed some bugs when MCoA was disabled. It now works fine when + not using MCoA. Interface prefence works also fine. +- Fixed bugs when one entry was invalidated: rules are now + correctly deleted. +- Fixed bugs when choosing the default rule: invalid BULE are + not looked up when searching for the default rule. +- Improved the MR and HA behaviour regarding error management. + + +----------------------------------------------------------------------- +nemo-0.2-mcoa-beta2-20060630 +News and Bugfixes list since nemo-0.2-mcoa-beta1-20060531: + +- The MR was in some cases installing the same rules twice. +- The MR had a problem to install some rules after recovering a failure. +- The MR was stuck in an infinite loop at startup if MCoA was disabled +- The MR could not clean properly tunnels if registration failed with HA +- Policy issues when Returning home with a usual MR but using MCoA code +- When the MR only have 1 active interface, handoff policy is different + (rules not erased and dereg bu not send during handoff time) +- When the MR receives a BAck without BID option, whereas it sent a BU + with a BID option, the MR only registers the prefered interface + +- If HA is not configured for MCoA, or MCoA not allowed for the HoA, then + BU is accepted but no BID option is sent in BAck + +- Merged the OptimisticHandoff patch +- Merged the PPP patch + +----------------------------------------------------------------------- diff -Nur mipv6-daemon-umip-0.4-nepl/README.MCOA mipv6-daemon-umip-0.4-nepl-mcoa/README.MCOA --- mipv6-daemon-umip-0.4-nepl/README.MCOA 1970-01-01 01:00:00.000000000 +0100 +++ mipv6-daemon-umip-0.4-nepl-mcoa/README.MCOA 2007-10-23 10:39:22.000000000 +0200 @@ -0,0 +1,154 @@ +-------------------------------------------- +Multiple CoA implementation on UMIP + +ver. 20071022 +-------------------------------------------- +Romain KUNTZ +Jean LORCHAT +Nautilus6 Project +-------------------------------------------- + + +1. What is Multiple CoA? +------------------------ + +Multiple CoA (Multiple Care-of Addresses Registration, MCoA) allows +a Mobile Node to register multiple Care-of Addresses at the same +time to its Home Agent. Main benefits, amongst others, are policy +routing and fault tolerance for the Mobile Router. + +The current implementation is based on +draft-ietf-monami6-multiplecoa-01 and is shipped as a tarball +that contains kernel patches and the userland. + +This implementation is based on UMIP, a Mobility platform on Linux. +More information about UMIP can be found on + + + +2. Limitations +-------------- + +Due to its early stage of development, the MCoA implementation still +have some important limitations. Please read them carefully to +understand what you cannot do with this implementation: + +- IPsec is not yet supported. It will in the future, but we now + concentrate on the non-IPsec part. + +- Returning home is not supported. Do not try to boot your MN at home + or to return home with one of its interface, this will not work. + This feature will be available in the future. + +- Once the MN has established multiple tunnels, the policy routing + only works FOR THE FORWARDED TRAFFIC. The traffic generated by the + MN itself will not be routed according to your policies. Only the + traffic from the MNN, routed via the Mobile Router, will be routed + according to the policies you installed on the MR. You can expect + some improvements in the next releases. + +- CN are not supported, ie the MN cannot register multiple CoA to + the CN. + +- The BID for each interface must be chosen between 1 and 251. This + limitation is due to the implementation design. We will try to + improve this point in the future. + +- This implementation does not support yet the Bulk Registration, + mainly because Bulk registration is still under discussion at + the IETF. + + +3. Known Bugs +------------- + +- Binding Refresh Request do not include the BID option +- Binding Error do not include the BID option +- The BU is sometimes sent via another interface than expected. +- Certainly many others! + +4. Instructions +--------------- + +a. Testbed Setup +---------------- + +Testbed setup, kernel and userlan compilation, configuration +files etc. are explained in the NEPL Howto available on: +http://www.nautilus6.org/doc/nepl-howto/ + +Please check this document (especially section 6) to setup +your MCoA testbed. + +b. Policy Routing +----------------- + +Once your MN has registered multiple CoA to your Home Agent, it can +use its multiple MN-HA tunnel for policy routing. + +The policy routing part is managed with ip6tables. Be sure to install +this tool on both your HA and MN. + +- How does it work? +------------------- + +The BID that you assigned to each interface will be used to mark the +packets. Packets marked with BID X will be routed through the +interface whose BID is X. If this interface is not available (because +it is down), then the packet will be routed through the most prefered +interface (the one with the best BidPriority). + +NOTE: +At the moment, ONLY PACKETS FORWARDED BY THE MR (ie packets sent by MNN) +can benefit from the policy routing. Packets generated by the MR itself +will not be routed according to your rules. We are currently working to +improve the current situation to also allow the MR to benefit from the +policy routing. + + +- How to mark a packet? +----------------------- + +Use the ip6tables tool and the MARK target to mark your packets with +the BID. Ip6tables rules must be done in the PREROUTING chain, in the +mangle table. + +For example, on the MN, to mark as 100 all icmpv6 packets whose +destination is 2001:a:b::1000, you can do: + +ip6tables -A PREROUTING -t mangle + -p icmpv6 --destination 2001:a:b::1000 + -j MARK --set-mark 100 + +Those packets will be sent through the interface whose BID is 100. +You will also need to create the "symetric" rule on the Home Agent: + +ip6tables -A PREROUTING -t mangle + -p icmpv6 --source 2001:a:b::1000 + -j MARK --set-mark 100 + +As you see, one current limitation is that each rules created on the +MN must be also created on the Home Agent. We plan to support in the +future some policy exchange mechanism between the MN and the HA in +order to configure automatically the HA. + +Read carefully the ip6tables manpage. You will be able to create +rules based on many parameters, and thus create a very subtle policy +routing on your MN. + + +5. Support +---------- + +The MCoA implementation is not supported by the UMIP team, so please +do not complain on their Mailing List. If you have any questions +regarding this work, or any BUG report, feel free to subscribe to +our support and announce mailing lists: + +http://www.nautilus6.org/ml.php + +-------------------------------------------- +Romain KUNTZ +Jean LORCHAT +Nautilus6 Project +-------------------------------------------- diff -Nur mipv6-daemon-umip-0.4-nepl/src/bcache.c mipv6-daemon-umip-0.4-nepl-mcoa/src/bcache.c --- mipv6-daemon-umip-0.4-nepl/src/bcache.c 2007-10-22 15:15:10.000000000 +0200 +++ mipv6-daemon-umip-0.4-nepl-mcoa/src/bcache.c 2007-11-05 14:18:59.000000000 +0100 @@ -39,6 +39,7 @@ #include "mh.h" #include "cn.h" #include "vt.h" +#include "prefix.h" #define BCACHE_BUCKETS 32 @@ -48,6 +49,8 @@ pthread_rwlock_t bc_lock; /* Protects binding cache */ +int bce_next_entry(void *bce_v, void *next_bce_v); + /** * get_bcache_count - returns number of home and cache entries * @type: HOMEREGENTRY, CACHEENTRY or ANY @@ -88,6 +91,12 @@ fprintf(out, " lifetime %ld\n ", e->lifetime.tv_sec); fprintf(out, " seqno %d\n", e->seqno); + if (e->bid) + fprintf(out, " BID = %d, Priority = %d\n", + e->bid, e->priority); + else + fprintf(out, " No BID assigned\n"); + fflush(out); } @@ -167,6 +176,7 @@ * @hinfo: home address info, optional if our_addr is present * @our_addr: local address (home address) * @peer_addr: address of CN + * @bid: BID of the bce * * Returns reference to non-null entry on success and null on failure. * If caller adjusts lifetime of entry, caller must call @@ -174,7 +184,8 @@ * call bcache_release_entry() after entry is not used anymore. **/ struct bcentry *bcache_get(const struct in6_addr *our_addr, - const struct in6_addr *peer_addr) + const struct in6_addr *peer_addr, + uint16_t bid) { struct bcentry *bce; @@ -182,7 +193,7 @@ pthread_rwlock_rdlock(&bc_lock); - bce = hash_get(&bc_hash, our_addr, peer_addr); + bce = hash_get(&bc_hash, our_addr, peer_addr, bid); if (bce) pthread_rwlock_wrlock(&bce->lock); @@ -206,16 +217,19 @@ * bce_type - get type of binding cache entry * @our_addr: our IPv6 address * @peer_addr: peer's IPv6 address + * @bid: BID of the bce * * Looks up entry from binding cache and returns its type. If not * found, returns -%ENOENT. **/ -int bce_type(const struct in6_addr *our_addr, const struct in6_addr *peer_addr) +int bce_type(const struct in6_addr *our_addr, + const struct in6_addr *peer_addr, + uint16_t bid) { struct bcentry *bce; int type; - bce = bcache_get(our_addr, peer_addr); + bce = bcache_get(our_addr, peer_addr, bid); if (bce == NULL) return -ENOENT; @@ -230,7 +244,9 @@ { int ret; - ret = hash_add(&bc_hash, bce, &bce->our_addr, &bce->peer_addr); + ret = hash_add(&bc_hash, bce, &bce->our_addr, + &bce->peer_addr, &bce->bid); + if (ret) return ret; @@ -248,7 +264,15 @@ bce->type == BCE_HOMEREG ? bce->lifetime : tmp, expires); add_task_abs(&expires, &bce->tqe, _expire); - xfrm_add_bce(&bce->our_addr, &bce->peer_addr, &bce->coa, 0); + /* MCoA + * Due to the BID, the BC may have several entries with the + * same peer_addr. Thus we add states and policies only if + * there are no entries yet for the our_addr/peer_addr pair + * (see bce->xfrm_create). + */ + xfrm_add_bce(&bce->our_addr, &bce->peer_addr, &bce->coa, 0, + bce->xfrm_create); + return 0; } @@ -316,22 +340,26 @@ } tsadd(expires, bce->add_time, expires); add_task_abs(&expires, &bce->tqe, _expire); - xfrm_add_bce(&bce->our_addr, &bce->peer_addr, &bce->coa, 1); + xfrm_add_bce(&bce->our_addr, &bce->peer_addr, &bce->coa, 1, 0); return 0; } /** * bcache_delete - deletes a bul entry + * If bid == 0, all entries that match our_addr/peer_addr are deleted **/ void bcache_delete(const struct in6_addr *our_addr, - const struct in6_addr *peer_addr) + const struct in6_addr *peer_addr, + uint16_t bid) { struct bcentry *bce; pthread_rwlock_wrlock(&bc_lock); - bce = hash_get(&bc_hash, our_addr, peer_addr); - if (bce) - bce_delete(bce, 0); + while ((bce = hash_get(&bc_hash, our_addr, peer_addr, bid))) { + if (bce) + bce_delete(bce, 0); + } + pthread_rwlock_unlock(&bc_lock); } @@ -364,8 +392,27 @@ pthread_rwlock_wrlock(&bce->lock); if (bce->type != BCE_DAD) { del_task(&bce->tqe); - if (bce->type != BCE_NONCE_BLOCK) - xfrm_del_bce(&bce->our_addr, &bce->peer_addr); + if (bce->type != BCE_NONCE_BLOCK) { + /* MCoA + * Due to the BID, the BC can have several entries + * with the same our_addr/peer_addr pair. We delete + * the xfrm data if we do not have any BCE for + * this peer_addr anymore, or we update it (we need + * to update the kernel's CoA to a valid one). + */ + if(mcoa_bce_count(&bce->peer_addr) == 1) + xfrm_del_bce(&bce->our_addr, &bce->peer_addr); + else { + struct bcentry *next = bce; + /* Look for another valid BCE */ + bcache_iterate_in(&bce->peer_addr, + bce_next_entry, &next); + /* Replace the CoA */ + xfrm_add_bce(&next->our_addr, + &next->peer_addr, + &next->coa, 1, 0); + } + } } if (bce->cleanup) bce->cleanup(bce); @@ -380,7 +427,7 @@ } } bcache_count--; - hash_delete(&bc_hash, &bce->our_addr, &bce->peer_addr); + hash_delete(&bc_hash, &bce->our_addr, &bce->peer_addr, bce->bid); pthread_rwlock_unlock(&bce->lock); bcache_free(bce); } @@ -424,3 +471,105 @@ pthread_rwlock_unlock(&bc_lock); return err; } + +/** + * bcache_iterate_in - apply function to every BC entry that match peer_addr + * @peer_addr: the peer_addr of the hash + * @func: function to apply + * @arg: extra data for @func + * + * Iterates through binding cache, calling @func for each entry that + * match peer_addr. Extra data may be passed to @func in @arg. @func + * takes a bcentry as its first argument and @arg as second argument. + * + * Be sure that bc_lock is locked before using this function, and + * unlocked after using it. + **/ +int bcache_iterate_in(const struct in6_addr *peer_addr, + int (* func)(void *, void *), + void *arg) +{ + int err; + err = hash_iterate_in(&bc_hash, peer_addr, func, arg); + return err; +} + +int bce_highest_priority(void *bce_v, void *best_bce_v) +{ + struct bcentry *bce = bce_v; + struct bcentry **best_bce = best_bce_v; + + assert(bce); + assert(best_bce); + + if (!(*best_bce) || (bce->priority > (*best_bce)->priority)) + (*best_bce) = bce; + + return 0; +} + +int bce_next_entry(void *bce_v, void *next_bce_v) +{ + struct bcentry *bce = bce_v; + struct bcentry **next_bce = next_bce_v; + + assert(bce); + assert(next_bce); + + if (bce->bid != (*next_bce)->bid) { + (*next_bce) = bce; + return 1; + } + + return 0; +} + +int mcoa_bce_inc_counter(void *bule_v, void *counter_v) +{ + int *counter = counter_v; + (*counter)++; + return 0; +} + +int mcoa_bce_count(const struct in6_addr *peer_addr) +{ + int count = 0; + bcache_iterate_in(peer_addr, mcoa_bce_inc_counter, &count); + + return count; +} + +/** + * bcache_add_mnps - Add MNPs to the bcache entry + * @bce: the bcache entry where we add the MNPs + * @mnps: the mnp list to add to the bce + * + * MNPs are added only if the entry does not exists yet in the BCE's + * MNP list. Returns the number of MNPs added. + **/ +int bce_add_mnps(struct bcentry *bce, struct list_head *mnps) +{ + struct list_head *list; + int counter = 0; + + list_for_each(list, mnps) { + struct prefix_list_entry *p1, *p2; + p1 = list_entry(list, struct prefix_list_entry, list); + if (prefix_list_find(&bce->mob_net_prefixes, + &p1->ple_prefix, p1->ple_plen)) + continue; + + p2 = malloc(sizeof(struct prefix_list_entry)); + if (p2 == NULL) { + prefix_list_free(&bce->mob_net_prefixes); + return -1; + } + + memcpy(p2, p1, sizeof(struct prefix_list_entry)); + list_add_tail(&p2->list, &bce->mob_net_prefixes); + counter++; + } + + return counter; +} + diff -Nur mipv6-daemon-umip-0.4-nepl/src/bcache.h mipv6-daemon-umip-0.4-nepl-mcoa/src/bcache.h --- mipv6-daemon-umip-0.4-nepl/src/bcache.h 2007-10-22 15:15:10.000000000 +0200 +++ mipv6-daemon-umip-0.4-nepl-mcoa/src/bcache.h 2007-10-23 10:30:03.000000000 +0200 @@ -20,6 +20,12 @@ uint16_t nonce_hoa; uint16_t type; /* Entry type */ uint16_t nemo_type; /* NEMO registration type */ + uint16_t bid; /* MCoA: Binding Identifier */ + uint8_t priority; /* MCoA: BID priority */ + int table; /* MCoA: Table id to where are stored + the routes */ + int xfrm_create; /* MCoA: If xfrm policies/states must + be created */ int unreach; /* ICMP dest unreach count */ int tunnel; /* Tunnel interface index */ int link; /* Home link interface index */ @@ -48,12 +54,16 @@ #define BCE_NEMO_IMPLICIT 2 #define BCE_NEMO_DYNAMIC 3 +#define BCE_TABLE_MIN 1 +#define BCE_TABLE_MAX 251 + struct bcentry *bcache_alloc(int type); void bcache_free(struct bcentry *bce); struct bcentry *bcache_get(const struct in6_addr *our_addr, - const struct in6_addr *peer_addr); + const struct in6_addr *peer_addr, + uint16_t bid); int bcache_add(struct bcentry *bce); @@ -63,28 +73,38 @@ int bcache_update_expire(struct bcentry *bce); void bcache_delete(const struct in6_addr *our_addr, - const struct in6_addr *peer_addr); + const struct in6_addr *peer_addr, + uint16_t bid); int bcache_init(void); void bcache_flush(void); void bcache_cleanup(void); int bcache_iterate(int (* func)(void *, void *), void *arg); +int bcache_iterate_in(const struct in6_addr *peer_addr, + int (* func)(void *, void *), + void *arg); void bcache_release_entry(struct bcentry *bce); int bce_type(const struct in6_addr *our_addr, - const struct in6_addr *peer_addr); + const struct in6_addr *peer_addr, + uint16_t bid); static inline int bce_exists(const struct in6_addr *our_addr, - const struct in6_addr *peer_addr) + const struct in6_addr *peer_addr, + uint16_t bid) { - return bce_type(our_addr, peer_addr) >= BCE_NONCE_BLOCK; + return bce_type(our_addr, peer_addr, bid) >= BCE_NONCE_BLOCK; } +int bce_add_mnps(struct bcentry *bce, struct list_head *mnps); +int bce_highest_priority(void *bce_v, void *best_bce_v); + void dump_bce(void *bce, void *os); int get_bcache_count(int type); +int mcoa_bce_count(const struct in6_addr *peer_addr); extern pthread_rwlock_t bc_lock; /* Protects binding cache */ diff -Nur mipv6-daemon-umip-0.4-nepl/src/bul.c mipv6-daemon-umip-0.4-nepl-mcoa/src/bul.c --- mipv6-daemon-umip-0.4-nepl/src/bul.c 2007-11-05 13:20:24.000000000 +0100 +++ mipv6-daemon-umip-0.4-nepl-mcoa/src/bul.c 2007-11-05 13:28:41.000000000 +0100 @@ -38,6 +38,8 @@ #include "xfrm.h" #include "debug.h" #include "retrout.h" +#include "tunnelctl.h" +#include "rtnl.h" #ifdef ENABLE_VT #include "vt.h" #endif @@ -55,7 +57,8 @@ static struct hash bul_hash; struct bulentry *create_bule(const struct in6_addr *hoa, - const struct in6_addr *cn_addr) + const struct in6_addr *cn_addr, + u_int16_t bid) { struct bulentry *bule; if ((bule = malloc(sizeof(*bule))) != NULL) { @@ -63,6 +66,11 @@ bule->hoa = *hoa; bule->last_coa = *hoa; bule->peer_addr = *cn_addr; + bule->bid = bid; + bule->if_tunnel = 0; + bule->rules = 0; + bule->home_block = 0; + bule->mcoa_dereg = 1; INIT_LIST_HEAD(&bule->tqe.list); bule->seq = random(); } @@ -97,6 +105,11 @@ NIP6ADDR(&e->peer_addr)); fprintf(out, " lifetime = %ld, ", e->lifetime.tv_sec); fprintf(out, " delay = %ld\n", tstomsec(e->delay)); + if (e->bid) + fprintf(out, " BID = %d, Priority = %d\n", + e->bid, e->priority); + else + fprintf(out, " No BID assigned\n"); fprintf(out, " flags: "); if (e->flags & IP6_MH_BU_HOME) fprintf(out, "IP6_MH_BU_HOME "); @@ -118,21 +131,23 @@ * @hinfo: home address info, optional if our_addr is present * @our_addr: local address (home address) * @peer_addr: address of CN + * @bid: Binding Unique Identifier * * Returns non-null entry on success and null on failure. Caller must * call del_task and add_task, if lifetime of the entry is changed. **/ struct bulentry *bul_get(struct home_addr_info *hinfo, const struct in6_addr *our_addr, - const struct in6_addr *peer_addr) + const struct in6_addr *peer_addr, + u_int16_t bid) { struct bulentry *bule; assert(hinfo || our_addr); if (hinfo) - bule = hash_get(&hinfo->bul, NULL, peer_addr); - else bule = hash_get(&bul_hash, our_addr, peer_addr); + bule = hash_get(&hinfo->bul, NULL, peer_addr, bid); + else bule = hash_get(&bul_hash, our_addr, peer_addr, bid); return bule; } @@ -175,9 +190,11 @@ assert(bule && tsisset(bule->lifetime) && hai); - if ((ret = hash_add(&bul_hash, bule, &bule->hoa, &bule->peer_addr)) < 0) + if ((ret = hash_add(&bul_hash, bule, &bule->hoa, &bule->peer_addr, + &bule->bid)) < 0) return ret; - if ((ret = hash_add(&hai->bul, bule, NULL, &bule->peer_addr)) < 0) + if ((ret = hash_add(&hai->bul, bule, NULL, &bule->peer_addr, + &bule->bid)) < 0) goto bul_free; clock_gettime(CLOCK_REALTIME, &bule->lastsent); @@ -197,9 +214,9 @@ add_task_abs(&timer_expire, &bule->tqe, bule->callback); return 0; home_bul_free: - hash_delete(&hai->bul, &bule->hoa, &bule->peer_addr); + hash_delete(&hai->bul, &bule->hoa, &bule->peer_addr, bule->bid); bul_free: - hash_delete(&bul_hash, &bule->hoa, &bule->peer_addr); + hash_delete(&bul_hash, &bule->hoa, &bule->peer_addr, bule->bid); return ret; } @@ -209,8 +226,8 @@ struct home_addr_info *hai = bule->home; del_task(&bule->tqe); - hash_delete(&bul_hash, &bule->hoa, &bule->peer_addr); - hash_delete(&hai->bul, NULL, &bule->peer_addr); + hash_delete(&bul_hash, &bule->hoa, &bule->peer_addr, bule->bid); + hash_delete(&hai->bul, NULL, &bule->peer_addr, bule->bid); if (!IN6_ARE_ADDR_EQUAL(&bule->hoa, &bule->coa)) { bule->last_coa = bule->coa; @@ -222,6 +239,7 @@ if (!(bule->flags & IP6_MH_BU_HOME)) mn_rr_delete_bule(bule); } + if (bule->flags & IP6_MH_BU_HOME) { if (bule->type == UNREACH_ENTRY) { pthread_mutex_lock(&hai->ha_list.c_lock); @@ -232,16 +250,28 @@ } else { if (hai->home_block & HOME_LINK_BLOCK) xfrm_unblock_link(hai); + if (bule->home_block & HOME_LINK_BLOCK) + xfrm_unblock_bule_link(bule); if (hai->home_block & HOME_ADDR_BLOCK) xfrm_unblock_hoa(hai); if (hai->home_block & NEMO_RA_BLOCK) xfrm_unblock_ra(hai); if (hai->home_block & NEMO_FWD_BLOCK) xfrm_unblock_fwd(hai); + if (bule->home_block & NEMO_FWD_BLOCK) + bule_unblock_fwd(bule); } } while (bule->ext_cleanup) bule->ext_cleanup(bule); + + /* MCoA + * If MCoA is used, the tunnel related to this BULE + * has to be deleted. + */ + if (bule->home->reg_mcoa && bule->type != NON_MIP_CN_ENTRY) + tunnel_del(bule->if_tunnel, NULL, NULL); + dbg("Deleting bule\n"); dbg_func(bule, dump_bule); free_bule(bule); @@ -312,3 +342,129 @@ struct hash *tmp = h ? h : &bul_hash; return hash_iterate(tmp, func, arg); } + +/** + * bul_iterate_in - apply function to every BUL entry that match peer_addr + * @peer_addr: the peer_addr of the hash + * @func: function to apply + * @arg: extra data for @func + * + * Iterates through binding update list, calling @func for each entry that + * match peer_addr. Extra data may be passed to @func in @arg. @func + * takes a BUL entry as its first argument and @arg as second argument. + **/ +int bul_iterate_in(struct in6_addr *peer_addr, + int (* func)(void *, void *), + void *arg) +{ + return hash_iterate_in(&bul_hash, peer_addr, func, arg); +} + +int bul_highest_priority(void *bule_v, void *best_bule_v) +{ + struct bulentry *bule = bule_v; + struct bulentry **best_bule = best_bule_v; + + assert(bule); + assert(best_bule); + + if(bule->type == NON_MIP_CN_ENTRY || + bule->type == UNREACH_ENTRY) + return 0; + + if (!(*best_bule) + || (bule->priority > (*best_bule)->priority + && bule->rules)) + (*best_bule) = bule; + + return 0; +} + +int mcoa_bule_inc_counter(void *bule_v, void *counter_v) +{ + struct bulentry *bule = bule_v; + int *counter = counter_v; + assert(counter); + + if(bule->type != NON_MIP_CN_ENTRY) + (*counter)++; + return 0; +} + +/** + * mcoa_bule_count - count the number of entry (!NON_MIP_CN_ENTRY) + * with the same peer_addr. + * @bule: the bule where to pick up the peer address + **/ + +int mcoa_bule_count(struct bulentry *bule) +{ + int count = 0; + assert(bule); + bul_iterate_in(&bule->peer_addr, mcoa_bule_inc_counter, &count); + return count; +} + + +int mcoa_bule_setdereg(void *bule_v, void *dereg_v) +{ + int *dereg = dereg_v; + struct bulentry *bule = bule_v; + + assert(dereg); + bule->mcoa_dereg = *(dereg); + return 0; +} + +/** + * mcoa_bule_dereg - Set the BULE mcoa_dereg flag to value for + * all entries that match bule->peer_addr. + * @bule: the bule where to pick up the peer address + * @value: the value to set + **/ +int mcoa_bule_dereg(struct bulentry *bule, int value) +{ + bul_iterate_in(&bule->peer_addr, mcoa_bule_setdereg, &value); + return 0; +} + +/** + * bule_block_fwd - Block forwarding for all MNPs + * via the fwmark (BID) of the bule + * @bule: the bule + */ +void bule_block_fwd(struct bulentry *bule) +{ + struct list_head *l; + if (!bule->rules) + return; + bule->home_block |= NEMO_FWD_BLOCK; + list_for_each(l, &bule->home->mob_net_prefixes) { + struct prefix_list_entry *p = NULL; + p = list_entry(l, struct prefix_list_entry, list); + rule_add(NULL, 0, IP6_RULE_PRIO_MIP6_BLOCK, + /*fwmark*/ bule->bid, RTN_BLACKHOLE, + &p->ple_prefix, p->ple_plen, + &in6addr_any, 0, 0); + } +} + +/** + * bule_unblock_fwd - unblock forwarding for all MNPs + * via the fwmark (BID) of the bule + * @bule: the bule + */ +void bule_unblock_fwd(struct bulentry *bule) +{ + struct list_head *l; + bule->home_block &= ~NEMO_FWD_BLOCK; + list_for_each(l, &bule->home->mob_net_prefixes) { + struct prefix_list_entry *p = NULL; + p = list_entry(l, struct prefix_list_entry, list); + rule_del(NULL, 0, IP6_RULE_PRIO_MIP6_BLOCK, + /*fwmark*/ bule->bid, RTN_BLACKHOLE, + &p->ple_prefix, p->ple_plen, + &in6addr_any, 0, 0); + } +} + diff -Nur mipv6-daemon-umip-0.4-nepl/src/bul.h mipv6-daemon-umip-0.4-nepl-mcoa/src/bul.h --- mipv6-daemon-umip-0.4-nepl/src/bul.h 2007-10-22 15:15:10.000000000 +0200 +++ mipv6-daemon-umip-0.4-nepl-mcoa/src/bul.h 2007-10-23 10:30:03.000000000 +0200 @@ -10,6 +10,9 @@ #include "hash.h" #include "list.h" +#define BULE_TABLE_MIN 1 +#define BULE_TABLE_MAX 251 + struct home_addr_info; struct retrout_info { @@ -31,10 +34,16 @@ struct in6_addr hoa; struct in6_addr coa; /* care-of address of the sent BU */ int if_coa; + int if_tunnel; /* MCoA: Tunnel iface for the BCE */ + int table; /* MCoA: Routing table where are + stored the routes */ + int rules; /* MCoA: if rules are installed for + this BULE */ int type; /* BUL / NON_MIP_CN / UNREACH */ uint16_t seq; /* sequence number of the latest BU */ uint16_t flags; /* BU send flags */ - struct in6_addr last_coa; /* Last good coa */ + uint16_t bid; /* MCoA: Binding Identifier */ + struct in6_addr last_coa; /* Last good coa */ struct timespec lastsent; struct timespec lifetime; /* lifetime sent in this BU */ struct timespec delay; /* call back time in ms*/ @@ -47,7 +56,11 @@ uint8_t use_alt_coa; /* Whether to use alt. CoA option */ uint8_t dereg; /* for calculating BSA key */ uint8_t do_send_bu; /* send bu / not send bu */ - + uint8_t priority; /* MCoA: BID priority */ + uint8_t home_block; /* MCoA: NEMO_FWD_BLOCK, ... */ + uint8_t mcoa_dereg; /* MCoA: if BCE needs to be + deregistered with a BU */ + /* Information for return routability */ struct retrout_info rr; uint8_t Kbm[HMAC_SHA1_KEY_SIZE]; @@ -84,7 +97,8 @@ struct bulentry *bul_get(struct home_addr_info *hinfo, const struct in6_addr *our_addr, - const struct in6_addr *peer_addr); + const struct in6_addr *peer_addr, + u_int16_t bid); int bul_add(struct bulentry *bule); @@ -94,7 +108,11 @@ void bul_update_expire(struct bulentry *bule); int bul_iterate(struct hash *h, int (* func)(void *bule, void *arg), void *arg); +int bul_iterate_in(struct in6_addr *peer_addr, + int (* func)(void *, void *), + void *arg); +int bul_highest_priority(void *bule_v, void *best_bule_v); int bul_init(void); int bul_home_init(struct home_addr_info *home); void bul_home_cleanup(struct hash *bul); @@ -102,7 +120,12 @@ void bul_cleanup(void); void dump_bule(void *bule, void *os); struct bulentry *create_bule(const struct in6_addr *hoa, - const struct in6_addr *cn_addr); + const struct in6_addr *cn_addr, + u_int16_t bid); void free_bule(struct bulentry *bule); +int mcoa_bule_count(struct bulentry *bule); +int mcoa_bule_dereg(struct bulentry *bule_v, int value); +void bule_block_fwd(struct bulentry *bule); +void bule_unblock_fwd(struct bulentry *bule); #endif diff -Nur mipv6-daemon-umip-0.4-nepl/src/cn.c mipv6-daemon-umip-0.4-nepl-mcoa/src/cn.c --- mipv6-daemon-umip-0.4-nepl/src/cn.c 2007-10-22 15:15:10.000000000 +0200 +++ mipv6-daemon-umip-0.4-nepl-mcoa/src/cn.c 2007-10-23 10:30:03.000000000 +0200 @@ -67,7 +67,11 @@ if (icmp6_parse_data(ip6h, optlen, &laddr, &raddr) < 0) return; - bce = bcache_get(laddr, raddr); + /* MCoA TODO2 + * Add MCoA support for the CN + * MCOA_NO_BID must be replaced with the BUI's BID + */ + bce = bcache_get(laddr, raddr, MCOA_NO_BID); if (bce == NULL) return; @@ -76,7 +80,7 @@ if (bce->unreach > ICMP_ERROR_PERSISTENT_THRESHOLD && bce->type != BCE_HOMEREG) { bcache_release_entry(bce); - bcache_delete(laddr, raddr); + bcache_delete(laddr, raddr, MCOA_NO_BID); dbg("BCE for %x:%x:%x:%x:%x:%x:%x:%x deleted " "due to receipt of ICMPv6 destination unreach\n", NIP6ADDR(raddr)); @@ -156,6 +160,7 @@ struct in6_addr_bundle out; struct ip6_mh_binding_update *bu; struct ip6_mh_opt_nonce_index *non_ind; + struct ip6_mh_opt_bid *bid_opt = NULL; struct bcentry *bce = NULL; struct timespec lft; int status, new = 0; @@ -175,7 +180,18 @@ bu_flags = bu->ip6mhbu_flags; non_ind = mh_opt(&bu->ip6mhbu_hdr, &mh_opts, IP6_MHOPT_NONCEID); - bce = bcache_get(out.src, out.dst); + /* MCoA TODO2 + * MCoA support for CN is not implemented yet + * At the moment we ignore the BUs with BUI option + */ + if ((bid_opt = mh_opt(&bu->ip6mhbu_hdr, &mh_opts, IP6_MHOPT_BID))) { + dbg("Received BU with BUI sub-option, but CN doesn't " + "support MCoA registration. BU skipped.\n"); + status = IP6_MH_BAS_REG_NOT_ALLOWED; + goto send_nack; + } + + bce = bcache_get(out.src, out.dst, MCOA_NO_BID); if (bce) { if ((bce->flags^bu_flags) & (IP6_MH_BU_HOME|IP6_MH_BU_MR)) { /* H-bit or R-bit mismatch, flags changed */ @@ -219,7 +235,7 @@ goto send_nack; } /* else get rid of it */ - bcache_delete(out.src, out.dst); + bcache_delete(out.src, out.dst, MCOA_NO_BID); } } else { if (bu_flags & IP6_MH_BU_HOME) { @@ -281,18 +297,18 @@ } /* dereg, success */ bcache_release_entry(bce); - bcache_delete(out.src, out.dst); + bcache_delete(out.src, out.dst, MCOA_NO_BID); status = IP6_MH_BAS_ACCEPTED; } if (bu_flags & IP6_MH_BU_ACK) - mh_send_ba(&out, status, 0, seqno, &lft, key, iif); + mh_send_ba(&out, status, 0, seqno, &lft, 0, 0, key, iif); return; send_nack: if (bce) { bcache_release_entry(bce); - bcache_delete(out.src, out.dst); + bcache_delete(out.src, out.dst, MCOA_NO_BID); } - mh_send_ba_err(&out, status, 0, seqno, pkey, iif); + mh_send_ba_err(&out, status, 0, seqno, 0, 0, pkey, iif); } static struct mh_handler cn_bu_handler = diff -Nur mipv6-daemon-umip-0.4-nepl/src/conf.c mipv6-daemon-umip-0.4-nepl-mcoa/src/conf.c --- mipv6-daemon-umip-0.4-nepl/src/conf.c 2007-11-05 13:19:58.000000000 +0100 +++ mipv6-daemon-umip-0.4-nepl-mcoa/src/conf.c 2007-11-05 14:30:55.000000000 +0100 @@ -74,6 +74,7 @@ { fprintf(stderr, "%s (%s) %s\n" + "With Multiple Care-of Addresses registration support\n" "%s\n" "This is free software; see the source for copying conditions. There is NO\n" "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n", @@ -218,6 +219,7 @@ /* HA options */ c->SendMobPfxAdvs = 1; + c->HaAcceptMCoAReg = 0; c->SendUnsolMobPfxAdvs = 1; c->MaxMobPfxAdvInterval = 86400; /* seconds */ c->MinMobPfxAdvInterval = 600; /* seconds */ @@ -317,8 +319,89 @@ dbg("MinMobPfxAdvInterval = %u\n", c->MinMobPfxAdvInterval); dbg("HaMaxBindingLife = %u\n", c->HaMaxBindingLife); dbg("HaAcceptMobRtr = %s\n", CONF_BOOL_STR(c->HaAcceptMobRtr)); + dbg("HaAcceptMCoAReg = %s\n", CONF_BOOL_STR(c->HaAcceptMCoAReg)); /* CN options */ dbg("DoRouteOptimizationCN = %s\n", CONF_BOOL_STR(c->DoRouteOptimizationCN)); } + +/* + * get_reliable_from_ifindex - return the reliability value + * from the interface index + * @iif: the interface index + */ +int get_reliable_from_ifindex(int iif) +{ + struct list_head *list; + + list_for_each(list, &conf.net_ifaces) { + struct net_iface *nif; + nif = list_entry(list, struct net_iface, list); + if (nif->ifindex == iif) { + if (is_if_mn(nif)) + return nif->mn_if_reliable; + return -1; + } + } + return -1; +} + +/* + * get_bid_from_ifindex - return the bid from the interface index + * @iif: the interface index + */ +uint16_t get_bid_from_ifindex(int iif) +{ + struct list_head *list; + + list_for_each(list, &conf.net_ifaces) { + struct net_iface *nif; + nif = list_entry(list, struct net_iface, list); + if (nif->ifindex == iif) { + if (is_if_mn(nif)) + return nif->mn_if_bid; + return 0; + } + } + return 0; +} + +/* + * get_prio_from_bid - return the priority from the bid + * @bid: the bid of the interface + */ +uint8_t get_prio_from_bid(uint16_t bid) +{ + struct list_head *list; + + list_for_each(list, &conf.net_ifaces) { + struct net_iface *nif; + nif = list_entry(list, struct net_iface, list); + if (nif->mn_if_bid == bid) { + if (is_if_mn(nif)) + return nif->mn_if_bidprio; + return 0; + } + } + return 0; +} + +/* + * get_highest_prio - return the highest BID priority from the + * interface list + */ +uint8_t get_highest_prio() +{ + struct list_head *list; + int priority = 0; + + list_for_each(list, &conf.net_ifaces) { + struct net_iface *nif; + nif = list_entry(list, struct net_iface, list); + if (nif->mn_if_bidprio >= priority) + priority = nif->mn_if_bidprio; + } + return priority; +} + diff -Nur mipv6-daemon-umip-0.4-nepl/src/conf.h mipv6-daemon-umip-0.4-nepl-mcoa/src/conf.h --- mipv6-daemon-umip-0.4-nepl/src/conf.h 2007-10-22 15:15:10.000000000 +0200 +++ mipv6-daemon-umip-0.4-nepl-mcoa/src/conf.h 2007-10-23 10:30:03.000000000 +0200 @@ -48,6 +48,7 @@ /* HA options */ char HaAcceptMobRtr; + char HaAcceptMCoAReg; char SendMobPfxAdvs; char SendUnsolMobPfxAdvs; unsigned int MaxMobPfxAdvInterval; @@ -66,6 +67,9 @@ int is_rtr; int mip6_if_entity; int mn_if_preference; + uint16_t mn_if_bid; + uint8_t mn_if_bidprio; + int mn_if_reliable; }; extern struct mip6_config conf; @@ -119,6 +123,11 @@ void conf_show(struct mip6_config *c); +int get_reliable_from_ifindex(int iif); +uint16_t get_bid_from_ifindex(int iif); +uint8_t get_prio_from_bid(uint16_t bid); +uint8_t get_highest_prio(); + int yyparse(void); int yylex(void); diff -Nur mipv6-daemon-umip-0.4-nepl/src/dhaad_mn.c mipv6-daemon-umip-0.4-nepl-mcoa/src/dhaad_mn.c --- mipv6-daemon-umip-0.4-nepl/src/dhaad_mn.c 2007-10-22 15:15:10.000000000 +0200 +++ mipv6-daemon-umip-0.4-nepl-mcoa/src/dhaad_mn.c 2007-10-23 10:30:03.000000000 +0200 @@ -120,8 +120,9 @@ pthread_rwlock_unlock(&mn_lock); return; } - t->dhaad_id = dhaad_send_request(hai->primary_coa.iif, - &hai->primary_coa.addr, + assert(hai->current_coa); + t->dhaad_id = dhaad_send_request(hai->current_coa->iif, + &hai->current_coa->addr, &hai->home_prefix, hai->home_plen, hai->mob_rtr? @@ -146,8 +147,9 @@ if (hai->mob_rtr && !(hai->home_block & NEMO_RA_BLOCK)) xfrm_block_ra(hai); t->dhaad_resends = 0; - t->dhaad_id = dhaad_send_request(hai->primary_coa.iif, - &hai->primary_coa.addr, + assert(hai->current_coa); + t->dhaad_id = dhaad_send_request(hai->current_coa->iif, + &hai->current_coa->addr, &hai->home_prefix, hai->home_plen, hai->mob_rtr? diff -Nur mipv6-daemon-umip-0.4-nepl/src/gram.y mipv6-daemon-umip-0.4-nepl-mcoa/src/gram.y --- mipv6-daemon-umip-0.4-nepl/src/gram.y 2007-10-22 15:15:10.000000000 +0200 +++ mipv6-daemon-umip-0.4-nepl-mcoa/src/gram.y 2007-10-23 10:30:03.000000000 +0200 @@ -47,15 +47,20 @@ #include "util.h" #include "ipsec.h" #include "rtnl.h" +#include "bul.h" struct net_iface ni = { .mip6_if_entity = MIP6_ENTITY_NO, .mn_if_preference = POL_MN_IF_DEF_PREFERENCE, + .mn_if_bid = POL_MN_IF_DEF_BID, + .mn_if_bidprio = POL_MN_IF_DEF_BID_PRIORITY, + .mn_if_reliable = POL_MN_IF_DEF_RELIABLE }; struct home_addr_info hai = { .ro_policies = LIST_HEAD_INIT(hai.ro_policies), - .mob_net_prefixes = LIST_HEAD_INIT(hai.mob_net_prefixes) + .mob_net_prefixes = LIST_HEAD_INIT(hai.mob_net_prefixes), + .mcoa = LIST_HEAD_INIT(hai.mcoa) }; LIST_HEAD(prefixes); @@ -185,6 +190,14 @@ %token ISMOBRTR %token HASERVEDPREFIX %token MOBRTRUSEEXPLICITMODE +%token HAACCEPTMCOAREG +%token MCOAREG +%token NOMCOAREG +%token BID +%token BIDPRIORITY +%token RELIABLE +%token REGMULTIPLECOA +%token IFMULTIPLECOA %token INV_TOKEN @@ -197,6 +210,7 @@ %type mnropolicyaddr %type dorouteopt %type bindaclpolval +%type mcoapolval %type prefixlen %type mip6entity %type xfrmaction @@ -306,6 +320,10 @@ { conf.HaAcceptMobRtr = $2; } + | HAACCEPTMCOAREG BOOL ';' + { + conf.HaAcceptMCoAReg = $2; + } | HASERVEDPREFIX prefixlistentry ';' { list_splice(&prefixes, @@ -375,6 +393,9 @@ memset(&ni, 0, sizeof(struct net_iface)); ni.mip6_if_entity = MIP6_ENTITY_NO; ni.mn_if_preference = POL_MN_IF_DEF_PREFERENCE; + ni.mn_if_bid = POL_MN_IF_DEF_BID; + ni.mn_if_bidprio = POL_MN_IF_DEF_BID_PRIORITY; + ni.mn_if_reliable = POL_MN_IF_DEF_RELIABLE; } ; @@ -392,7 +413,44 @@ } | MNIFPREFERENCE NUMBER ';' { - ni.mn_if_preference = $2; + if (ni.mn_if_bid) { + uerror("You cannot set MnIfPreference if BID " + "is set. Use BidPriority instead."); + return -1; + } + ni.mn_if_preference = $2; + } + | BID NUMBER ';' + { + if (ni.mn_if_preference) { + uerror("If BID is set, you cannot set " + "MnIfPreference. Use BidPriority instead."); + return -1; + } + if ($2 < BULE_TABLE_MIN || $2 > BULE_TABLE_MAX) { + uerror("BID error (%d <= Bid <= %d)", + BULE_TABLE_MIN, BULE_TABLE_MAX); + return -1; + } + ni.mn_if_bid = $2; + } + | BIDPRIORITY NUMBER ';' + { + if (ni.mn_if_preference) { + uerror("You cannot set BidPriority if " + "MnIfPreference is set."); + return -1; + } + if ($2 < 1 || $2 > 255) { + uerror("BidPriotity error " + "(%d <= BidPriority <= %d)",1, 255); + return -1; + } + ni.mn_if_bidprio = $2; + } + | RELIABLE BOOL ';' + { + ni.mn_if_reliable = $2; } ; @@ -432,15 +490,28 @@ INIT_LIST_HEAD(&nhai->ro_policies); INIT_LIST_HEAD(&nhai->ha_list.home_agents); INIT_LIST_HEAD(&nhai->mob_net_prefixes); + INIT_LIST_HEAD(&nhai->mcoa); nhai->ha_list.dhaad_id = -1; list_splice(&hai.ro_policies, &nhai->ro_policies); list_splice(&hai.mob_net_prefixes, &nhai->mob_net_prefixes); + list_splice(&hai.mcoa, &nhai->mcoa); + if (!hai.reg_mcoa) { + /* If MCoA registration is not used, we + * initialize nhai->current_coa + */ + nhai->current_coa = malloc(sizeof(struct mn_addr)); + if (nhai->current_coa == NULL) { + uerror("out of memory"); + return -1; + } + } list_add_tail(&nhai->list, &conf.home_addrs); memset(&hai, 0, sizeof(struct home_addr_info)); INIT_LIST_HEAD(&hai.ro_policies); INIT_LIST_HEAD(&hai.mob_net_prefixes); + INIT_LIST_HEAD(&hai.mcoa); } ; @@ -468,6 +539,11 @@ ipv6_addr_prefix(&hai.home_prefix, &$2, $4); hai.home_plen = $4; } + | REGMULTIPLECOA BOOL ';' + { + hai.reg_mcoa = $2; + } + | IFMULTIPLECOA iflist ';' ; homeaddress : homeaddrdef prefixlistsub @@ -695,7 +771,12 @@ | NUMBER { $$ = $1; } ; -bindaclpolicy : ADDR prefixlistsub bindaclpolval +mcoapolval : MCOAREG { $$ = 1; } + | NOMCOAREG { $$ = 0; } + | '' { $$ = 0; } + ; + +bindaclpolicy : ADDR prefixlistsub mcoapolval bindaclpolval { bae = malloc(sizeof(struct policy_bind_acl_entry)); if (bae == NULL) { @@ -707,7 +788,8 @@ bae->plen = 128; INIT_LIST_HEAD(&bae->mob_net_prefixes); bae->mnp_count = mv_prefixes(&bae->mob_net_prefixes); - bae->bind_policy = $3; + bae->mcoa_reg = $3; + bae->bind_policy = $4; list_add_tail(&bae->list, &conf.bind_acl); } ; @@ -745,4 +827,58 @@ list_add_tail(&p->list, &prefixes); } ; + +iflist : iflistentry + | iflist ',' iflistentry + ; + +iflistentry : QSTRING + { + struct mn_addr *mncoa; + struct list_head *l; + int declared = 0, bid = 0; + + /* Check that this interface has been + * previously declared as "Interface" */ + list_for_each(l, &conf.net_ifaces) { + struct net_iface *ni; + ni = list_entry(l, struct net_iface, + list); + + if(if_nametoindex($1) == ni->ifindex) { + declared = 1; + bid = ni->mn_if_bid; + break; + } + } + + if(!declared) { + uerror("Interface %s has not been " + "declared as Interface\n", $1); + return -1; + } + + if(!bid) { + uerror("Interface %s has no BID " + "although you declared it in " + "IfMultipleCoA\n", $1); + return -1; + } + + mncoa = malloc(sizeof(struct mn_addr)); + if (mncoa == NULL) { + fprintf(stderr, "%s: out of memory\n", + __FUNCTION__); + return -1; + } + memset(mncoa, 0, sizeof(struct mn_addr)); + if ((mncoa->iif = if_nametoindex($1)) == 0) { + fprintf(stderr, "%s: %s is not a valid " + "interface name\n", __FUNCTION__, $1); + return -1; + } + free($1); + list_add_tail(&mncoa->list, &hai.mcoa); + } + ; %% diff -Nur mipv6-daemon-umip-0.4-nepl/src/ha.c mipv6-daemon-umip-0.4-nepl-mcoa/src/ha.c --- mipv6-daemon-umip-0.4-nepl/src/ha.c 2007-10-26 13:13:00.000000000 +0200 +++ mipv6-daemon-umip-0.4-nepl-mcoa/src/ha.c 2007-10-26 13:13:00.000000000 +0200 @@ -60,6 +60,14 @@ #include "ndisc.h" #include "prefix.h" +#define HA_DEBUG_LEVEL 1 + +#if HA_DEBUG_LEVEL >= 1 +#define MDBG dbg +#else +#define MDBG(...) +#endif /* MDBG */ + static pthread_mutex_t bu_worker_mutex; static volatile unsigned long bu_worker_count = 0; static pthread_cond_t cond; @@ -504,7 +512,7 @@ static void nemo_ha_del_mnp_routes(struct list_head *old_mnps, struct list_head *new_mnps, - int ifindex, int all) + int table, int ifindex, int all) { struct list_head *list; list_for_each(list, old_mnps) { @@ -514,14 +522,22 @@ prefix_list_find(new_mnps, &p->ple_prefix, p->ple_plen)) continue; - route_del(ifindex, RT6_TABLE_MIP6, IP6_RT_PRIO_MIP6_FWD, + route_del(ifindex, table, IP6_RT_PRIO_MIP6_FWD, NULL, 0, &p->ple_prefix, p->ple_plen, NULL); + + /* MCoA + * Delete the MNP fwmark rule + */ + if(table != RT6_TABLE_MIP6) + rule_del(NULL, table, IP6_RULE_PRIO_MIP6_FWD_FWM, + table, RTN_UNICAST, &in6addr_any, 0, + &p->ple_prefix, p->ple_plen, 0); } } static int nemo_ha_add_mnp_routes(struct list_head *old_mnps, struct list_head *new_mnps, - int ifindex, int all) + int table, int ifindex, int all) { struct list_head *list; list_for_each(list, new_mnps) { @@ -530,14 +546,129 @@ if (!all && prefix_list_find(old_mnps, &p->ple_prefix, p->ple_plen)) continue; - if (route_add(ifindex, RT6_TABLE_MIP6, RTPROT_MIP, + if (route_add(ifindex, table, RTPROT_MIP, 0, IP6_RT_PRIO_MIP6_FWD, NULL, 0, &p->ple_prefix, p->ple_plen, NULL) < 0) return -1; + /* MCoA + * Add the MNP fwmark rule + */ + if(table != RT6_TABLE_MIP6 + && rule_add(NULL, table, IP6_RULE_PRIO_MIP6_FWD_FWM, + table, RTN_UNICAST, &in6addr_any, 0, + &p->ple_prefix, p->ple_plen, 0) < 0) + return -1; } return 0; } +static int mcoa_ha_init_rt_table(struct bcentry *bce) +{ + /* MCoA TODO2 + * At the moment, the routing table number is the BID number. + * BID thus must be between BCE_TABLE_MIN and BCE_TABLE_MAX + */ + assert(bce); + + /* If BID is not assigned, default table is taken */ + if (!bce->bid) + return (bce->table = RT6_TABLE_MIP6); + + if (bce->bid >= BCE_TABLE_MIN && bce->bid <= BCE_TABLE_MAX) + return (bce->table = bce->bid); + + return -1; +} + +static int mcoa_ha_del_dflt_rules(struct bcentry *bce) +{ + struct list_head *list; + struct prefix_list_entry *pfx; + struct bcentry *best_bce = bce; + const struct in6_addr *peer_addr = &bce->peer_addr; + int8_t priority = 0; + + MDBG("Current BCE has the best priority (%d), " + "erasing default rules\n", best_bce->priority); + + /* Erase the default HoA rule */ + rule_del(NULL, 0, IP6_RULE_PRIO_MIP6_FWD_MCOA, 0, + RTN_UNICAST, &in6addr_any, 0, peer_addr, 128, 0); + + /* Erase the default MNP rules */ + list_for_each(list, &bce->mob_net_prefixes) { + pfx = list_entry(list, struct prefix_list_entry, list); + rule_del(NULL, 0, IP6_RULE_PRIO_MIP6_FWD_MCOA, 0, + RTN_UNICAST, &in6addr_any, 0, + &pfx->ple_prefix, pfx->ple_plen, 0); + } + + /* Create new rule with next best bce available */ + priority = bce->priority; + bce->priority = 0; + bcache_iterate_in(&bce->peer_addr, bce_highest_priority, + &best_bce); + bce->priority = priority; + + if(best_bce != bce) { + MDBG("Rules replaced with BCE prio %d\n", + best_bce->priority); + rule_add(NULL, best_bce->table, + IP6_RULE_PRIO_MIP6_FWD_MCOA, 0, RTN_UNICAST, + &in6addr_any, 0, peer_addr, 128, 0); + + /* Default prefix rules */ + list_for_each(list, &bce->mob_net_prefixes) { + pfx = list_entry(list, + struct prefix_list_entry, list); + rule_add(NULL, best_bce->table, + IP6_RULE_PRIO_MIP6_FWD_MCOA, 0, + RTN_UNICAST, &in6addr_any, 0, + &pfx->ple_prefix, pfx->ple_plen, 0); + } + } else + MDBG("No other BCE available, no dflt rules added\n"); + + return 0; +} + +static int mcoa_ha_add_dflt_rules(struct bcentry *bce) +{ + struct list_head *list; + const struct in6_addr *peer_addr = &bce->peer_addr; + + MDBG("BCE has the best priority (%d), adding default HoA " + "and prefix rules\n", bce->priority); + + /* Erase the default HoA rule */ + rule_del(NULL, 0, IP6_RULE_PRIO_MIP6_FWD_MCOA, 0, + RTN_UNICAST, &in6addr_any, 0, peer_addr, 128, 0); + + /* Add the default HoA rule */ + if (rule_add(NULL, bce->table, + IP6_RULE_PRIO_MIP6_FWD_MCOA, 0, RTN_UNICAST, + &in6addr_any, 0, peer_addr, 128, 0) < 0) + return -1; + + /* Delete and add the default prefix rules */ + list_for_each(list, &bce->mob_net_prefixes) { + struct prefix_list_entry *pfx; + pfx = list_entry(list, struct prefix_list_entry, list); + + rule_del(NULL, 0, IP6_RULE_PRIO_MIP6_FWD_MCOA, + 0, RTN_UNICAST, &in6addr_any, 0, + &pfx->ple_prefix, pfx->ple_plen, 0); + + if (rule_add(NULL, bce->table, + IP6_RULE_PRIO_MIP6_FWD_MCOA, 0, + RTN_UNICAST, &in6addr_any, 0, + &pfx->ple_prefix, pfx->ple_plen, 0) < 0) + return -1; + } + + return 0; +} + struct home_tnl_ops_parm { struct bcentry *bce; int ba_status; @@ -548,6 +679,7 @@ { const struct in6_addr *our_addr, *peer_addr, *coa, *old_coa; struct list_head *mnp; + struct bcentry *best_bce = NULL; assert(old_if); @@ -565,12 +697,33 @@ ha_ipsec_tnl_pol_del(our_addr, peer_addr, p->bce->tunnel, mnp); } /* delete HoA route */ + /* MCoA: The route is deleted in the table given by p->bce->table */ + /* RKRK: for the moment the route is installed in the MAIN table. + * See home_tnl_add */ + //MDBG("Deleting old default route in table %d\n", p->bce->table); + MDBG("Deleting old default route in table %d\n", RT6_TABLE_MAIN); + //route_del(old_if, p->bce->table, route_del(old_if, RT6_TABLE_MAIN, IP6_RT_PRIO_MIP6_FWD, NULL, 0, peer_addr, 128, NULL); - + /* delete MNP routes */ nemo_ha_del_mnp_routes(&p->bce->mob_net_prefixes, - &p->mob_net_prefixes, old_if, 1); + &p->mob_net_prefixes, p->bce->table, + old_if, 1); + /* MCoA + * Delete the HoA and fwmark rules, and if needed replace + * the default HoA and MNP rules with the best available one + */ + if(p->bce->table != RT6_TABLE_MIP6) { + rule_del(NULL, p->bce->table, IP6_RULE_PRIO_MIP6_FWD_FWM, + p->bce->table, RTN_UNICAST, &in6addr_any, 0, + peer_addr, 128, 0); + best_bce = p->bce; + bcache_iterate_in(&p->bce->peer_addr, + bce_highest_priority, &best_bce); + if(best_bce == p->bce) + mcoa_ha_del_dflt_rules(p->bce); + } /* update tunnel interface */ p->bce->tunnel = new_if; @@ -581,6 +734,7 @@ { const struct in6_addr *our_addr, *peer_addr, *coa, *old_coa; struct list_head *mnp; + struct bcentry *best_bce = NULL; assert(new_if); @@ -595,7 +749,8 @@ /* add MNP routes */ if (nemo_ha_add_mnp_routes(&p->bce->mob_net_prefixes, - &p->mob_net_prefixes, new_if, 1) < 0) { + &p->mob_net_prefixes, p->bce->table, + new_if, 1) < 0) { if (p->bce->nemo_type == BCE_NEMO_EXPLICIT) p->ba_status = IP6_MH_BAS_INVAL_PRFX; else @@ -603,12 +758,39 @@ goto err; } /* add HoA route */ + /* MCoA: The route is added in the table given by p->bce->table */ + /* RKRK: does not work. Route has to be added in the MAIN table. + * Need to check why! */ + //MDBG("Adding route in table %d via iface %d\n", p->bce->table, new_if); + MDBG("Adding route in table %d via iface %d\n", RT6_TABLE_MAIN, new_if); + //if (route_add(new_if, p->bce->table, if (route_add(new_if, RT6_TABLE_MAIN, RTPROT_MIP, 0, IP6_RT_PRIO_MIP6_FWD, NULL, 0, peer_addr, 128, NULL) < 0) { p->ba_status = IP6_MH_BAS_INSUFFICIENT; goto err; } + /* MCoA + * Add the HoA and fwmark rules, and if needed replace + * the default HoA and MNP rules with the best available one + */ + if (p->bce->table != RT6_TABLE_MIP6) { + if (rule_add(NULL, p->bce->table, + IP6_RULE_PRIO_MIP6_FWD_FWM, + p->bce->table, RTN_UNICAST, + &in6addr_any, 0, peer_addr, 128, 0) < 0) { + p->ba_status = IP6_MH_BAS_INSUFFICIENT; + goto err; + } + best_bce = p->bce; + bcache_iterate_in(&p->bce->peer_addr, + bce_highest_priority, &best_bce); + if(best_bce == p->bce + && mcoa_ha_add_dflt_rules(p->bce) < 0) { + p->ba_status = IP6_MH_BAS_INSUFFICIENT; + goto err; + } + } /* add SP entry */ if (conf.UseMnHaIPsec) { if (ha_ipsec_tnl_pol_add(our_addr, peer_addr, @@ -675,12 +857,22 @@ static void home_cleanup(struct bcentry *bce) { - mpd_cancel_mpa(&bce->our_addr, &bce->peer_addr); + int nb_entry = mcoa_bce_count(&bce->peer_addr); + + /* MCoA + * MPA is canceled if this is the last BCE entry + */ + if (nb_entry == 1) + mpd_cancel_mpa(&bce->our_addr, &bce->peer_addr); if (bce->link > 0) { - route_del(bce->link, RT6_TABLE_MIP6, IP6_RT_PRIO_MIP6_OUT, + route_del(bce->link, bce->table, IP6_RT_PRIO_MIP6_OUT, &bce->our_addr, 128, &bce->peer_addr, 128, NULL); - proxy_nd_stop(bce->link, &bce->peer_addr, bce->flags); + /* MCoA + * Proxy ND is stopped if this is the last BCE entry + */ + if (nb_entry == 1) + proxy_nd_stop(bce->link, &bce->peer_addr, bce->flags); } if (bce->tunnel > 0) { struct home_tnl_ops_parm p = { @@ -692,6 +884,9 @@ if (conf.UseMnHaIPsec) { ha_mn_ipsec_pol_mod(&bce->our_addr, &bce->peer_addr); } + + /* MCoA: Freeing the BCE's MNPs list */ + prefix_list_free(&bce->mob_net_prefixes); } @@ -768,19 +963,28 @@ { struct ha_recv_bu_args *arg = varg; struct in6_addr_bundle out; - struct bcentry *bce; + struct bcentry *bce, *bce_exists; struct timespec lft, tmp; - int iif, status, new, home_ifindex; - uint16_t bu_flags, seqno; - uint8_t ba_flags; + int iif, status, status_bid, new, home_ifindex; + int update_dflt_table; + uint16_t bu_flags, seqno, bid; + uint8_t ba_flags, bid_priority, bid_flags; struct home_tnl_ops_parm p; + struct ip6_mh_opt_bid *bid_opt; pthread_dbg("thread started"); restart: home_ifindex = 0; new = 0; + bid = 0; + bid_priority = 0; + bid_flags = 0; + update_dflt_table = 0; ba_flags = 0; - lft = arg->lft; + status = IP6_MH_BAS_UNSPECIFIED; + status_bid = 0; + bce = NULL; + lft = arg->lft; iif = arg->iif; bu_flags = arg->bu->ip6mhbu_flags; seqno = ntohs(arg->bu->ip6mhbu_seqno); @@ -795,8 +999,79 @@ else out.bind_coa = NULL; out.local_coa = NULL; + bid_opt = NULL; - bce = bcache_get(out.src, out.dst); + /* MCoA + * We check if a BCE already exists for this HoA + * We take the first one available + */ + bce_exists = bcache_get(out.src, out.dst, MCOA_NO_BID); + if(bce_exists) + bcache_release_entry(bce_exists); + + /* MCoA + * we need the BID before looking at the BCE + */ + bid_opt = mh_opt(&arg->bu->ip6mhbu_hdr, + &arg->mh_opts, + IP6_MHOPT_BID); + + if (bid_opt && !default_mcoa_reg(out.dst)) { + /* MCoA: BAck without BID option will be sent */ + MDBG("BUI option, but MCoA registration not allowed.\n"); + } else if (bid_opt) { + MDBG("BUI option, and HA is configured for MCoA.\n"); + bid = ntohs(bid_opt->ip6mobid_bid); + bid_priority = bid_opt->ip6mobid_priority; + bid_flags = ntohs(bid_opt->ip6mobid_reserved); + + if (!bid) { + MDBG("BID invalid, BU discarded.\n"); + goto out; + } + + MDBG("BID = %d, Priority = %d\n", bid, bid_priority); + /* MCoA TODO1 + * if B flag is set but no altcoa option: + * BAck status 145. + * To update according to latest draft. + */ + if (bid_flags & IP6_OPT_BID_BULK) { + /* MCoA TODO1 + * At the moment we do not check the AltCoA option, + * and just reject the BU if B flag is set + */ + MDBG("BULK Reg., but not supported yet.\n"); + status = IP6_MH_BAS_MCOA_BULK_FAILED; + goto send_nack; + } + if (bce_exists && !bce_exists->bid) { + /* MCoA + * The BCE has a BID = 0, this means no MCoA + * are registered. We delete the entry and set + * BA status to 144. + * New registratration is done later + */ + MDBG("BID option but BCE has no BID\n"); + MDBG("Delete BCE and register new one.\n"); + bcache_delete(out.src, out.dst, MCOA_NO_BID); + status_bid = IP6_MH_BAS_CONFLICT_BINDING; + } + } else /* No BUI option in BU */ { + if(tsisset(lft) && bce_exists && bce_exists->bid) { + /* MCoA + * A BCE exists with a BID != 0 but no BUI + * sub-option are set in the BU: we delete + * all existing entries for this HoA. New + * registration is done later. + */ + MDBG("No BID option but BCE has BID.\n"); + MDBG("Delete all BCE and register new one.\n"); + bcache_delete(out.src, out.dst, MCOA_NO_BID); + } + } + + bce = bcache_get(out.src, out.dst, bid); if (bce) { if (bce->type != BCE_NONCE_BLOCK) { /* H-bit or R-bit mismatch, flags changed */ @@ -841,7 +1116,7 @@ goto send_nack; } /* else get rid of it */ - bcache_delete(out.src, out.dst); + bcache_delete(out.src, out.dst, bid); } } else if (!tsisset(lft)) { status = IP6_MH_BAS_NOT_HA; @@ -849,9 +1124,21 @@ } if ((status = mpd_prefix_check(out.src, out.dst, &lft, &home_ifindex, new)) < 0) { - if (!(bu_flags & IP6_MH_BU_MR) || - !prefix_list_find(&conf.nemo_ha_served_prefixes, - out.dst, 0)) { + if ((bu_flags & IP6_MH_BU_MR) && + prefix_list_find(&conf.nemo_ha_served_prefixes, + out.dst, 0)) { + struct ha_interface *i = ha_get_if_by_addr(out.src); + if (i != NULL) { + home_ifindex = i->ifindex; + /* set the prefix lifetime to the maximum */ + tssetsec(lft, MAX_BINDING_LIFETIME); + } else { + /* not home agent for this subnet */ + status = IP6_MH_BAS_NOT_HOME_SUBNET; + goto send_nack; + } + } else { + /* not home agent for this subnet */ status = IP6_MH_BAS_NOT_HOME_SUBNET; goto send_nack; @@ -862,6 +1149,10 @@ out.src, arg->bu, arg->len); if (status >= IP6_MH_BAS_UNSPECIFIED) goto send_nack; + + if(status_bid) + status = status_bid; + /* lifetime may be further decreased by local policy */ if (conf.pmgr.max_binding_life(out.dst, out.bind_coa, out.src, arg->bu, arg->len, &lft, &tmp)) { @@ -883,24 +1174,38 @@ bce->type = BCE_DAD; bce->cleanup = NULL; bce->link = home_ifindex; - + bce->bid = bid; + bce->priority = bid_priority; + if(bce_exists) + bce->xfrm_create = 0; + else + bce->xfrm_create = 1; + + /* MCoA: Initialize the routing table used by the BCE */ + if(mcoa_ha_init_rt_table(bce) < 0) { + free(bce); + bce = NULL; + status = IP6_MH_BAS_INSUFFICIENT; + goto send_nack; + } if (bcache_add_homereg(bce) < 0) { free(bce); bce = NULL; status = IP6_MH_BAS_INSUFFICIENT; goto send_nack; } - if (!(arg->flags & HA_BU_F_SKIP_DAD)) { + /* MCoA: Do DAD only if no entries yet for this HoA */ + if (!(arg->flags & HA_BU_F_SKIP_DAD) && !bce_exists) { /* Do DAD for home address */ if (ndisc_do_dad(home_ifindex, out.dst, bu_flags & IP6_MH_BU_LLOCAL) < 0) { - bcache_delete(out.src, out.dst); + bcache_delete(out.src, out.dst, bid); bce = NULL; status = IP6_MH_BAS_DAD_FAILED; goto send_nack; } } - bce = bcache_get(out.src, out.dst); + bce = bcache_get(out.src, out.dst, bid); if (!bce) { BUG("BCE deleted before DAD completed!"); status = IP6_MH_BAS_UNSPECIFIED; @@ -932,6 +1237,12 @@ bce->seqno = seqno; bce->flags = bu_flags; bce->lifetime = lft; + /* MCoA TODO2 + * MNPs are added to the BCE. + * Need to maintain this list when deregistration + */ + bce_add_mnps(bce, &p.mob_net_prefixes); + if (new) { if (tunnel_add(out.src, out.bind_coa, 0, home_tnl_ops, &p) < 0) { @@ -943,7 +1254,8 @@ } bce->cleanup = home_cleanup; - if (route_add(bce->link, RT6_TABLE_MIP6, + /* MCoA: The route is added in the table given by the BCE */ + if (route_add(bce->link, bce->table, RTPROT_MIP, 0, IP6_RT_PRIO_MIP6_OUT, &bce->our_addr, 128, &bce->peer_addr, 128, NULL) < 0) { @@ -951,16 +1263,23 @@ goto send_nack; } - if (proxy_nd_start(bce->link, out.dst, out.src, - bu_flags) < 0) { - status = IP6_MH_BAS_INSUFFICIENT; - goto send_nack; + /* MCoA: Do Proxy ND only if no entries yet for this HoA */ + if (!bce_exists) { + if (proxy_nd_start(bce->link, out.dst, + out.src, bu_flags) < 0) { + status = IP6_MH_BAS_INSUFFICIENT; + goto send_nack; + } } bce->type = BCE_HOMEREG; bcache_complete_homereg(bce); } else { bce->old_coa = bce->coa; bce->coa = *out.bind_coa; + if (tsisset(lft)) { + bce->bid = bid; + bce->priority = bid_priority; + } if (tunnel_mod(bce->tunnel, out.src, out.bind_coa, 0, home_tnl_ops, &p) < 0) { if (p.ba_status >= IP6_MH_BAS_UNSPECIFIED) @@ -974,7 +1293,10 @@ /* bce is always valid here */ bcache_release_entry(bce); if (!tsisset(lft)) - bcache_delete(out.src, out.dst); + /* MCoA: We delete one specific entry (bid is set) + * or all entries for this HoA (if bid is set to 0) + */ + bcache_delete(out.src, out.dst, bid); if ((bu_flags & IP6_MH_BU_KEYM) && conf.pmgr.use_keymgm(out.dst, out.src)) @@ -995,12 +1317,16 @@ * have a binding before sending this Binding Update, * discard the connections to the home address. */ } - if (status < IP6_MH_BAS_UNSPECIFIED && bu_flags & IP6_MH_BU_MR) + if ((status < IP6_MH_BAS_UNSPECIFIED + || status == IP6_MH_BAS_CONFLICT_BINDING) + && bu_flags & IP6_MH_BU_MR) ba_flags |= IP6_MH_BA_MR; if (!(arg->flags & HA_BU_F_SKIP_BA)) - mh_send_ba(&out, status, ba_flags, seqno, &lft, NULL, iif); - if (new && tsisset(lft)) + mh_send_ba(&out, status, ba_flags, seqno, &lft, + bid, bid_priority, NULL, iif); + /* MCoA: Start MPA only if no entries yet for this HoA */ + if (!bce_exists && new && tsisset(lft)) mpd_start_mpa(&bce->our_addr, &bce->peer_addr); out: free(arg); @@ -1020,10 +1346,12 @@ send_nack: if (bce) { bcache_release_entry(bce); - bcache_delete(out.src, out.dst); + bcache_delete(out.src, out.dst, bid); } if (!(arg->flags & HA_BU_F_SKIP_BA)) - mh_send_ba_err(&out, status, 0, seqno, NULL, iif); + /* MCoA: Specify the BID and BID priority to the BAck */ + mh_send_ba_err(&out, status, 0, seqno, bid, + bid_priority, NULL, iif); goto out; } @@ -1049,12 +1377,13 @@ arg = malloc(sizeof(struct ha_recv_bu_args) + len); if (!arg) { - if (bce_exists(out.src, out.dst)) - bcache_delete(out.src, out.dst); + if (bce_exists(out.src, out.dst, MCOA_NO_BID)) + bcache_delete(out.src, out.dst, MCOA_NO_BID); if (!(arg->flags & HA_BU_F_SKIP_BA)) mh_send_ba_err(&out, IP6_MH_BAS_INSUFFICIENT, 0, - ntohs(bu->ip6mhbu_seqno), NULL, iif); + ntohs(bu->ip6mhbu_seqno), 0, 0, + NULL, iif); return -ENOMEM; } arg->src = *out.src; @@ -1126,8 +1455,9 @@ return -1; if (mpd_ha_init() < 0) return -1; + /* MCoA: Rule to the default table (RT6_TABLE_MIP6) */ if (rule_add(NULL, RT6_TABLE_MIP6, - IP6_RULE_PRIO_MIP6_FWD, RTN_UNICAST, + IP6_RULE_PRIO_MIP6_FWD, 0, RTN_UNICAST, &in6addr_any, 0, &in6addr_any, 0, 0) < 0) return -1; icmp6_handler_reg(ND_ROUTER_ADVERT, &ha_ra_handler); @@ -1145,7 +1475,7 @@ pthread_mutex_unlock(&bu_worker_mutex); bcache_flush(); rule_del(NULL, RT6_TABLE_MIP6, - IP6_RULE_PRIO_MIP6_FWD, RTN_UNICAST, + IP6_RULE_PRIO_MIP6_FWD, 0, RTN_UNICAST, &in6addr_any, 0, &in6addr_any, 0, 0); mpd_ha_cleanup(); dhaad_ha_cleanup(); diff -Nur mipv6-daemon-umip-0.4-nepl/src/hash.c mipv6-daemon-umip-0.4-nepl-mcoa/src/hash.c --- mipv6-daemon-umip-0.4-nepl/src/hash.c 2007-10-22 15:15:10.000000000 +0200 +++ mipv6-daemon-umip-0.4-nepl-mcoa/src/hash.c 2007-10-23 10:30:03.000000000 +0200 @@ -47,6 +47,7 @@ struct hash_entry { struct in6_addr *our_addr; /* pointer to our_addr in data */ struct in6_addr *peer_addr; /* pointer to peer_addr in data */ + uint16_t *bid; /* pointer to bid in data, can be NULL */ struct hash_entry *next; void *data; }; @@ -78,7 +79,7 @@ } static int match2(struct hash_entry *h, const struct in6_addr *our_addr, - const struct in6_addr *peer_addr) + const struct in6_addr *peer_addr, uint16_t bid) { assert(h); assert(our_addr && h->our_addr); @@ -89,9 +90,15 @@ printf("h->peer %s\n", inet_ntop(AF_INET6, h->peer_addr, s, sizeof(s))); printf("our_addr %s\n", inet_ntop(AF_INET6, our_addr, s, sizeof(s))); printf("h->our_addr %s\n", inet_ntop(AF_INET6, h->our_addr, s, sizeof(s))); + printf("bid %d\n", bid); + if(h->bid != NULL) + printf("h->bid %d\n", *(h->bid)); + else + printf("h->bid is NULL\n"); #endif if (IN6_ARE_ADDR_EQUAL(h->peer_addr, peer_addr) && - IN6_ARE_ADDR_EQUAL(h->our_addr, our_addr)) { + IN6_ARE_ADDR_EQUAL(h->our_addr, our_addr) && + (bid == 0 || (h->bid != NULL && bid == *(h->bid)))) { return 1; } return 0; @@ -99,7 +106,7 @@ } static int match1(struct hash_entry *h, const struct in6_addr *dummy, - const struct in6_addr *peer_addr) + const struct in6_addr *peer_addr, uint16_t bid) { assert(h); assert(h->peer_addr && peer_addr); @@ -107,8 +114,14 @@ char s[INET6_ADDRSTRLEN]; printf("match1: peer %s\n", inet_ntop(AF_INET6, peer_addr, s, sizeof(s))); printf("h->peer %s\n", inet_ntop(AF_INET6, h->peer_addr, s, sizeof(s))); + printf("bid %d\n", bid); + if(h->bid != NULL) + printf("h->bid %d\n", *(h->bid)); + else + printf("h->bid is NULL\n"); #endif - if (!IN6_ARE_ADDR_EQUAL(h->peer_addr, peer_addr)) + if (!IN6_ARE_ADDR_EQUAL(h->peer_addr, peer_addr) || + (bid !=0 && h->bid != NULL && bid != *(h->bid))) return 0; return 1; } @@ -155,7 +168,8 @@ void *hash_get(const struct hash *h, const struct in6_addr *our_addr, - const struct in6_addr *peer_addr) + const struct in6_addr *peer_addr, + uint16_t bid) { struct hash_entry *hptr = NULL; @@ -165,7 +179,7 @@ hptr = h->hash_buckets[h->calc_hash(h->buckets, our_addr , peer_addr)]; while(hptr) { - if (h->match(hptr, our_addr, peer_addr)) + if (h->match(hptr, our_addr, peer_addr, bid)) return hptr->data; hptr = hptr->next; } @@ -173,7 +187,8 @@ } int hash_add(const struct hash *h, void *data, - struct in6_addr *our_addr, struct in6_addr *peer_addr) + struct in6_addr *our_addr, struct in6_addr *peer_addr, + uint16_t *bid) { struct hash_entry *hptr = NULL, *new; uint32_t hash_ind; @@ -185,17 +200,19 @@ assert(peer_addr); if ((new = (struct hash_entry *)malloc(sizeof(struct hash_entry))) == NULL) { - dbg("out of memeory\n"); + dbg("out of memory\n"); return -ENOMEM; } new->our_addr = our_addr; new->peer_addr = peer_addr; new->data = data; + new->bid = bid; hash_ind = h->calc_hash(h->buckets, our_addr, peer_addr); hptr = h->hash_buckets[hash_ind]; if (hptr) { while (hptr->next){ - if (h->match(hptr, our_addr, peer_addr)) { + if (h->match(hptr, our_addr, peer_addr, + (bid) ? (*bid) : 0)) { free(new); return -EEXIST; } @@ -231,9 +248,35 @@ return 0; } +int hash_iterate_in(const struct hash *h, + const struct in6_addr *peer_addr, + int (*func)(void *, void *), + void *arg) +{ + int err = 0; + int i; + struct hash_entry *hptr, *nptr; + + assert(func); + + for (i=0; i < h->buckets; i++) { + if((hptr = h->hash_buckets[i]) != NULL) { + while(hptr) { + nptr = hptr->next; + if (IN6_ARE_ADDR_EQUAL(hptr->peer_addr, peer_addr) + && (err = func(hptr->data, arg))) + return err; + hptr = nptr; + } + } + } + return 0; +} + void hash_delete(const struct hash *h, const struct in6_addr *our_addr, - const struct in6_addr *peer_addr) + const struct in6_addr *peer_addr, + uint16_t bid) { struct hash_entry *hptr, *pptr, *head; int hash_ind; @@ -247,7 +290,7 @@ pptr = hptr = head; while (hptr) { - if (h->match(hptr, our_addr, peer_addr)){ + if (h->match(hptr, our_addr, peer_addr, bid)){ if (hptr != head) pptr->next = hptr->next; else { diff -Nur mipv6-daemon-umip-0.4-nepl/src/hash.h mipv6-daemon-umip-0.4-nepl-mcoa/src/hash.h --- mipv6-daemon-umip-0.4-nepl/src/hash.h 2007-10-22 15:15:10.000000000 +0200 +++ mipv6-daemon-umip-0.4-nepl-mcoa/src/hash.h 2007-10-23 10:30:03.000000000 +0200 @@ -20,7 +20,9 @@ int buckets; int type; uint32_t (*calc_hash)(int, const struct in6_addr *, const struct in6_addr *); - int (*match)(struct hash_entry *, const struct in6_addr *, const struct in6_addr *); + int (*match)(struct hash_entry *, const struct in6_addr *, + const struct in6_addr *, uint16_t); + struct hash_entry **hash_buckets; }; @@ -37,19 +39,24 @@ */ void hash_cleanup(struct hash *h); /* - * Get data stored in a hash entry based on one or two addresses, depending on type of hash + * Get data stored in a hash entry based on one or two addresses and the bid, + * depending on type of hash. bid=0 means we do not use bid as a search key. */ void *hash_get(const struct hash *h, const struct in6_addr *our_addr, - const struct in6_addr *peer_addr) ; + const struct in6_addr *peer_addr, + uint16_t bid); /* Iterate through the hash and call func for every entry */ int hash_iterate(const struct hash *h, int (*func)(void *data, void *arg), void *arg); +int hash_iterate_in(const struct hash *h, const struct in6_addr *peer_addr, + int (*func)(void *, void *), void *arg); /* Add a hash entry to hash */ int hash_add(const struct hash *h, void *data, - struct in6_addr *our_addr, struct in6_addr *peer_addr); + struct in6_addr *our_addr, struct in6_addr *peer_addr, + uint16_t *bid); /* *Delete entry from hash @@ -57,6 +64,7 @@ void hash_delete(const struct hash *h, const struct in6_addr *our_addr, - const struct in6_addr *peer_addr); + const struct in6_addr *peer_addr, + uint16_t bid); #endif /* _HASH_H */ diff -Nur mipv6-daemon-umip-0.4-nepl/src/mh.c mipv6-daemon-umip-0.4-nepl-mcoa/src/mh.c --- mipv6-daemon-umip-0.4-nepl/src/mh.c 2007-10-22 15:15:10.000000000 +0200 +++ mipv6-daemon-umip-0.4-nepl-mcoa/src/mh.c 2007-10-23 10:30:03.000000000 +0200 @@ -77,6 +77,7 @@ 0, /* Nonce Index */ 0, /* Binding Auth Data */ 1, /* Mobile Network Prefix */ + 1 /* Binding Unique Identifier */ }; #define __MH_SENTINEL (IP6_MH_TYPE_MAX + 1) @@ -443,6 +444,41 @@ return 0; } +/* MCoA + * mh_create_opt_bid - Create a Binding Unique Identifier Option + * MCoA TODO1: need to be adapted for BULK registration + */ +int mh_create_opt_bid(struct iovec *iov, uint16_t bid, + uint8_t priority, uint8_t bid_flags) +{ + int optlen = sizeof(struct ip6_mh_opt_bid); + struct ip6_mh_opt_bid *opt_bid; + uint8_t *data; + + iov->iov_base = malloc(optlen); + iov->iov_len = optlen; + + if (iov->iov_base == NULL) + return -ENOMEM; + + memset(iov->iov_base, 0, iov->iov_len); + data = (uint8_t *)iov->iov_base; + + opt_bid = (struct ip6_mh_opt_bid *)data; + + opt_bid->ip6mobid_type = IP6_MHOPT_BID; + opt_bid->ip6mobid_len = 4; + opt_bid->ip6mobid_bid = htons(bid); + opt_bid->ip6mobid_priority = priority; + opt_bid->ip6mobid_reserved |= bid_flags; + + data += sizeof(struct ip6_mh_opt_bid); + MDBG("BUI sub-option created with BID = %d and priority %d\n", + bid, priority); + + return 0; +} + static size_t mh_length(struct iovec *vec, int count) { size_t len = 0; @@ -487,6 +523,9 @@ case IP6_MHOPT_MOB_NET_PRFX: pad = optpad(8, 4, len); /* 8n+4 */ break; + case IP6_MHOPT_BID: + pad = optpad(2, 0, len); /* 2n */ + break; } if (pad > 0) { create_opt_pad(&out[n++], pad); @@ -497,9 +536,10 @@ out[n].iov_base = in[m].iov_base; n++; } - if (count == 1) { + if (count == 1 || len%8 != 0) { pad = optpad(8, 0, len); create_opt_pad(&out[n++], pad); + MDBG("Added %d bytes for padding\n", pad); } return n; @@ -984,11 +1024,14 @@ void mh_send_ba(const struct in6_addr_bundle *addrs, uint8_t status, uint8_t flags, uint16_t sequence, - const struct timespec *lifetime, const uint8_t *key, int iif) + const struct timespec *lifetime, + const uint16_t bid, + const uint8_t priority, + const uint8_t *key, int iif) { int iovlen = 1; struct ip6_mh_binding_ack *ba; - struct iovec mh_vec[2]; + struct iovec mh_vec[3]; MDBG("status %d\n", status); @@ -1012,6 +1055,9 @@ } if (key) mh_create_opt_auth_data(&mh_vec[iovlen++]); + /* MCoA: Create Binding Unique Identifier Option if needed */ + if (bid) + mh_create_opt_bid(&mh_vec[iovlen++], bid, priority, 0); mh_send(addrs, mh_vec, iovlen, key, iif); free_iov_data(mh_vec, iovlen); } @@ -1058,8 +1104,11 @@ return -1; if (!IN6_ARE_ADDR_EQUAL(out_addrs->bind_coa, peer_addr)) { /* check that there is no circular reference */ - if (bce_exists(our_addr, out_addrs->bind_coa)) + if (bce_exists(our_addr, out_addrs->bind_coa, + MCOA_NO_BID)) { + MDBG("Circular reference found in BC\n"); return -1; + } tssetsec(*lifetime, ntohs(bu->ip6mhbu_lifetime) << 2); } } diff -Nur mipv6-daemon-umip-0.4-nepl/src/mh.h mipv6-daemon-umip-0.4-nepl-mcoa/src/mh.h --- mipv6-daemon-umip-0.4-nepl/src/mh.h 2007-10-22 15:15:10.000000000 +0200 +++ mipv6-daemon-umip-0.4-nepl-mcoa/src/mh.h 2007-10-23 10:30:03.000000000 +0200 @@ -10,7 +10,7 @@ /* If new types or options appear, these should be updated. */ #define IP6_MH_TYPE_MAX IP6_MH_TYPE_BERROR -#define IP6_MHOPT_MAX IP6_MHOPT_MOB_NET_PRFX +#define IP6_MHOPT_MAX IP6_MHOPT_BID struct in6_addr_bundle { struct in6_addr *src; @@ -43,14 +43,20 @@ void mh_send_ba(const struct in6_addr_bundle *addrs, uint8_t status, uint8_t flags, uint16_t sequence, const struct timespec *lifetime, + const uint16_t bid, + const uint8_t priority, const uint8_t *key, int iif); static inline void mh_send_ba_err(const struct in6_addr_bundle *addrs, uint8_t status, uint8_t flags, - uint16_t seqno, const uint8_t *key, int iif) + uint16_t seqno, + const uint16_t bid, + const uint8_t priority, + const uint8_t *key, int iif) { struct timespec zero = { 0, 0 }; - mh_send_ba(addrs, status, flags, seqno, &zero, key, iif); + mh_send_ba(addrs, status, flags, seqno, &zero, + bid, priority, key, iif); } void mh_send_be(struct in6_addr *dst, @@ -79,6 +85,9 @@ int mh_create_opt_mob_net_prefix(struct iovec *iov, int mnp_count, struct list_head *mnps); +int mh_create_opt_bid(struct iovec *iov, uint16_t bid, uint8_t priority, + uint8_t bid_flags); + static inline void *mh_opt(const struct ip6_mh *mh, const struct mh_options *mh_opts, uint8_t type) { diff -Nur mipv6-daemon-umip-0.4-nepl/src/mipv6.h mipv6-daemon-umip-0.4-nepl-mcoa/src/mipv6.h --- mipv6-daemon-umip-0.4-nepl/src/mipv6.h 2007-10-22 15:15:10.000000000 +0200 +++ mipv6-daemon-umip-0.4-nepl-mcoa/src/mipv6.h 2007-10-23 10:30:03.000000000 +0200 @@ -23,6 +23,7 @@ /* Constants below have no explicit names in the spec. */ #define MAX_BINDING_LIFETIME (0xffff << 2) /* seconds */ +#define MCOA_NO_BID 0 /* BID is null */ /* Maximum time for a binding to be unused for CN to still send a BRR * before the binding expires */ diff -Nur mipv6-daemon-umip-0.4-nepl/src/mn.c mipv6-daemon-umip-0.4-nepl-mcoa/src/mn.c --- mipv6-daemon-umip-0.4-nepl/src/mn.c 2007-10-22 15:15:10.000000000 +0200 +++ mipv6-daemon-umip-0.4-nepl-mcoa/src/mn.c 2007-10-23 10:30:03.000000000 +0200 @@ -35,6 +35,8 @@ #include #include #include +#include +#include #include #include @@ -94,10 +96,44 @@ const struct timespec min_valid_bu_lifetime_ts = { MIN_VALID_BU_LIFETIME, 0 }; +struct flag_hoa_args { + struct home_addr_info *target; + struct bulentry *bule; + int flag; +}; + static int pending_bas = 0; static void mn_send_home_bu(struct home_addr_info *hai); static int mn_ext_tunnel_ops(int request, int old_if, int new_if, void *data); +static int flag_hoa(struct ifaddrmsg *ifa, struct rtattr *rta_tb[], void *arg); +static int mcoa_iface_rules_add(int iif, struct home_addr_info *hai); +static struct bulentry* mcoa_iface_rules_del(int iif, + struct home_addr_info *hai, + int replace); +static void mcoa_mn_rules_del(struct home_addr_info *hinfo); +static int nemo_mr_rules_add(struct home_addr_info *hinfo); +static void nemo_mr_rules_del(struct home_addr_info *hinfo); + +static int mcoa_mn_init_rt_table(struct bulentry *bule) +{ + /* MCoA TODO2 + * At the moment, the routing table number + * is the BID number. BID must be between + * BCE_TABLE_MIN and BCE_TABLE_MAX + */ + assert(bule); + + /* If BID is not assigned, default table is taken */ + if (!bule->bid) + return (bule->table = RT6_TABLE_MIP6); + + if (bule->bid >= BULE_TABLE_MIN && bule->bid <= BULE_TABLE_MAX) + return (bule->table = bule->bid); + + MDBG("Routing Table index is out of range"); + return -1; +} static int mn_block_rule_del(struct home_addr_info *hai) { @@ -108,9 +144,9 @@ return ret; } - if ((ret = rule_del(NULL, 0, IP6_RULE_PRIO_MIP6_BLOCK, RTN_BLACKHOLE, - &hai->hoa.addr, 128, &in6addr_any, 0, - FIB_RULE_FIND_SADDR)) < 0) + if ((ret = rule_del(NULL, 0, IP6_RULE_PRIO_MIP6_BLOCK, 0, + RTN_BLACKHOLE, &hai->hoa.addr, 128, &in6addr_any, + 0, FIB_RULE_FIND_SADDR)) < 0) MDBG("failed to delete blackhole rule.\n"); else hai->home_block &= ~HOME_ADDR_RULE_BLOCK; @@ -126,9 +162,9 @@ MDBG("blackhole is already set.\n"); return ret; } - if ((ret = rule_add(NULL, 0, IP6_RULE_PRIO_MIP6_BLOCK, RTN_BLACKHOLE, - &hai->hoa.addr, 128, &in6addr_any, 0, - FIB_RULE_FIND_SADDR)) < 0) + if ((ret = rule_add(NULL, 0, IP6_RULE_PRIO_MIP6_BLOCK, + 0, RTN_BLACKHOLE, &hai->hoa.addr, 128, + &in6addr_any, 0, FIB_RULE_FIND_SADDR)) < 0) MDBG("failed to add blackhole rule.\n"); else hai->home_block |= HOME_ADDR_RULE_BLOCK; @@ -156,7 +192,10 @@ struct home_addr_info *hai = e->home; struct in6_addr hoa, peer_addr; int type = e->type; + int rules = e->rules; + int if_coa = e->if_coa; uint16_t flags = e->flags; + uint16_t bid = e->bid; if (type != BUL_ENTRY) return; @@ -173,7 +212,7 @@ if (hai->at_home) return; - e = create_bule(&hoa, &peer_addr); + e = create_bule(&hoa, &peer_addr, hai->reg_mcoa?bid:0); if (e == NULL) return; @@ -185,6 +224,19 @@ e->lifetime = NON_MIP_CN_LTIME_TS; e->delay = NON_MIP_CN_LTIME_TS; e->callback = bul_expire; + e->priority = get_prio_from_bid(e->bid); + e->mcoa_dereg = 0; + /* MCoA: No tunnels are linked to this BULE anymore */ + e->if_tunnel = 0; + /* MCoA: We prevent MIPL to install rules for this entry */ + e->rules = 1; + mcoa_mn_init_rt_table(e); + + /* MCoA: Erase rules corresponding to this BULE */ + /* TODO2: this should be done when bul is deleted */ + if(hai->reg_mcoa && rules) + mcoa_iface_rules_del(if_coa, hai, 1); + if (bul_add(e) < 0) bul_delete(e); } @@ -279,7 +331,10 @@ pthread_rwlock_wrlock(&mn_lock); if (mn_rr_error_check(laddr, raddr, &addr)) laddr = &addr; - e = bul_get(NULL, laddr, raddr); + /* MCoA TODO1 + * All bule with same laddr, raddr should be invalidated + */ + e = bul_get(NULL, laddr, raddr, get_bid_from_ifindex(iif)); if (e != NULL) { if (e->flags & IP6_MH_BU_HOME) { if (!conf.UseMnHaIPsec && @@ -328,6 +383,19 @@ } if (bule->flags & IP6_MH_BU_HOME) { struct home_addr_info *hai = bule->home; + /* MCoA TODO1: to update according to latest draft + * Binding Unique Identifier Option + * For deregistration, BUI option is sent only + * when some specific entry needs to be deleted. + * If all entries are deleted, no need to send BUI + */ + if ((bu->ip6mhbu_lifetime || !bule->mcoa_dereg) + && hai->reg_mcoa && bule->bid + && mh_create_opt_bid(&iov[iov_ind++], bule->bid, + bule->priority, 0) < 0) { + free_iov_data(iov, iov_ind); + return -ENOMEM; + } if (bule->flags & IP6_MH_BU_MR && bu->ip6mhbu_lifetime && bule->home->mnp_count > 0 && conf.MobRtrUseExplicitMode && mh_create_opt_mob_net_prefix(&iov[iov_ind++], @@ -353,7 +421,29 @@ addrs.remote_coa = NULL; addrs.bind_coa = &bule->coa; + /* MCoA + * before sending BU, if !bule->rules then + * add rule from HoA to HA in table bule->table + * This to be able to send a BU if no rules are in + * the RPDB (eg. because of the Reliability option) + */ + if(bule->home->reg_mcoa && !bule->rules) + /* MCoA: Rule for HoA to HA from the MN */ + rule_add(NULL, bule->table, + IP6_RULE_PRIO_MIP6_HOA_OUT_MCOA, 0, RTN_UNICAST, + &bule->home->hoa.addr, 128, &bule->peer_addr, 128, + FIB_RULE_FIND_SADDR); + ret = mh_send(&addrs, iov, iov_ind, bind_key, bule->if_coa); + + /* MCoA + * Delete the previously installed rule + */ + if(bule->home->reg_mcoa && !bule->rules) + rule_del(NULL, bule->table, + IP6_RULE_PRIO_MIP6_HOA_OUT_MCOA, 0, RTN_UNICAST, + &bule->home->hoa.addr, 128, &bule->peer_addr, 128, + FIB_RULE_FIND_SADDR); if (ret <= 0) MDBG("mh_send failed ret: %d\n", ret); @@ -363,15 +453,35 @@ return ret; } +struct mn_addr *mcoa_get_current_coa(struct home_addr_info *hai, int iif) +{ + struct list_head *l; + + if(!hai->reg_mcoa) + return hai->current_coa; + + list_for_each(l, &hai->mcoa) { + struct mn_addr *coa_entry; + coa_entry = list_entry(l, struct mn_addr, list); + if (coa_entry->iif == iif) + return coa_entry; + } + + return NULL; +} + static int mn_get_home_lifetime(struct home_addr_info *hai, - struct timespec *lifetime, int dereg) + struct timespec *lifetime, + int iif, int dereg) { if (!hai->at_home && !dereg) { - struct mn_addr *coa = &hai->primary_coa; + /* MCoA: Pick up the CoA from the correct iface */ + struct mn_addr *coa = mcoa_get_current_coa(hai, iif); struct timespec now; unsigned long coa_lft; unsigned long hoa_lft; + assert(coa); clock_gettime(CLOCK_REALTIME, &now); coa_lft = mpd_curr_lft(&now, &coa->timestamp, @@ -403,16 +513,20 @@ if (!hai->at_home && !dereg) { struct bulentry *e; - e = bul_get(hai, NULL, &hai->ha_addr); + e = bul_get(hai, NULL, &hai->ha_addr, + hai->reg_mcoa? + get_bid_from_ifindex(hai->current_coa->iif) + :MCOA_NO_BID); if (e == NULL || !(e->flags & IP6_MH_BU_HOME)) { MDBG("No valid home registration"); } else { struct timespec now; - struct mn_addr *coa = &hai->primary_coa; + struct mn_addr *coa = hai->current_coa; unsigned long coa_lft; unsigned long home_lft; + assert(coa); clock_gettime(CLOCK_REALTIME, &now); coa_lft = mpd_curr_lft(&now, &coa->timestamp, @@ -432,6 +546,21 @@ return 1; } +/* MCoA + * Deregister a specific bule + */ +static int mn_dereg_bule(struct bulentry *bule) +{ + if (bule->type != NON_MIP_CN_ENTRY) { + bule->seq++; + bule->mcoa_dereg = 0; + tsclear(bule->lifetime); + mn_send_bu_msg(bule); + } + bul_delete(bule); + return 0; +} + static int mn_dereg(void *vbule, void *arg) { struct bulentry *bule = vbule; @@ -445,7 +574,20 @@ } bule->seq++; tsclear(bule->lifetime); - mn_send_bu_msg(bule); + /* MCoA + * When a MN wants to deregister Multiple CoA, + * one BU is enough to deregister all the CoA + */ + if(bule->home->reg_mcoa && bule->mcoa_dereg) { + mn_send_bu_msg(bule); + /* MCoA + * Flag the other entries that match the + * peer address (to avoid sending multiple + * BUs) + */ + mcoa_bule_dereg(bule, 0); + } else if (!bule->home->reg_mcoa) + mn_send_bu_msg(bule); } bul_delete(bule); return 0; @@ -486,8 +628,9 @@ int dereg; if (bule->flags & IP6_MH_BU_HOME) - dereg = mn_get_home_lifetime(bule->home, &bule->lifetime, 0); - else + dereg = mn_get_home_lifetime(bule->home, &bule->lifetime, + bule->if_coa, 0); + else dereg = mn_get_ro_lifetime(bule->home, &bule->lifetime, bule->dereg); @@ -544,6 +687,7 @@ if (!task_interrupted()) { struct bulentry *bule = tq_data(tqe, struct bulentry, tqe); int expired; + int iif = bule->if_coa; MDBG("Bul refresh type: %d\n", bule->type); clock_gettime(CLOCK_REALTIME, &bule->lastsent); @@ -554,6 +698,10 @@ bule->seq++; bule->callback = bu_resend; + /* MCoA : Check for interface reliability */ + if(bule->home->reg_mcoa + && get_reliable_from_ifindex(iif) == 0) + mcoa_iface_rules_del(iif, bule->home, 1); pre_bu_bul_update(bule); mn_send_bu_msg(bule); @@ -626,14 +774,53 @@ return 0; } -int nemo_mr_tnl_routes_add(struct home_addr_info *hai, int ifindex) +/* + * Copy home address between two interfaces + */ +static int cp_hoa(struct ifaddrmsg *ifa, struct rtattr *rta_tb[], void *arg) +{ + struct mv_hoa_args *mha = arg; + struct home_addr_info *hai = mha->target; + struct mn_addr *hoa = &hai->hoa; + int err; + struct timespec now; + uint32_t preferred = PREFIX_LIFETIME_INFINITE; + uint32_t valid = PREFIX_LIFETIME_INFINITE; + int plen = (mha->if_next == hai->if_tunnel ? 128 : hai->plen); + + clock_gettime(CLOCK_REALTIME, &now); + + if (hai->lladdr_comp && rta_tb[IFA_CACHEINFO] != NULL) { + struct ifa_cacheinfo *ci = RTA_DATA(rta_tb[IFA_CACHEINFO]); + mn_update_hoa_lifetime(hoa, &now, + ci->ifa_valid, ci->ifa_prefered); + valid = ci->ifa_valid; + preferred = ci->ifa_prefered; + } + if (mha->if_next == ifa->ifa_index) + return 0; + + MDBG("Copy HoA %x:%x:%x:%x:%x:%x:%x:%x/%d from iface %d to %d\n", + NIP6ADDR(&hoa->addr), plen, ifa->ifa_index, mha->if_next); + + err = addr_add(&hoa->addr, plen, + ifa->ifa_flags|IFA_F_HOMEADDRESS, + ifa->ifa_scope, mha->if_next, preferred, valid); + if (err < 0) + return err; + + return 0; +} + +int nemo_mr_tnl_routes_add(struct home_addr_info *hai, + int ifindex, int rtable) { struct list_head *l; struct prefix_list_entry *pe; list_for_each(l, &hai->mob_net_prefixes) { struct prefix_list_entry *p; p = list_entry(l, struct prefix_list_entry, list); - if (route_add(ifindex, RT6_TABLE_MIP6, RTPROT_MIP, + if (route_add(ifindex, rtable, RTPROT_MIP, 0, IP6_RT_PRIO_MIP6_FWD, &p->ple_prefix, p->ple_plen, &in6addr_any, 0, NULL) < 0) { @@ -646,7 +833,7 @@ list_for_each(l, &hai->mob_net_prefixes) { struct prefix_list_entry *p; p = list_entry(l, struct prefix_list_entry, list); - route_del(ifindex, RT6_TABLE_MIP6, IP6_RT_PRIO_MIP6_FWD, + route_del(ifindex, rtable, IP6_RT_PRIO_MIP6_FWD, &p->ple_prefix, p->ple_plen, &in6addr_any, 0, NULL); if (p == pe) break; @@ -654,44 +841,47 @@ return -1; } -static int mn_tnl_state_add(struct home_addr_info *hai, int ifindex, int all) +static int mn_tnl_state_add(struct home_addr_info *hai, + int ifindex, int all, int rtable) { int err = 0; if (hai->home_reg_status != HOME_REG_NONE) { if ((err = mn_ro_pol_add(hai, ifindex, all)) < 0) return err; - if ((err = route_add(ifindex, RT6_TABLE_MIP6, RTPROT_MIP, 0, + if ((err = route_add(ifindex, rtable, RTPROT_MIP, 0, IP6_RT_PRIO_MIP6_OUT, &in6addr_any, 0, &in6addr_any, 0, NULL)) < 0) { mn_ro_pol_del(hai, ifindex, all); } } if (hai->mob_rtr && - (err = nemo_mr_tnl_routes_add(hai, ifindex)) < 0) { - route_del(ifindex, RT6_TABLE_MIP6, IP6_RT_PRIO_MIP6_OUT, + (err = nemo_mr_tnl_routes_add(hai, ifindex, rtable)) < 0) { + route_del(ifindex, rtable, IP6_RT_PRIO_MIP6_OUT, &hai->hoa.addr, 128, &in6addr_any, 0, NULL); mn_ro_pol_del(hai, ifindex, all); } return err; } -static void nemo_mr_tnl_routes_del(struct home_addr_info *hai, int ifindex) +static void nemo_mr_tnl_routes_del(struct home_addr_info *hai, + int ifindex, int rtable) { struct list_head *l; list_for_each(l, &hai->mob_net_prefixes) { struct prefix_list_entry *p; p = list_entry(l, struct prefix_list_entry, list); - route_del(ifindex, RT6_TABLE_MIP6, IP6_RT_PRIO_MIP6_FWD, + route_del(ifindex, rtable, IP6_RT_PRIO_MIP6_FWD, &p->ple_prefix, p->ple_plen, &in6addr_any, 0, NULL); } } -static void mn_tnl_state_del(struct home_addr_info *hai, int ifindex, int all) +static void mn_tnl_state_del(struct home_addr_info *hai, int ifindex, + int all, int rtable) { if (hai->home_reg_status != HOME_REG_NONE) { if (hai->mob_rtr) - nemo_mr_tnl_routes_del(hai, ifindex); - route_del(ifindex, RT6_TABLE_MIP6, IP6_RT_PRIO_MIP6_OUT, + nemo_mr_tnl_routes_del(hai, ifindex, rtable); + route_del(ifindex, rtable, IP6_RT_PRIO_MIP6_OUT, &hai->hoa.addr, 128, &in6addr_any, 0, NULL); mn_ro_pol_del(hai, ifindex, all); } @@ -715,8 +905,18 @@ static void mn_pol_ext_cleanup(struct bulentry *bule) { MDBG("\n"); - mpd_cancel_mps(&bule->hoa, &bule->peer_addr); - mn_tnl_state_del(bule->home, bule->home->if_tunnel, 0); + /* MCoA + * MPS is canceled only if the BULE is the last entry + * for the HoA, or when at home + */ + if (mcoa_bule_count(bule) == 0 || bule->home->at_home) + mpd_cancel_mps(&bule->hoa, &bule->peer_addr); + if (bule->home->reg_mcoa) + mn_tnl_state_del(bule->home, bule->if_tunnel, 0, + bule->table); + else + mn_tnl_state_del(bule->home, bule->home->if_tunnel, + 0, bule->table); if (conf.UseMnHaIPsec) { mn_ipsec_tnl_update(&bule->peer_addr, &bule->hoa, bule); @@ -730,12 +930,13 @@ struct timespec *lifetime) { int err = 0; + assert(hai->current_coa); bule->type = BUL_ENTRY; bule->flags = (IP6_MH_BU_HOME | IP6_MH_BU_ACK | hai->lladdr_comp | hai->mob_rtr); bule->coa_changed = -1; - bule->coa = hai->primary_coa.addr; - bule->if_coa = hai->primary_coa.iif; + bule->coa = hai->current_coa->addr; + bule->if_coa = hai->current_coa->iif; bule->lifetime = *lifetime; bule->delay = conf.InitialBindackTimeoutFirstReg_ts; bule->callback = bu_resend; @@ -744,10 +945,47 @@ bule->ext_cleanup = mn_pol_ext_cleanup; bule->home = hai; bule->consecutive_resends = 0; + bule->priority = get_prio_from_bid(bule->bid); hai->home_reg_status = HOME_REG_UNCERTAIN; - if ((err = mn_tnl_state_add(hai, hai->if_tunnel, 0)) < 0) + /* MCoA: MCoA requires one tunnel per CoA. Need to create a new one */ + if (bule->if_tunnel == 0 + && hai->reg_mcoa + && get_bid_from_ifindex(hai->current_coa->iif)) { + MDBG("Creating new tunnel for iface %d (BID %d)\n", + hai->current_coa->iif, + get_bid_from_ifindex(hai->current_coa->iif)); + bule->if_tunnel = tunnel_add(&hai->hoa.addr, + &hai->ha_addr, + hai->current_coa->iif, + NULL, NULL); + if (bule->if_tunnel <= 0) { + MDBG("Failed to create MN-HA tunnel\n"); + return -1; + } else { + MDBG("MN-HA tunnel created (iface tnl = %d)\n", + bule->if_tunnel); + struct flag_hoa_args arg; + arg.target = hai; + arg.flag = 1; + arg.bule = bule; + if (addr_do(&hai->hoa.addr, 128, + bule->if_tunnel, &arg, + flag_hoa) < 0) { + MDBG("addr_do failed for tunnel %d\n", + bule->if_tunnel); + return -1; + } + } + } else if (bule->if_tunnel == 0) + /* MCoA + * If MCoA is not used, then initialise the tunnel + * number to the one in hai + */ + bule->if_tunnel = hai->if_tunnel; + + if ((err = mn_tnl_state_add(hai, bule->if_tunnel, 0, bule->table)) < 0) MDBG("Failed to initialize new bule for HA\n"); else MDBG("New bule for HA\n"); @@ -781,14 +1019,18 @@ movement_t type_movement = MIP6_TYPE_MOVEMENT_UNKNOWN; TRACE; + assert(hai->current_coa); if (IN6_IS_ADDR_UNSPECIFIED(&hai->ha_addr)) { MDBG("HA not set for home link\n"); return; } - mn_get_home_lifetime(hai, &lifetime, 0); + mn_get_home_lifetime(hai, &lifetime, hai->current_coa->iif, 0); - if ((bule = bul_get(hai, NULL, &hai->ha_addr)) == NULL) { + if ((bule = bul_get(hai, NULL, &hai->ha_addr, + hai->reg_mcoa? + get_bid_from_ifindex(hai->current_coa->iif) + :MCOA_NO_BID)) == NULL) { assert(!hai->at_home); /* Create new bul entry for HA */ if (!tsisset(lifetime)) { @@ -807,14 +1049,22 @@ mn_change_ha(hai); return; } - bule = create_bule(&hai->hoa.addr, &hai->ha_addr); + bule = create_bule(&hai->hoa.addr, &hai->ha_addr, + hai->reg_mcoa? + get_bid_from_ifindex(hai->current_coa->iif) + :MCOA_NO_BID); if (bule == NULL) return; - if (process_first_home_bu(bule, hai, &lifetime) < 0 || + if (mcoa_mn_init_rt_table(bule) < 0 || + process_first_home_bu(bule, hai, &lifetime) < 0 || bul_add(bule) < 0) { bul_delete(bule); return; } + /* MCoA: Check for interface reliability */ + if(get_reliable_from_ifindex(bule->if_coa) > 0) + /* MCoA: Add the rules */ + mcoa_iface_rules_add(hai->current_coa->iif, hai); type_movement = MIP6_TYPE_MOVEMENT_HL2FL; MDBG("New bule for HA\n"); } else if (bule->type == BUL_ENTRY) { @@ -829,8 +1079,8 @@ } type_movement = MIP6_TYPE_MOVEMENT_HL2FL; } else { - bule->coa = hai->primary_coa.addr; - bule->if_coa = hai->primary_coa.iif; + bule->coa = hai->current_coa->addr; + bule->if_coa = hai->current_coa->iif; /* Rate limiting home registration binding updates is necessary for multihomed MNs */ @@ -850,6 +1100,10 @@ bule->callback = bu_resend; } bule->seq++; + /* MCoA: Check for interface reliability */ + if(bule->home->reg_mcoa + && get_reliable_from_ifindex(bule->if_coa) == 0) + mcoa_iface_rules_del(bule->if_coa, bule->home, 1); pre_bu_bul_update(bule); MDBG("Bule for HA exists. Updating it.\n"); } else { @@ -885,10 +1139,13 @@ &bule->hoa, bule); } } + /* MCoA: Update the current routing table */ + hai->table = bule->table; /* Before bul_iterate, tunnel modification should be done. */ - tunnel_mod(hai->if_tunnel, &hai->primary_coa.addr, &hai->ha_addr, - hai->primary_coa.iif, mn_ext_tunnel_ops, hai); - + bule->if_tunnel = tunnel_mod(bule->if_tunnel, &hai->current_coa->addr, + &hai->ha_addr, hai->current_coa->iif, + mn_ext_tunnel_ops, hai); + hai->if_tunnel = bule->if_tunnel; bule->last_coa = bule->coa; bule->coa_changed = 0; @@ -1017,14 +1274,87 @@ return -1; } +static int mn_disable_mcoa(struct bulentry *bule) +{ +/* + struct bulentry *bule_del; + struct home_addr_info *hai; + struct flag_hoa_args arg; + struct list_head *l, *tmp; + struct net_iface *best_iface = NULL; +*/ + + /* MCoA TODO1: At the moment we simply exit */ + MDBG("HA does not accept MCoA registration. Check "\ + "your HA or change your MR configuration\n"); + kill(0, SIGINT); + return 0; + +/* + assert(bule); + hai = bule->home; + mcoa_mn_rules_del(hai); + while((bule_del = bul_get(hai, NULL, + &bule->peer_addr, MCOA_NO_BID))) + bul_delete(bule_del); + + hai->if_tunnel = tunnel_add(&hai->hoa.addr, &hai->ha_addr, + hai->if_home, NULL, NULL); + if (hai->if_tunnel <= 0) { + MDBG("failed to create MN-HA tunnel\n"); + goto clean_err; + } + + arg.target = hai; + arg.flag = 1; + if (addr_do(&hai->hoa.addr, 128, hai->if_tunnel, + &arg, flag_hoa) < 0) + goto clean_err; + + nemo_mr_rules_del(hai); + hai->reg_mcoa = 0; + nemo_mr_rules_add(hai); + if (rule_add(NULL, RT6_TABLE_MIP6, IP6_RULE_PRIO_MIP6_HOA_OUT, 0, + RTN_UNICAST, &hai->hoa.addr, + 128, &in6addr_any, 0, FIB_RULE_FIND_SADDR) < 0) + goto clean_err; + + list_for_each(l, &conf.net_ifaces) { + struct net_iface *iface = NULL; + iface = list_entry(l, struct net_iface, list); + if (!best_iface + || iface->mn_if_bidprio > best_iface->mn_if_bidprio) + best_iface = iface; + } + list_for_each_safe(l, tmp, &conf.net_ifaces) { + struct net_iface *iface = NULL; + iface = list_entry(l, struct net_iface, list); + if (iface != best_iface) { + MDBG("Deleting iface %d\n", iface->ifindex); + list_del(l); + } + } + return 1; + +clean_err: + rule_del(NULL, RT6_TABLE_MIP6, + IP6_RULE_PRIO_MIP6_HOA_OUT, 0, RTN_UNICAST, + &hai->hoa.addr, 128, &in6addr_any, 0, FIB_RULE_FIND_SADDR); + tunnel_del(hai->if_tunnel, NULL, NULL); + + return 0; +*/ +} + static void mn_recv_ba(const struct ip6_mh *mh, ssize_t len, const struct in6_addr_bundle *in, int iif) { struct ip6_mh_binding_ack *ba; + struct ip6_mh_opt_bid *bui; struct mh_options mh_opts; struct bulentry *bule; struct timespec now, ba_lifetime, br_adv; - uint16_t seqno; + uint16_t seqno, bid; TRACE; @@ -1036,25 +1366,32 @@ ba = (struct ip6_mh_binding_ack *)mh; pthread_rwlock_wrlock(&mn_lock); - bule = bul_get(NULL, in->dst, in->src); + + /* MCoA: Lookup in BA options for the BUI sub-option and the BID */ + bui = mh_opt(&ba->ip6mhba_hdr, &mh_opts, IP6_MHOPT_BID); + bid = bui ? ntohs(bui->ip6mobid_bid) : MCOA_NO_BID; + + bule = bul_get(NULL, in->dst, in->src, bid); if (!bule || bule->type != BUL_ENTRY) { MDBG("Got BA without corresponding BUL entry " "from %x:%x:%x:%x:%x:%x:%x:%x " "to home address %x:%x:%x:%x:%x:%x:%x:%x " - "with coa %x:%x:%x:%x:%x:%x:%x:%x\n", + "with coa %x:%x:%x:%x:%x:%x:%x:%x" + "and bid %d\n", NIP6ADDR(in->src), NIP6ADDR(in->dst), NIP6ADDR(in->local_coa != NULL ? - in->local_coa : &in6addr_any)); + in->local_coa : &in6addr_any), + bid); pthread_rwlock_unlock(&mn_lock); return; } dbg("Got BA from %x:%x:%x:%x:%x:%x:%x:%x " "to home address %x:%x:%x:%x:%x:%x:%x:%x " - "with coa %x:%x:%x:%x:%x:%x:%x:%x and status %d\n", + "with coa %x:%x:%x:%x:%x:%x:%x:%x, bid %d and status %d\n", NIP6ADDR(in->src), NIP6ADDR(in->dst), NIP6ADDR(in->local_coa != NULL ? in->local_coa : &in6addr_any), - ba->ip6mhba_status); + bid, ba->ip6mhba_status); dbg("Dumping corresponding BULE\n"); dbg_func(bule, dump_bule); /* First check authenticator */ @@ -1076,6 +1413,17 @@ return; } } + /* MCoA: Check if BID matches */ + if (bule->bid && !bui) { + /* The BACK did not have any BID suboption whereas + * we sent a BU with a BID option. MCoA is disabled + * on the node. + */ + MDBG("Got BA with no BID option, disabling MCoA registration.\n"); + mn_disable_mcoa(bule); + pthread_rwlock_unlock(&mn_lock); + return; + } bule->do_send_bu = 0; bule->consecutive_resends = 0; clock_gettime(CLOCK_REALTIME, &now); @@ -1086,11 +1434,18 @@ bule->seq = seqno + 1; if (bule->flags & IP6_MH_BU_HOME) mn_get_home_lifetime(bule->home, - &bule->lifetime, 0); + &bule->lifetime, + bule->if_coa, + 0); else mn_get_ro_lifetime(bule->home, &bule->lifetime, 0); bule->callback = bu_resend; + /* MCoA: Check for interface reliability */ + if(bule->home->reg_mcoa + && get_reliable_from_ifindex(bule->if_coa) == 0) + mcoa_iface_rules_del(bule->if_coa, + bule->home, 1); pre_bu_bul_update(bule); mn_send_bu_msg(bule); bule->delay = conf.InitialBindackTimeoutReReg_ts; @@ -1137,6 +1492,10 @@ tssetsec(ba_lifetime, ntohs(ba->ip6mhba_lifetime) << 2); br_adv = ba_lifetime; tsadd(bule->lastsent, ba_lifetime, bule->hard_expire); + /* MCoA: Check for interface reliability */ + if(bule->home->reg_mcoa + && get_reliable_from_ifindex(bule->if_coa) == 0) + mcoa_iface_rules_add(bule->if_coa, bule->home); if (!(bule->flags & IP6_MH_BU_HOME) || !conf.OptimisticHandoff) post_ba_bul_update(bule); if (bule->flags & IP6_MH_BU_HOME) { @@ -1266,11 +1625,6 @@ return NULL; } -struct flag_hoa_args { - struct home_addr_info *target; - int flag; -}; - static int flag_hoa(struct ifaddrmsg *ifa, struct rtattr *rta_tb[], void *arg) { /* @@ -1279,13 +1633,18 @@ struct flag_hoa_args *fhoa = arg; struct home_addr_info *hai = fhoa->target; + struct bulentry *bule = fhoa->bule; struct mn_addr *hoa = &hai->hoa; struct in6_addr *addr = RTA_DATA(rta_tb[IFA_ADDRESS]); struct timespec now; uint32_t preferred; uint32_t valid; - int err; - int plen = (ifa->ifa_index == hai->if_tunnel ? 128 : hai->plen); + int err, plen; + + if (hai->reg_mcoa && bule != NULL) + plen = (ifa->ifa_index == bule->if_tunnel ? 128 : hai->plen); + else + plen = (ifa->ifa_index == hai->if_tunnel ? 128 : hai->plen); clock_gettime(CLOCK_REALTIME, &now); @@ -1321,6 +1680,220 @@ return 0; } +static int mcoa_iface_dflt_hoa_rule_del(struct bulentry *bule, + struct bulentry *best_bule, + struct home_addr_info *hai, + int replace) +{ + /* MCoA: Delete the default HoA rule */ + rule_del(NULL, bule->bid, IP6_RULE_PRIO_MIP6_HOA_OUT, + 0, RTN_UNICAST, &hai->hoa.addr, 128, + &in6addr_any, 0, FIB_RULE_FIND_SADDR); + + /* MCoA + * Do not replace the default rules + * if there are no other best bule + */ + if(best_bule && best_bule != bule && replace) { + if (rule_add(NULL, best_bule->bid, + IP6_RULE_PRIO_MIP6_HOA_OUT, 0, RTN_UNICAST, + &hai->hoa.addr, 128, &in6addr_any, 0, + FIB_RULE_FIND_SADDR) < 0) + return -1; + } + return 0; +} + +static void mcoa_iface_dflt_mnp_rules_del(struct bulentry *bule, + struct bulentry *best_bule, + struct home_addr_info *hai, + struct prefix_list_entry *p, + int replace) +{ + /* MCoA: Delete the default MNP rule */ + rule_del(NULL, bule->bid, IP6_RULE_PRIO_MIP6_FWD_MCOA, + 0, RTN_UNICAST, &p->ple_prefix, p->ple_plen, + &in6addr_any, 0, 0); + + /* MCoA + * Do not replace the default rules + * if there are no other best bule + */ + if(best_bule && best_bule != bule && replace) { + rule_add(NULL, best_bule->bid, + IP6_RULE_PRIO_MIP6_FWD_MCOA, 0, RTN_UNICAST, + &p->ple_prefix, p->ple_plen, &in6addr_any, 0, 0); + } +} + +static struct bulentry* mcoa_iface_rules_del(int iif, + struct home_addr_info *hai, + int replace) +{ + uint16_t bid = hai->reg_mcoa?get_bid_from_ifindex(iif):MCOA_NO_BID; + uint8_t prio_tmp = 0; + struct bulentry *bule, *best_bule = NULL; + struct list_head *l; + int delete_dflt_mnp = 0; + + bule = bul_get(hai, NULL, &hai->ha_addr, bid); + if (!hai->reg_mcoa || (bule && !bule->rules)) + return NULL; + + /* MCoA: Delete rule for HoA traffic from the MN */ + rule_del(NULL, bid, IP6_RULE_PRIO_MIP6_HOA_OUT_MCOA, + /* fwmark */ bid, RTN_UNICAST, + &hai->hoa.addr, 128, &in6addr_any, 0, + FIB_RULE_FIND_SADDR); + + bul_iterate_in(&hai->ha_addr, bul_highest_priority, + &best_bule); + if(bule && best_bule == bule) { + delete_dflt_mnp = 1; + /* MCoA: search for the next best BULE */ + prio_tmp = bule->priority; + bule->priority = 0; + bul_iterate_in(&hai->ha_addr, + bul_highest_priority, &best_bule); + bule->priority = prio_tmp; + /* MCoA: delete the default HoA rule */ + mcoa_iface_dflt_hoa_rule_del(bule, best_bule, hai, replace); + } + + list_for_each(l, &hai->mob_net_prefixes) { + struct prefix_list_entry *p = NULL; + p = list_entry(l, struct prefix_list_entry, list); + /* MCoA: Delete MNP rule */ + rule_del(NULL, bid, IP6_RULE_PRIO_MIP6_FWD_FWM, + /*fwmark*/ bid, RTN_UNICAST, &p->ple_prefix, + p->ple_plen, &in6addr_any, 0, 0); + if(delete_dflt_mnp) { + /* MCoA: Delete default MNP rule */ + mcoa_iface_dflt_mnp_rules_del(bule, best_bule, + hai, p, replace); + } + } + + if (bule) { + bule->rules = 0; + MDBG("MNP and HoA rules deleted for iface %d bid %d\n", + iif, bule->bid); + } + + return bule; +} + +static void mcoa_mn_rules_del(struct home_addr_info *hinfo) +{ + struct list_head *l; + + list_for_each(l, &hinfo->mcoa) { + struct mn_addr *iface = NULL; + iface = list_entry(l, struct mn_addr, list); + mcoa_iface_rules_del(iface->iif, hinfo, 0); + } + rule_del(NULL, 0, IP6_RULE_PRIO_MIP6_HOA_OUT, + 0, RTN_UNICAST, &hinfo->hoa.addr, 128, + &in6addr_any, 0, FIB_RULE_FIND_SADDR); + if (hinfo->at_home && hinfo->reg_mcoa) { + list_for_each(l, &hinfo->mob_net_prefixes) { + struct prefix_list_entry *p = NULL; + p = list_entry(l, struct prefix_list_entry, + list); + rule_del(NULL, RT6_TABLE_MIP6, + IP6_RULE_PRIO_MIP6_FWD, 0, + RTN_UNICAST, &p->ple_prefix, + p->ple_plen, &in6addr_any, 0, 0); + } + } +} + +static int mcoa_iface_rules_add(int iif, struct home_addr_info *hai) +{ + uint16_t bid = hai->reg_mcoa?get_bid_from_ifindex(iif):MCOA_NO_BID; + struct bulentry *bule, *best_bule = NULL; + struct prefix_list_entry *pe = NULL; + struct list_head *l; + uint8_t prio; + + bule = bul_get(hai, NULL, &hai->ha_addr, bid); + if (!bid || !hai->reg_mcoa || !bule || (bule && bule->rules)) + return 0; + + prio = get_prio_from_bid(bid); + + /* MCoA: Rule for HoA traffic from the MN */ + if (rule_add(NULL, bid, IP6_RULE_PRIO_MIP6_HOA_OUT_MCOA, + /* fwmark */ bid, RTN_UNICAST, + &hai->hoa.addr, 128, &in6addr_any, 0, + FIB_RULE_FIND_SADDR) < 0) + goto undo; + + bul_iterate_in(&hai->ha_addr, bul_highest_priority, + &best_bule); + + if (!best_bule || prio >= best_bule->priority) { + rule_del(NULL, 0, IP6_RULE_PRIO_MIP6_HOA_OUT, + 0, RTN_UNICAST, &hai->hoa.addr, 128, + &in6addr_any, 0, FIB_RULE_FIND_SADDR); + if (rule_add(NULL, bid, IP6_RULE_PRIO_MIP6_HOA_OUT, + 0, RTN_UNICAST, &hai->hoa.addr, 128, + &in6addr_any, 0, FIB_RULE_FIND_SADDR) < 0) + goto undo; + } + + /* MCoA: Rule for forwarded traffic from the NEMO */ + list_for_each(l, &hai->mob_net_prefixes) { + struct prefix_list_entry *p = NULL; + p = list_entry(l, struct prefix_list_entry, list); + if (rule_add(NULL, bid, IP6_RULE_PRIO_MIP6_FWD_FWM, + /*fwmark*/ bid, RTN_UNICAST, + &p->ple_prefix, p->ple_plen, + &in6addr_any, 0, 0) < 0) { + pe = p; + goto undo; + } + /* MCoA + * Add default rules if the bule has the highest + * priority + */ + if (!best_bule || prio >= best_bule->priority) { + rule_del(NULL, 0, IP6_RULE_PRIO_MIP6_FWD_MCOA, + 0, RTN_UNICAST, &p->ple_prefix, p->ple_plen, + &in6addr_any, 0, 0); + if (rule_add(NULL, bid, IP6_RULE_PRIO_MIP6_FWD_MCOA, + 0, RTN_UNICAST, &p->ple_prefix, p->ple_plen, + &in6addr_any, 0, 0) < 0) + goto undo; + } + } + + bule->rules = 1; + MDBG("MNP and HoA Rules added for interface %d.\n", iif); + return 0; + +undo: + rule_del(NULL, bid, IP6_RULE_PRIO_MIP6_HOA_OUT_MCOA, + /* fwmark */ bid, RTN_UNICAST, + &hai->hoa.addr, 128, &in6addr_any, 0, + FIB_RULE_FIND_SADDR); + + rule_del(NULL, bid, IP6_RULE_PRIO_MIP6_HOA_OUT, + 0, RTN_UNICAST, &hai->hoa.addr, 128, + &in6addr_any, 0, FIB_RULE_FIND_SADDR); + + list_for_each(l, &hai->mob_net_prefixes) { + struct prefix_list_entry *p = NULL; + p = list_entry(l, struct prefix_list_entry, list); + rule_del(NULL, bid, IP6_RULE_PRIO_MIP6_FWD_FWM, + /*fwmark*/ bid, RTN_UNICAST, &p->ple_prefix, + p->ple_plen, &in6addr_any, 0, 0); + if (p == pe) + break; + } + return -1; +} + static void nemo_mr_rules_del(struct home_addr_info *hinfo) { struct list_head *l; @@ -1328,11 +1901,17 @@ list_for_each(l, &hinfo->mob_net_prefixes) { struct prefix_list_entry *p = NULL; p = list_entry(l, struct prefix_list_entry, list); - rule_del(NULL, RT6_TABLE_MIP6, - IP6_RULE_PRIO_MIP6_FWD, RTN_UNICAST, - &p->ple_prefix, p->ple_plen, &in6addr_any, 0, 0); + if (hinfo->reg_mcoa) { + rule_del(NULL, 0, IP6_RULE_PRIO_MIP6_FWD_MCOA, 0, + RTN_UNICAST, &p->ple_prefix, p->ple_plen, + &in6addr_any, 0, 0); + } else + rule_del(NULL, RT6_TABLE_MIP6, + IP6_RULE_PRIO_MIP6_FWD, 0, RTN_UNICAST, + &p->ple_prefix, p->ple_plen, &in6addr_any, + 0, 0); rule_del(NULL, RT6_TABLE_MAIN, - IP6_RULE_PRIO_MIP6_MNP_IN, RTN_UNICAST, + IP6_RULE_PRIO_MIP6_MNP_IN, 0, RTN_UNICAST, &in6addr_any, 0, &p->ple_prefix, p->ple_plen, 0); } } @@ -1346,19 +1925,21 @@ struct prefix_list_entry *p = NULL; p = list_entry(l, struct prefix_list_entry, list); if (rule_add(NULL, RT6_TABLE_MAIN, - IP6_RULE_PRIO_MIP6_MNP_IN, RTN_UNICAST, + IP6_RULE_PRIO_MIP6_MNP_IN, 0, RTN_UNICAST, &in6addr_any, 0, &p->ple_prefix, p->ple_plen, 0) < 0) { pe = p; goto undo; } - if (rule_add(NULL, RT6_TABLE_MIP6, - IP6_RULE_PRIO_MIP6_FWD, RTN_UNICAST, - &p->ple_prefix, p->ple_plen, - &in6addr_any, 0, 0) < 0) { + if (!hinfo->reg_mcoa + && rule_add(NULL, RT6_TABLE_MIP6, + IP6_RULE_PRIO_MIP6_FWD, 0, RTN_UNICAST, + &p->ple_prefix, p->ple_plen, + &in6addr_any, 0, 0) < 0) { rule_del(NULL, RT6_TABLE_MAIN, - IP6_RULE_PRIO_MIP6_MNP_IN, RTN_UNICAST, - &in6addr_any, 0, &p->ple_prefix, p->ple_plen, 0); + IP6_RULE_PRIO_MIP6_MNP_IN, 0, + RTN_UNICAST, &in6addr_any, 0, + &p->ple_prefix, p->ple_plen, 0); pe = p; goto undo; } @@ -1368,11 +1949,14 @@ list_for_each(l, &hinfo->mob_net_prefixes) { struct prefix_list_entry *p = NULL; p = list_entry(l, struct prefix_list_entry, list); - rule_del(NULL, RT6_TABLE_MIP6, - IP6_RULE_PRIO_MIP6_FWD, RTN_UNICAST, - &p->ple_prefix, p->ple_plen, &in6addr_any, 0, 0); + if (!hinfo->reg_mcoa) { + rule_del(NULL, RT6_TABLE_MIP6, + IP6_RULE_PRIO_MIP6_FWD, 0, RTN_UNICAST, + &p->ple_prefix, p->ple_plen, + &in6addr_any, 0, 0); + } rule_del(NULL, RT6_TABLE_MAIN, - IP6_RULE_PRIO_MIP6_MNP_IN, RTN_UNICAST, + IP6_RULE_PRIO_MIP6_MNP_IN, 0, RTN_UNICAST, &in6addr_any, 0, &p->ple_prefix, p->ple_plen, 0); if (p == pe) break; @@ -1380,6 +1964,11 @@ return -1; } +struct mcoa_tnl_id { + struct list_head list; + int tnl_id; +}; + static void clean_home_addr_info(struct home_addr_info *hai) { struct flag_hoa_args arg; @@ -1390,17 +1979,24 @@ nemo_mr_rules_del(hai); arg.target = hai; arg.flag = 0; - addr_do(&hai->hoa.addr, plen, - hai->hoa.iif, &arg, flag_hoa); + /* MCoA: Flag HoA on the tunnel iface is not necessary here */ + if (!hai->reg_mcoa) + addr_do(&hai->hoa.addr, plen, + hai->hoa.iif, &arg, flag_hoa); bul_iterate(&hai->bul, mn_dereg, NULL); - bul_home_cleanup(&hai->bul); - + /* MCoA: Delete all mn rules */ + if (hai->reg_mcoa) + mcoa_mn_rules_del(hai); mn_block_rule_del(hai); - rule_del(NULL, RT6_TABLE_MIP6, - IP6_RULE_PRIO_MIP6_HOA_OUT, RTN_UNICAST, - &hai->hoa.addr, 128, &in6addr_any, 0, FIB_RULE_FIND_SADDR); - tunnel_del(hai->if_tunnel, NULL, NULL); + if (!hai->reg_mcoa) { + rule_del(NULL, RT6_TABLE_MIP6, + IP6_RULE_PRIO_MIP6_HOA_OUT, 0, RTN_UNICAST, + &hai->hoa.addr, 128, &in6addr_any, 0, + FIB_RULE_FIND_SADDR); + tunnel_del(hai->if_tunnel, NULL, NULL); + } + bul_home_cleanup(&hai->bul); dhaad_stop(hai); free(hai); } @@ -1448,6 +2044,11 @@ &hai->mob_net_prefixes) < 0) goto mutex_undo; + INIT_LIST_HEAD(&hai->mcoa); + if (hai->reg_mcoa && + prefix_list_copy(&conf_hai->mcoa, &hai->mcoa) < 0) + goto mutex_undo; + INIT_LIST_HEAD(&hai->ro_policies); if (rpl_copy(&conf_hai->ro_policies, &hai->ro_policies) < 0) goto mnp_undo; @@ -1493,17 +2094,22 @@ MDBG("HA address %x:%x:%x:%x:%x:%x:%x:%x\n", NIP6ADDR(&hai->ha_addr)); } - hai->if_tunnel = tunnel_add(&hai->hoa.addr, &hai->ha_addr, - hai->if_home, NULL, NULL); - - if (hai->if_tunnel <= 0) { - MDBG("failed to create MN-HA tunnel\n"); - goto clean_err; - } - if (rule_add(NULL, RT6_TABLE_MIP6, - IP6_RULE_PRIO_MIP6_HOA_OUT, RTN_UNICAST, - &hai->hoa.addr, 128, &in6addr_any, 0, FIB_RULE_FIND_SADDR) < 0) { - goto clean_err; + /* MCoA: tunnel is created only if MCoA registration is not used */ + if(!hai->reg_mcoa) { + hai->if_tunnel = tunnel_add(&hai->hoa.addr, + &hai->ha_addr, + hai->if_home, NULL, NULL); + + if (hai->if_tunnel <= 0) { + MDBG("failed to create MN-HA tunnel\n"); + goto clean_err; + } + if (rule_add(NULL, RT6_TABLE_MIP6, + IP6_RULE_PRIO_MIP6_HOA_OUT, 0, RTN_UNICAST, + &hai->hoa.addr, 128, &in6addr_any, 0, + FIB_RULE_FIND_SADDR) < 0) { + goto clean_err; + } } if (mn_block_rule_add(hai) < 0) @@ -1524,16 +2130,25 @@ PREFIX_LIFETIME_INFINITE, PREFIX_LIFETIME_INFINITE); - arg.target = hai; - arg.flag = 1; - - if (addr_do(&hai->hoa.addr, 128, - hai->if_tunnel, &arg, flag_hoa) < 0) { - goto clean_err; + /* MCoA */ + if(!hai->reg_mcoa) { + arg.target = hai; + arg.flag = 1; + + if (addr_do(&hai->hoa.addr, 128, + hai->if_tunnel, &arg, flag_hoa) < 0) { + goto clean_err; + } } if (hai->mob_rtr && nemo_mr_rules_add(hai) < 0) { goto clean_err; } + /* MCoA TODO1 + * For Home-CoA support, hai->at_home must be initialized to 0 + * if(hai->reg_mcoa) hai->at_home = 0; + * BUT Home-CoA solution seems to be deprecated by the no-NDP + * solution + */ hai->at_home = hai->hoa.iif == hai->if_home; pthread_rwlock_wrlock(&mn_lock); list_add(&hai->list, &home_addr_list); @@ -1635,7 +2250,11 @@ if (!hai->at_home) { struct bulentry *e; - e = bul_get(hai, NULL, &hai->ha_addr); + /* MCoA TODO1 + * Do the same for all entries that match &hai->ha_addr + */ + e = bul_get(hai, NULL, &hai->ha_addr, MCOA_NO_BID); + if (e == NULL || !(e->flags & IP6_MH_BU_HOME)) return -ENOENT; @@ -1668,8 +2287,9 @@ return ret; hai = mn_get_home_addr(hoa); if (hai) { - *coa = hai->primary_coa.addr; - ret = hai->primary_coa.iif; + assert(hai->current_coa); + *coa = hai->current_coa->addr; + ret = hai->current_coa->iif; } else { MDBG("Failed to find a home address info\n"); ret = -1; @@ -1683,14 +2303,14 @@ static inline void linklocal_rt_rules_del(void) { rule_del(NULL, RT6_TABLE_MAIN, - IP6_RULE_PRIO_MIP6_COA_OUT, RTN_UNICAST, + IP6_RULE_PRIO_MIP6_COA_OUT, 0, RTN_UNICAST, &linklocal_prefix, 64, &in6addr_any, 0, 0); } static inline int linklocal_rt_rules_add(void) { return rule_add(NULL, RT6_TABLE_MAIN, - IP6_RULE_PRIO_MIP6_COA_OUT, RTN_UNICAST, + IP6_RULE_PRIO_MIP6_COA_OUT, 0, RTN_UNICAST, &linklocal_prefix, 64, &in6addr_any, 0, 0); } @@ -1705,12 +2325,13 @@ mha.if_next = new_if; mha.target = hai; + /* MCoA: Update state for the correct table (hai->table) */ if (hai->hoa.iif == old_if && - (mn_tnl_state_add(hai, new_if, 1) || + (mn_tnl_state_add(hai, new_if, 1, hai->table) || addr_do(&hai->hoa.addr, 128, old_if, &mha, mv_hoa) < 0)) goto undo; - mn_tnl_state_del(hai, old_if, 1); + mn_tnl_state_del(hai, old_if, 1, hai->table); hai->if_tunnel = new_if; return 0; undo: @@ -1718,28 +2339,52 @@ if (hai->hoa.iif == new_if) addr_do(&hai->hoa.addr, 128, new_if, &mha, mv_hoa); - mn_tnl_state_del(hai, new_if, 1); + mn_tnl_state_del(hai, new_if, 1, hai->table); return -1; } static int mn_move(struct home_addr_info *hai) { struct mv_hoa_args mha; + struct list_head *l; mha.target = hai; TRACE; + assert(hai->current_coa); if (hai->at_home) { int plen = (hai->hoa.iif == hai->if_tunnel ? 128 : hai->plen); struct bulentry *e; MDBG("in home net\n"); + /* MCoA + * If we are in the home network, add a rule to + * RT6_TABLE_MIP6 to allow routing of the packets from + * the MNNs (routes in RT6_TABLE_MIP6 are + * added/deleted in movement.c/md_update_router_stats, + * __md_free_router) and the MR. + */ + if (hai->reg_mcoa) { + list_for_each(l, &hai->mob_net_prefixes) { + struct prefix_list_entry *p = NULL; + p = list_entry(l, struct prefix_list_entry, + list); + rule_add(NULL, RT6_TABLE_MIP6, + IP6_RULE_PRIO_MIP6_FWD, 0, + RTN_UNICAST, &p->ple_prefix, + p->ple_plen, &in6addr_any, 0, 0); + } + } if (hai->home_reg_status == HOME_REG_NONE && - (e = bul_get(hai, NULL, &hai->ha_addr)) != NULL && + (e = bul_get(hai, NULL, &hai->ha_addr, + hai->reg_mcoa? + get_bid_from_ifindex(hai->current_coa->iif) + :MCOA_NO_BID)) + != NULL && e->flags & IP6_MH_BU_HOME && e->type != BUL_ENTRY) bul_delete(e); - if (hai->hoa.iif != hai->primary_coa.iif) { - mha.if_next = hai->primary_coa.iif; + if (hai->hoa.iif != hai->current_coa->iif) { + mha.if_next = hai->current_coa->iif; addr_do(&hai->hoa.addr, plen, hai->hoa.iif, &mha, mv_hoa); if (hai->home_reg_status == HOME_REG_NONE) { @@ -1760,10 +2405,33 @@ hai->pend_ba = 0; pending_bas--; } + /* MCoA + * When in a foreign network, delete the + * rules to RT6_TABLE_MIP6 + */ + if (hai->reg_mcoa) { + list_for_each(l, &hai->mob_net_prefixes) { + struct prefix_list_entry *p = NULL; + p = list_entry(l, struct prefix_list_entry, + list); + rule_del(NULL, RT6_TABLE_MIP6, + IP6_RULE_PRIO_MIP6_FWD, 0, + RTN_UNICAST, &p->ple_prefix, + p->ple_plen, &in6addr_any, 0, 0); + } + } if (hai->hoa.iif != hai->if_tunnel) { mha.if_next = hai->if_tunnel; - addr_do(&hai->hoa.addr, hai->plen, - hai->hoa.iif, &mha, mv_hoa); + /* MCoA + * HoA is moved if we do not use MCoA, + * HoA is copied if we use MCoA + */ + if (hai->reg_mcoa) + addr_do(&hai->hoa.addr, hai->plen, + hai->hoa.iif, &mha, cp_hoa); + else + addr_do(&hai->hoa.addr, hai->plen, + hai->hoa.iif, &mha, mv_hoa); } do_handoff(hai); } @@ -1951,6 +2619,7 @@ struct in6_addr solicit; int type = FLUSH_ALL; + assert(hai->current_coa); if ((sock = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) < 0) return ret; @@ -1963,22 +2632,22 @@ sizeof(struct icmp6_filter)); ipv6_addr_solict_mult(&hai->hoa.addr, &solicit); - if_mc_group(sock, hai->primary_coa.iif, &in6addr_all_nodes_mc, + if_mc_group(sock, hai->current_coa->iif, &in6addr_all_nodes_mc, IPV6_JOIN_GROUP); - if_mc_group(sock, hai->primary_coa.iif, &solicit, IPV6_JOIN_GROUP); + if_mc_group(sock, hai->current_coa->iif, &solicit, IPV6_JOIN_GROUP); if (hai->home_reg_status == HOME_REG_NONE) { if (hai->lladdr_comp) { struct in6_addr lladdr; ipv6_addr_llocal(&hai->hoa.addr, &lladdr); if (mn_addr_do_dad(sock, NULL, &lladdr, 64, - hai->primary_coa.iif, 0) < 0) { + hai->current_coa->iif, 0) < 0) { MDBG("Link-local DAD failed!\n"); goto err; } } if (mn_addr_do_dad(sock, hai, &hai->hoa.addr, hai->plen, - hai->primary_coa.iif, 0) < 0) { + hai->current_coa->iif, 0) < 0) { MDBG("HoA DAD failed!\n"); goto err; } @@ -1986,7 +2655,7 @@ mn_send_home_na(hai); ret = mn_move(hai); } else if (!mn_addr_do_dad(sock, hai, &hai->hoa.addr, - hai->plen, hai->primary_coa.iif, 1)) { + hai->plen, hai->current_coa->iif, 1)) { ret = mn_move(hai); } out: @@ -2009,15 +2678,19 @@ static void mn_coa_updated(struct home_addr_info *hai) { + assert(hai->current_coa); if (!hai->at_home) { struct bulentry *e; struct mn_addr *coa; - e = bul_get(hai, NULL, &hai->ha_addr); + e = bul_get(hai, NULL, &hai->ha_addr, + hai->reg_mcoa? + get_bid_from_ifindex(hai->current_coa->iif) + :MCOA_NO_BID); if (e == NULL || !(e->flags & IP6_MH_BU_HOME)) return; - coa = &hai->primary_coa; + coa = hai->current_coa; MDBG2("preferred_time %u valid_life %u\n", coa->preferred_time.tv_sec, @@ -2075,6 +2748,10 @@ list_for_each(l, iface_list) { struct md_inet6_iface *iface; iface = list_entry(l, struct md_inet6_iface, list); + /* MCoA TODO1 + * If MCoA is used, MCoA preference must be used instead + * of interfaces preference + */ if (mn_verify_iface(iface) && (best_iface == NULL || (best_iface)->preference > iface->preference || @@ -2119,6 +2796,106 @@ return best_coa; } +static int mcoa_mn_make_ho_verdict(const struct movement_event *me, + const struct home_addr_info *hai, + struct md_router **next_rtr, + struct md_coa **next_coa) +{ + /* MCoA + * The handoff algorithm is different from + * mn_make_ho_verdict because we do not perform vertical + * handover, but only horizontal handovers. + */ + struct md_router *rtr; + struct md_coa *coa = NULL; + int force = 0; + + /* MCoA TODO1 + * For Home-CoA support, remove this if(){} + * BUT Home-CoA seems to be deprecated + */ + if (me->iface != NULL && + (rtr = md_get_first_router(&me->iface->default_rtr)) != NULL && + mn_is_at_home(&rtr->prefixes, &hai->home_prefix, hai->home_plen)) { + *next_rtr = rtr; + *next_coa = NULL; + return MN_HO_RETURN_HOME; + } + switch (me->event_type) { + case ME_DHAAD: + force = 1; + break; + case ME_IFACE_DOWN: + case ME_LINK_DOWN: + case ME_RTR_EXPIRED: + break; + case ME_RTR_NEW: + assert(!list_empty(&me->iface->default_rtr)); + case ME_LINK_UP: + assert(me->iface != NULL); + break; + case ME_RTR_BACK: + case ME_RTR_UPDATED: + assert(me->iface != NULL); + assert(!list_empty(&me->iface->default_rtr)); + + *next_coa = md_get_coa(&me->iface->coas, + &hai->current_coa->addr); + if (*next_coa == NULL) + break; + *next_rtr = md_get_first_router(&me->iface->default_rtr); + return MN_HO_REESTABLISH; + case ME_COA_NEW: + assert(me->iface != NULL); + assert(me->coa != NULL); + assert(me->iface->ifindex == me->coa->ifindex); + break; + case ME_COA_EXPIRED: + assert(me->iface != NULL); + assert(me->coa != NULL); + assert(me->iface->ifindex == me->coa->ifindex); + + if (IN6_ARE_ADDR_EQUAL(&hai->current_coa->addr, + &me->coa->addr)) + break; + return MN_HO_IGNORE; + case ME_COA_LFT_DEC: + case ME_COA_LFT_INC: + assert(me->iface != NULL); + assert(me->coa != NULL); + assert(me->iface->ifindex == me->coa->ifindex); + + if (!IN6_ARE_ADDR_EQUAL(&hai->current_coa->addr, + &me->coa->addr)) { + return MN_HO_IGNORE; + } + + *next_coa = me->coa; + return MN_HO_CHECK_LIFETIME; + default: + return MN_HO_IGNORE; + } + if (!mn_verify_iface(me->iface)) + return MN_HO_INVALIDATE; + + coa = mn_get_coa(hai, me->iface->ifindex, + &hai->current_coa->addr, &me->iface->coas); + + if (coa == NULL) + return MN_HO_INVALIDATE; + + if (!force && + IN6_ARE_ADDR_EQUAL(&coa->addr, &hai->current_coa->addr)) + return MN_HO_IGNORE; + + if (list_empty(&me->iface->default_rtr)) + return MN_HO_IGNORE; + + *next_rtr = md_get_first_router(&me->iface->default_rtr); + *next_coa = coa; + return MN_HO_PROCEED; +} + static int mn_make_ho_verdict(const struct movement_event *me, const struct home_addr_info *hai, struct md_router **next_rtr, @@ -2127,9 +2904,11 @@ struct md_inet6_iface *old_iface = NULL, *new_iface = NULL; struct md_router *rtr; struct md_coa *coa = NULL; - int pref_iif = hai->primary_coa.iif; /* prefer current CoA interface */ + int pref_iif; int force = 0; + assert(hai->current_coa); + pref_iif = hai->current_coa->iif; /* prefer current CoA interface */ if (me->iface != NULL && (rtr = md_get_first_router(&me->iface->default_rtr)) != NULL && mn_is_at_home(&rtr->prefixes, &hai->home_prefix, hai->home_plen)) { @@ -2170,7 +2949,7 @@ return MN_HO_IGNORE; *next_coa = md_get_coa(&old_iface->coas, - &hai->primary_coa.addr); + &hai->current_coa->addr); if (*next_coa == NULL) break; *next_rtr = md_get_first_router(&old_iface->default_rtr); @@ -2187,7 +2966,7 @@ if (old_iface == NULL || (old_iface == me->iface && - IN6_ARE_ADDR_EQUAL(&hai->primary_coa.addr, + IN6_ARE_ADDR_EQUAL(&hai->current_coa->addr, &me->coa->addr))) break; return MN_HO_IGNORE; @@ -2201,7 +2980,7 @@ break; if (old_iface != me->iface || - !IN6_ARE_ADDR_EQUAL(&hai->primary_coa.addr, + !IN6_ARE_ADDR_EQUAL(&hai->current_coa->addr, &me->coa->addr)) return MN_HO_IGNORE; @@ -2216,13 +2995,13 @@ return MN_HO_INVALIDATE; coa = mn_get_coa(hai, new_iface->ifindex, - &hai->primary_coa.addr, &new_iface->coas); + &hai->current_coa->addr, &new_iface->coas); if (coa == NULL) return MN_HO_INVALIDATE; if (!force && new_iface == old_iface && - IN6_ARE_ADDR_EQUAL(&coa->addr, &hai->primary_coa.addr)) + IN6_ARE_ADDR_EQUAL(&coa->addr, &hai->current_coa->addr)) return MN_HO_IGNORE; if (list_empty(&new_iface->default_rtr)) @@ -2233,6 +3012,100 @@ return MN_HO_PROCEED; } +static void mcoa_mn_chk_ho_verdict(struct home_addr_info *hai, + const struct movement_event *event) +{ + struct md_router *rtr = NULL; + struct md_coa *coa = NULL; + struct bulentry *bule; + int move_home = 0; + int active_coa = 0; + + if ((bule = bul_get(hai, NULL, &hai->ha_addr, + hai->reg_mcoa? + get_bid_from_ifindex(event->iface->ifindex) + :MCOA_NO_BID))) + active_coa = mcoa_bule_count(bule); + + if (event->event_type == ME_COA_EXPIRED && + IN6_ARE_ADDR_EQUAL(&event->coa->addr, &hai->hoa.addr)) + return; + + hai->current_coa = mcoa_get_current_coa(hai, event->iface->ifindex); + if (hai->current_coa == NULL) + return; + + hai->verdict = mcoa_mn_make_ho_verdict(event, hai, &rtr, &coa); + MDBG2("Verdict %d for iface %d\n", + hai->verdict, hai->current_coa->iif); + + if (hai->verdict == MN_HO_IGNORE) + return; + + if (hai->verdict == MN_HO_INVALIDATE) { + /* MCoA + * Unless this is the last available interface + * Rules related to this iface are deleted, + * BULE is deleted, and deregistration BU is sent + */ + if (active_coa > 1 + && (bule = mcoa_iface_rules_del(hai->current_coa->iif, + hai, 1))) + /* MCoA TODO2 + * Send a deregistration BU + * Is it the correct place to do that? + * because when BACK is received, BULE has + * already been deleted + * (actually BAck cannot be received at the BULE's CoA + * because the iface is down...) + */ + mn_dereg_bule(bule); + hai->current_coa = NULL; + return; + } + if (rtr != NULL) + move_home = mn_home_rtr_chk(hai, rtr); + + if (hai->verdict == MN_HO_CHECK_LIFETIME) { + mn_update_coa_lifetime(hai->current_coa, coa); + mn_coa_updated(hai); + } else if (hai->verdict == MN_HO_REESTABLISH) { + mn_update_coa_lifetime(hai->current_coa, coa); + mn_coa_updated(hai); + } else { + switch (hai->verdict) { + case MN_HO_PROCEED: + hai->current_coa->addr = coa->addr; + mn_update_coa_lifetime(hai->current_coa, coa); + /* MCoA + * Rules related to this iface are added if needed + */ + mcoa_iface_rules_add(hai->current_coa->iif, hai); + break; + case MN_HO_RETURN_HOME: + hai->current_coa->addr = hai->hoa.addr; + mcoa_iface_rules_del(hai->current_coa->iif, hai, 1); + break; + default: + return; + } + if (hai->at_home && !hai->pend_ba) { + /* check if router is HA */ + if (hai->home_reg_status != HOME_REG_NONE && + rtr_addr_chk(rtr, &hai->ha_addr)) { + mn_move(hai); + } else if (hai->home_reg_status != HOME_REG_NONE || + move_home) { + int type = FLUSH_FAILED; + mn_do_dad(hai, 0); + if (hai->home_reg_status == HOME_REG_NONE) + bul_iterate(&hai->bul, + _bul_flush, &type); + } + } + } +} + static void mn_chk_ho_verdict(struct home_addr_info *hai, const struct movement_event *event) { @@ -2244,35 +3117,36 @@ IN6_ARE_ADDR_EQUAL(&event->coa->addr, &hai->hoa.addr)) return; + assert(hai->current_coa); hai->verdict = mn_make_ho_verdict(event, hai, &rtr, &coa); if (hai->verdict == MN_HO_IGNORE) return; if (hai->verdict == MN_HO_INVALIDATE) { - hai->primary_coa.iif = 0; + hai->current_coa->iif = 0; return; } if (rtr != NULL) move_home = mn_home_rtr_chk(hai, rtr); if (hai->verdict == MN_HO_CHECK_LIFETIME) { - mn_update_coa_lifetime(&hai->primary_coa, coa); + mn_update_coa_lifetime(hai->current_coa, coa); mn_coa_updated(hai); } else if (hai->verdict == MN_HO_REESTABLISH) { - hai->primary_coa.iif = coa->ifindex; - mn_update_coa_lifetime(&hai->primary_coa, coa); + hai->current_coa->iif = coa->ifindex; + mn_update_coa_lifetime(hai->current_coa, coa); mn_coa_updated(hai); } else { switch (hai->verdict) { case MN_HO_PROCEED: - hai->primary_coa.iif = coa->ifindex; - hai->primary_coa.addr = coa->addr; - mn_update_coa_lifetime(&hai->primary_coa, coa); + hai->current_coa->iif = coa->ifindex; + hai->current_coa->addr = coa->addr; + mn_update_coa_lifetime(hai->current_coa, coa); break; case MN_HO_RETURN_HOME: - hai->primary_coa.iif = rtr->ifindex; - hai->primary_coa.addr = hai->hoa.addr; + hai->current_coa->iif = rtr->ifindex; + hai->current_coa->addr = hai->hoa.addr; break; default: return; @@ -2311,14 +3185,20 @@ return 0; } dhaad_stop(hai); - mn_chk_ho_verdict(hai, event); + if (hai->reg_mcoa) + mcoa_mn_chk_ho_verdict(hai, event); + else + mn_chk_ho_verdict(hai, event); } else { if (event->event_type == ME_COA_EXPIRED) mn_rr_delete_co(&event->coa->addr); list_for_each(lh, &home_addr_list) { hai = list_entry(lh, struct home_addr_info, list); - mn_chk_ho_verdict(hai, event); + if (hai->reg_mcoa) + mcoa_mn_chk_ho_verdict(hai, event); + else + mn_chk_ho_verdict(hai, event); } } } @@ -2397,7 +3277,9 @@ "from %x:%x:%x:%x:%x:%x:%x:%x\n", NIP6ADDR(cn), NIP6ADDR(hoa)); - bule = bul_get(NULL, hoa, cn); + bule = bul_get(NULL, hoa, cn, + hai->reg_mcoa? + get_bid_from_ifindex(iif):MCOA_NO_BID); if (bule) { /* If BUL entry exists, RR is done or in progress */ @@ -2405,7 +3287,9 @@ return; } - bule = create_bule(hoa, cn); + bule = create_bule(hoa, cn, + hai->reg_mcoa? + get_bid_from_ifindex(iif):MCOA_NO_BID); if (!bule) { MDBG("Malloc failed at starting of RO\n"); pthread_rwlock_unlock(&mn_lock); @@ -2419,6 +3303,18 @@ bule->coa_changed = -1; bule->home = hai; bule->rr.state = RR_NOT_STARTED; + bule->priority = get_prio_from_bid(bule->bid); + /* MCoA TODO2 + * At the moment we prevent MIPL from installing rules for + * this entry + */ + bule->rules = 1; + /* MCoA TODO2 + * Support for CN + * The table index must be filled with + * mcoa_mn_init_rt_table(bule) + */ + bule->table = RT6_TABLE_MIP6; mn_reset_ro_bule(bule); @@ -2451,7 +3347,10 @@ hoa = in->dst; pthread_rwlock_wrlock(&mn_lock); /* Do we have BUL entry for cn? If not, drop. */ - if ((e = bul_get(NULL, hoa, cn)) == NULL) { + /* MCoA TODO2 + * get BID from BRR BUI option + */ + if ((e = bul_get(NULL, hoa, cn, get_bid_from_ifindex(iif))) == NULL) { pthread_rwlock_unlock(&mn_lock); return; } @@ -2499,7 +3398,11 @@ hoa = in->dst; /* Do we have BUL entry for cn? If not, drop. */ - if ((e = bul_get(NULL, hoa, cn)) == NULL || e->type != BUL_ENTRY) + /* MCoA TODO2 + * get BID from BUI option in BERROR + */ + if ((e = bul_get(NULL, hoa, cn, get_bid_from_ifindex(iif))) == NULL + || e->type != BUL_ENTRY) goto out; clock_gettime(CLOCK_REALTIME, &now); diff -Nur mipv6-daemon-umip-0.4-nepl/src/mn.h mipv6-daemon-umip-0.4-nepl-mcoa/src/mn.h --- mipv6-daemon-umip-0.4-nepl/src/mn.h 2007-11-05 13:19:58.000000000 +0100 +++ mipv6-daemon-umip-0.4-nepl-mcoa/src/mn.h 2007-11-05 14:31:14.000000000 +0100 @@ -52,6 +52,7 @@ #define NEMO_FWD_BLOCK 0x10 struct mn_addr { + struct list_head list; struct in6_addr addr; int iif; struct timespec timestamp; @@ -75,7 +76,8 @@ uint8_t home_plen; struct in6_addr home_prefix; struct hash bul; /* Binding Update List */ - struct mn_addr primary_coa; + struct mn_addr *current_coa; /* MCoA: pointer to a member of mcoa */ + struct list_head mcoa; /* MCoA: list of all active CoAs */ struct list_head ro_policies; struct ha_candidate_list ha_list; struct in6_addr ha_addr; @@ -84,9 +86,11 @@ int if_tunnel; int if_home; int if_block; + int table; /* MCoA: routing table number where routes are stored */ short hwalen; uint8_t altcoa; uint16_t mob_rtr; + uint8_t reg_mcoa; char name[IF_NAMESIZE]; int mnp_count; struct list_head mob_net_prefixes; diff -Nur mipv6-daemon-umip-0.4-nepl/src/movement.c mipv6-daemon-umip-0.4-nepl-mcoa/src/movement.c --- mipv6-daemon-umip-0.4-nepl/src/movement.c 2007-10-22 15:15:10.000000000 +0200 +++ mipv6-daemon-umip-0.4-nepl-mcoa/src/movement.c 2007-10-23 10:30:03.000000000 +0200 @@ -165,7 +165,7 @@ struct in6_addr prefix; ipv6_addr_prefix(&prefix, &p->ple_prefix, p->ple_plen); rule_del(NULL, RT6_TABLE_MAIN, IP6_RULE_PRIO_MIP6_COA_OUT, - RTN_UNICAST, &prefix, p->ple_plen, + 0, RTN_UNICAST, &prefix, p->ple_plen, &in6addr_any, 0, 0); } @@ -312,10 +312,10 @@ static void md_block_rule_del(struct md_inet6_iface *iface) { - rule_del(NULL, 0, IP6_RULE_PRIO_MIP6_BLOCK_HOA, RTN_BLACKHOLE, + rule_del(NULL, 0, IP6_RULE_PRIO_MIP6_BLOCK_HOA, 0, RTN_BLACKHOLE, &in6addr_any, 0, &in6addr_any, 0, 0); - rule_del(NULL, RT6_TABLE_MAIN, IP6_RULE_PRIO_MIP6_COA_OUT, RTN_UNICAST, - &in6addr_any, 128, &in6addr_any, 0, 0); + rule_del(NULL, RT6_TABLE_MAIN, IP6_RULE_PRIO_MIP6_COA_OUT, 0, + RTN_UNICAST, &in6addr_any, 128, &in6addr_any, 0, 0); iface->iface_flags &= ~MD_BLOCK_TRAFFIC; } @@ -1290,7 +1290,7 @@ struct in6_addr prefix; ipv6_addr_prefix(&prefix, &p->ple_prefix, p->ple_plen); rule_add(NULL, RT6_TABLE_MAIN, IP6_RULE_PRIO_MIP6_COA_OUT, - RTN_UNICAST, &prefix, p->ple_plen, + 0, RTN_UNICAST, &prefix, p->ple_plen, &in6addr_any, 0, 0); } @@ -1361,13 +1361,13 @@ iface->iface_flags |= MD_BLOCK_TRAFFIC; /* Allow DAD probes and RS messages */ rule_add(NULL, RT6_TABLE_MAIN, - IP6_RULE_PRIO_MIP6_COA_OUT, RTN_UNICAST, + IP6_RULE_PRIO_MIP6_COA_OUT, 0, RTN_UNICAST, &in6addr_any, 128, &in6addr_any, 0, 0); /* drop outgoing global traffic until DAD has been performed on CoA to make routing and tunnel end-point updates atomic during handoff */ return rule_add(NULL, 0, - IP6_RULE_PRIO_MIP6_BLOCK_HOA, RTN_BLACKHOLE, + IP6_RULE_PRIO_MIP6_BLOCK_HOA, 0, RTN_BLACKHOLE, &in6addr_any, 0, &in6addr_any, 0, 0); } diff -Nur mipv6-daemon-umip-0.4-nepl/src/mpdisc_ha.c mipv6-daemon-umip-0.4-nepl-mcoa/src/mpdisc_ha.c --- mipv6-daemon-umip-0.4-nepl/src/mpdisc_ha.c 2007-10-22 15:15:10.000000000 +0200 +++ mipv6-daemon-umip-0.4-nepl-mcoa/src/mpdisc_ha.c 2007-10-23 10:30:03.000000000 +0200 @@ -68,7 +68,7 @@ const struct in6_addr *hoa) { - return hash_get(&mpa_hash, ha, hoa); + return hash_get(&mpa_hash, ha, hoa, 0); } /* HA functions */ @@ -214,7 +214,7 @@ pthread_mutex_lock(&mpa_lock); e = mpa_get(ha, hoa); if (e != NULL) { - hash_delete(&mpa_hash, &e->ha, &e->hoa); + hash_delete(&mpa_hash, &e->ha, &e->hoa, 0); if (tsisset(e->delay)) del_task(&e->tqe); free(e); @@ -239,7 +239,7 @@ memset(e, 0, sizeof(struct mpa_entry)); e->ha = *ha; e->hoa = *hoa; - if (hash_add(&mpa_hash, e, &e->ha, &e->hoa)) { + if (hash_add(&mpa_hash, e, &e->ha, &e->hoa, NULL)) { free(e); goto out; } diff -Nur mipv6-daemon-umip-0.4-nepl/src/mpdisc_mn.c mipv6-daemon-umip-0.4-nepl-mcoa/src/mpdisc_mn.c --- mipv6-daemon-umip-0.4-nepl/src/mpdisc_mn.c 2007-10-22 15:15:10.000000000 +0200 +++ mipv6-daemon-umip-0.4-nepl-mcoa/src/mpdisc_mn.c 2007-10-23 10:30:03.000000000 +0200 @@ -65,7 +65,7 @@ static inline struct mps_entry *mps_get(const struct in6_addr *hoa, const struct in6_addr *ha) { - return hash_get(&mps_hash, hoa, ha); + return hash_get(&mps_hash, hoa, ha, 0); } #ifdef ENABLE_VT @@ -116,7 +116,7 @@ e = mps_get(hoa, ha); if (e != NULL) { dbg("canceling MPS\n"); - hash_delete(&mps_hash, &e->hoa, &e->ha); + hash_delete(&mps_hash, &e->hoa, &e->ha, 0); if (tsisset(e->delay)) del_task(&e->tqe); free(e); @@ -175,7 +175,7 @@ memset(e, 0, sizeof(struct mps_entry)); e->hoa = *hoa; e->ha = *ha; - if (hash_add(&mps_hash, e, &e->hoa, &e->ha)) { + if (hash_add(&mps_hash, e, &e->hoa, &e->ha, NULL)) { free(e); goto out; } diff -Nur mipv6-daemon-umip-0.4-nepl/src/ndisc.h mipv6-daemon-umip-0.4-nepl-mcoa/src/ndisc.h --- mipv6-daemon-umip-0.4-nepl/src/ndisc.h 2007-10-22 15:15:10.000000000 +0200 +++ mipv6-daemon-umip-0.4-nepl-mcoa/src/ndisc.h 2007-10-23 10:30:03.000000000 +0200 @@ -21,6 +21,7 @@ return 1; /* supported virtual devices */ case ARPHRD_SIT: + return 4; case ARPHRD_TUNNEL6: case ARPHRD_PPP: case ARPHRD_IPGRE: diff -Nur mipv6-daemon-umip-0.4-nepl/src/policy.c mipv6-daemon-umip-0.4-nepl-mcoa/src/policy.c --- mipv6-daemon-umip-0.4-nepl/src/policy.c 2007-10-22 15:15:10.000000000 +0200 +++ mipv6-daemon-umip-0.4-nepl-mcoa/src/policy.c 2007-10-23 10:30:03.000000000 +0200 @@ -49,6 +49,25 @@ int def_bind_policy = IP6_MH_BAS_PROHIBIT; /** + * default_mcoa_reg - Check if MCoA reg is allowed + * @remote_hoa: remote MN's home address + * + * Returns 0 if not allowed, 1 if allowed. + **/ +int default_mcoa_reg(const struct in6_addr *remote_hoa) +{ + struct policy_bind_acl_entry *acl; + int ret = 1; + + pthread_rwlock_rdlock(&policy_lock); + acl = hash_get(&policy_bind_acl_hash, NULL, remote_hoa, 0); + if (!conf.HaAcceptMCoAReg || (acl && !acl->mcoa_reg)) + ret = 0; + pthread_rwlock_unlock(&policy_lock); + return ret; +} + +/** * default_best_iface - select best interface during handoff * @hoa: MN's home address * @ha: HA's address @@ -148,7 +167,7 @@ return IP6_MH_BAS_MR_OP_NOT_PERMITTED; pthread_rwlock_rdlock(&policy_lock); - acl = hash_get(&policy_bind_acl_hash, NULL, remote_hoa); + acl = hash_get(&policy_bind_acl_hash, NULL, remote_hoa, 0); if (acl != NULL) { ret = acl->bind_policy; if (ret < IP6_MH_BAS_UNSPECIFIED && @@ -213,8 +232,16 @@ struct net_iface *nif; nif = list_entry(list, struct net_iface, list); if (nif->ifindex == iif) { - if (is_if_mn(nif)) - return nif->mn_if_preference; + if (is_if_mn(nif)) { + /* MCoA + * Return the BID Priority if the + * interface preference is 0 + */ + if(nif->mn_if_preference) + return nif->mn_if_preference; + else + return nif->mn_if_bidprio; + } return 0; } } @@ -259,7 +286,7 @@ int ret = 0; struct policy_bind_acl_entry *acl; pthread_rwlock_rdlock(&policy_lock); - acl = hash_get(&policy_bind_acl_hash, NULL, hoa); + acl = hash_get(&policy_bind_acl_hash, NULL, hoa, 0); if (acl != NULL) ret = acl->mnp_count; pthread_rwlock_unlock(&policy_lock); @@ -275,7 +302,7 @@ struct policy_bind_acl_entry *acl; pthread_rwlock_rdlock(&policy_lock); - acl = hash_get(&policy_bind_acl_hash, NULL, hoa); + acl = hash_get(&policy_bind_acl_hash, NULL, hoa, 0); if (acl != NULL) { struct list_head *l; list_for_each(l, &acl->mob_net_prefixes) { @@ -314,7 +341,7 @@ static int policy_bind_acl_add(struct policy_bind_acl_entry *acl) { int err; - err = hash_add(&policy_bind_acl_hash, acl, NULL, &acl->hoa); + err = hash_add(&policy_bind_acl_hash, acl, NULL, &acl->hoa, NULL); if (!err) { list_del(&acl->list); } diff -Nur mipv6-daemon-umip-0.4-nepl/src/policy.h mipv6-daemon-umip-0.4-nepl-mcoa/src/policy.h --- mipv6-daemon-umip-0.4-nepl/src/policy.h 2007-10-22 15:15:10.000000000 +0200 +++ mipv6-daemon-umip-0.4-nepl-mcoa/src/policy.h 2007-10-23 10:30:03.000000000 +0200 @@ -6,7 +6,13 @@ #include #include "list.h" -#define POL_MN_IF_DEF_PREFERENCE 5 +/* MCoA + * Changed POL_MN_IF_DEF_PREFERENCE from 5 to 0 + */ +#define POL_MN_IF_DEF_PREFERENCE 0 +#define POL_MN_IF_DEF_BID 0 +#define POL_MN_IF_DEF_BID_PRIORITY 0 +#define POL_MN_IF_DEF_RELIABLE 1 struct ip6_mh_binding_update; struct nd_router_advert; @@ -17,11 +23,20 @@ struct in6_addr hoa; int plen; int bind_policy; + int mcoa_reg; int mnp_count; struct list_head mob_net_prefixes; }; /** + * default_mcoa_reg - Check if MCoA registration is allowed + * @remote_hoa: remote MN's home address + * + * Returns 0 if not allowed, 1 if allowed. + **/ +int default_mcoa_reg(const struct in6_addr *remote_hoa); + +/** * default_best_iface - select best interface during handoff * @hoa: MN's home address * @ha: HA's address diff -Nur mipv6-daemon-umip-0.4-nepl/src/retrout.c mipv6-daemon-umip-0.4-nepl-mcoa/src/retrout.c --- mipv6-daemon-umip-0.4-nepl/src/retrout.c 2007-10-22 15:15:10.000000000 +0200 +++ mipv6-daemon-umip-0.4-nepl-mcoa/src/retrout.c 2007-10-23 10:30:03.000000000 +0200 @@ -151,7 +151,7 @@ rre->own2 = *own2; rre->iif = iif; - if (hash_add(&rrl_hash, rre, &rre->own1, &rre->peer) < 0) { + if (hash_add(&rrl_hash, rre, &rre->own1, &rre->peer, NULL) < 0) { rrl_delete(rre); return NULL; } @@ -168,7 +168,7 @@ assert(our_addr); - rre = (struct rrlentry *)hash_get(&rrl_hash, our_addr, peer_addr); + rre = (struct rrlentry *)hash_get(&rrl_hash, our_addr, peer_addr, 0); if (rre != NULL && rre->type != type) return NULL; @@ -188,7 +188,7 @@ TRACE; del_task(&rre->tqe); - hash_delete(&rrl_hash, &rre->own1, &rre->peer); + hash_delete(&rrl_hash, &rre->own1, &rre->peer, 0); if (rre->type == COT_ENTRY) { struct list_head *list, *n; @@ -629,7 +629,7 @@ const struct in6_addr *peer, struct in6_addr *hoa) { - struct rrlentry *rre = hash_get(&rrl_hash, own, peer); + struct rrlentry *rre = hash_get(&rrl_hash, own, peer, 0); if (rre == NULL || !rre->wait) return 0; @@ -694,7 +694,11 @@ struct addr_holder *ah; ah = list_entry(list, struct addr_holder, list); - bule = bul_get(NULL, &ah->addr, cn_addr); + /* MCoA TODO2 + * MCoA does not work with RR + * At the moment the first entry that match is chosen + */ + bule = bul_get(NULL, &ah->addr, cn_addr, MCOA_NO_BID); if (bule == NULL || bule->type != BUL_ENTRY || !IN6_ARE_ADDR_EQUAL(&rre_co->own1, &bule->coa)) { @@ -774,7 +778,11 @@ pthread_rwlock_unlock(&mn_lock); return; } - bule = bul_get(NULL, home_addr, cn_addr); + /* MCoA TODO2 + * MCoA does not work with RR + * At the moment the first entry that match is chosen + */ + bule = bul_get(NULL, home_addr, cn_addr, MCOA_NO_BID); if (bule == NULL || bule->type != BUL_ENTRY) { rrl_delete(rre_ho); diff -Nur mipv6-daemon-umip-0.4-nepl/src/rtnl.c mipv6-daemon-umip-0.4-nepl-mcoa/src/rtnl.c --- mipv6-daemon-umip-0.4-nepl/src/rtnl.c 2007-10-22 15:15:10.000000000 +0200 +++ mipv6-daemon-umip-0.4-nepl-mcoa/src/rtnl.c 2007-10-23 10:30:03.000000000 +0200 @@ -299,7 +299,7 @@ } static int rule_mod(const char *iface, int cmd, uint8_t table, - uint32_t priority, uint8_t action, + uint32_t priority, uint32_t fwmark, uint8_t action, const struct in6_addr *src, int src_plen, const struct in6_addr *dst, int dst_plen, int flags) { @@ -331,6 +331,8 @@ addattr_l(n, sizeof(buf), RTA_SRC, src, sizeof(*src)); if (priority) addattr32(n, sizeof(buf), RTA_PRIORITY, priority); + if (fwmark) + addattr32(n, sizeof(buf), RTA_PROTOINFO, fwmark); if (iface) addattr_l(n, sizeof(buf), RTA_IIF, iface, strlen(iface) + 1); @@ -349,12 +351,12 @@ * negative otherwise. **/ int rule_add(const char *iface, uint8_t table, - uint32_t priority, uint8_t action, + uint32_t priority, uint32_t fwmark, uint8_t action, const struct in6_addr *src, int src_plen, const struct in6_addr *dst, int dst_plen, int flags) { return rule_mod(iface, RTM_NEWRULE, table, - priority, action, + priority, fwmark, action, src, src_plen, dst, dst_plen, flags); } @@ -370,12 +372,12 @@ * otherwise. **/ int rule_del(const char *iface, uint8_t table, - uint32_t priority, uint8_t action, + uint32_t priority, uint32_t fwmark, uint8_t action, const struct in6_addr *src, int src_plen, const struct in6_addr *dst, int dst_plen, int flags) { return rule_mod(iface, RTM_DELRULE, table, - priority, action, + priority, fwmark, action, src, src_plen, dst, dst_plen, flags); } diff -Nur mipv6-daemon-umip-0.4-nepl/src/rtnl.h mipv6-daemon-umip-0.4-nepl-mcoa/src/rtnl.h --- mipv6-daemon-umip-0.4-nepl/src/rtnl.h 2007-10-22 15:15:10.000000000 +0200 +++ mipv6-daemon-umip-0.4-nepl-mcoa/src/rtnl.h 2007-10-23 10:30:03.000000000 +0200 @@ -17,11 +17,14 @@ #define IP6_RT_PRIO_ADDRCONF 256 #define IP6_RULE_PRIO_MIP6_MNP_IN 1000 -#define IP6_RULE_PRIO_MIP6_HOA_OUT 1001 -#define IP6_RULE_PRIO_MIP6_COA_OUT 1002 -#define IP6_RULE_PRIO_MIP6_BLOCK 1003 -#define IP6_RULE_PRIO_MIP6_BLOCK_HOA 1004 -#define IP6_RULE_PRIO_MIP6_FWD 1005 +#define IP6_RULE_PRIO_MIP6_HOA_OUT_MCOA 1001 +#define IP6_RULE_PRIO_MIP6_HOA_OUT 1002 +#define IP6_RULE_PRIO_MIP6_COA_OUT 1003 +#define IP6_RULE_PRIO_MIP6_BLOCK 1004 +#define IP6_RULE_PRIO_MIP6_BLOCK_HOA 1005 +#define IP6_RULE_PRIO_MIP6_FWD_FWM 1006 +#define IP6_RULE_PRIO_MIP6_FWD_MCOA 1007 +#define IP6_RULE_PRIO_MIP6_FWD 1008 static inline int rtnl_route_open(struct rtnl_handle *rth, unsigned subscriptions) @@ -74,12 +77,12 @@ const struct in6_addr *gateway); int rule_add(const char *iface, uint8_t table, - uint32_t priority, uint8_t action, + uint32_t priority, uint32_t fwmark, uint8_t action, const struct in6_addr *src, int src_plen, const struct in6_addr *dst, int dst_plen, int flags); int rule_del(const char *iface, uint8_t table, - uint32_t priority, uint8_t action, + uint32_t priority, uint32_t fwmark, uint8_t action, const struct in6_addr *src, int src_plen, const struct in6_addr *dst, int dst_plen,int flags); diff -Nur mipv6-daemon-umip-0.4-nepl/src/scan.l mipv6-daemon-umip-0.4-nepl-mcoa/src/scan.l --- mipv6-daemon-umip-0.4-nepl/src/scan.l 2007-10-22 15:15:10.000000000 +0200 +++ mipv6-daemon-umip-0.4-nepl-mcoa/src/scan.l 2007-10-23 10:30:03.000000000 +0200 @@ -137,7 +137,15 @@ MnDiscardHaParamProb { return MNDISCARDHAPARAMPROB; } OptimisticHandoff { return OPTIMISTICHANDOFF; } HaAcceptMobRtr { return HAACCEPTMOBRTR; } +HaAcceptMCoAReg { return HAACCEPTMCOAREG; } +MCoAReg { return MCOAREG; } +NoMCoAReg { return NOMCOAREG; } IsMobRtr { return ISMOBRTR; } +Bid { return BID; } +BidPriority { return BIDPRIORITY; } +Reliable { return RELIABLE; } +RegMultipleCoA { return REGMULTIPLECOA; } +IfMultipleCoA { return IFMULTIPLECOA; } HaServedPrefix { return HASERVEDPREFIX; } HomePrefix { return HOMEPREFIX; } MobRtrUseExplicitMode { return MOBRTRUSEEXPLICITMODE; } diff -Nur mipv6-daemon-umip-0.4-nepl/src/tunnelctl.c mipv6-daemon-umip-0.4-nepl-mcoa/src/tunnelctl.c --- mipv6-daemon-umip-0.4-nepl/src/tunnelctl.c 2007-10-22 15:15:10.000000000 +0200 +++ mipv6-daemon-umip-0.4-nepl-mcoa/src/tunnelctl.c 2007-10-23 10:30:03.000000000 +0200 @@ -157,7 +157,7 @@ if (tnl->users == 0) { struct ifreq ifr; list_del(&tnl->list); - hash_delete(&tnl_hash, &tnl->parm.laddr, &tnl->parm.raddr); + hash_delete(&tnl_hash, &tnl->parm.laddr, &tnl->parm.raddr, 0); strcpy(ifr.ifr_name, tnl->parm.name); if ((res = ioctl(tnl_fd, SIOCDELTUNNEL, &ifr)) < 0) { TDBG("SIOCDELTUNNEL failed status %d %s\n", @@ -249,7 +249,7 @@ TDBG("no device called %s\n", tnl->parm.name); goto err; } - if (hash_add(&tnl_hash, tnl, &tnl->parm.laddr, &tnl->parm.raddr) < 0) + if (hash_add(&tnl_hash, tnl, &tnl->parm.laddr, &tnl->parm.raddr, NULL) < 0) goto err; list_add_tail(&tnl->list, &tnl_list); @@ -287,7 +287,7 @@ int res; pthread_mutex_lock(&tnl_lock); - if ((tnl = hash_get(&tnl_hash, local, remote)) != NULL) { + if ((tnl = hash_get(&tnl_hash, local, remote, 0)) != NULL) { tnl->users++; TDBG("tunnel %s (%d) from %x:%x:%x:%x:%x:%x:%x:%x " "to %x:%x:%x:%x:%x:%x:%x:%x user count increased to %d\n", @@ -339,9 +339,10 @@ errno, strerror(errno)); return -1; } - hash_delete(&tnl_hash, &tnl->parm.laddr, &tnl->parm.raddr); + hash_delete(&tnl_hash, &tnl->parm.laddr, &tnl->parm.raddr, 0); memcpy(&tnl->parm, &parm, sizeof(struct ip6_tnl_parm)); - if (hash_add(&tnl_hash, tnl, &tnl->parm.laddr, &tnl->parm.raddr) < 0) { + if (hash_add(&tnl_hash, tnl, &tnl->parm.laddr, + &tnl->parm.raddr, NULL) < 0) { free(tnl); return -1; } @@ -387,7 +388,7 @@ old = get_tnl(ifindex); assert(old != NULL); - if ((new = hash_get(&tnl_hash, local, remote)) != NULL) { + if ((new = hash_get(&tnl_hash, local, remote, 0)) != NULL) { if (new != old) { new->users++; TDBG("tunnel %s (%d) from %x:%x:%x:%x:%x:%x:%x:%x " @@ -448,7 +449,7 @@ { struct mip6_tnl *tnl = (struct mip6_tnl *) data; list_del(&tnl->list); - hash_delete(&tnl_hash, &tnl->parm.laddr, &tnl->parm.raddr); + hash_delete(&tnl_hash, &tnl->parm.laddr, &tnl->parm.raddr, 0); free(tnl); return 0; } diff -Nur mipv6-daemon-umip-0.4-nepl/src/vt.c mipv6-daemon-umip-0.4-nepl-mcoa/src/vt.c --- mipv6-daemon-umip-0.4-nepl/src/vt.c 2007-10-22 15:15:10.000000000 +0200 +++ mipv6-daemon-umip-0.4-nepl-mcoa/src/vt.c 2007-10-23 10:30:03.000000000 +0200 @@ -428,6 +428,12 @@ fprintf(vh->vh_stream, " last_coa %x:%x:%x:%x:%x:%x:%x:%x", NIP6ADDR(&bule->last_coa)); + if (bule->bid) { + fprintf(vh->vh_stream, " BID %d", bule->bid); + fprintf(vh->vh_stream, " BidPriority %d", + bule->priority); + } + fprintf(vh->vh_stream, "\n"); if (!(bule->flags & IP6_MH_BU_HOME)) { @@ -580,6 +586,11 @@ fprintf(vh->vh_stream, " coa %x:%x:%x:%x:%x:%x:%x:%x", NIP6ADDR(&bce->coa)); + if (bce->bid) { + fprintf(vh->vh_stream, " BID %d", bce->bid); + fprintf(vh->vh_stream, " BidPriority %d", bce->priority); + } + if (vh->vh_opt.verbose == VT_BOOL_TRUE) fprintf(vh->vh_stream, " nonce %u", bce->nonce_coa); diff -Nur mipv6-daemon-umip-0.4-nepl/src/xfrm.c mipv6-daemon-umip-0.4-nepl-mcoa/src/xfrm.c --- mipv6-daemon-umip-0.4-nepl/src/xfrm.c 2007-10-22 18:33:26.000000000 +0200 +++ mipv6-daemon-umip-0.4-nepl-mcoa/src/xfrm.c 2007-11-05 14:04:30.000000000 +0100 @@ -56,7 +56,7 @@ #include "conf.h" #include "ipsec.h" -#define XFRM_DEBUG_LEVEL 1 +#define XFRM_DEBUG_LEVEL 2 #if XFRM_DEBUG_LEVEL >= 1 #define XDBG dbg @@ -1561,7 +1561,7 @@ int res = -1; pthread_rwlock_rdlock(&mn_lock); if ((hai = mn_get_home_addr(our_addr)) != NULL) { - if ((e = bul_get(hai, NULL, peer_addr)) != NULL) { + if ((e = bul_get(hai, NULL, peer_addr, MCOA_NO_BID)) != NULL) { if (e->type == BUL_ENTRY && !_xfrm_add_bule_bce(our_addr, peer_addr, replace)) res = 0; @@ -1588,30 +1588,34 @@ int xfrm_add_bce(const struct in6_addr *our_addr, const struct in6_addr *peer_addr, const struct in6_addr *coa, - int replace) + int replace, + int create) { struct xfrm_selector sel; /* Create policy for outbound RO data traffic */ - set_selector(peer_addr, our_addr, 0, 0, 0, 0, &sel); - if (xfrm_state_add(&sel, IPPROTO_ROUTING, coa, replace, 0)){ - /* - * WORKAROUND - * In some cases, MN fail to add it because of the state - * inserted by kernel when notifying aquire. So,update it. - */ - if (xfrm_state_add(&sel, IPPROTO_ROUTING, coa, 1, 0)) - return -1; - } - set_selector(our_addr, peer_addr, 0, 0, 0, 0, &sel); - if (xfrm_state_add(&sel, IPPROTO_DSTOPTS, coa, replace, 0)){ - /* - * WORKAROUND - * In some cases, MN fail to add it because of the state - * inserted by kernel when notifying aquire. So,update it. - */ - if (xfrm_state_add(&sel, IPPROTO_DSTOPTS, coa, 1, 0)) - return -1; + if (create || replace) { + set_selector(peer_addr, our_addr, 0, 0, 0, 0, &sel); + if (xfrm_state_add(&sel, IPPROTO_ROUTING, coa, replace, 0)){ + /* + * WORKAROUND + * In some cases, MN fail to add it because of the state + * inserted by kernel when notifying aquire. So,update it. + */ + if (xfrm_state_add(&sel, IPPROTO_ROUTING, coa, 1, 0)) + return -1; + } + + set_selector(our_addr, peer_addr, 0, 0, 0, 0, &sel); + if (xfrm_state_add(&sel, IPPROTO_DSTOPTS, coa, replace, 0)){ + /* + * WORKAROUND + * In some cases, MN fail to add it because of the state + * inserted by kernel when notifying aquire. So,update it. + */ + if (xfrm_state_add(&sel, IPPROTO_DSTOPTS, coa, 1, 0)) + return -1; + } } if (is_mn() && !xfrm_bule_bce_update(our_addr, peer_addr, replace)) return 0; @@ -1620,7 +1624,8 @@ _ha_mn_ipsec_pol_mod, NULL) < 0) return -1; } - _xfrm_add_bce(our_addr, peer_addr, replace); + if (create || replace) + _xfrm_add_bce(our_addr, peer_addr, replace); return 0; } @@ -1649,7 +1654,7 @@ /* for MN-MN communications, checking BUL to insert RO policy */ pthread_rwlock_rdlock(&mn_lock); if ((hai = mn_get_home_addr(our_addr)) != NULL) { - if ((e = bul_get(hai, NULL, peer_addr)) != NULL) { + if ((e = bul_get(hai, NULL, peer_addr, MCOA_NO_BID)) != NULL) { if (e->type == BUL_ENTRY) _xfrm_add_bule_bce(our_addr, peer_addr, 0); } @@ -1742,7 +1747,7 @@ static int _xfrm_bce_reset(struct bulentry *bule) { /* for MN - MN communication */ - struct bcentry *bce = bcache_get(&bule->hoa, &bule->peer_addr); + struct bcentry *bce = bcache_get(&bule->hoa, &bule->peer_addr, MCOA_NO_BID); int res = 0; if (!bce) return 0; @@ -1760,21 +1765,34 @@ static int _xfrm_del_bule_data(struct bulentry *bule) { struct xfrm_selector sel; + int bule_exists = mcoa_bule_count(bule); int prio; - set_selector(&bule->peer_addr, &bule->hoa, 0, 0, 0, 0, &sel); - xfrm_state_del(IPPROTO_DSTOPTS, &sel); + /* MCoA + * XFRM states are deleted only when the last BULE for + * the same peer is deleted, or when returning home + */ + if (!bule_exists || !bule->home->reg_mcoa || bule->home->at_home) { + XDBG2("Last entry for the peer, deleting XFRM states\n"); + set_selector(&bule->peer_addr, &bule->hoa, 0, 0, 0, 0, &sel); + xfrm_state_del(IPPROTO_DSTOPTS, &sel); + } if (bule->home->home_reg_status != HOME_REG_NONE && mn_has_cn_ro_pol(bule)) { struct xfrm_user_tmpl tmpl; create_trig_dstopt_tmpl(&tmpl, &bule->peer_addr, &bule->hoa); + /* MCoA: replaced bule->home->if_tunnel with bule->if_tunnel */ set_selector(&bule->peer_addr, &bule->hoa, - 0, 0, 0, bule->home->if_tunnel, &sel); + 0, 0, 0, bule->if_tunnel, &sel); xfrm_mip_policy_add(&sel, 1, XFRM_POLICY_OUT, XFRM_POLICY_ALLOW, MIP6_PRIO_RO_TRIG, &tmpl, 1); - } else - _mn_bule_ro_pol_del(bule, bule->home->if_tunnel); + } else if(!bule_exists || !bule->home->reg_mcoa || bule->home->at_home) { + /* MCoA: policies deleted only if this is the last BULE + * for the same peer, or when returning home */ + /* MCoA: replaced bule->home->if_tunnel with bule->if_tunnel */ + _mn_bule_ro_pol_del(bule, bule->if_tunnel); + } if (!(bule->flags & IP6_MH_BU_HOME)) _xfrm_bce_reset(bule); @@ -1785,20 +1803,25 @@ /* MN - CN/HA case, BU out */ prio = (bule->flags & IP6_MH_BU_HOME ? MIP6_PRIO_HOME_SIG : MIP6_PRIO_RO_SIG); - if (bule->flags & IP6_MH_BU_ACK) { + if ((bule->flags & IP6_MH_BU_ACK) + && (!bule_exists || !bule->home->reg_mcoa + || bule->home->at_home)) { set_selector(&bule->hoa, &bule->peer_addr, IPPROTO_MH, IP6_MH_TYPE_BACK, 0, 0, &sel); if (xfrm_mip_policy_add(&sel, 1, XFRM_POLICY_IN, XFRM_POLICY_ALLOW, prio, NULL, 0)) return -1; } - set_selector(&bule->peer_addr, &bule->hoa, IPPROTO_MH, - IP6_MH_TYPE_BU, 0, 0, &sel); - if (xfrm_mip_policy_add(&sel, 1, XFRM_POLICY_OUT, - XFRM_POLICY_ALLOW, prio, NULL, 0)) - return -1; + if (!bule_exists || !bule->home->reg_mcoa + || bule->home->at_home) { + set_selector(&bule->peer_addr, &bule->hoa, IPPROTO_MH, + IP6_MH_TYPE_BU, 0, 0, &sel); + if (xfrm_mip_policy_add(&sel, 1, XFRM_POLICY_OUT, + XFRM_POLICY_ALLOW, prio, NULL, 0)) + return -1; + bule->xfrm_state &= ~BUL_XFRM_STATE_DATA; + } - bule->xfrm_state &= ~BUL_XFRM_STATE_DATA; return 0; } @@ -1823,7 +1846,14 @@ { if (bule->xfrm_state & BUL_XFRM_STATE_DATA) _xfrm_del_bule_data(bule); - _xfrm_del_bule_sig(bule); + /* MCoA + * XFRM policies are deleted only when the last BULE for the + * same peer is deleted, or when returning home + */ + if (mcoa_bule_count(bule) == 0 || bule->home->at_home) { + XDBG2("Last entry for the peer, deleting XFRM policies\n"); + _xfrm_del_bule_sig(bule); + } } /* before sending BU, MN should insert policy/state only for BU/BA */ @@ -1835,14 +1865,26 @@ int rdata = bule->xfrm_state & BUL_XFRM_STATE_DATA; int prio; int exist = 0; + int bule_count = mcoa_bule_count(bule); if (bule->flags & IP6_MH_BU_HOME) { struct home_addr_info *hai = bule->home; if (hai->home_block & HOME_LINK_BLOCK) xfrm_unblock_link(hai); - xfrm_block_link(hai); - if (hai->mob_rtr && !(hai->home_block & NEMO_FWD_BLOCK)) - xfrm_block_fwd(hai); + if (bule->home_block & HOME_LINK_BLOCK) + xfrm_unblock_bule_link(bule); + if (!hai->reg_mcoa) + xfrm_block_link(hai); + else + xfrm_block_bule_link(bule); + if (hai->mob_rtr) { + if (!hai->reg_mcoa && + !(hai->home_block & NEMO_FWD_BLOCK)) + xfrm_block_fwd(hai); + else if (hai->reg_mcoa && + !(bule->home_block & NEMO_FWD_BLOCK)) + bule_block_fwd(bule); + } } if (IN6_ARE_ADDR_EQUAL(&bule->hoa, &bule->coa)) { if (rdata) @@ -1856,25 +1898,32 @@ return -1; } /* MN - CN/HA case, BU out */ - prio = (bule->flags & IP6_MH_BU_HOME ? - MIP6_PRIO_HOME_SIG : MIP6_PRIO_RO_SIG); - if (bule->flags & IP6_MH_BU_ACK) { - create_rh_tmpl(&tmpl); - set_selector(&bule->hoa, &bule->peer_addr, IPPROTO_MH, - IP6_MH_TYPE_BACK, 0, 0, &sel); - if (xfrm_mip_policy_add(&sel, rsig, XFRM_POLICY_IN, + if (bule_count == 1) { + /* MCoA + * If another BULE already exists for this HoA/HA addr + * we do not need to install the policies once again + */ + prio = (bule->flags & IP6_MH_BU_HOME ? + MIP6_PRIO_HOME_SIG : MIP6_PRIO_RO_SIG); + if (bule->flags & IP6_MH_BU_ACK) { + create_rh_tmpl(&tmpl); + set_selector(&bule->hoa, &bule->peer_addr, IPPROTO_MH, + IP6_MH_TYPE_BACK, 0, 0, &sel); + if (xfrm_mip_policy_add(&sel, rsig, XFRM_POLICY_IN, + XFRM_POLICY_ALLOW, prio, &tmpl, 1)) + return -1; + } + create_dstopt_tmpl(&tmpl, &bule->peer_addr, &bule->hoa); + set_selector(&bule->peer_addr, &bule->hoa, IPPROTO_MH, + IP6_MH_TYPE_BU, 0, 0, &sel); + if (xfrm_mip_policy_add(&sel, rsig, XFRM_POLICY_OUT, XFRM_POLICY_ALLOW, prio, &tmpl, 1)) return -1; } - create_dstopt_tmpl(&tmpl, &bule->peer_addr, &bule->hoa); - set_selector(&bule->peer_addr, &bule->hoa, IPPROTO_MH, - IP6_MH_TYPE_BU, 0, 0, &sel); - if (xfrm_mip_policy_add(&sel, rsig, XFRM_POLICY_OUT, - XFRM_POLICY_ALLOW, prio, &tmpl, 1)) - return -1; if (!(bule->flags & IP6_MH_BU_HOME)) { - struct bcentry *bce = bcache_get(&bule->hoa, &bule->peer_addr); + struct bcentry *bce = bcache_get(&bule->hoa, &bule->peer_addr, + MCOA_NO_BID); if (bce) { if (bce->type > BCE_NONCE_BLOCK && _xfrm_add_bce(&bule->hoa, &bule->peer_addr, 1)) { @@ -1885,11 +1934,16 @@ exist = 1; } } - if(!exist &&_mn_bule_ro_pol_add(bule, bule->home->if_tunnel, rdata)) + /* MCoA: Replaced bule->home->if_tunnel with bule->if_tunnel */ + if(!exist && bule_count == 1 + && _mn_bule_ro_pol_add(bule, bule->if_tunnel, rdata)) return -1; set_selector(&bule->peer_addr, &bule->hoa, 0, 0, 0, 0, &sel); /* XXX: acquired state is already inserted */ - if (!(bule->flags & IP6_MH_BU_HOME)) { + /* MCoA: Add the states if there is only 1 BCE for the peer, + * otherwise just update the CoA + */ + if (!(bule->flags & IP6_MH_BU_HOME) || mcoa_bule_count(bule) > 1) { XDBG2("%s: original rdata = %d\n", __FUNCTION__, rdata); rdata = 1; } @@ -1908,15 +1962,20 @@ struct home_addr_info *hai = bule->home; if (hai->home_block & HOME_LINK_BLOCK) xfrm_unblock_link(hai); + if (bule->home_block & HOME_LINK_BLOCK) + xfrm_unblock_bule_link(bule); if (hai->home_block & NEMO_FWD_BLOCK) xfrm_unblock_fwd(hai); + if (bule->home_block & NEMO_FWD_BLOCK) + bule_unblock_fwd(bule); } /* check if XFRM policies and states have already been cleaned up */ if (IN6_ARE_ADDR_EQUAL(&bule->hoa, &bule->coa)) return 0; if (!(bule->flags & IP6_MH_BU_HOME)) { - struct bcentry *bce = bcache_get(&bule->hoa, &bule->peer_addr); + struct bcentry *bce = bcache_get(&bule->hoa, &bule->peer_addr, + MCOA_NO_BID); if (bce) { if (bce->type > BCE_NONCE_BLOCK && _xfrm_add_bule_bce(&bule->hoa, @@ -1931,11 +1990,27 @@ } prio = (bule->flags & IP6_MH_BU_HOME ? MIP6_PRIO_HOME_DATA : MIP6_PRIO_RO_BULE_DATA); - set_selector(&bule->peer_addr, &bule->hoa, 0, 0, 0, - bule->home->if_tunnel, &sel); - create_dstopt_tmpl(&tmpls[0], &bule->peer_addr, &bule->hoa); - ret = xfrm_mip_policy_add(&sel, 1, XFRM_POLICY_OUT, - XFRM_POLICY_ALLOW, prio, tmpls, 1); + + /* MCoA: Replaced bule->home->if_tunnel with bule->if_tunnel */ + set_selector(&bule->peer_addr, &bule->hoa, 0, 0, 0, + bule->if_tunnel, &sel); + + /* MCoA TODO1: to update according to the latest draft. + * When MCoA is used, do not use HAO dest. option + * for traffic from the MN/MR to the HA. Tunnel packets + * instead. + */ + if(bule->home->reg_mcoa) { + ret = xfrm_mip_policy_add(&sel, 1, XFRM_POLICY_OUT, + XFRM_POLICY_ALLOW, prio, + NULL, 0); + } else { + create_dstopt_tmpl(&tmpls[0], &bule->peer_addr, &bule->hoa); + ret = xfrm_mip_policy_add(&sel, 1, XFRM_POLICY_OUT, + XFRM_POLICY_ALLOW, prio, + tmpls, 1); + } + if (ret) XDBG("failed to insert outbound policy\n"); @@ -2132,6 +2207,31 @@ xfrm_cn_cleanup(); } +/* blocking all payload packets from MN via a specific interface */ +int xfrm_block_bule_link(struct bulentry *bule) +{ + int ret = 0; + struct xfrm_selector sel; + bule->home_block |= HOME_LINK_BLOCK; + /* allow MN to send NA messages */ + /* RK: such policy is already installed in xfrm_cn_init at startup, + * so we update it (update field to 1) */ + set_selector(&in6addr_any, &in6addr_any, IPPROTO_ICMPV6, + ND_NEIGHBOR_ADVERT, 0, bule->if_coa, &sel); + if ((ret = xfrm_mip_policy_add(&sel, 1 /* update */, XFRM_POLICY_OUT, + XFRM_POLICY_ALLOW, + MIP6_PRIO_HOME_SIG_ANY, NULL, 0))) + return ret; + + /* block any packets from HoA to the CN */ + set_selector(&in6addr_any, &in6addr_any, 0, 0, 0, bule->if_coa, &sel); + if ((ret = xfrm_mip_policy_add(&sel, 0, XFRM_POLICY_OUT, + XFRM_POLICY_BLOCK, + MIP6_PRIO_HOME_BLOCK, NULL, 0))) + return ret; + return ret; +} + /* blocking all payload packets from MN */ int xfrm_block_link(struct home_addr_info *hai) { @@ -2149,6 +2249,21 @@ return ret; } +void xfrm_unblock_bule_link(struct bulentry *bule) +{ + struct xfrm_selector sel; + set_selector(&in6addr_any, &in6addr_any, 0, 0, 0, bule->if_coa, &sel); + xfrm_mip_policy_del(&sel, XFRM_POLICY_OUT); + /* RK: instead of deleting the policy, update it to its previous state + * (the one installed in xfrm_cn_init) */ + set_selector(&in6addr_any, &in6addr_any, IPPROTO_ICMPV6, + ND_NEIGHBOR_ADVERT, 0, 0, &sel); + if (xfrm_mip_policy_add(&sel, 1 /* update */, XFRM_POLICY_OUT, XFRM_POLICY_ALLOW, + MIP6_PRIO_NO_RO_SIG_ANY, NULL, 0) < 0) + XDBG("Could not update NA policy\n"); + bule->home_block &= ~HOME_LINK_BLOCK; +} + void xfrm_unblock_link(struct home_addr_info *hai) { struct xfrm_selector sel; diff -Nur mipv6-daemon-umip-0.4-nepl/src/xfrm.h mipv6-daemon-umip-0.4-nepl-mcoa/src/xfrm.h --- mipv6-daemon-umip-0.4-nepl/src/xfrm.h 2007-10-22 15:15:10.000000000 +0200 +++ mipv6-daemon-umip-0.4-nepl-mcoa/src/xfrm.h 2007-10-23 10:30:03.000000000 +0200 @@ -51,7 +51,8 @@ int xfrm_add_bce(const struct in6_addr *our_addr, const struct in6_addr *peer_addr, const struct in6_addr *coa, - int replace); + int replace, + int create); void xfrm_del_bce(const struct in6_addr *our_addr, const struct in6_addr *peer_addr); @@ -82,7 +83,9 @@ int cn_wildrecv_bu_pol_add(void); void cn_wildrecv_bu_pol_del(void); +int xfrm_block_bule_link(struct bulentry *bule); int xfrm_block_link(struct home_addr_info *hai); +void xfrm_unblock_bule_link(struct bulentry *bule); void xfrm_unblock_link(struct home_addr_info *hai); int xfrm_block_hoa(struct home_addr_info *hai);